blob: b3dd97ddad340ae8af69ac9f7812760ff9975792 [file] [log] [blame]
Derek Becketta7f8c4f2020-10-01 10:27:38 -07001# Lint as: python2, python3
J. Richard Barnette384056b2012-04-16 11:04:46 -07002# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -07003# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5#
6# Expects to be run in an environment with sudo and no interactive password
7# prompt, such as within the Chromium OS development chroot.
8
Congbin Guoa1f9cba2018-07-03 11:36:59 -07009import ast
10import logging
Vadim Bendeburyb80ba592012-12-07 15:02:34 -080011import os
Congbin Guoa1f9cba2018-07-03 11:36:59 -070012import re
Dana Goyettec9e7bef2020-07-21 13:32:30 -070013import six
Derek Becketta7f8c4f2020-10-01 10:27:38 -070014import six.moves.xmlrpc_client
15import six.moves.http_client
16import time
Dana Goyettec9e7bef2020-07-21 13:32:30 -070017
Simran Basi741b5d42012-05-18 11:27:15 -070018from autotest_lib.client.common_lib import error
Mary Ruthvenecf12712019-06-26 17:36:21 -070019from autotest_lib.client.common_lib import lsbrelease_utils
Dana Goyette0d92eaf2020-08-07 22:23:10 -070020from autotest_lib.client.common_lib import seven
Congbin Guo42427612019-02-12 10:22:06 -080021from autotest_lib.server import utils as server_utils
Ricky Liangc31aab32014-07-03 16:23:29 +080022from autotest_lib.server.cros.servo import firmware_programmer
Brent Peterson0781db72020-02-13 13:13:37 -080023from autotest_lib.server.cros.faft.utils.config import Config as FAFTConfig
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070024
J. Richard Barnette41320ee2013-03-11 13:00:13 -070025
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -070026# Regex to match XMLRPC errors due to a servod control not existing.
Dana Goyette0d92eaf2020-08-07 22:23:10 -070027# Servod uses both 'No control %s' and 'No control named %s' in exceptions.
28NO_CONTROL_RE = re.compile(r'No control(?: named)? (?P<name>\w*\.?\w*)')
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -070029
Ruben Rodriguez Buchillon03fded92020-04-13 19:55:10 -070030# Please see servo/drv/pty_driver.py for error messages to match.
31
32# This common prefix can apply to all subtypes of console errors.
Garry Wang53fc8f32020-09-18 13:30:08 -070033# The first portion is an optional qualifier of the type
34# of error that occurred. Each error is or'd.
Ruben Rodriguez Buchillon03fded92020-04-13 19:55:10 -070035CONSOLE_COMMON_RE = (r'((Timeout waiting for response|'
36 r'Known error [\w\'\".\s]+). )?'
37 # The second portion is an optional name for the console
38 # source
39 r'(\w+\: )?')
Ruben Rodriguez Buchillon5f7ee682019-09-30 12:50:09 -070040
Ruben Rodriguez Buchillon2afbeea2020-02-19 15:24:20 -080041# Regex to match XMLRPC errors due to a console being unresponsive.
Ruben Rodriguez Buchillon03fded92020-04-13 19:55:10 -070042NO_CONSOLE_OUTPUT_RE = re.compile(r'%sNo data was sent from the pty\.' %
43 CONSOLE_COMMON_RE)
Ruben Rodriguez Buchillon2afbeea2020-02-19 15:24:20 -080044
45
Ruben Rodriguez Buchillon348d93f2020-02-19 15:30:22 -080046# Regex to match XMLRPC errors due to a console control failing, but the
47# underlying Console being responsive.
Ruben Rodriguez Buchillon03fded92020-04-13 19:55:10 -070048CONSOLE_MISMATCH_RE = re.compile(r'%sThere was output:' % CONSOLE_COMMON_RE)
Ruben Rodriguez Buchillon348d93f2020-02-19 15:30:22 -080049
50
Ruben Rodriguez Buchillon5f7ee682019-09-30 12:50:09 -070051# The minimum voltage on the charger port on servo v4 that is expected. This is
52# to query whether a charger is plugged into servo v4 and thus pd control
53# capabilities can be used.
54V4_CHG_ATTACHED_MIN_VOLTAGE_MV = 4400
55
Ruben Rodriguez Buchillon2afbeea2020-02-19 15:24:20 -080056
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -070057class ControlUnavailableError(error.TestFail):
58 """Custom error class to indicate a control is unavailable on servod."""
59 pass
60
61
Ruben Rodriguez Buchillon348d93f2020-02-19 15:30:22 -080062class ConsoleError(error.TestFail):
63 """Common error class for servod console-back control failures."""
64 pass
65
66
67class UnresponsiveConsoleError(ConsoleError):
Ruben Rodriguez Buchillon2afbeea2020-02-19 15:24:20 -080068 """Error for: A console control fails for lack of console output."""
69 pass
70
71
Ruben Rodriguez Buchillon348d93f2020-02-19 15:30:22 -080072class ResponsiveConsoleError(ConsoleError):
73 """Error for: A console control fails but console is responsive."""
74 pass
75
76
Derek Becketta7f8c4f2020-10-01 10:27:38 -070077class ServodBadResponse(six.moves.http_client.BadStatusLine):
Dana Goyette0d92eaf2020-08-07 22:23:10 -070078 """Indicates a bad HTTP response from servod"""
79
Dana Goyettec9e7bef2020-07-21 13:32:30 -070080 def __init__(self, when, line):
81 """
82
83 @param when: Description of the operation being performed (get/set)
Dana Goyette0d92eaf2020-08-07 22:23:10 -070084 @param line: The line that came from the server, often an empty string.
Dana Goyettec9e7bef2020-07-21 13:32:30 -070085 """
Dana Goyette0d92eaf2020-08-07 22:23:10 -070086 super(ServodBadResponse, self).__init__(line)
Dana Goyettec9e7bef2020-07-21 13:32:30 -070087 self.when = when
88
89 def __str__(self):
90 """String representation of the exception"""
Dana Goyette0d92eaf2020-08-07 22:23:10 -070091 return '%s -- StatusLine=%s' % (self.when, self.line)
Dana Goyettec9e7bef2020-07-21 13:32:30 -070092
93
Dana Goyette0d92eaf2020-08-07 22:23:10 -070094class ServodEmptyResponse(ServodBadResponse):
95 """Indicates an empty response from servod, possibly because it exited."""
96 pass
Dana Goyettec9e7bef2020-07-21 13:32:30 -070097
Dana Goyette0d92eaf2020-08-07 22:23:10 -070098
99class ServodConnectionError(seven.SOCKET_ERRORS[0]):
100 """Indicates socket errors seen during communication with servod"""
Dana Goyettec9e7bef2020-07-21 13:32:30 -0700101
102 def __init__(self, when, errno, strerror, filename):
Dana Goyette0d92eaf2020-08-07 22:23:10 -0700103 """Instance initializer
104
105 The filename is used to add details to the exception message:
106 [Errno 104] Connection reset by peer: "<Servo 'ipaddr:9999'>"
Dana Goyettec9e7bef2020-07-21 13:32:30 -0700107
108 @param when: Description of the operation being performed at the time
109 @param errno: errno value, such as ECONNRESET
110 @param strerror: OS-provided description ("connection reset by peer")
111 @param filename: Something to report as a path, such as a socket address
112 """
113 # [Errno 104] [Setting ctrl:val] Connection reset by peer: <Servo...
114 self.when = when
115 super(ServodConnectionError, self).__init__(errno, strerror, filename)
116
117 def __str__(self):
118 """String representation of the exception"""
Dana Goyette0d92eaf2020-08-07 22:23:10 -0700119 return '%s -- [Errno %d] %s: %r' % (self.when, self.errno,
120 self.strerror, self.filename)
121
122
123# TODO: once in python 3, inherit from AbstractContextManager
124class _WrapServoErrors(object):
125 """
126 Wrap an operation, replacing BadStatusLine and socket.error with
127 servo-specific versions, and extracting exception info from xmlrplib.Fault.
128
129 @param servo_name: The servo object, used to add the servo name to errors.
130 See the ServodConnectionError docstring.
131 @param description: String to use when describing what was being done
132 @raise ServodBadStatusLine: if exception is a httplib.BadStatusLine
133 @raise ServodSocketError: if exception is a socket.error
134 @raise ControlUnavailableError: if Fault matches NO_CONTROL_RE
135 @raise UnresponsiveConsoleError: if Fault matches NO_CONSOLE_OUTPUT_RE
136 @raise ResponsiveConsoleError: if Fault matches CONSOLE_MISMATCH_RE
137 """
138
139 def __init__(self, servo, description):
140 self.servo_name = str(servo)
141 self.description = description
142
143 @staticmethod
144 def _get_xmlrpclib_exception(xmlexc):
145 """Get meaningful exception string from xmlrpc.
146
147 Args:
148 xmlexc: xmlrpclib.Fault object
149
150 xmlrpclib.Fault.faultString has the following format:
151
152 <type 'exception type'>:'actual error message'
153
154 Parse and return the real exception from the servod side instead of the
155 less meaningful one like,
156 <Fault 1: "<type 'exceptions.AttributeError'>:'tca6416' object has no
157 attribute 'hw_driver'">
158
159 Returns:
160 string of underlying exception raised in servod.
161 """
162 return re.sub('^.*>:', '', xmlexc.faultString)
163
Brent Peterson8eda97f2020-11-12 13:10:40 -0500164 @staticmethod
165 def _log_exception(exc_type, exc_val, exc_tb):
166 """Log exception information"""
167 if exc_val is not None:
168 logging.debug(
169 'Wrapped exception:', exc_info=(exc_type, exc_val, exc_tb))
170
Dana Goyette0d92eaf2020-08-07 22:23:10 -0700171 def __enter__(self):
172 """Enter the context"""
173 return self
174
175 def __exit__(self, exc_type, exc_val, exc_tb):
176 """Exit the context, handling the exception if there was one"""
177 try:
Derek Becketta7f8c4f2020-10-01 10:27:38 -0700178 if isinstance(exc_val, six.moves.http_client.BadStatusLine):
Brent Peterson8eda97f2020-11-12 13:10:40 -0500179 self._log_exception(exc_type, exc_val, exc_tb)
Dana Goyette0d92eaf2020-08-07 22:23:10 -0700180 if exc_val.line in ('', "''"):
181 err = ServodEmptyResponse(self.description, exc_val.line)
182 else:
183 err = ServodBadResponse(self.description, exc_val.line)
184 six.reraise(err.__class__, err, exc_tb)
185
186 if isinstance(exc_val, seven.SOCKET_ERRORS):
Brent Peterson8eda97f2020-11-12 13:10:40 -0500187 self._log_exception(exc_type, exc_val, exc_tb)
Dana Goyette0d92eaf2020-08-07 22:23:10 -0700188 err = ServodConnectionError(self.description, exc_val.args[0],
189 exc_val.args[1], self.servo_name)
190 six.reraise(err.__class__, err, exc_tb)
191
Derek Becketta7f8c4f2020-10-01 10:27:38 -0700192 if isinstance(exc_val, six.moves.xmlrpc_client.Fault):
Dana Goyette0d92eaf2020-08-07 22:23:10 -0700193 err_str = self._get_xmlrpclib_exception(exc_val)
194 err_msg = '%s :: %s' % (self.description, err_str)
195 unknown_ctrl = re.search(NO_CONTROL_RE, err_str)
196 if not unknown_ctrl:
197 # Log the full text for errors, except unavailable controls.
Brent Peterson8eda97f2020-11-12 13:10:40 -0500198 self._log_exception(exc_type, exc_val, exc_tb)
Dana Goyette0d92eaf2020-08-07 22:23:10 -0700199 logging.debug(err_msg)
200 if unknown_ctrl:
201 # The error message for unavailable controls is huge, since
202 # it reports all known controls. Don't log the full text.
203 unknown_ctrl_name = unknown_ctrl.group('name')
Evan Benn36d60592020-10-16 11:06:20 +1100204 logging.debug('%s :: No control named %r',
Dana Goyette0d92eaf2020-08-07 22:23:10 -0700205 self.description, unknown_ctrl_name)
206 err = ControlUnavailableError(
207 'No control named %r' % unknown_ctrl_name)
208 elif re.search(NO_CONSOLE_OUTPUT_RE, err_str):
209 err = UnresponsiveConsoleError(
210 'Console not printing output. %s.' %
211 self.description)
212 elif re.search(CONSOLE_MISMATCH_RE, err_str):
213 err = ResponsiveConsoleError(
214 'Control failed but console alive. %s.' %
215 self.description)
216 else:
217 err = error.TestFail(err_msg)
218 six.reraise(err.__class__, err, exc_tb)
219 finally:
220 del exc_tb
Dana Goyettec9e7bef2020-07-21 13:32:30 -0700221
222
Brent Peterson8df4a5b2020-03-06 11:50:49 -0800223def _extract_image_from_tarball(tarball, dest_dir, image_candidates, timeout):
Congbin Guo42427612019-02-12 10:22:06 -0800224 """Try extracting the image_candidates from the tarball.
225
226 @param tarball: The path of the tarball.
227 @param dest_path: The path of the destination.
228 @param image_candidates: A tuple of the paths of image candidates.
Brent Peterson8df4a5b2020-03-06 11:50:49 -0800229 @param timeout: Time to wait in seconds before timing out.
Congbin Guo42427612019-02-12 10:22:06 -0800230
231 @return: The first path from the image candidates, which succeeds, or None
232 if all the image candidates fail.
233 """
Brent Peterson1cb623a2020-01-09 13:14:28 -0800234
235 # Create the firmware_name subdirectory if it doesn't exist
236 if not os.path.exists(dest_dir):
237 os.mkdir(dest_dir)
238
Brent Petersoncf7ac142020-02-12 14:38:35 -0800239 # Generate a list of all tarball files
Brent Peterson8df4a5b2020-03-06 11:50:49 -0800240 stdout = server_utils.system_output('tar tf %s' % tarball,
241 timeout=timeout,
242 ignore_status=True)
243 tarball_files = stdout.splitlines()
Brent Petersoncf7ac142020-02-12 14:38:35 -0800244
245 # Check if image candidates are in the list of tarball files
Congbin Guo42427612019-02-12 10:22:06 -0800246 for image in image_candidates:
Brent Petersoncf7ac142020-02-12 14:38:35 -0800247 if image in tarball_files:
248 # Extract and return the first image candidate found
Brent Peterson8df4a5b2020-03-06 11:50:49 -0800249 tar_cmd = 'tar xf %s -C %s %s' % (tarball, dest_dir, image)
250 status = server_utils.system(tar_cmd,
251 timeout=timeout,
252 ignore_status=True)
Brent Petersoncf7ac142020-02-12 14:38:35 -0800253 if status == 0:
254 return image
Congbin Guo42427612019-02-12 10:22:06 -0800255 return None
256
257
J. Richard Barnette0c9c5882014-06-11 15:27:05 -0700258class _PowerStateController(object):
259
260 """Class to provide board-specific power operations.
261
262 This class is responsible for "power on" and "power off"
263 operations that can operate without making assumptions in
264 advance about board state. It offers an interface that
265 abstracts out the different sequences required for different
266 board types.
267
268 """
J. Richard Barnette0c9c5882014-06-11 15:27:05 -0700269 # Constants acceptable to be passed for the `rec_mode` parameter
270 # to power_on().
271 #
272 # REC_ON: Boot the DUT in recovery mode, i.e. boot from USB or
273 # SD card.
274 # REC_OFF: Boot in normal mode, i.e. boot from internal storage.
275
276 REC_ON = 'rec'
277 REC_OFF = 'on'
Shelley Chen65938622018-05-16 07:45:54 -0700278 REC_ON_FORCE_MRC = 'rec_force_mrc'
J. Richard Barnette0c9c5882014-06-11 15:27:05 -0700279
280 # Delay in seconds needed between asserting and de-asserting
281 # warm reset.
282 _RESET_HOLD_TIME = 0.5
283
Ruben Rodriguez Buchillonaed7c892020-02-06 13:11:14 -0800284
J. Richard Barnette0c9c5882014-06-11 15:27:05 -0700285 def __init__(self, servo):
286 """Initialize the power state control.
287
288 @param servo Servo object providing the underlying `set` and `get`
289 methods for the target controls.
290
291 """
292 self._servo = servo
Ruben Rodriguez Buchillonaed7c892020-02-06 13:11:14 -0800293 self.supported = self._servo.has_control('power_state')
Patrick Georgib3bcb7f2020-08-05 17:34:03 +0200294 self.last_rec_mode = self.REC_OFF
Ruben Rodriguez Buchillonaed7c892020-02-06 13:11:14 -0800295 if not self.supported:
296 logging.info('Servo setup does not support power-state operations. '
297 'All power-state calls will lead to error.TestFail')
J. Richard Barnette0c9c5882014-06-11 15:27:05 -0700298
Greg Edelstonb73cddd2020-02-11 11:06:29 -0700299 def _check_supported(self):
300 """Throw an error if dts mode control is not supported."""
301 if not self.supported:
302 raise error.TestFail('power_state controls not supported')
303
J. Richard Barnette0c9c5882014-06-11 15:27:05 -0700304 def reset(self):
305 """Force the DUT to reset.
306
307 The DUT is guaranteed to be on at the end of this call,
308 regardless of its previous state, provided that there is
309 working OS software. This also guarantees that the EC has
310 been restarted.
311
312 """
Greg Edelstonb73cddd2020-02-11 11:06:29 -0700313 self._check_supported()
J. Richard Barnette0c9c5882014-06-11 15:27:05 -0700314 self._servo.set_nocheck('power_state', 'reset')
315
Otabek Kasimov8ff1ce72020-04-17 20:17:59 -0700316 def cr50_reset(self):
317 """Force the DUT to reset.
318
319 The DUT is guaranteed to be on at the end of this call,
320 regardless of its previous state, provided that there is
321 working OS software. This also guarantees that the EC has
322 been restarted. Works only for ccd connections.
323
324 """
325 self._check_supported()
326 self._servo.set_nocheck('power_state', 'cr50_reset')
327
J. Richard Barnette0c9c5882014-06-11 15:27:05 -0700328 def warm_reset(self):
329 """Apply warm reset to the DUT.
330
331 This asserts, then de-asserts the 'warm_reset' signal.
332 Generally, this causes the board to restart.
333
334 """
Ravutappa951ca432019-05-15 09:52:52 -0700335 # TODO: warm_reset support has added to power_state.py. Once it
336 # available to labstation remove fallback method.
Greg Edelstonb73cddd2020-02-11 11:06:29 -0700337 self._check_supported()
Ravutappa951ca432019-05-15 09:52:52 -0700338 try:
339 self._servo.set_nocheck('power_state', 'warm_reset')
340 except error.TestFail as err:
341 logging.info("Fallback to warm_reset control method")
342 self._servo.set_get_all(['warm_reset:on',
J. Richard Barnette0c9c5882014-06-11 15:27:05 -0700343 'sleep:%.4f' % self._RESET_HOLD_TIME,
344 'warm_reset:off'])
Gregory Nisbet1f18f882020-07-07 10:31:59 -0700345
J. Richard Barnette0c9c5882014-06-11 15:27:05 -0700346 def power_off(self):
347 """Force the DUT to power off.
348
349 The DUT is guaranteed to be off at the end of this call,
350 regardless of its previous state, provided that there is
351 working EC and boot firmware. There is no requirement for
352 working OS software.
353
354 """
Greg Edelstonb73cddd2020-02-11 11:06:29 -0700355 self._check_supported()
J. Richard Barnette0c9c5882014-06-11 15:27:05 -0700356 self._servo.set_nocheck('power_state', 'off')
357
358 def power_on(self, rec_mode=REC_OFF):
359 """Force the DUT to power on.
360
361 Prior to calling this function, the DUT must be powered off,
362 e.g. with a call to `power_off()`.
363
364 At power on, recovery mode is set as specified by the
365 corresponding argument. When booting with recovery mode on, it
366 is the caller's responsibility to unplug/plug in a bootable
367 external storage device.
368
369 If the DUT requires a delay after powering on but before
370 processing inputs such as USB stick insertion, the delay is
371 handled by this method; the caller is not responsible for such
372 delays.
373
374 @param rec_mode Setting of recovery mode to be applied at
375 power on. default: REC_OFF aka 'off'
376
377 """
Greg Edelstonb73cddd2020-02-11 11:06:29 -0700378 self._check_supported()
J. Richard Barnette0c9c5882014-06-11 15:27:05 -0700379 self._servo.set_nocheck('power_state', rec_mode)
Patrick Georgib3bcb7f2020-08-05 17:34:03 +0200380 self.last_rec_mode = rec_mode
381
382 def retry_power_on(self):
383 """Retry powering on the DUT.
384
385 After power_on(...) the system might not come up reliably, although
386 the reasons aren't known yet. This function retries turning on the
387 system again, trying to bring it in the last state that power_on()
388 attempted to reach.
389 """
390 self._check_supported()
391 self._servo.set_nocheck('power_state', self.last_rec_mode)
J. Richard Barnette0c9c5882014-06-11 15:27:05 -0700392
393
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700394class _Uart(object):
Congbin Guo0f00be82019-04-18 17:51:14 -0700395 """Class to capture UART streams of CPU, EC, Cr50, etc."""
Mary Ruthvenad0e5272020-08-12 16:00:58 -0700396 _UartToCapture = (
397 'cpu',
398 'cr50',
399 'ec',
400 'servo_micro',
401 'servo_v4',
402 'usbpd',
403 'ccd_cr50.ec',
404 'ccd_cr50.cpu',
405 'ccd_cr50.cr50'
406 )
407
Congbin Guo0f00be82019-04-18 17:51:14 -0700408
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700409 def __init__(self, servo):
410 self._servo = servo
411 self._streams = []
Congbin Guo0f00be82019-04-18 17:51:14 -0700412 self.logs_dir = None
Congbin Guofe4d4e82019-04-18 17:51:14 -0700413
Congbin Guo0f00be82019-04-18 17:51:14 -0700414 def _start_stop_capture(self, uart, start):
415 """Helper function to start/stop capturing on specified UART.
416
417 @param uart: The UART name to start/stop capturing.
418 @param start: True to start capturing, otherwise stop.
419
Ruben Rodriguez Buchillonecb525f2019-09-03 14:52:14 -0700420 @returns True if the operation completes successfully.
421 False if the UART capturing is not supported or failed due to
422 an error.
Congbin Guo0f00be82019-04-18 17:51:14 -0700423 """
424 logging.debug('%s capturing %s UART.', 'Start' if start else 'Stop',
425 uart)
Ruben Rodriguez Buchillonb0e2a9f2019-08-12 12:40:44 -0700426 uart_cmd = '%s_uart_capture' % uart
Ruben Rodriguez Buchillonecb525f2019-09-03 14:52:14 -0700427 target_level = 'on' if start else 'off'
428 level = None
Ruben Rodriguez Buchillonb0e2a9f2019-08-12 12:40:44 -0700429 if self._servo.has_control(uart_cmd):
Ruben Rodriguez Buchillon9f485642019-08-21 14:32:22 -0700430 # Do our own implementation of set() here as not_applicable
431 # should also count as a valid control.
Ruben Rodriguez Buchillonecb525f2019-09-03 14:52:14 -0700432 logging.debug('Trying to set %s to %s.', uart_cmd, target_level)
433 try:
434 self._servo.set_nocheck(uart_cmd, target_level)
435 level = self._servo.get(uart_cmd)
436 except error.TestFail as e:
437 # Any sort of test failure here should not stop the test. This
438 # is just to capture more output. Log and move on.
439 logging.warning('Failed to set %s to %s. %s. Ignoring.',
440 uart_cmd, target_level, str(e))
441 if level == target_level:
Garry Wang53fc8f32020-09-18 13:30:08 -0700442 logging.debug('Managed to set %s to %s.', uart_cmd, level)
Ruben Rodriguez Buchillonecb525f2019-09-03 14:52:14 -0700443 else:
Garry Wang53fc8f32020-09-18 13:30:08 -0700444 logging.debug('Failed to set %s to %s. Got %s.', uart_cmd,
445 target_level, level)
Ruben Rodriguez Buchillonecb525f2019-09-03 14:52:14 -0700446 return level == target_level
Congbin Guo0f00be82019-04-18 17:51:14 -0700447
448 def start_capture(self):
449 """Start capturing UART streams."""
450 for uart in self._UartToCapture:
Mary Ruthven20e84d12021-03-18 11:15:51 -0700451 # Always try to start the uart. Only add it to _streams if it's not
452 # in the list.
453 if (self._start_stop_capture(uart, True)
454 and uart not in self._streams):
Mary Ruthvenf2dd7a42020-08-12 16:18:24 -0700455 self._streams.append(uart)
456
457 def get_logfile(self, uart):
458 """Return the path to the uart logfile or none if logs_dir isn't set."""
459 if not self.logs_dir:
460 return None
461 return os.path.join(self.logs_dir, '%s_uart.txt' % uart)
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700462
Congbin Guofc3b8962019-03-22 17:38:46 -0700463 def dump(self):
464 """Dump UART streams to log files accordingly."""
Congbin Guo0f00be82019-04-18 17:51:14 -0700465 if not self.logs_dir:
Congbin Guofc3b8962019-03-22 17:38:46 -0700466 return
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700467
Mary Ruthvenf2dd7a42020-08-12 16:18:24 -0700468 for uart in self._streams:
469 logfile_fullname = self.get_logfile(uart)
470 stream = '%s_uart_stream' % uart
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700471 try:
472 content = self._servo.get(stream)
473 except Exception as err:
474 logging.warn('Failed to get UART log for %s: %s', stream, err)
475 continue
476
Kuang-che Wu12192fc2019-08-31 09:55:00 +0800477 if content == 'not_applicable':
478 logging.warn('%s is not applicable', stream)
479 continue
480
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700481 # The UART stream may contain non-printable characters, and servo
482 # returns it in string representation. We use `ast.leteral_eval`
483 # to revert it back.
484 with open(logfile_fullname, 'a') as fd:
Kuang-che Wu12192fc2019-08-31 09:55:00 +0800485 try:
486 fd.write(ast.literal_eval(content))
487 except ValueError:
488 logging.exception('Invalid value for %s: %r', stream,
489 content)
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700490
491 def stop_capture(self):
492 """Stop capturing UART streams."""
Congbin Guo0f00be82019-04-18 17:51:14 -0700493 for uart in self._UartToCapture:
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700494 try:
Congbin Guo0f00be82019-04-18 17:51:14 -0700495 self._start_stop_capture(uart, False)
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700496 except Exception as err:
497 logging.warn('Failed to stop UART logging for %s: %s', uart,
498 err)
499
500
J. Richard Barnette384056b2012-04-16 11:04:46 -0700501class Servo(object):
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700502
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700503 """Manages control of a Servo board.
504
505 Servo is a board developed by hardware group to aide in the debug and
506 control of various partner devices. Servo's features include the simulation
507 of pressing the power button, closing the lid, and pressing Ctrl-d. This
508 class manages setting up and communicating with a servo demon (servod)
509 process. It provides both high-level functions for common servo tasks and
510 low-level functions for directly setting and reading gpios.
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700511
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700512 """
513
Chrome Bot9a1137d2011-07-19 14:35:00 -0700514 # Power button press delays in seconds.
J. Richard Barnetted2e4cbd2012-06-29 12:18:40 -0700515 #
J. Richard Barnette5383f072012-07-26 17:35:40 -0700516 # The EC specification says that 8.0 seconds should be enough
517 # for the long power press. However, some platforms need a bit
518 # more time. Empirical testing has found these requirements:
519 # Alex: 8.2 seconds
520 # ZGB: 8.5 seconds
521 # The actual value is set to the largest known necessary value.
522 #
523 # TODO(jrbarnette) Being generous is the right thing to do for
524 # existing platforms, but if this code is to be used for
525 # qualification of new hardware, we should be less generous.
Chrome Bot9a1137d2011-07-19 14:35:00 -0700526 SHORT_DELAY = 0.1
J. Richard Barnette5383f072012-07-26 17:35:40 -0700527
Todd Broch31c82502011-08-29 08:14:39 -0700528 # Maximum number of times to re-read power button on release.
Todd Brochcf7c6652012-02-24 13:03:59 -0800529 GET_RETRY_MAX = 10
Chrome Bot9a1137d2011-07-19 14:35:00 -0700530
J. Richard Barnette5383f072012-07-26 17:35:40 -0700531 # Delays to deal with DUT state transitions.
Chrome Bot9a1137d2011-07-19 14:35:00 -0700532 SLEEP_DELAY = 6
533 BOOT_DELAY = 10
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700534
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700535 # Default minimum time interval between 'press' and 'release'
536 # keyboard events.
Vic Yang0aca1c22012-11-19 18:33:56 -0800537 SERVO_KEY_PRESS_DELAY = 0.1
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700538
Tom Wai-Hong Tam93b5c092015-05-14 02:50:43 +0800539 # Time to toggle recovery switch on and off.
540 REC_TOGGLE_DELAY = 0.1
541
Tom Wai-Hong Tamf9ded092015-05-20 05:37:22 +0800542 # Time to toggle development switch on and off.
543 DEV_TOGGLE_DELAY = 0.1
544
Jon Salzc88e5b62011-11-30 14:38:54 +0800545 # Time between an usb disk plugged-in and detected in the system.
Ruben Rodriguez Buchillon11daf2c2020-02-25 12:31:40 -0800546 USB_DETECTION_DELAY = 5
Jon Salzc88e5b62011-11-30 14:38:54 +0800547
Simran Basib7850bb2013-07-24 12:33:42 -0700548 # Time to wait before timing out on servo initialization.
549 INIT_TIMEOUT_SECS = 10
Simran Basi59331342013-07-12 12:06:29 -0700550
Brent Peterson8df4a5b2020-03-06 11:50:49 -0800551 # Time to wait before timing out when extracting firmware images.
552 #
553 # This was increased from 60 seconds to support boards with very
554 # large (>500MB) firmware archives taking longer than expected to
555 # extract firmware on the lab host machines (b/149419503).
venkata srinivasa raju penmetcha38a01ba2021-01-14 18:25:30 -0800556 EXTRACT_TIMEOUT_SECS = 300
Brent Peterson8df4a5b2020-03-06 11:50:49 -0800557
Dawid Niedzwieckide89cd12020-10-16 10:10:02 +0200558 # The VBUS voltage threshold used to detect if VBUS is supplied
559 VBUS_THRESHOLD = 3000.0
560
Mary Ruthven86fc1612021-01-28 13:45:32 -0800561 # List of servos that connect to a debug header on the board.
562 FLEX_SERVOS = ['c2d2', 'servo_micro', 'servo_v3']
563
Otabek Kasimovb887a002020-12-29 02:54:38 -0800564 def __init__(self, servo_host, servo_serial=None, delay_init=False):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700565 """Sets up the servo communication infrastructure.
566
Fang Deng5d518f42013-08-02 14:04:32 -0700567 @param servo_host: A ServoHost object representing
568 the host running servod.
Dana Goyettea2f00ea2019-06-26 16:14:12 -0700569 @type servo_host: autotest_lib.server.hosts.servo_host.ServoHost
Ricky Liang0dd379c2014-04-23 16:29:08 +0800570 @param servo_serial: Serial number of the servo board.
Otabek Kasimovb887a002020-12-29 02:54:38 -0800571 @param delay_init: Delay cache servo_type and power_state to prevent
572 attempt to connect to the servod.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700573 """
Fang Deng5d518f42013-08-02 14:04:32 -0700574 # TODO(fdeng): crbug.com/298379
575 # We should move servo_host object out of servo object
576 # to minimize the dependencies on the rest of Autotest.
577 self._servo_host = servo_host
Ricky Liang0dd379c2014-04-23 16:29:08 +0800578 self._servo_serial = servo_serial
Otabek Kasimovb887a002020-12-29 02:54:38 -0800579 self._servo_type = None
580 self._power_state = None
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700581 self._programmer = None
Dana Goyette4dc0adc2019-05-06 14:51:53 -0700582 self._prev_log_inode = None
583 self._prev_log_size = 0
Mary Ruthven90035582020-06-18 12:17:15 -0700584 self._ccd_watchdog_disabled = False
Otabek Kasimovb887a002020-12-29 02:54:38 -0800585 if not delay_init:
586 self._servo_type = self.get_servo_version()
587 self._power_state = _PowerStateController(self)
588 self._uart = _Uart(self)
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800589
Dana Goyetteafa62fd2020-03-16 13:45:27 -0700590 def __str__(self):
591 """Description of this object and address, for use in errors"""
592 return "<%s '%s:%s'>" % (
593 type(self).__name__,
594 self._servo_host.hostname,
595 self._servo_host.servo_port)
596
Andrew McRaef0679932020-08-13 09:15:23 +1000597 @property
598 def _server(self):
Dana Goyette0d92eaf2020-08-07 22:23:10 -0700599 with _WrapServoErrors(
600 servo=self, description='get_servod_server_proxy()'):
Andrew McRaef0679932020-08-13 09:15:23 +1000601 return self._servo_host.get_servod_server_proxy()
602
Ricky Liang0dd379c2014-04-23 16:29:08 +0800603 @property
604 def servo_serial(self):
605 """Returns the serial number of the servo board."""
606 return self._servo_serial
607
Tom Wai-Hong Tam22ee0e52013-04-03 13:36:39 +0800608 def get_power_state_controller(self):
J. Richard Barnette75136b32013-03-26 13:38:44 -0700609 """Return the power state controller for this Servo.
610
611 The power state controller provides board-independent
612 interfaces for reset, power-on, power-off operations.
613
614 """
Otabek Kasimovb887a002020-12-29 02:54:38 -0800615 if self._power_state is None:
616 self._power_state = _PowerStateController(self)
J. Richard Barnette75136b32013-03-26 13:38:44 -0700617 return self._power_state
618
Fang Deng5d518f42013-08-02 14:04:32 -0700619
Mary Ruthvena3c52b82019-10-09 14:09:08 -0700620 def initialize_dut(self, cold_reset=False, enable_main=True):
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700621 """Initializes a dut for testing purposes.
622
623 This sets various servo signals back to default values
624 appropriate for the target board. By default, if the DUT
625 is already on, it stays on. If the DUT is powered off
626 before initialization, its state afterward is unspecified.
627
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700628 Rationale: Basic initialization of servo sets the lid open,
629 when there is a lid. This operation won't affect powered on
630 units; however, setting the lid open may power on a unit
J. Richard Barnette75136b32013-03-26 13:38:44 -0700631 that's off, depending on the board type and previous state
632 of the device.
633
J. Richard Barnette4b6af0d2014-06-05 09:57:20 -0700634 If `cold_reset` is a true value, the DUT and its EC will be
635 reset, and the DUT rebooted in normal mode.
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700636
637 @param cold_reset If True, cold reset the device after
638 initialization.
Mary Ruthvena3c52b82019-10-09 14:09:08 -0700639 @param enable_main If True, make sure the main servo device has
640 control of the dut.
641
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700642 """
Mary Ruthvena3c52b82019-10-09 14:09:08 -0700643 if enable_main:
644 self.enable_main_servo_device()
645
Dana Goyette0d92eaf2020-08-07 22:23:10 -0700646 with _WrapServoErrors(
647 servo=self, description='initialize_dut()->hwinit()'):
Dana Goyettea2f00ea2019-06-26 16:14:12 -0700648 self._server.hwinit()
Mengqi Guo51d5bea2020-02-03 18:34:45 -0800649 if self.has_control('usb_mux_oe1'):
650 self.set('usb_mux_oe1', 'on')
651 self.switch_usbkey('off')
652 else:
653 logging.warning('Servod command \'usb_mux_oe1\' is not available. '
654 'Any USB drive related servo routines will fail.')
Ruben Rodriguez Buchillon78ad9272020-09-11 12:48:57 -0700655 # Create a record of SBU voltages if this is running support servo (v4,
656 # v4p1).
657 # TODO(coconutruben): eventually, replace this with a metric to track
658 # SBU voltages wrt servo-hw/dut-hw
Otabek Kasimov7d10e412021-04-30 15:19:50 -0700659 if self.has_control('servo_dut_sbu1_mv'):
Gregory Nisbet8c2ee932020-11-24 10:30:34 -0800660 # Attempt to take a reading of sbu1 and sbu2 multiple times to
661 # account for situations where the two lines exchange hi/lo roles
662 # frequently.
663 for i in range(10):
Ruben Rodriguez Buchillon78ad9272020-09-11 12:48:57 -0700664 try:
Otabek Kasimov7d10e412021-04-30 15:19:50 -0700665 sbu1 = int(self.get('servo_dut_sbu1_mv'))
666 sbu2 = int(self.get('servo_dut_sbu2_mv'))
Gregory Nisbet8c2ee932020-11-24 10:30:34 -0800667 logging.info('attempt %d sbu1 %d sbu2 %d', i, sbu1, sbu2)
Ruben Rodriguez Buchillon78ad9272020-09-11 12:48:57 -0700668 except error.TestFail as e:
669 # This is a nice to have but if reading this fails, it
670 # shouldn't interfere with the test.
Gregory Nisbet8c2ee932020-11-24 10:30:34 -0800671 logging.exception(e)
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700672 self._uart.start_capture()
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700673 if cold_reset:
Otabek Kasimovb887a002020-12-29 02:54:38 -0800674 if not self.get_power_state_controller().supported:
Ruben Rodriguez Buchillonaed7c892020-02-06 13:11:14 -0800675 logging.info('Cold-reset for DUT requested, but servo '
676 'setup does not support power_state. Skipping.')
677 else:
Otabek Kasimovb887a002020-12-29 02:54:38 -0800678 self.get_power_state_controller().reset()
Dana Goyette0d92eaf2020-08-07 22:23:10 -0700679 with _WrapServoErrors(
680 servo=self, description='initialize_dut()->get_version()'):
Dana Goyettec9e7bef2020-07-21 13:32:30 -0700681 version = self._server.get_version()
682 logging.debug('Servo initialized, version is %s', version)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700683
684
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800685 def is_localhost(self):
686 """Is the servod hosted locally?
687
688 Returns:
689 True if local hosted; otherwise, False.
690 """
Fang Deng5d518f42013-08-02 14:04:32 -0700691 return self._servo_host.is_localhost()
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800692
693
Mary Ruthvenecf12712019-06-26 17:36:21 -0700694 def get_os_version(self):
695 """Returns the chromeos release version."""
696 lsb_release_content = self.system_output('cat /etc/lsb-release',
697 ignore_status=True)
698 return lsbrelease_utils.get_chromeos_release_builder_path(
699 lsb_release_content=lsb_release_content)
700
701
Mary Ruthven83bb5952019-06-27 12:34:05 -0700702 def get_servod_version(self):
703 """Returns the servod version."""
Ruben Rodriguez Buchillona1ad37f2020-04-24 16:51:07 -0700704 # TODO: use system_output once servod --sversion prints to stdout
705 try:
Evan Benn30c79aa2020-10-28 15:15:27 +1100706 result = self._servo_host.run('servod --sversion 2>&1')
Ruben Rodriguez Buchillona1ad37f2020-04-24 16:51:07 -0700707 except error.AutoservRunError as e:
708 if 'command execution error' in str(e):
709 # Fall back to version if sversion is not supported yet.
Evan Benn30c79aa2020-10-28 15:15:27 +1100710 result = self._servo_host.run('servod --version 2>&1')
Ruben Rodriguez Buchillona1ad37f2020-04-24 16:51:07 -0700711 return result.stdout.strip() or result.stderr.strip()
712 # An actually unexpected error occurred, just raise.
713 raise e
714 sversion = result.stdout or result.stderr
715 # The sversion output contains 3 lines:
716 # servod v1.0.816-ff8e966 // the extended version with git hash
717 # 2020-04-08 01:10:29 // the time of the latest commit
718 # chromeos-ci-legacy-us-central1-b-x32-55-u8zc // builder information
719 # For debugging purposes, we mainly care about the version, and the
720 # timestamp.
721 return ' '.join(sversion.split()[1:4])
Mary Ruthven83bb5952019-06-27 12:34:05 -0700722
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700723 def power_long_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700724 """Simulate a long power button press."""
J. Richard Barnettef12bff22012-07-18 14:41:06 -0700725 # After a long power press, the EC may ignore the next power
726 # button press (at least on Alex). To guarantee that this
727 # won't happen, we need to allow the EC one second to
728 # collect itself.
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800729 # long_press is defined as 8.5s in servod
730 self.set_nocheck('power_key', 'long_press')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700731
732
733 def power_normal_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700734 """Simulate a normal power button press."""
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800735 # press is defined as 1.2s in servod
736 self.set_nocheck('power_key', 'press')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700737
738
739 def power_short_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700740 """Simulate a short power button press."""
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800741 # tab is defined as 0.2s in servod
742 self.set_nocheck('power_key', 'tab')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700743
744
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800745 def power_key(self, press_secs='tab'):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700746 """Simulate a power button press.
747
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800748 @param press_secs: int, float, str; time to press key in seconds or
749 known shorthand: 'tab' 'press' 'long_press'
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700750 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800751 self.set_nocheck('power_key', press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700752
753
Ruben Zakarian47c1d4f2019-09-30 14:17:41 -0700754 def pwr_button(self, action='press'):
755 """Simulate a power button press.
756
757 @param action: str; could be press or could be release.
758 """
759 self.set_nocheck('pwr_button', action)
760
761
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700762 def lid_open(self):
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800763 """Simulate opening the lid and raise exception if all attempts fail"""
764 self.set('lid_open', 'yes')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700765
766
767 def lid_close(self):
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800768 """Simulate closing the lid and raise exception if all attempts fail
Craig Harrison48997262011-06-27 14:31:10 -0700769
770 Waits 6 seconds to ensure the device is fully asleep before returning.
771 """
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800772 self.set('lid_open', 'no')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700773 time.sleep(Servo.SLEEP_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700774
Ruben Zakarian47c1d4f2019-09-30 14:17:41 -0700775
776 def vbus_power_get(self):
777 """Get current vbus_power."""
778 return self.get('vbus_power')
779
780
Shelley Chenc26575a2015-09-18 10:56:16 -0700781 def volume_up(self, timeout=300):
Kevin Chenge448a8b2016-09-09 10:18:11 -0700782 """Simulate pushing the volume down button.
783
784 @param timeout: Timeout for setting the volume.
785 """
Shelley Chenc26575a2015-09-18 10:56:16 -0700786 self.set_get_all(['volume_up:yes',
787 'sleep:%.4f' % self.SERVO_KEY_PRESS_DELAY,
788 'volume_up:no'])
789 # we need to wait for commands to take effect before moving on
790 time_left = float(timeout)
791 while time_left > 0.0:
792 value = self.get('volume_up')
793 if value == 'no':
794 return
795 time.sleep(self.SHORT_DELAY)
796 time_left = time_left - self.SHORT_DELAY
797 raise error.TestFail("Failed setting volume_up to no")
798
799 def volume_down(self, timeout=300):
Kevin Chenge448a8b2016-09-09 10:18:11 -0700800 """Simulate pushing the volume down button.
801
802 @param timeout: Timeout for setting the volume.
803 """
Shelley Chenc26575a2015-09-18 10:56:16 -0700804 self.set_get_all(['volume_down:yes',
805 'sleep:%.4f' % self.SERVO_KEY_PRESS_DELAY,
806 'volume_down:no'])
807 # we need to wait for commands to take effect before moving on
808 time_left = float(timeout)
809 while time_left > 0.0:
810 value = self.get('volume_down')
811 if value == 'no':
812 return
813 time.sleep(self.SHORT_DELAY)
814 time_left = time_left - self.SHORT_DELAY
815 raise error.TestFail("Failed setting volume_down to no")
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700816
Yu-Ping Wud52fac92019-11-13 11:12:08 +0800817 def arrow_up(self, press_secs='tab'):
818 """Simulate arrow up key presses.
819
820 @param press_secs: int, float, str; time to press key in seconds or
821 known shorthand: 'tab' 'press' 'long_press'.
822 """
823 # TODO: Remove this check after a lab update to include CL:1913684
824 if not self.has_control('arrow_up'):
825 logging.warning('Control arrow_up ignored. '
826 'Please update hdctools')
827 return
828 self.set_nocheck('arrow_up', press_secs)
829
830 def arrow_down(self, press_secs='tab'):
831 """Simulate arrow down key presses.
832
833 @param press_secs: int, float, str; time to press key in seconds or
834 known shorthand: 'tab' 'press' 'long_press'.
835 """
836 # TODO: Remove this check after a lab update to include CL:1913684
837 if not self.has_control('arrow_down'):
838 logging.warning('Control arrow_down ignored. '
839 'Please update hdctools')
840 return
841 self.set_nocheck('arrow_down', press_secs)
842
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800843 def ctrl_d(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800844 """Simulate Ctrl-d simultaneous button presses.
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800845
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800846 @param press_secs: int, float, str; time to press key in seconds or
847 known shorthand: 'tab' 'press' 'long_press'
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800848 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800849 self.set_nocheck('ctrl_d', press_secs)
Gediminas Ramanauskas9da80f22012-11-14 12:59:43 -0800850
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800851
Yu-Ping Wu7f9c0182020-05-26 09:48:48 +0800852 def ctrl_s(self, press_secs='tab'):
853 """Simulate Ctrl-s simultaneous button presses.
854
855 @param press_secs: int, float, str; time to press key in seconds or
856 known shorthand: 'tab' 'press' 'long_press'
857 """
858 self.set_nocheck('ctrl_s', press_secs)
859
860
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800861 def ctrl_u(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800862 """Simulate Ctrl-u simultaneous button presses.
863
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800864 @param press_secs: int, float, str; time to press key in seconds or
865 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800866 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800867 self.set_nocheck('ctrl_u', press_secs)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700868
869
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800870 def ctrl_enter(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800871 """Simulate Ctrl-enter simultaneous button presses.
872
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800873 @param press_secs: int, float, str; time to press key in seconds or
874 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800875 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800876 self.set_nocheck('ctrl_enter', press_secs)
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -0700877
878
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800879 def ctrl_key(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800880 """Simulate Enter key button press.
881
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800882 @param press_secs: int, float, str; time to press key in seconds or
883 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800884 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800885 self.set_nocheck('ctrl_key', press_secs)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700886
887
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800888 def enter_key(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800889 """Simulate Enter key button press.
890
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800891 @param press_secs: int, float, str; time to press key in seconds or
892 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800893 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800894 self.set_nocheck('enter_key', press_secs)
Todd Broch9753bd42012-03-21 10:15:08 -0700895
896
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800897 def refresh_key(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800898 """Simulate Refresh key (F3) button press.
899
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800900 @param press_secs: int, float, str; time to press key in seconds or
901 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800902 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800903 self.set_nocheck('refresh_key', press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700904
905
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800906 def ctrl_refresh_key(self, press_secs='tab'):
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800907 """Simulate Ctrl and Refresh (F3) simultaneous press.
908
909 This key combination is an alternative of Space key.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800910
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800911 @param press_secs: int, float, str; time to press key in seconds or
912 known shorthand: 'tab' 'press' 'long_press'
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800913 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800914 self.set_nocheck('ctrl_refresh_key', press_secs)
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800915
916
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800917 def imaginary_key(self, press_secs='tab'):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700918 """Simulate imaginary key button press.
919
920 Maps to a key that doesn't physically exist.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800921
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800922 @param press_secs: int, float, str; time to press key in seconds or
923 known shorthand: 'tab' 'press' 'long_press'
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700924 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800925 self.set_nocheck('imaginary_key', press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700926
927
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800928 def sysrq_x(self, press_secs='tab'):
Vincent Palatine7dc9282016-07-14 11:31:58 +0200929 """Simulate Alt VolumeUp X simulataneous press.
930
931 This key combination is the kernel system request (sysrq) X.
932
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800933 @param press_secs: int, float, str; time to press key in seconds or
934 known shorthand: 'tab' 'press' 'long_press'
Vincent Palatine7dc9282016-07-14 11:31:58 +0200935 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800936 self.set_nocheck('sysrq_x', press_secs)
Vincent Palatine7dc9282016-07-14 11:31:58 +0200937
938
Tom Wai-Hong Tam93b5c092015-05-14 02:50:43 +0800939 def toggle_recovery_switch(self):
940 """Toggle recovery switch on and off."""
941 self.enable_recovery_mode()
942 time.sleep(self.REC_TOGGLE_DELAY)
943 self.disable_recovery_mode()
944
945
Craig Harrison6b36b122011-06-28 17:58:43 -0700946 def enable_recovery_mode(self):
947 """Enable recovery mode on device."""
948 self.set('rec_mode', 'on')
949
950
951 def disable_recovery_mode(self):
952 """Disable recovery mode on device."""
953 self.set('rec_mode', 'off')
954
955
Tom Wai-Hong Tamf9ded092015-05-20 05:37:22 +0800956 def toggle_development_switch(self):
957 """Toggle development switch on and off."""
958 self.enable_development_mode()
959 time.sleep(self.DEV_TOGGLE_DELAY)
960 self.disable_development_mode()
961
962
Craig Harrison6b36b122011-06-28 17:58:43 -0700963 def enable_development_mode(self):
964 """Enable development mode on device."""
965 self.set('dev_mode', 'on')
966
967
968 def disable_development_mode(self):
969 """Disable development mode on device."""
970 self.set('dev_mode', 'off')
971
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700972 def boot_devmode(self):
973 """Boot a dev-mode device that is powered off."""
Tom Wai-Hong Tam23869102012-01-17 15:41:30 +0800974 self.power_short_press()
Craig Harrison48997262011-06-27 14:31:10 -0700975 self.pass_devmode()
976
977
978 def pass_devmode(self):
979 """Pass through boot screens in dev-mode."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700980 time.sleep(Servo.BOOT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700981 self.ctrl_d()
Chrome Bot9a1137d2011-07-19 14:35:00 -0700982 time.sleep(Servo.BOOT_DELAY)
Craig Harrison48997262011-06-27 14:31:10 -0700983
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700984
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800985 def get_board(self):
Wai-Hong Tam0f904002017-09-19 15:52:22 -0700986 """Get the board connected to servod."""
Dana Goyette0d92eaf2020-08-07 22:23:10 -0700987 with _WrapServoErrors(servo=self, description='get_board()'):
Dana Goyetteafa62fd2020-03-16 13:45:27 -0700988 return self._server.get_board()
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800989
990
Wai-Hong Tam0f904002017-09-19 15:52:22 -0700991 def get_base_board(self):
992 """Get the board of the base connected to servod."""
Wai-Hong Tam7f6169e2017-09-29 09:23:48 -0700993 try:
Dana Goyette0d92eaf2020-08-07 22:23:10 -0700994 with _WrapServoErrors(servo=self, description='get_base_board()'):
Dana Goyettec9e7bef2020-07-21 13:32:30 -0700995 return self._server.get_base_board()
Derek Becketta7f8c4f2020-10-01 10:27:38 -0700996 except six.moves.xmlrpc_client.Fault as e:
Wai-Hong Tam7f6169e2017-09-29 09:23:48 -0700997 # TODO(waihong): Remove the following compatibility check when
998 # the new versions of hdctools are deployed.
999 if 'not supported' in str(e):
1000 logging.warning('The servod is too old that get_base_board '
Dana Goyette0d92eaf2020-08-07 22:23:10 -07001001 'not supported.')
Wai-Hong Tam7f6169e2017-09-29 09:23:48 -07001002 return ''
1003 raise
Wai-Hong Tam0f904002017-09-19 15:52:22 -07001004
Namyoon Woo0fdf76e2020-02-26 09:14:50 -08001005 def get_ec_board(self):
1006 """Get the board name from EC."""
Otabek Kasimov7d10e412021-04-30 15:19:50 -07001007 if self.has_control('active_dut_controller'):
Namyoon Woo0fdf76e2020-02-26 09:14:50 -08001008 # If servo v4 is allowing dual_v4 devices, then choose the
1009 # active device.
Otabek Kasimov7d10e412021-04-30 15:19:50 -07001010 active_device = self.get('active_dut_controller')
Namyoon Woo0fdf76e2020-02-26 09:14:50 -08001011 if active_device == self.get_main_servo_device():
1012 active_device = ''
1013 else:
1014 active_device = ''
1015 return self.get('ec_board', prefix=active_device)
Wai-Hong Tam0f904002017-09-19 15:52:22 -07001016
Wai-Hong Tamcc399ee2017-12-08 12:43:28 -08001017 def get_ec_active_copy(self):
1018 """Get the active copy of the EC image."""
1019 return self.get('ec_active_copy')
1020
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -08001021 def has_control(self, ctrl_name, prefix=''):
1022 """Query servod server to determine if |ctrl_name| is a valid control.
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -07001023
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -08001024 @param ctrl_name Name of the control.
1025 @param prefix: prefix to route control to correct servo device.
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -07001026
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -08001027 @returns: true if |ctrl_name| is a known control, false otherwise.
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -07001028 """
Namyoon Woo0fdf76e2020-02-26 09:14:50 -08001029 ctrl_name = self._build_ctrl_name(ctrl_name, prefix)
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -07001030 try:
1031 # If the control exists, doc() will work.
Dana Goyette0d92eaf2020-08-07 22:23:10 -07001032 with _WrapServoErrors(
1033 servo=self,
1034 description='has_control(%s)->doc()' % ctrl_name):
Dana Goyettec9e7bef2020-07-21 13:32:30 -07001035 self._server.doc(ctrl_name)
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -07001036 return True
Dana Goyette0d92eaf2020-08-07 22:23:10 -07001037 except ControlUnavailableError:
1038 return False
Todd Brochefe72cb2012-07-11 19:58:53 -07001039
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -08001040 def _build_ctrl_name(self, ctrl_name, prefix):
1041 """Helper to build the control name if a prefix is used.
1042
1043 @param ctrl_name Name of the control.
1044 @param prefix: prefix to route control to correct servo device.
1045
1046 @returns: [|prefix|.]ctrl_name depending on whether prefix is non-empty.
1047 """
1048 assert ctrl_name
1049 if prefix:
1050 return '%s.%s' % (prefix, ctrl_name)
1051 return ctrl_name
1052
1053 def get(self, ctrl_name, prefix=''):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -07001054 """Get the value of a gpio from Servod.
1055
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -08001056 @param ctrl_name Name of the control.
1057 @param prefix: prefix to route control to correct servo device.
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -07001058
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -08001059 @returns: server response to |ctrl_name| request.
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -07001060
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -08001061 @raise ControlUnavailableError: if |ctrl_name| not a known control.
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -07001062 @raise error.TestFail: for all other failures doing get().
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -07001063 """
Namyoon Woo0fdf76e2020-02-26 09:14:50 -08001064 ctrl_name = self._build_ctrl_name(ctrl_name, prefix)
Dana Goyette0d92eaf2020-08-07 22:23:10 -07001065 with _WrapServoErrors(
1066 servo=self, description='Getting %s' % ctrl_name):
1067 return self._server.get(ctrl_name)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -07001068
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -08001069 def set(self, ctrl_name, ctrl_value, prefix=''):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -07001070 """Set and check the value of a gpio using Servod.
1071
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -08001072 @param ctrl_name: Name of the control.
1073 @param ctrl_value: New setting for the control.
1074 @param prefix: prefix to route control to correct servo device.
Dana Goyette7ff06c92019-10-11 13:38:03 -07001075 @raise error.TestFail: if the control value fails to change.
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -07001076 """
Namyoon Woo0fdf76e2020-02-26 09:14:50 -08001077 ctrl_name = self._build_ctrl_name(ctrl_name, prefix)
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -08001078 self.set_nocheck(ctrl_name, ctrl_value)
Todd Brochcf7c6652012-02-24 13:03:59 -08001079 retry_count = Servo.GET_RETRY_MAX
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -08001080 actual_value = self.get(ctrl_name)
1081 while ctrl_value != actual_value and retry_count:
1082 logging.warning("%s != %s, retry %d", ctrl_name, ctrl_value,
Dana Goyette7ff06c92019-10-11 13:38:03 -07001083 retry_count)
Todd Brochcf7c6652012-02-24 13:03:59 -08001084 retry_count -= 1
1085 time.sleep(Servo.SHORT_DELAY)
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -08001086 actual_value = self.get(ctrl_name)
Dana Goyette7ff06c92019-10-11 13:38:03 -07001087
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -08001088 if ctrl_value != actual_value:
Dana Goyette7ff06c92019-10-11 13:38:03 -07001089 raise error.TestFail(
1090 'Servo failed to set %s to %s. Got %s.'
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -08001091 % (ctrl_name, ctrl_value, actual_value))
Craig Harrison2b6c6fc2011-06-23 10:34:02 -07001092
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -08001093 def set_nocheck(self, ctrl_name, ctrl_value, prefix=''):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -07001094 """Set the value of a gpio using Servod.
1095
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -08001096 @param ctrl_name Name of the control.
1097 @param ctrl_value New setting for the control.
1098 @param prefix: prefix to route control to correct servo device.
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -07001099
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -08001100 @raise ControlUnavailableError: if |ctrl_name| not a known control.
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -07001101 @raise error.TestFail: for all other failures doing set().
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -07001102 """
Namyoon Woo0fdf76e2020-02-26 09:14:50 -08001103 ctrl_name = self._build_ctrl_name(ctrl_name, prefix)
Ruben Rodriguez Buchillon1c0219c2019-06-25 12:30:44 -07001104 # The real danger here is to pass a None value through the xmlrpc.
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -08001105 assert ctrl_value is not None
Dana Goyettec9e7bef2020-07-21 13:32:30 -07001106 description = 'Setting %s to %r' % (ctrl_name, ctrl_value)
1107 logging.debug('%s', description)
Dana Goyette0d92eaf2020-08-07 22:23:10 -07001108 with _WrapServoErrors(servo=self, description=description):
1109 self._server.set(ctrl_name, ctrl_value)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -07001110
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +08001111 def set_get_all(self, controls):
1112 """Set &| get one or more control values.
1113
1114 @param controls: list of strings, controls to set &| get.
1115
1116 @raise: error.TestError in case error occurs setting/getting values.
1117 """
Dana Goyettec9e7bef2020-07-21 13:32:30 -07001118 description = 'Set/get all: %s' % str(controls)
1119 logging.debug('%s', description)
Dana Goyette0d92eaf2020-08-07 22:23:10 -07001120 with _WrapServoErrors(servo=self, description=description):
1121 return self._server.set_get_all(controls)
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +08001122
1123
Ruben Rodriguez Buchillon11daf2c2020-02-25 12:31:40 -08001124 def probe_host_usb_dev(self):
Jon Salzc88e5b62011-11-30 14:38:54 +08001125 """Probe the USB disk device plugged-in the servo from the host side.
1126
Kevin Chengeb06fe72016-08-22 15:26:32 -07001127 It uses servod to discover if there is a usb device attached to it.
Jon Salzc88e5b62011-11-30 14:38:54 +08001128
Kevin Chenga22c4a82016-10-07 14:13:25 -07001129 @return: String of USB disk path (e.g. '/dev/sdb') or None.
Jon Salzc88e5b62011-11-30 14:38:54 +08001130 """
Ruben Rodriguez Buchillon9a4bfc32018-10-16 20:46:25 +08001131 # Set up Servo's usb mux.
Ruben Rodriguez Buchillon11daf2c2020-02-25 12:31:40 -08001132 return self.get('image_usbkey_dev') or None
Jon Salzc88e5b62011-11-30 14:38:54 +08001133
1134
Mike Truty49153d82012-08-21 22:27:30 -05001135 def image_to_servo_usb(self, image_path=None,
Otabek Kasimov50691312020-06-04 20:29:35 -07001136 make_image_noninteractive=False,
1137 power_off_dut=True):
Mike Truty49153d82012-08-21 22:27:30 -05001138 """Install an image to the USB key plugged into the servo.
1139
1140 This method may copy any image to the servo USB key including a
1141 recovery image or a test image. These images are frequently used
1142 for test purposes such as restoring a corrupted image or conducting
1143 an upgrade of ec/fw/kernel as part of a test of a specific image part.
1144
Otabek Kasimov50691312020-06-04 20:29:35 -07001145 @param image_path: Path on the host to the recovery image.
1146 @param make_image_noninteractive: Make the recovery image
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -07001147 noninteractive, therefore the DUT
1148 will reboot automatically after
1149 installation.
Otabek Kasimov50691312020-06-04 20:29:35 -07001150 @param power_off_dut: To put the DUT in power off mode.
Mike Truty49153d82012-08-21 22:27:30 -05001151 """
J. Richard Barnette41320ee2013-03-11 13:00:13 -07001152 # We're about to start plugging/unplugging the USB key. We
1153 # don't know the state of the DUT, or what it might choose
1154 # to do to the device after hotplug. To avoid surprises,
1155 # force the DUT to be off.
Otabek Kasimov50691312020-06-04 20:29:35 -07001156 if power_off_dut:
Otabek Kasimovb887a002020-12-29 02:54:38 -08001157 self.get_power_state_controller().power_off()
Mike Truty49153d82012-08-21 22:27:30 -05001158
Mike Truty49153d82012-08-21 22:27:30 -05001159 if image_path:
Tom Wai-Hong Tam42f136d2012-10-26 11:11:23 +08001160 logging.info('Searching for usb device and copying image to it. '
1161 'Please wait a few minutes...')
Ruben Rodriguez Buchillon11daf2c2020-02-25 12:31:40 -08001162 # The servod control automatically sets up the host in the host
1163 # direction.
1164 try:
1165 self.set_nocheck('download_image_to_usb_dev', image_path)
1166 except error.TestFail as e:
1167 logging.error('Failed to transfer requested image to USB. %s.'
1168 'Please take a look at Servo Logs.', str(e))
Mike Truty49153d82012-08-21 22:27:30 -05001169 raise error.AutotestError('Download image to usb failed.')
1170 if make_image_noninteractive:
1171 logging.info('Making image noninteractive')
Ruben Rodriguez Buchillon11daf2c2020-02-25 12:31:40 -08001172 try:
1173 dev = self.probe_host_usb_dev()
1174 if not dev:
1175 # This is fine but also should never happen: if we
1176 # successfully download an image but somehow cannot
1177 # find the stick again, it needs to be investigated.
1178 raise error.TestFail('No image usb key detected '
1179 'after successful download. '
1180 'Please investigate.')
1181 # The modification has to happen on partition 1.
1182 dev_partition = '%s1' % dev
1183 self.set_nocheck('make_usb_dev_image_noninteractive',
1184 dev_partition)
1185 except error.TestFail as e:
1186 logging.error('Failed to make image noninteractive. %s.'
1187 'Please take a look at Servo Logs.',
1188 str(e))
Mike Truty49153d82012-08-21 22:27:30 -05001189
Garry Wang53fc8f32020-09-18 13:30:08 -07001190 def boot_in_recovery_mode(self, snk_mode=False):
1191 """Boot host DUT in recovery mode.
1192
1193 @param snk_mode: If True, switch servo_v4 role to 'snk' mode before
1194 boot DUT into recovery mode.
1195 """
Ruben Rodriguez Buchillon11daf2c2020-02-25 12:31:40 -08001196 # This call has a built-in delay to ensure that we wait a timeout
1197 # for the stick to enumerate and settle on the DUT side.
Kalin Stoyanovb3c11f32018-05-11 09:02:00 -07001198 self.switch_usbkey('dut')
Garry Wang53fc8f32020-09-18 13:30:08 -07001199 # Switch servo_v4 mode to snk as the DUT won't able to see usb drive
1200 # in recovery mode if the servo is in src mode(see crbug.com/1129165).
1201 if snk_mode:
1202 logging.info('Setting servo_v4 role to snk mode in order to make'
1203 ' the DUT can see usb drive while in recovery mode.')
1204 self.set_servo_v4_role('snk')
1205
Otabek Kasimovb0e1e5d2020-05-06 18:53:51 -07001206 try:
Otabek Kasimovb887a002020-12-29 02:54:38 -08001207 power_state = self.get_power_state_controller()
1208 power_state.power_on(rec_mode=power_state.REC_ON)
Otabek Kasimovb0e1e5d2020-05-06 18:53:51 -07001209 except error.TestFail as e:
Garry Wang53fc8f32020-09-18 13:30:08 -07001210 self.set_servo_v4_role('src')
Otabek Kasimovb0e1e5d2020-05-06 18:53:51 -07001211 logging.error('Failed to boot DUT in recovery mode. %s.', str(e))
1212 raise error.AutotestError('Failed to boot DUT in recovery mode.')
Mike Truty49153d82012-08-21 22:27:30 -05001213
Garry Wang53fc8f32020-09-18 13:30:08 -07001214 def install_recovery_image(self,
1215 image_path=None,
1216 make_image_noninteractive=False,
1217 snk_mode=False):
Dan Shic67f1332016-04-06 12:38:06 -07001218 """Install the recovery image specified by the path onto the DUT.
Jon Salzc88e5b62011-11-30 14:38:54 +08001219
1220 This method uses google recovery mode to install a recovery image
1221 onto a DUT through the use of a USB stick that is mounted on a servo
Tom Wai-Hong Tama07115e2012-01-09 12:27:01 +08001222 board specified by the usb_dev. If no image path is specified
Jon Salzc88e5b62011-11-30 14:38:54 +08001223 we use the recovery image already on the usb image.
1224
Garry Wang53fc8f32020-09-18 13:30:08 -07001225 This method will switch servo_v4 role to 'snk' mode in order to make
1226 the DUT can see the usb drive plugged on servo, the caller should
1227 set servo_v4 role back to 'src' mode one the DUT exit recovery mode.
1228
Dan Shic67f1332016-04-06 12:38:06 -07001229 @param image_path: Path on the host to the recovery image.
1230 @param make_image_noninteractive: Make the recovery image
1231 noninteractive, therefore the DUT will reboot automatically
1232 after installation.
Garry Wang53fc8f32020-09-18 13:30:08 -07001233 @param snk_mode: If True, switch servo_v4 role to 'snk' mode before
1234 boot DUT into recovery mode.
Jon Salzc88e5b62011-11-30 14:38:54 +08001235 """
Mike Truty49153d82012-08-21 22:27:30 -05001236 self.image_to_servo_usb(image_path, make_image_noninteractive)
Garry Wangd2656812019-08-07 17:29:16 -07001237 # Give the DUT some time to power_off if we skip
1238 # download image to usb. (crbug.com/982993)
1239 if not image_path:
1240 time.sleep(10)
Garry Wang53fc8f32020-09-18 13:30:08 -07001241 self.boot_in_recovery_mode(snk_mode=snk_mode)
Jon Salzc88e5b62011-11-30 14:38:54 +08001242
1243
Vadim Bendeburyb80ba592012-12-07 15:02:34 -08001244 def _scp_image(self, image_path):
1245 """Copy image to the servo host.
1246
1247 When programming a firmware image on the DUT, the image must be
1248 located on the host to which the servo device is connected. Sometimes
1249 servo is controlled by a remote host, in this case the image needs to
Dana Goyetted5a95542019-12-30 11:14:14 -08001250 be transferred to the remote host. This adds the servod port number, to
1251 make sure tests for different DUTs don't trample on each other's files.
Namyoon Wood18a4462020-06-03 09:37:36 -07001252 Along with the firmware image, any subsidiary files in the same
1253 directory shall be copied to the host as well.
Vadim Bendeburyb80ba592012-12-07 15:02:34 -08001254
1255 @param image_path: a string, name of the firmware image file to be
1256 transferred.
1257 @return: a string, full path name of the copied file on the remote.
1258 """
Namyoon Wood18a4462020-06-03 09:37:36 -07001259 src_path = os.path.dirname(image_path)
1260 dest_path = os.path.join('/tmp', 'dut_%d' % self._servo_host.servo_port)
1261 logging.info('Copying %s to %s', src_path, dest_path)
Namyoon Woo0093aff2020-06-10 09:58:02 -07001262 # Copy a directory, src_path to dest_path. send_file() will create a
1263 # directory named basename(src_path) under dest_path, and copy all files
1264 # in src_path to the destination.
Namyoon Wood18a4462020-06-03 09:37:36 -07001265 self._servo_host.send_file(src_path, dest_path, delete_dest=True)
Namyoon Woo0093aff2020-06-10 09:58:02 -07001266
1267 # Make a image path of the destination.
1268 # e.g. /tmp/dut_9999/EC/ec.bin
1269 rv = os.path.join(dest_path, os.path.basename(src_path))
1270 return os.path.join(rv, os.path.basename(image_path))
Vadim Bendeburyb80ba592012-12-07 15:02:34 -08001271
1272
Dan Shifecdaf42015-07-28 10:17:26 -07001273 def system(self, command, timeout=3600):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -07001274 """Execute the passed in command on the servod host.
1275
1276 @param command Command to be executed.
Dan Shifecdaf42015-07-28 10:17:26 -07001277 @param timeout Maximum number of seconds of runtime allowed. Default to
1278 1 hour.
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -07001279 """
Vadim Bendebury89ec24e2012-12-17 12:54:18 -08001280 logging.info('Will execute on servo host: %s', command)
Fang Deng5d518f42013-08-02 14:04:32 -07001281 self._servo_host.run(command, timeout=timeout)
Vadim Bendebury89ec24e2012-12-17 12:54:18 -08001282
1283
Dan Shifecdaf42015-07-28 10:17:26 -07001284 def system_output(self, command, timeout=3600,
Vadim Bendebury89ec24e2012-12-17 12:54:18 -08001285 ignore_status=False, args=()):
1286 """Execute the passed in command on the servod host, return stdout.
1287
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -07001288 @param command a string, the command to execute
1289 @param timeout an int, max number of seconds to wait til command
Dan Shifecdaf42015-07-28 10:17:26 -07001290 execution completes. Default to 1 hour.
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -07001291 @param ignore_status a Boolean, if true - ignore command's nonzero exit
Vadim Bendebury89ec24e2012-12-17 12:54:18 -08001292 status, otherwise an exception will be thrown
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -07001293 @param args a tuple of strings, each becoming a separate command line
Vadim Bendebury89ec24e2012-12-17 12:54:18 -08001294 parameter for the command
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -07001295 @return command's stdout as a string.
Vadim Bendebury89ec24e2012-12-17 12:54:18 -08001296 """
Fang Deng5d518f42013-08-02 14:04:32 -07001297 return self._servo_host.run(command, timeout=timeout,
1298 ignore_status=ignore_status,
1299 args=args).stdout.strip()
Vadim Bendeburyb80ba592012-12-07 15:02:34 -08001300
1301
Mary Ruthven38d90af2019-08-16 13:13:31 -07001302 def get_servo_version(self, active=False):
Dan Shia5fef052015-05-18 23:28:47 -07001303 """Get the version of the servo, e.g., servo_v2 or servo_v3.
1304
Mary Ruthven38d90af2019-08-16 13:13:31 -07001305 @param active: Only return the servo type with the active device.
Dan Shia5fef052015-05-18 23:28:47 -07001306 @return: The version of the servo.
1307
1308 """
Dana Goyette0d92eaf2020-08-07 22:23:10 -07001309 with _WrapServoErrors(
1310 servo=self, description='get_servo_version()->get_version()'):
Dana Goyetteafa62fd2020-03-16 13:45:27 -07001311 servo_type = self._server.get_version()
Mary Ruthven38d90af2019-08-16 13:13:31 -07001312 if '_and_' not in servo_type or not active:
1313 return servo_type
1314
1315 # If servo v4 is using ccd and servo micro, modify the servo type to
1316 # reflect the active device.
Otabek Kasimov7d10e412021-04-30 15:19:50 -07001317 active_device = self.get('active_dut_controller')
Mary Ruthven38d90af2019-08-16 13:13:31 -07001318 if active_device in servo_type:
1319 logging.info('%s is active', active_device)
1320 return 'servo_v4_with_' + active_device
1321
1322 logging.warn("%s is active even though it's not in servo type",
1323 active_device)
1324 return servo_type
Dan Shia5fef052015-05-18 23:28:47 -07001325
1326
Otabek Kasimov41301a22020-05-10 15:28:21 -07001327 def get_servo_type(self):
Otabek Kasimovb887a002020-12-29 02:54:38 -08001328 if self._servo_type is None:
1329 self._servo_type = self.get_servo_version()
Otabek Kasimov41301a22020-05-10 15:28:21 -07001330 return self._servo_type
1331
Greg Edelston38035e82021-02-08 12:28:40 -07001332 def get_servo_v4_type(self):
1333 """Return the servo_v4_type (such as 'type-c'), or None if not v4."""
1334 if not hasattr(self, '_servo_v4_type'):
1335 if 'servo_v4' in self.get_servo_type():
Otabek Kasimov7d10e412021-04-30 15:19:50 -07001336 self._servo_v4_type = self.get('root.dut_connection_type')
Greg Edelston38035e82021-02-08 12:28:40 -07001337 else:
1338 self._servo_v4_type = None
1339 return self._servo_v4_type
1340
1341 def is_servo_v4_type_a(self):
1342 """True if the servo is v4 and type-a, else False."""
1343 return self.get_servo_v4_type() == 'type-a'
1344
1345 def is_servo_v4_type_c(self):
1346 """True if the servo is v4 and type-c, else False."""
1347 return self.get_servo_v4_type() == 'type-c'
1348
Mary Ruthvena3c52b82019-10-09 14:09:08 -07001349 def get_main_servo_device(self):
1350 """Return the main servo device"""
Otabek Kasimovb887a002020-12-29 02:54:38 -08001351 return self.get_servo_type().split('_with_')[-1].split('_and_')[0]
Mary Ruthvena3c52b82019-10-09 14:09:08 -07001352
1353
1354 def enable_main_servo_device(self):
1355 """Make sure the main device has control of the dut."""
Otabek Kasimov7d10e412021-04-30 15:19:50 -07001356 if not self.has_control('active_dut_controller'):
Mary Ruthvena3c52b82019-10-09 14:09:08 -07001357 return
Otabek Kasimov7d10e412021-04-30 15:19:50 -07001358 self.set('active_dut_controller', self.get_main_servo_device())
Mary Ruthvena3c52b82019-10-09 14:09:08 -07001359
1360
Ruben Rodriguez Buchillon1823bad2019-09-30 17:04:34 -07001361 def main_device_is_ccd(self):
1362 """Whether the main servo device (no prefixes) is a ccd device."""
Mary Ruthvenc844d872021-01-28 13:41:21 -08001363 servo = self.get_servo_type()
Mary Ruthven86fc1612021-01-28 13:45:32 -08001364 return 'ccd_cr50' in servo and not self.main_device_is_flex()
Mary Ruthven2724ee62019-07-16 11:16:59 -07001365
1366
Ruben Rodriguez Buchillon1823bad2019-09-30 17:04:34 -07001367 def main_device_is_flex(self):
1368 """Whether the main servo device (no prefixes) is a legacy device."""
Mary Ruthven86fc1612021-01-28 13:45:32 -08001369 servo = self.get_servo_type()
1370 return any([flex in servo for flex in self.FLEX_SERVOS])
Ruben Rodriguez Buchillon1823bad2019-09-30 17:04:34 -07001371
Ruben Rodriguez Buchillon82c94ad2019-09-30 14:49:25 -07001372
1373 def main_device_is_active(self):
1374 """Return whether the main device is the active device.
1375
1376 This is only relevant for a dual setup with ccd and legacy on the same
1377 DUT. The main device is the servo that has no prefix on its controls.
1378 This helper answers the question whether that device is also the
1379 active device or not.
1380 """
1381 # TODO(coconutruben): The current implementation of the dual setup only
1382 # ever has legacy as the main device. Therefore, it suffices to ask
1383 # whether the active device is ccd.
1384 if not self.dts_mode_is_valid():
1385 # Use dts support as a proxy to whether the servo setup could
1386 # support a dual role. Only those setups now support legacy and ccd.
1387 return True
Otabek Kasimov7d10e412021-04-30 15:19:50 -07001388 active_device = self.get('active_dut_controller')
Ruben Rodriguez Buchillon82c94ad2019-09-30 14:49:25 -07001389 return 'ccd_cr50' not in active_device
1390
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001391 def _initialize_programmer(self, rw_only=False):
1392 """Initialize the firmware programmer.
1393
1394 @param rw_only: True to initialize a programmer which only
1395 programs the RW portions.
1396 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -07001397 if self._programmer:
1398 return
1399 # Initialize firmware programmer
Otabek Kasimovb887a002020-12-29 02:54:38 -08001400 servo_type = self.get_servo_type()
1401 if servo_type.startswith('servo_v2'):
J. Richard Barnette8f19b392014-08-11 14:05:39 -07001402 self._programmer = firmware_programmer.ProgrammerV2(self)
Wai-Hong Tamc36c4d22016-09-09 10:39:45 -07001403 self._programmer_rw = firmware_programmer.ProgrammerV2RwOnly(self)
Kevin Chengdf2e29f2016-09-09 02:31:22 -07001404 # Both servo v3 and v4 use the same programming methods so just leverage
1405 # ProgrammerV3 for servo v4 as well.
Otabek Kasimovb887a002020-12-29 02:54:38 -08001406 elif (servo_type.startswith('servo_v3')
1407 or servo_type.startswith('servo_v4')):
Dan Shia5fef052015-05-18 23:28:47 -07001408 self._programmer = firmware_programmer.ProgrammerV3(self)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001409 self._programmer_rw = firmware_programmer.ProgrammerV3RwOnly(self)
J. Richard Barnette8f19b392014-08-11 14:05:39 -07001410 else:
1411 raise error.TestError(
1412 'No firmware programmer for servo version: %s' %
Otabek Kasimovb887a002020-12-29 02:54:38 -08001413 self.get_servo_type())
J. Richard Barnette8f19b392014-08-11 14:05:39 -07001414
1415
Garry Wang61cfe0b2020-08-21 16:26:00 -07001416 def program_bios(self, image, rw_only=False, copy_image=True):
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -08001417 """Program bios on DUT with given image.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -08001418
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -08001419 @param image: a string, file name of the BIOS image to program
1420 on the DUT.
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001421 @param rw_only: True to only program the RW portion of BIOS.
Garry Wang61cfe0b2020-08-21 16:26:00 -07001422 @param copy_image: True indicates we need scp the image to servohost
1423 while False means the image file is already on
1424 servohost.
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -08001425
Vadim Bendebury1a7ec632013-02-17 16:22:09 -08001426 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -07001427 self._initialize_programmer()
Garry Wang61cfe0b2020-08-21 16:26:00 -07001428 # We don't need scp if test runs locally.
1429 if copy_image and not self.is_localhost():
Ricky Liangc31aab32014-07-03 16:23:29 +08001430 image = self._scp_image(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001431 if rw_only:
1432 self._programmer_rw.program_bios(image)
1433 else:
1434 self._programmer.program_bios(image)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -08001435
1436
Garry Wang61cfe0b2020-08-21 16:26:00 -07001437 def program_ec(self, image, rw_only=False, copy_image=True):
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -08001438 """Program ec on DUT with given image.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -08001439
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -08001440 @param image: a string, file name of the EC image to program
1441 on the DUT.
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001442 @param rw_only: True to only program the RW portion of EC.
Garry Wang61cfe0b2020-08-21 16:26:00 -07001443 @param copy_image: True indicates we need scp the image to servohost
1444 while False means the image file is already on
1445 servohost.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -08001446 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -07001447 self._initialize_programmer()
Garry Wang61cfe0b2020-08-21 16:26:00 -07001448 # We don't need scp if test runs locally.
1449 if copy_image and not self.is_localhost():
Ricky Liangc31aab32014-07-03 16:23:29 +08001450 image = self._scp_image(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001451 if rw_only:
Congbin Guo42427612019-02-12 10:22:06 -08001452 self._programmer_rw.program_ec(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001453 else:
Congbin Guo42427612019-02-12 10:22:06 -08001454 self._programmer.program_ec(image)
1455
1456
Brent Peterson1cb623a2020-01-09 13:14:28 -08001457 def extract_ec_image(self, board, model, tarball_path):
1458 """Helper function to extract EC image from downloaded tarball.
Congbin Guo42427612019-02-12 10:22:06 -08001459
Shelley Chenac61d5a2019-06-24 15:35:46 -07001460 @param board: The DUT board name.
Congbin Guo42427612019-02-12 10:22:06 -08001461 @param model: The DUT model name.
1462 @param tarball_path: The path of the downloaded build tarball.
Justin TerAvest1d0c1bb2019-12-13 11:53:25 -07001463
Brent Peterson1cb623a2020-01-09 13:14:28 -08001464 @return: Path to extracted EC image.
1465 """
Brent Peterson0781db72020-02-13 13:13:37 -08001466
1467 # Ignore extracting EC image and re-programming if not a Chrome EC
1468 chrome_ec = FAFTConfig(board).chrome_ec
1469 if not chrome_ec:
Namyoon Woo0fdf76e2020-02-26 09:14:50 -08001470 logging.warn('Not a Chrome EC, ignore re-programming it')
Brent Peterson0781db72020-02-13 13:13:37 -08001471 return None
1472
Philip Chen64272762020-10-16 15:45:52 -07001473 # Try to retrieve firmware build target from the version reported
1474 # by the EC. If this doesn't work, we assume the firmware build
1475 # target is the same as the model name.
Namyoon Woo0fdf76e2020-02-26 09:14:50 -08001476 try:
Philip Chen64272762020-10-16 15:45:52 -07001477 fw_target = self.get_ec_board()
Namyoon Woo0fdf76e2020-02-26 09:14:50 -08001478 except Exception as err:
1479 logging.warn('Failed to get ec_board value; ignoring')
Philip Chen64272762020-10-16 15:45:52 -07001480 fw_target = model
Namyoon Woo0fdf76e2020-02-26 09:14:50 -08001481 pass
Congbin Guo42427612019-02-12 10:22:06 -08001482
Philip Chen64272762020-10-16 15:45:52 -07001483 # Array of candidates for EC image
1484 ec_image_candidates = [
1485 'ec.bin',
1486 '%s/ec.bin' % fw_target,
1487 '%s/ec.bin' % board
1488 ]
1489
Brent Peterson1cb623a2020-01-09 13:14:28 -08001490 # Extract EC image from tarball
1491 dest_dir = os.path.join(os.path.dirname(tarball_path), 'EC')
Brent Peterson8df4a5b2020-03-06 11:50:49 -08001492 ec_image = _extract_image_from_tarball(tarball_path,
1493 dest_dir,
1494 ec_image_candidates,
1495 self.EXTRACT_TIMEOUT_SECS)
Congbin Guo42427612019-02-12 10:22:06 -08001496
Brent Peterson0781db72020-02-13 13:13:37 -08001497 # Check if EC image was found and return path or raise error
Brent Peterson669edf42020-02-07 15:07:54 -08001498 if ec_image:
Namyoon Woo643f0f82020-04-24 15:28:52 -07001499 # Extract subsidiary binaries for EC
1500 # Find a monitor binary for NPCX_UUT chip type, if any.
1501 mon_candidates = [candidate.replace('ec.bin', 'npcx_monitor.bin')
1502 for candidate in ec_image_candidates]
1503 _extract_image_from_tarball(tarball_path, dest_dir, mon_candidates,
1504 self.EXTRACT_TIMEOUT_SECS)
1505
Brent Peterson669edf42020-02-07 15:07:54 -08001506 return os.path.join(dest_dir, ec_image)
1507 else:
Namyoon Woo34e90e92020-05-20 17:05:03 -07001508 raise error.TestError('Failed to extract EC image from %s' %
Brent Peterson0781db72020-02-13 13:13:37 -08001509 tarball_path)
Brent Peterson1cb623a2020-01-09 13:14:28 -08001510
1511
1512 def extract_bios_image(self, board, model, tarball_path):
1513 """Helper function to extract BIOS image from downloaded tarball.
1514
1515 @param board: The DUT board name.
1516 @param model: The DUT model name.
1517 @param tarball_path: The path of the downloaded build tarball.
1518
1519 @return: Path to extracted BIOS image.
1520 """
1521
Philip Chen64272762020-10-16 15:45:52 -07001522 # Try to retrieve firmware build target from the version reported
1523 # by the EC. If this doesn't work, we assume the firmware build
1524 # target is the same as the model name.
Namyoon Woo0fdf76e2020-02-26 09:14:50 -08001525 try:
Philip Chen64272762020-10-16 15:45:52 -07001526 fw_target = self.get_ec_board()
Namyoon Woo0fdf76e2020-02-26 09:14:50 -08001527 except Exception as err:
1528 logging.warn('Failed to get ec_board value; ignoring')
Philip Chen64272762020-10-16 15:45:52 -07001529 fw_target = model
Namyoon Woo0fdf76e2020-02-26 09:14:50 -08001530 pass
Brent Peterson1cb623a2020-01-09 13:14:28 -08001531
Philip Chen64272762020-10-16 15:45:52 -07001532 # Array of candidates for BIOS image
1533 bios_image_candidates = [
1534 'image.bin',
1535 'image-%s.bin' % fw_target,
1536 'image-%s.bin' % board
1537 ]
1538
Brent Peterson1cb623a2020-01-09 13:14:28 -08001539 # Extract BIOS image from tarball
1540 dest_dir = os.path.join(os.path.dirname(tarball_path), 'BIOS')
Brent Peterson8df4a5b2020-03-06 11:50:49 -08001541 bios_image = _extract_image_from_tarball(tarball_path,
1542 dest_dir,
1543 bios_image_candidates,
1544 self.EXTRACT_TIMEOUT_SECS)
Brent Peterson1cb623a2020-01-09 13:14:28 -08001545
Brent Peterson669edf42020-02-07 15:07:54 -08001546 # Check if BIOS image was found and return path or raise error
1547 if bios_image:
1548 return os.path.join(dest_dir, bios_image)
1549 else:
Namyoon Woo34e90e92020-05-20 17:05:03 -07001550 raise error.TestError('Failed to extract BIOS image from %s' %
Brent Peterson669edf42020-02-07 15:07:54 -08001551 tarball_path)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001552
Fang Dengafb88142013-05-30 17:44:31 -07001553
Fang Dengafb88142013-05-30 17:44:31 -07001554 def switch_usbkey(self, usb_state):
1555 """Connect USB flash stick to either host or DUT, or turn USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001556
1557 This function switches the servo multiplexer to provide electrical
Fang Dengafb88142013-05-30 17:44:31 -07001558 connection between the USB port J3 and either host or DUT side. It
1559 can also be used to turn the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001560
Fang Dengafb88142013-05-30 17:44:31 -07001561 @param usb_state: A string, one of 'dut', 'host', or 'off'.
1562 'dut' and 'host' indicate which side the
1563 USB flash device is required to be connected to.
1564 'off' indicates turning the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001565
Fang Dengafb88142013-05-30 17:44:31 -07001566 @raise: error.TestError in case the parameter is not 'dut'
1567 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001568 """
Ruben Rodriguez Buchillon11daf2c2020-02-25 12:31:40 -08001569 if self.get_usbkey_state() == usb_state:
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001570 return
1571
Fang Dengafb88142013-05-30 17:44:31 -07001572 if usb_state == 'off':
Ruben Rodriguez Buchillon11daf2c2020-02-25 12:31:40 -08001573 self.set_nocheck('image_usbkey_pwr', 'off')
Dan Shia5fef052015-05-18 23:28:47 -07001574 return
Fang Dengafb88142013-05-30 17:44:31 -07001575 elif usb_state == 'host':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001576 mux_direction = 'servo_sees_usbkey'
Fang Dengafb88142013-05-30 17:44:31 -07001577 elif usb_state == 'dut':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001578 mux_direction = 'dut_sees_usbkey'
1579 else:
Fang Dengafb88142013-05-30 17:44:31 -07001580 raise error.TestError('Unknown USB state request: %s' % usb_state)
Ruben Rodriguez Buchillon11daf2c2020-02-25 12:31:40 -08001581 # On the servod side, this control will ensure that
1582 # - the port is power cycled if it is changing directions
1583 # - the port ends up in a powered state after this call
1584 # - if facing the host side, the call only returns once a usb block
1585 # device is detected, or after a generous timeout (10s)
1586 self.set('image_usbkey_direction', mux_direction)
1587 # As servod makes no guarantees when switching to the dut side,
1588 # add a detection delay here when facing the dut.
Greg Edelston5979b7a2020-05-26 12:18:12 -06001589 if mux_direction == 'dut_sees_usbkey':
Ruben Rodriguez Buchillon11daf2c2020-02-25 12:31:40 -08001590 time.sleep(self.USB_DETECTION_DELAY)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001591
Ruben Rodriguez Buchillon11daf2c2020-02-25 12:31:40 -08001592 def get_usbkey_state(self):
Fang Dengafb88142013-05-30 17:44:31 -07001593 """Get which side USB is connected to or 'off' if usb power is off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001594
Fang Dengafb88142013-05-30 17:44:31 -07001595 @return: A string, one of 'dut', 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001596 """
Ruben Rodriguez Buchillon11daf2c2020-02-25 12:31:40 -08001597 pwr = self.get('image_usbkey_pwr')
1598 if pwr == 'off':
1599 return pwr
Ruben Rodriguez Buchillonf2de4772020-03-13 16:43:47 -07001600 direction = self.get('image_usbkey_direction')
1601 if direction == 'servo_sees_usbkey':
1602 return 'host'
1603 if direction == 'dut_sees_usbkey':
1604 return 'dut'
1605 raise error.TestFail('image_usbkey_direction set an unknown mux '
1606 'direction: %s' % direction)
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001607
Wai-Hong Tam60377262018-03-01 10:55:39 -08001608 def set_servo_v4_role(self, role):
1609 """Set the power role of servo v4, either 'src' or 'snk'.
1610
1611 It does nothing if not a servo v4.
1612
1613 @param role: Power role for DUT port on servo v4, either 'src' or 'snk'.
1614 """
Otabek Kasimovb887a002020-12-29 02:54:38 -08001615 if not self.get_servo_type().startswith('servo_v4'):
Wai-Hong Tam60377262018-03-01 10:55:39 -08001616 logging.debug('Not a servo v4, unable to set role to %s.', role)
Garry Wang53fc8f32020-09-18 13:30:08 -07001617 return
1618
Otabek Kasimov7d10e412021-04-30 15:19:50 -07001619 if not self.has_control('servo_pd_role'):
Garry Wang53fc8f32020-09-18 13:30:08 -07001620 logging.debug(
1621 'Servo does not has servo_v4_role control, unable'
1622 ' to set role to %s.', role)
1623 return
1624
Otabek Kasimov7d10e412021-04-30 15:19:50 -07001625 value = self.get('servo_pd_role')
Garry Wang53fc8f32020-09-18 13:30:08 -07001626 if value != role:
Otabek Kasimov7d10e412021-04-30 15:19:50 -07001627 self.set_nocheck('servo_pd_role', role)
Garry Wang53fc8f32020-09-18 13:30:08 -07001628 else:
1629 logging.debug('Already in the role: %s.', role)
Wai-Hong Tam60377262018-03-01 10:55:39 -08001630
Dawid Niedzwieckif6cbdd52020-11-17 09:15:09 +01001631 def get_servo_v4_role(self):
1632 """Get the power role of servo v4, either 'src' or 'snk'.
1633
1634 It returns None if not a servo v4.
1635 """
Otabek Kasimovb887a002020-12-29 02:54:38 -08001636 if not self.get_servo_type().startswith('servo_v4'):
Dawid Niedzwieckif6cbdd52020-11-17 09:15:09 +01001637 logging.debug('Not a servo v4, unable to get role')
1638 return None
1639
Otabek Kasimov7d10e412021-04-30 15:19:50 -07001640 if not self.has_control('servo_pd_role'):
Dawid Niedzwieckif6cbdd52020-11-17 09:15:09 +01001641 logging.debug(
1642 'Servo does not has servo_v4_role control, unable'
1643 ' to get the role.')
1644 return None
1645
Otabek Kasimov7d10e412021-04-30 15:19:50 -07001646 return self.get('servo_pd_role')
Dawid Niedzwieckif6cbdd52020-11-17 09:15:09 +01001647
Eric Yilun Linee8e6f52020-02-26 16:08:39 +08001648 def set_servo_v4_pd_comm(self, en):
1649 """Set the PD communication of servo v4, either 'on' or 'off'.
1650
1651 It does nothing if not a servo v4.
1652
1653 @param en: a string of 'on' or 'off' for PD communication.
1654 """
Otabek Kasimovb887a002020-12-29 02:54:38 -08001655 if self.get_servo_type().startswith('servo_v4'):
Otabek Kasimov8855f4d2021-05-18 18:22:04 -07001656 self.set_nocheck('servo_pd_comm', en)
Eric Yilun Linee8e6f52020-02-26 16:08:39 +08001657 else:
1658 logging.debug('Not a servo v4, unable to set PD comm to %s.', en)
Wai-Hong Tam60377262018-03-01 10:55:39 -08001659
Ruben Rodriguez Buchillon5f7ee682019-09-30 12:50:09 -07001660 def supports_built_in_pd_control(self):
1661 """Return whether the servo type supports pd charging and control."""
Greg Edelston38035e82021-02-08 12:28:40 -07001662 # Only servo v4 type-c supports this feature.
1663 if not self.is_servo_v4_type_c():
1664 logging.info('PD controls require a servo v4 type-c.')
Ruben Rodriguez Buchillon5f7ee682019-09-30 12:50:09 -07001665 return False
1666 # Lastly, one cannot really do anything without a plugged in charger.
1667 chg_port_mv = self.get('ppchg5_mv')
1668 if chg_port_mv < V4_CHG_ATTACHED_MIN_VOLTAGE_MV:
Otabek Kasimoved031712021-01-29 17:45:25 -08001669 logging.info(
1670 'It appears that no charger is plugged into servo v4. '
1671 'Charger port voltage: %dmV', chg_port_mv)
Ruben Rodriguez Buchillon5f7ee682019-09-30 12:50:09 -07001672 return False
1673 logging.info('Charger port voltage: %dmV', chg_port_mv)
1674 return True
1675
Ruben Rodriguez Buchillon82c94ad2019-09-30 14:49:25 -07001676 def dts_mode_is_valid(self):
1677 """Return whether servo setup supports dts mode control for cr50."""
Greg Edelston38035e82021-02-08 12:28:40 -07001678 # Only servo v4 type-c supports this feature.
1679 return self.is_servo_v4_type_c()
Ruben Rodriguez Buchillon82c94ad2019-09-30 14:49:25 -07001680
1681 def dts_mode_is_safe(self):
1682 """Return whether servo setup supports dts mode without losing access.
1683
1684 DTS mode control exists but the main device might go through ccd.
1685 In that case, it's only safe to control dts mode if the main device
1686 is legacy as otherwise the connection to the main device cuts out.
1687 """
1688 return self.dts_mode_is_valid() and self.main_device_is_flex()
1689
1690 def get_dts_mode(self):
1691 """Return servo dts mode.
1692
1693 @returns: on/off whether dts is on or off
1694 """
1695 if not self.dts_mode_is_valid():
1696 logging.info('Not a valid servo setup. Unable to get dts mode.')
1697 return
Otabek Kasimov7d10e412021-04-30 15:19:50 -07001698 return self.get('servo_dts_mode')
Ruben Rodriguez Buchillon82c94ad2019-09-30 14:49:25 -07001699
Mary Ruthven90035582020-06-18 12:17:15 -07001700 def ccd_watchdog_enable(self, enable):
1701 """Control the ccd watchdog."""
Otabek Kasimovb887a002020-12-29 02:54:38 -08001702 if 'ccd' not in self.get_servo_type():
Mary Ruthven90035582020-06-18 12:17:15 -07001703 return
1704 if self._ccd_watchdog_disabled and enable:
1705 logging.info('CCD watchdog disabled for test')
1706 return
1707 control = 'watchdog_add' if enable else 'watchdog_remove'
1708 self.set_nocheck(control, 'ccd')
1709
1710 def disable_ccd_watchdog_for_test(self):
1711 """Prevent servo from enabling the watchdog."""
1712 self._ccd_watchdog_disabled = True
1713 self.ccd_watchdog_enable(False)
1714
1715 def allow_ccd_watchdog_for_test(self):
1716 """Allow servo to enable the ccd watchdog."""
1717 self._ccd_watchdog_disabled = False
1718 self.ccd_watchdog_enable(True)
1719
Ruben Rodriguez Buchillon82c94ad2019-09-30 14:49:25 -07001720 def set_dts_mode(self, state):
1721 """Set servo dts mode to off or on.
Mary Ruthven739b2922019-08-22 11:16:06 -07001722
1723 It does nothing if not a servo v4. Disable the ccd watchdog if we're
1724 disabling dts mode. CCD will disconnect. The watchdog only allows CCD
1725 to disconnect for 10 seconds until it kills servod. Disable the
1726 watchdog, so CCD can stay disconnected indefinitely.
1727
1728 @param state: Set servo v4 dts mode 'off' or 'on'.
1729 """
Ruben Rodriguez Buchillon82c94ad2019-09-30 14:49:25 -07001730 if not self.dts_mode_is_valid():
1731 logging.info('Not a valid servo setup. Unable to set dts mode %s.',
1732 state)
Mary Ruthven739b2922019-08-22 11:16:06 -07001733 return
1734
Mary Ruthven739b2922019-08-22 11:16:06 -07001735 enable_watchdog = state == 'on'
1736
Mary Ruthven90035582020-06-18 12:17:15 -07001737 if not enable_watchdog:
1738 self.ccd_watchdog_enable(False)
Mary Ruthven739b2922019-08-22 11:16:06 -07001739
Otabek Kasimov7d10e412021-04-30 15:19:50 -07001740 self.set_nocheck('servo_dts_mode', state)
Mary Ruthven739b2922019-08-22 11:16:06 -07001741
Mary Ruthven90035582020-06-18 12:17:15 -07001742 if enable_watchdog:
1743 self.ccd_watchdog_enable(True)
Mary Ruthven739b2922019-08-22 11:16:06 -07001744
1745
Ruben Rodriguez Buchillon6f36a962019-10-02 14:55:59 -07001746 def _get_servo_type_fw_version(self, servo_type, prefix=''):
1747 """Helper to handle fw retrieval for micro/v4 vs ccd.
1748
Mary Ruthven5b0ed102021-01-28 13:46:43 -08001749 @param servo_type: one of 'servo_v4', 'servo_micro', 'ccd_cr50', 'c2d2'
Ruben Rodriguez Buchillon6f36a962019-10-02 14:55:59 -07001750 @param prefix: whether the control has a prefix
1751
1752 @returns: fw version for non-ccd devices, cr50 version for ccd device
1753 """
1754 if servo_type == 'ccd_cr50':
1755 # ccd_cr50 runs on cr50, so need to query the cr50 fw.
1756 servo_type = 'cr50'
1757 cmd = '%s_version' % servo_type
1758 try:
1759 return self.get(cmd, prefix=prefix)
1760 except error.TestFail:
1761 # Do not fail here, simply report the version as unknown.
1762 logging.warn('Unable to query %r to get servo fw version.', cmd)
1763 return 'unknown'
1764
1765
1766 def get_servo_fw_versions(self):
1767 """Retrieve a summary of attached servos and their firmware.
1768
1769 Note: that only the Google firmware owned servos supports this e.g.
1770 micro, v4, etc. For anything else, the dictionary will have no entry.
1771 If no device is has Google owned firmware (e.g. v3) then the result
1772 is an empty dictionary.
1773
1774 @returns: dict, a collection of each attached servo & their firmware.
1775 """
1776 def get_fw_version_tag(tag, dev):
1777 return '%s_version.%s' % (dev, tag)
1778
1779 fw_versions = {}
Otabek Kasimov7d10e412021-04-30 15:19:50 -07001780 # Note, this works because v4p1 starts with v4 as well.
1781 # TODO(coconutruben): make this more robust so that it can work on
1782 # a future v-whatever as well.
Otabek Kasimovb887a002020-12-29 02:54:38 -08001783 if 'servo_v4' not in self.get_servo_type():
Ruben Rodriguez Buchillon6f36a962019-10-02 14:55:59 -07001784 return {}
Otabek Kasimov7d10e412021-04-30 15:19:50 -07001785 # v4 or v4p1
1786 v4_flavor = self.get_servo_type().split('_with_')[0]
1787 v4_tag = get_fw_version_tag('root', v4_flavor)
1788 fw_versions[v4_tag] = self._get_servo_type_fw_version('servo_fw',
1789 prefix='root')
Otabek Kasimovb887a002020-12-29 02:54:38 -08001790 if 'with' in self.get_servo_type():
1791 dut_devs = self.get_servo_type().split('_with_')[1].split('_and_')
Ruben Rodriguez Buchillon6f36a962019-10-02 14:55:59 -07001792 main_tag = get_fw_version_tag('main', dut_devs[0])
1793 fw_versions[main_tag] = self._get_servo_type_fw_version(dut_devs[0])
1794 if len(dut_devs) == 2:
1795 # Right now, the only way for this to happen is for a dual setup
1796 # to exist where ccd is attached on top of servo micro. Thus, we
1797 # know that the prefix is ccd_cr50 and the type is ccd_cr50.
1798 # TODO(coconutruben): If the new servod is not deployed by
1799 # the time that there are more cases of '_and_' devices,
1800 # this needs to be reworked.
1801 dual_tag = get_fw_version_tag('ccd_flex_secondary', dut_devs[1])
1802 fw = self._get_servo_type_fw_version(dut_devs[1], 'ccd_cr50')
1803 fw_versions[dual_tag] = fw
1804 return fw_versions
1805
Congbin Guofc3b8962019-03-22 17:38:46 -07001806 @property
1807 def uart_logs_dir(self):
1808 """Return the directory to save UART logs."""
1809 return self._uart.logs_dir if self._uart else ""
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001810
Congbin Guofc3b8962019-03-22 17:38:46 -07001811
1812 @uart_logs_dir.setter
1813 def uart_logs_dir(self, logs_dir):
1814 """Set directory to save UART logs.
1815
1816 @param logs_dir String of directory name."""
Mary Ruthvenf2dd7a42020-08-12 16:18:24 -07001817 self._uart.logs_dir = logs_dir
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001818
Mary Ruthvenf2dd7a42020-08-12 16:18:24 -07001819 def get_uart_logfile(self, uart):
1820 """Return the path to the uart log file."""
1821 return self._uart.get_logfile(uart)
1822
1823 def record_uart_capture(self, outdir=None):
1824 """Save uart stream output."""
1825 if outdir and not self.uart_logs_dir:
1826 self.uart_logs_dir = outdir
1827 self._uart.dump()
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001828
Ruben Rodriguez Buchillon93084d02020-01-21 15:17:36 -08001829 def close(self, outdir=None):
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001830 """Close the servo object."""
Garry Wang53fc8f32020-09-18 13:30:08 -07001831 # We want to ensure that servo_v4 is in src mode to avoid DUTs
1832 # left in discharge state after a task.
1833 try:
1834 self.set_servo_v4_role('src')
1835 except Exception as e:
1836 logging.info(
1837 'Unexpected error while setting servo_v4 role'
1838 ' to src; %s', e)
1839
Mary Ruthvenf2dd7a42020-08-12 16:18:24 -07001840 self._uart.stop_capture()
1841 self.record_uart_capture(outdir)
Gregory Nisbet1f18f882020-07-07 10:31:59 -07001842
1843 def ec_reboot(self):
1844 """Reboot Just the embedded controller."""
1845 self.set_nocheck('ec_uart_flush', 'off')
1846 self.set_nocheck('ec_uart_cmd', 'reboot')
1847 self.set_nocheck('ec_uart_flush', 'on')
Dawid Niedzwieckide89cd12020-10-16 10:10:02 +02001848
1849 def get_vbus_voltage(self):
1850 """Get the voltage of VBUS'.
1851
1852 @returns The voltage of VBUS, if vbus_voltage is supported.
1853 None , if vbus_voltage is not supported.
1854 """
1855 if not self.has_control('vbus_voltage'):
1856 logging.debug('Servo does not have vbus_voltage control,'
1857 'unable to get vbus voltage')
1858 return None
1859
1860 return self.get('vbus_voltage')