1# Copyright 2020 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 time 7 8from autotest_lib.client.common_lib import error 9from autotest_lib.server.cros.faft.firmware_test import FirmwareTest 10 11 12class firmware_ECChargingState(FirmwareTest): 13 """ 14 Type-C servo-v4 based EC charging state test. 15 """ 16 version = 1 17 18 # The delay to wait for the AC state to update. 19 AC_STATE_UPDATE_DELAY = 3 20 21 # We wait for up to 3 hrs for the battery to report fully charged. 22 FULL_CHARGE_TIMEOUT = 60 * 60 * 3 23 24 # The period to check battery state while charging. 25 CHECK_BATT_STATE_WAIT = 60 26 27 def initialize(self, host, cmdline_args): 28 super(firmware_ECChargingState, self).initialize(host, cmdline_args) 29 if not self.check_ec_capability(['battery', 'charging']): 30 raise error.TestNAError("Nothing needs to be tested on this DUT") 31 if self.servo.get_servo_version() != 'servo_v4_with_ccd_cr50': 32 raise error.TestNAError("This test can only be run with servo-v4 " 33 "+ CCD. If you don't have a Type-C servo-v4, please run " 34 "the test manually.") 35 if host.is_ac_connected() != True: 36 raise error.TestFail("This test must be run with AC power.") 37 self.switcher.setup_mode('normal') 38 self.ec.send_command("chan save") 39 self.ec.send_command("chan 0") 40 self.set_dut_low_power_idle_delay(20) 41 42 def cleanup(self): 43 try: 44 self.ec.send_command("chan restore") 45 self.restore_dut_low_power_idle_delay() 46 except Exception as e: 47 logging.error("Caught exception: %s", str(e)) 48 super(firmware_ECChargingState, self).cleanup() 49 50 def check_ac_state(self): 51 """Check if AC is plugged.""" 52 ac_state = int(self.ec.send_command_get_output("chgstate", 53 ["ac\s*=\s*(0|1)\s*"])[0][1]) 54 if ac_state == 1: 55 return 'on' 56 elif ac_state == 0: 57 return 'off' 58 else: 59 return 'unknown' 60 61 def get_battery_level(self): 62 """Get battery charge percentage.""" 63 batt_level = int(self.ec.send_command_get_output("battery", 64 ["Charge:\s+(\d+)\s+"])[0][1]) 65 return batt_level 66 67 def run_once(self, host): 68 """Execute the main body of the test.""" 69 70 if host.is_ac_connected() != True: 71 raise error.TestFail("This test must be run with AC power.") 72 73 logging.info("Suspend, unplug AC, and then wake up the device.") 74 self.suspend() 75 self.switcher.wait_for_client_offline() 76 77 # Call set_servo_v4_role_to_snk() instead of directly setting 78 # servo_v4 role to snk, so servo_v4_role can be recovered to 79 # default src in cleanup(). 80 self.set_servo_v4_role_to_snk() 81 time.sleep(self.AC_STATE_UPDATE_DELAY) 82 83 # Verify servo v4 is sinking power. 84 if self.check_ac_state() != 'off': 85 raise error.TestFail("Fail to unplug AC.") 86 87 self.servo.power_normal_press() 88 self.switcher.wait_for_client() 89 90 batt_state = host.get_battery_state() 91 if batt_state != 'Discharging': 92 raise error.TestFail("Wrong battery state. Expected: " 93 "Discharging, got: %s." % batt_state) 94 95 logging.info("Suspend, plug AC, and then wake up the device.") 96 self.suspend() 97 self.switcher.wait_for_client_offline() 98 self.servo.set_servo_v4_role('src') 99 time.sleep(self.AC_STATE_UPDATE_DELAY) 100 101 # Verify servo v4 is sourcing power. 102 if self.check_ac_state() != 'on': 103 raise error.TestFail("Fail to plug AC.") 104 105 self.servo.power_normal_press() 106 self.switcher.wait_for_client() 107 108 batt_state = host.get_battery_state() 109 if batt_state != 'Charging' and batt_state != 'Fully charged': 110 raise error.TestFail("Wrong battery state. Expected: " 111 "Charging/Fully charged, got: %s." % batt_state) 112 113 logging.info("Keep charging until the battery reports fully charged.") 114 deadline = time.time() + self.FULL_CHARGE_TIMEOUT 115 while time.time() < deadline: 116 batt_state = host.get_battery_state() 117 if batt_state == 'Fully charged': 118 logging.info("The battery reports fully charged.") 119 return 120 elif batt_state == 'Charging': 121 logging.info("Wait for the battery to be fully charged. " 122 "The current battery level is %d%%.", 123 self.get_battery_level()) 124 else: 125 raise error.TestFail("The battery state is %s. " 126 "Is AC unplugged?", batt_state) 127 time.sleep(self.CHECK_BATT_STATE_WAIT) 128 129 raise error.TestFail("The battery does not report fully charged " 130 "before timeout is reached. The final battery level is %d%%.", 131 self.get_battery_level()) 132