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
6
7from autotest_lib.client.common_lib import error
8from autotest_lib.server.cros.faft.firmware_test import FirmwareTest
9
10class firmware_ECCharging(FirmwareTest):
11    """
12    Servo based EC charging control test.
13    """
14    version = 1
15
16    # Threshold of trickle charging current in mA
17    TRICKLE_CHARGE_THRESHOLD = 100
18
19    def initialize(self, host, cmdline_args):
20        super(firmware_ECCharging, self).initialize(host, cmdline_args)
21        # Don't bother if there is no Chrome EC.
22        if not self.check_ec_capability():
23            raise error.TestNAError("Nothing needs to be tested on this device")
24        # Only run in normal mode
25        self.switcher.setup_mode('normal')
26        self.ec.send_command("chan 0")
27
28
29    def cleanup(self):
30        try:
31            self.ec.send_command("chan 0xffffffff")
32        except Exception as e:
33            logging.error("Caught exception: %s", str(e))
34        super(firmware_ECCharging, self).cleanup()
35
36
37    def _get_battery_desired_voltage(self):
38        """Get battery desired voltage value."""
39        voltage = int(self.ec.send_command_get_output("battery",
40                ["V-desired:\s+0x[0-9a-f]*\s+=\s+(\d+)\s+mV"])[0][1])
41        logging.info("Battery desired voltage = %d mV", voltage)
42        return voltage
43
44
45    def _get_battery_desired_current(self):
46        """Get battery desired current value."""
47        current = int(self.ec.send_command_get_output("battery",
48                ["I-desired:\s+0x[0-9a-f]*\s+=\s+(\d+)\s+mA"])[0][1])
49        logging.info("Battery desired current = %d mA", current)
50        return current
51
52
53    def _get_battery_actual_voltage(self):
54        """Get the actual voltage from charger to battery."""
55        voltage = int(self.ec.send_command_get_output("battery",
56                ["V:\s+0x[0-9a-f]*\s+=\s+(\d+)\s+mV"])[0][1])
57        logging.info("Battery actual voltage = %d mV", voltage)
58        return voltage
59
60
61    def _get_battery_actual_current(self):
62        """Get the actual current from charger to battery."""
63        current = int(self.ec.send_command_get_output("battery",
64                ["I:\s+0x[0-9a-f]*\s+=\s+([0-9-]+)\s+mA"])[0][1])
65        logging.info("Battery actual current = %d mA", current)
66        return current
67
68
69    def _get_battery_charge(self):
70        """Get battery charge state."""
71        charge = int(self.ec.send_command_get_output("battery",
72                ["Charge:\s+(\d+)\s+"])[0][1])
73        logging.info("Battery charge = %d %%", charge)
74        return charge
75
76
77    def _get_charger_target_voltage(self):
78        """Get target charging voltage set in charger."""
79        voltage = int(self.ec.send_command_get_output("charger",
80                ["V_batt:\s+(\d+)\s"])[0][1])
81        logging.info("Charger target voltage = %d mV", voltage)
82        return voltage
83
84
85    def _get_charger_target_current(self):
86        """Get target charging current set in charger."""
87        current = int(self.ec.send_command_get_output("charger",
88                ["I_batt:\s+(\d+)\s"])[0][1])
89        logging.info("Charger target current = %d mA", current)
90        return current
91
92
93    def _get_trickle_charging(self):
94        """Check if we are trickle charging battery."""
95        return (self._get_battery_desired_current() <
96                self.TRICKLE_CHARGE_THRESHOLD)
97
98
99    def _check_target_value(self):
100        """Check charger target values are correct.
101
102        Raise:
103          error.TestFail: Raised when check fails.
104        """
105        if (self._get_charger_target_voltage() >=
106                1.05 * self._get_battery_desired_voltage()):
107            raise error.TestFail("Charger target voltage is too high.")
108        if (self._get_charger_target_current() >=
109                1.05 * self._get_battery_desired_current()):
110            raise error.TestFail("Charger target current is too high.")
111
112
113    def _check_actual_value(self):
114        """Check actual voltage/current values are correct.
115
116        Raise:
117          error.TestFail: Raised when check fails.
118        """
119        if (self._get_battery_actual_voltage() >=
120                1.05 * self._get_charger_target_voltage()):
121            raise error.TestFail("Battery actual voltage is too high.")
122        if (self._get_battery_actual_current() >=
123                1.05 * self._get_charger_target_current()):
124            raise error.TestFail("Battery actual current is too high.")
125
126
127    def run_once(self):
128        """Execute the main body of the test.
129        """
130        if not self.check_ec_capability(['battery', 'charging']):
131            raise error.TestNAError("Nothing needs to be tested on this device")
132        if self._get_battery_charge() == 100:
133            logging.info("Battery is full. Unable to test.")
134            return
135        if self._get_trickle_charging():
136            logging.info("Trickling charging battery. Unable to test.")
137            return
138        if self._get_battery_actual_current() < 0:
139            raise error.TestFail("This test must be run with AC power.")
140
141        logging.info("Checking charger target values...")
142        self._check_target_value()
143
144        logging.info("Checking battery actual values...")
145        self._check_actual_value()
146