blob: 442cb4a8fd58c9db566965467a9c300915f70bc8 [file] [log] [blame]
J. Richard Barnette384056b2012-04-16 11:04:46 -07001# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -07002# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4#
5# Expects to be run in an environment with sudo and no interactive password
6# prompt, such as within the Chromium OS development chroot.
7
Congbin Guoa1f9cba2018-07-03 11:36:59 -07008import ast
9import logging
Vadim Bendeburyb80ba592012-12-07 15:02:34 -080010import os
Congbin Guoa1f9cba2018-07-03 11:36:59 -070011import re
Dana Goyettea2f00ea2019-06-26 16:14:12 -070012import socket
Congbin Guoa1f9cba2018-07-03 11:36:59 -070013import time
14import xmlrpclib
Vadim Bendeburyb80ba592012-12-07 15:02:34 -080015
Simran Basi741b5d42012-05-18 11:27:15 -070016from autotest_lib.client.common_lib import error
Mary Ruthvenecf12712019-06-26 17:36:21 -070017from autotest_lib.client.common_lib import lsbrelease_utils
Congbin Guo42427612019-02-12 10:22:06 -080018from autotest_lib.server import utils as server_utils
Ricky Liangc31aab32014-07-03 16:23:29 +080019from autotest_lib.server.cros.servo import firmware_programmer
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070020
Kevin Chenga22c4a82016-10-07 14:13:25 -070021# Time to wait when probing for a usb device, it takes on avg 17 seconds
22# to do a full probe.
23_USB_PROBE_TIMEOUT = 40
24
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.
27NO_CONTROL_RE = re.compile(r'No control named (\w*\.?\w*)')
28
Ruben Rodriguez Buchillon5f7ee682019-09-30 12:50:09 -070029
30# The minimum voltage on the charger port on servo v4 that is expected. This is
31# to query whether a charger is plugged into servo v4 and thus pd control
32# capabilities can be used.
33V4_CHG_ATTACHED_MIN_VOLTAGE_MV = 4400
34
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -070035class ControlUnavailableError(error.TestFail):
36 """Custom error class to indicate a control is unavailable on servod."""
37 pass
38
39
Congbin Guo42427612019-02-12 10:22:06 -080040def _extract_image_from_tarball(tarball, dest_dir, image_candidates):
41 """Try extracting the image_candidates from the tarball.
42
43 @param tarball: The path of the tarball.
44 @param dest_path: The path of the destination.
45 @param image_candidates: A tuple of the paths of image candidates.
46
47 @return: The first path from the image candidates, which succeeds, or None
48 if all the image candidates fail.
49 """
Brent Peterson1cb623a2020-01-09 13:14:28 -080050
51 # Create the firmware_name subdirectory if it doesn't exist
52 if not os.path.exists(dest_dir):
53 os.mkdir(dest_dir)
54
55 # Try to extract image candidates from tarball
Congbin Guo42427612019-02-12 10:22:06 -080056 for image in image_candidates:
57 status = server_utils.system(
58 ('tar xf %s -C %s %s' % (tarball, dest_dir, image)),
59 timeout=60, ignore_status=True)
60 if status == 0:
61 return image
62 return None
63
64
J. Richard Barnette0c9c5882014-06-11 15:27:05 -070065class _PowerStateController(object):
66
67 """Class to provide board-specific power operations.
68
69 This class is responsible for "power on" and "power off"
70 operations that can operate without making assumptions in
71 advance about board state. It offers an interface that
72 abstracts out the different sequences required for different
73 board types.
74
75 """
76
77 # Constants acceptable to be passed for the `rec_mode` parameter
78 # to power_on().
79 #
80 # REC_ON: Boot the DUT in recovery mode, i.e. boot from USB or
81 # SD card.
82 # REC_OFF: Boot in normal mode, i.e. boot from internal storage.
83
84 REC_ON = 'rec'
85 REC_OFF = 'on'
Shelley Chen65938622018-05-16 07:45:54 -070086 REC_ON_FORCE_MRC = 'rec_force_mrc'
J. Richard Barnette0c9c5882014-06-11 15:27:05 -070087
88 # Delay in seconds needed between asserting and de-asserting
89 # warm reset.
90 _RESET_HOLD_TIME = 0.5
91
92 def __init__(self, servo):
93 """Initialize the power state control.
94
95 @param servo Servo object providing the underlying `set` and `get`
96 methods for the target controls.
97
98 """
99 self._servo = servo
100
101 def reset(self):
102 """Force the DUT to reset.
103
104 The DUT is guaranteed to be on at the end of this call,
105 regardless of its previous state, provided that there is
106 working OS software. This also guarantees that the EC has
107 been restarted.
108
109 """
110 self._servo.set_nocheck('power_state', 'reset')
111
112 def warm_reset(self):
113 """Apply warm reset to the DUT.
114
115 This asserts, then de-asserts the 'warm_reset' signal.
116 Generally, this causes the board to restart.
117
118 """
Ravutappa951ca432019-05-15 09:52:52 -0700119 # TODO: warm_reset support has added to power_state.py. Once it
120 # available to labstation remove fallback method.
121 try:
122 self._servo.set_nocheck('power_state', 'warm_reset')
123 except error.TestFail as err:
124 logging.info("Fallback to warm_reset control method")
125 self._servo.set_get_all(['warm_reset:on',
J. Richard Barnette0c9c5882014-06-11 15:27:05 -0700126 'sleep:%.4f' % self._RESET_HOLD_TIME,
127 'warm_reset:off'])
128
J. Richard Barnette0c9c5882014-06-11 15:27:05 -0700129 def power_off(self):
130 """Force the DUT to power off.
131
132 The DUT is guaranteed to be off at the end of this call,
133 regardless of its previous state, provided that there is
134 working EC and boot firmware. There is no requirement for
135 working OS software.
136
137 """
138 self._servo.set_nocheck('power_state', 'off')
139
140 def power_on(self, rec_mode=REC_OFF):
141 """Force the DUT to power on.
142
143 Prior to calling this function, the DUT must be powered off,
144 e.g. with a call to `power_off()`.
145
146 At power on, recovery mode is set as specified by the
147 corresponding argument. When booting with recovery mode on, it
148 is the caller's responsibility to unplug/plug in a bootable
149 external storage device.
150
151 If the DUT requires a delay after powering on but before
152 processing inputs such as USB stick insertion, the delay is
153 handled by this method; the caller is not responsible for such
154 delays.
155
156 @param rec_mode Setting of recovery mode to be applied at
157 power on. default: REC_OFF aka 'off'
158
159 """
160 self._servo.set_nocheck('power_state', rec_mode)
161
162
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700163class _Uart(object):
Congbin Guo0f00be82019-04-18 17:51:14 -0700164 """Class to capture UART streams of CPU, EC, Cr50, etc."""
165 _UartToCapture = ('cpu', 'ec', 'cr50', 'servo_v4', 'servo_micro', 'usbpd')
166
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700167 def __init__(self, servo):
168 self._servo = servo
169 self._streams = []
Congbin Guo0f00be82019-04-18 17:51:14 -0700170 self.logs_dir = None
Congbin Guofe4d4e82019-04-18 17:51:14 -0700171
Congbin Guo0f00be82019-04-18 17:51:14 -0700172 def _start_stop_capture(self, uart, start):
173 """Helper function to start/stop capturing on specified UART.
174
175 @param uart: The UART name to start/stop capturing.
176 @param start: True to start capturing, otherwise stop.
177
Ruben Rodriguez Buchillonecb525f2019-09-03 14:52:14 -0700178 @returns True if the operation completes successfully.
179 False if the UART capturing is not supported or failed due to
180 an error.
Congbin Guo0f00be82019-04-18 17:51:14 -0700181 """
182 logging.debug('%s capturing %s UART.', 'Start' if start else 'Stop',
183 uart)
Ruben Rodriguez Buchillonb0e2a9f2019-08-12 12:40:44 -0700184 uart_cmd = '%s_uart_capture' % uart
Ruben Rodriguez Buchillonecb525f2019-09-03 14:52:14 -0700185 target_level = 'on' if start else 'off'
186 level = None
Ruben Rodriguez Buchillonb0e2a9f2019-08-12 12:40:44 -0700187 if self._servo.has_control(uart_cmd):
Ruben Rodriguez Buchillon9f485642019-08-21 14:32:22 -0700188 # Do our own implementation of set() here as not_applicable
189 # should also count as a valid control.
Ruben Rodriguez Buchillonecb525f2019-09-03 14:52:14 -0700190 logging.debug('Trying to set %s to %s.', uart_cmd, target_level)
191 try:
192 self._servo.set_nocheck(uart_cmd, target_level)
193 level = self._servo.get(uart_cmd)
194 except error.TestFail as e:
195 # Any sort of test failure here should not stop the test. This
196 # is just to capture more output. Log and move on.
197 logging.warning('Failed to set %s to %s. %s. Ignoring.',
198 uart_cmd, target_level, str(e))
199 if level == target_level:
200 logging.debug('Managed to set %s to %s.', uart_cmd, level)
201 else:
202 logging.debug('Failed to set %s to %s. Got %s.', uart_cmd,
203 target_level, level)
204 return level == target_level
Congbin Guo0f00be82019-04-18 17:51:14 -0700205
206 def start_capture(self):
207 """Start capturing UART streams."""
208 for uart in self._UartToCapture:
209 if self._start_stop_capture(uart, True):
210 self._streams.append(('%s_uart_stream' % uart, '%s_uart.log' %
211 uart))
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700212
Congbin Guofc3b8962019-03-22 17:38:46 -0700213 def dump(self):
214 """Dump UART streams to log files accordingly."""
Congbin Guo0f00be82019-04-18 17:51:14 -0700215 if not self.logs_dir:
Congbin Guofc3b8962019-03-22 17:38:46 -0700216 return
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700217
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700218 for stream, logfile in self._streams:
Congbin Guo0f00be82019-04-18 17:51:14 -0700219 logfile_fullname = os.path.join(self.logs_dir, logfile)
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700220 try:
221 content = self._servo.get(stream)
222 except Exception as err:
223 logging.warn('Failed to get UART log for %s: %s', stream, err)
224 continue
225
Kuang-che Wu12192fc2019-08-31 09:55:00 +0800226 if content == 'not_applicable':
227 logging.warn('%s is not applicable', stream)
228 continue
229
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700230 # The UART stream may contain non-printable characters, and servo
231 # returns it in string representation. We use `ast.leteral_eval`
232 # to revert it back.
233 with open(logfile_fullname, 'a') as fd:
Kuang-che Wu12192fc2019-08-31 09:55:00 +0800234 try:
235 fd.write(ast.literal_eval(content))
236 except ValueError:
237 logging.exception('Invalid value for %s: %r', stream,
238 content)
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700239
240 def stop_capture(self):
241 """Stop capturing UART streams."""
Congbin Guo0f00be82019-04-18 17:51:14 -0700242 for uart in self._UartToCapture:
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700243 try:
Congbin Guo0f00be82019-04-18 17:51:14 -0700244 self._start_stop_capture(uart, False)
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700245 except Exception as err:
246 logging.warn('Failed to stop UART logging for %s: %s', uart,
247 err)
248
249
J. Richard Barnette384056b2012-04-16 11:04:46 -0700250class Servo(object):
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700251
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700252 """Manages control of a Servo board.
253
254 Servo is a board developed by hardware group to aide in the debug and
255 control of various partner devices. Servo's features include the simulation
256 of pressing the power button, closing the lid, and pressing Ctrl-d. This
257 class manages setting up and communicating with a servo demon (servod)
258 process. It provides both high-level functions for common servo tasks and
259 low-level functions for directly setting and reading gpios.
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700260
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700261 """
262
Chrome Bot9a1137d2011-07-19 14:35:00 -0700263 # Power button press delays in seconds.
J. Richard Barnetted2e4cbd2012-06-29 12:18:40 -0700264 #
J. Richard Barnette5383f072012-07-26 17:35:40 -0700265 # The EC specification says that 8.0 seconds should be enough
266 # for the long power press. However, some platforms need a bit
267 # more time. Empirical testing has found these requirements:
268 # Alex: 8.2 seconds
269 # ZGB: 8.5 seconds
270 # The actual value is set to the largest known necessary value.
271 #
272 # TODO(jrbarnette) Being generous is the right thing to do for
273 # existing platforms, but if this code is to be used for
274 # qualification of new hardware, we should be less generous.
Chrome Bot9a1137d2011-07-19 14:35:00 -0700275 SHORT_DELAY = 0.1
J. Richard Barnette5383f072012-07-26 17:35:40 -0700276
Todd Broch31c82502011-08-29 08:14:39 -0700277 # Maximum number of times to re-read power button on release.
Todd Brochcf7c6652012-02-24 13:03:59 -0800278 GET_RETRY_MAX = 10
Chrome Bot9a1137d2011-07-19 14:35:00 -0700279
J. Richard Barnette5383f072012-07-26 17:35:40 -0700280 # Delays to deal with DUT state transitions.
Chrome Bot9a1137d2011-07-19 14:35:00 -0700281 SLEEP_DELAY = 6
282 BOOT_DELAY = 10
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700283
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700284 # Default minimum time interval between 'press' and 'release'
285 # keyboard events.
Vic Yang0aca1c22012-11-19 18:33:56 -0800286 SERVO_KEY_PRESS_DELAY = 0.1
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700287
Tom Wai-Hong Tam93b5c092015-05-14 02:50:43 +0800288 # Time to toggle recovery switch on and off.
289 REC_TOGGLE_DELAY = 0.1
290
Tom Wai-Hong Tamf9ded092015-05-20 05:37:22 +0800291 # Time to toggle development switch on and off.
292 DEV_TOGGLE_DELAY = 0.1
293
Jon Salzc88e5b62011-11-30 14:38:54 +0800294 # Time between an usb disk plugged-in and detected in the system.
295 USB_DETECTION_DELAY = 10
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800296 # Time to keep USB power off before and after USB mux direction is changed
297 USB_POWEROFF_DELAY = 2
Jon Salzc88e5b62011-11-30 14:38:54 +0800298
Simran Basib7850bb2013-07-24 12:33:42 -0700299 # Time to wait before timing out on servo initialization.
300 INIT_TIMEOUT_SECS = 10
Simran Basi59331342013-07-12 12:06:29 -0700301
J. Richard Barnette67ccb872012-04-19 16:34:56 -0700302
Ricky Liang0dd379c2014-04-23 16:29:08 +0800303 def __init__(self, servo_host, servo_serial=None):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700304 """Sets up the servo communication infrastructure.
305
Fang Deng5d518f42013-08-02 14:04:32 -0700306 @param servo_host: A ServoHost object representing
307 the host running servod.
Dana Goyettea2f00ea2019-06-26 16:14:12 -0700308 @type servo_host: autotest_lib.server.hosts.servo_host.ServoHost
Ricky Liang0dd379c2014-04-23 16:29:08 +0800309 @param servo_serial: Serial number of the servo board.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700310 """
Fang Deng5d518f42013-08-02 14:04:32 -0700311 # TODO(fdeng): crbug.com/298379
312 # We should move servo_host object out of servo object
313 # to minimize the dependencies on the rest of Autotest.
314 self._servo_host = servo_host
Ricky Liang0dd379c2014-04-23 16:29:08 +0800315 self._servo_serial = servo_serial
Richard Barnette180efe62016-12-02 23:20:44 +0000316 self._server = servo_host.get_servod_server_proxy()
Ruben Rodriguez Buchillonf3f63b52020-01-27 17:56:49 -0800317 self._servo_type = self.get_servo_version()
J. Richard Barnette0c9c5882014-06-11 15:27:05 -0700318 self._power_state = _PowerStateController(self)
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700319 self._uart = _Uart(self)
Fang Dengafb88142013-05-30 17:44:31 -0700320 self._usb_state = None
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700321 self._programmer = None
Dana Goyette4dc0adc2019-05-06 14:51:53 -0700322 self._prev_log_inode = None
323 self._prev_log_size = 0
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800324
Ricky Liang0dd379c2014-04-23 16:29:08 +0800325 @property
326 def servo_serial(self):
327 """Returns the serial number of the servo board."""
328 return self._servo_serial
329
Dana Goyette0b6e6402019-10-04 11:09:24 -0700330 def rotate_servod_logs(self, filename=None, directory=None):
331 """Save the latest servod log into a local directory, then rotate logs.
Dana Goyette4dc0adc2019-05-06 14:51:53 -0700332
Dana Goyette0b6e6402019-10-04 11:09:24 -0700333 The files will be <filename>.DEBUG, <filename>.INFO, <filename>.WARNING,
334 or just <filename>.log if not using split level logging.
335
336 @param filename: local filename prefix (no file extension) to use.
337 If None, rotate log but don't save it.
338 @param directory: local directory to save logs into (if unset, use cwd)
Dana Goyette4dc0adc2019-05-06 14:51:53 -0700339 """
Dana Goyette0b6e6402019-10-04 11:09:24 -0700340 if self.is_localhost():
341 # Local servod usually runs without log-dir, so can't be collected.
342 # TODO(crbug.com/1011516): allow localhost when rotation is enabled
343 return
344
345 log_dir = '/var/log/servod_%s' % self._servo_host.servo_port
346
347 if filename:
Dana Goyettec8613342019-11-13 15:03:28 -0800348 logging.info("Saving servod logs: %s/%s.*", directory or '.',
349 filename)
Dana Goyette0b6e6402019-10-04 11:09:24 -0700350 # TODO(crrev.com/c/1793030): remove no-level case once CL is pushed
351 for level_name in ('', 'DEBUG', 'INFO', 'WARNING'):
352
353 remote_path = os.path.join(log_dir, 'latest')
354 if level_name:
355 remote_path += '.%s' % level_name
356
357 local_path = '%s.%s' % (filename, level_name or 'log')
358 if directory:
359 local_path = os.path.join(directory, local_path)
360
361 try:
362 self._servo_host.get_file(
363 remote_path, local_path, try_rsync=False)
364
365 except error.AutoservRunError as e:
366 result = e.result_obj
367 if result.exit_status != 0:
368 stderr = result.stderr.strip()
369
370 # File not existing is okay, but warn for anything else.
371 if 'no such' not in stderr.lower():
372 logging.warn(
373 "Couldn't retrieve servod log: %s",
374 stderr or '\n%s' % result)
375
376 try:
377 if os.stat(local_path).st_size == 0:
378 os.unlink(local_path)
379 except EnvironmentError:
380 pass
381
382 else:
383 # No filename given, so caller wants to discard the log lines.
384 # Remove the symlinks to prevent old log-dir links from being
385 # picked up multiple times when using servod without log-dir.
386 remote_path = os.path.join(log_dir, 'latest*')
387 self._servo_host.run(
388 "rm %s" % remote_path,
389 stderr_tee=None, ignore_status=True)
390
391 # Servod log rotation renames current log, then creates a new file with
392 # the old name: log.<date> -> log.<date>.1.tbz2 -> log.<date>.2.tbz2
393
394 # Must rotate after copying, or the copy would be the new, empty file.
395 try:
396 self.set_nocheck('rotate_servod_logs', 'yes')
397 except ControlUnavailableError as e:
Dana Goyettec8613342019-11-13 15:03:28 -0800398 # Missing control (possibly old servod)
Dana Goyette0b6e6402019-10-04 11:09:24 -0700399 logging.warn("Couldn't rotate servod logs: %s", str(e))
Dana Goyettec8613342019-11-13 15:03:28 -0800400 except error.TestFail:
401 # Control exists but gave an error; don't let it fail the test.
402 # The error is already logged in set_nocheck().
403 pass
Ricky Liang0dd379c2014-04-23 16:29:08 +0800404
Tom Wai-Hong Tam22ee0e52013-04-03 13:36:39 +0800405 def get_power_state_controller(self):
J. Richard Barnette75136b32013-03-26 13:38:44 -0700406 """Return the power state controller for this Servo.
407
408 The power state controller provides board-independent
409 interfaces for reset, power-on, power-off operations.
410
411 """
412 return self._power_state
413
Fang Deng5d518f42013-08-02 14:04:32 -0700414
Mary Ruthvena3c52b82019-10-09 14:09:08 -0700415 def initialize_dut(self, cold_reset=False, enable_main=True):
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700416 """Initializes a dut for testing purposes.
417
418 This sets various servo signals back to default values
419 appropriate for the target board. By default, if the DUT
420 is already on, it stays on. If the DUT is powered off
421 before initialization, its state afterward is unspecified.
422
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700423 Rationale: Basic initialization of servo sets the lid open,
424 when there is a lid. This operation won't affect powered on
425 units; however, setting the lid open may power on a unit
J. Richard Barnette75136b32013-03-26 13:38:44 -0700426 that's off, depending on the board type and previous state
427 of the device.
428
J. Richard Barnette4b6af0d2014-06-05 09:57:20 -0700429 If `cold_reset` is a true value, the DUT and its EC will be
430 reset, and the DUT rebooted in normal mode.
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700431
432 @param cold_reset If True, cold reset the device after
433 initialization.
Mary Ruthvena3c52b82019-10-09 14:09:08 -0700434 @param enable_main If True, make sure the main servo device has
435 control of the dut.
436
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700437 """
Mary Ruthvena3c52b82019-10-09 14:09:08 -0700438 if enable_main:
439 self.enable_main_servo_device()
440
Dana Goyettea2f00ea2019-06-26 16:14:12 -0700441 try:
442 self._server.hwinit()
443 except socket.error as e:
444 e.filename = '%s:%s' % (self._servo_host.hostname,
445 self._servo_host.servo_port)
446 raise
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700447 self._usb_state = None
Mengqi Guo51d5bea2020-02-03 18:34:45 -0800448 if self.has_control('usb_mux_oe1'):
449 self.set('usb_mux_oe1', 'on')
450 self.switch_usbkey('off')
451 else:
452 logging.warning('Servod command \'usb_mux_oe1\' is not available. '
453 'Any USB drive related servo routines will fail.')
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700454 self._uart.start_capture()
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700455 if cold_reset:
J. Richard Barnette4b6af0d2014-06-05 09:57:20 -0700456 self._power_state.reset()
J. Richard Barnette1777f5d2014-09-03 15:18:19 -0700457 logging.debug('Servo initialized, version is %s',
458 self._server.get_version())
Ruben Rodriguez Buchillonb0e2a9f2019-08-12 12:40:44 -0700459 if self.has_control('init_keyboard'):
460 # This indicates the servod version does not
461 # have explicit keyboard initialization yet.
462 # Ignore this.
Ruben Rodriguez Buchillon05298242019-05-20 11:29:06 -0700463 # TODO(coconutruben): change this back to set() about a month
464 # after crrev.com/c/1586239 has been merged (or whenever that
465 # logic is in the labstation images).
466 self.set_nocheck('init_keyboard','on')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700467
468
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800469 def is_localhost(self):
470 """Is the servod hosted locally?
471
472 Returns:
473 True if local hosted; otherwise, False.
474 """
Fang Deng5d518f42013-08-02 14:04:32 -0700475 return self._servo_host.is_localhost()
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800476
477
Mary Ruthvenecf12712019-06-26 17:36:21 -0700478 def get_os_version(self):
479 """Returns the chromeos release version."""
480 lsb_release_content = self.system_output('cat /etc/lsb-release',
481 ignore_status=True)
482 return lsbrelease_utils.get_chromeos_release_builder_path(
483 lsb_release_content=lsb_release_content)
484
485
Mary Ruthven83bb5952019-06-27 12:34:05 -0700486 def get_servod_version(self):
487 """Returns the servod version."""
488 result = self._servo_host.run('servod --version')
489 # TODO: use system_output once servod --version prints to stdout
490 stdout = result.stdout.strip()
491 return stdout if stdout else result.stderr.strip()
492
493
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700494 def power_long_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700495 """Simulate a long power button press."""
J. Richard Barnettef12bff22012-07-18 14:41:06 -0700496 # After a long power press, the EC may ignore the next power
497 # button press (at least on Alex). To guarantee that this
498 # won't happen, we need to allow the EC one second to
499 # collect itself.
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800500 # long_press is defined as 8.5s in servod
501 self.set_nocheck('power_key', 'long_press')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700502
503
504 def power_normal_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700505 """Simulate a normal power button press."""
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800506 # press is defined as 1.2s in servod
507 self.set_nocheck('power_key', 'press')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700508
509
510 def power_short_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700511 """Simulate a short power button press."""
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800512 # tab is defined as 0.2s in servod
513 self.set_nocheck('power_key', 'tab')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700514
515
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800516 def power_key(self, press_secs='tab'):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700517 """Simulate a power button press.
518
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800519 @param press_secs: int, float, str; time to press key in seconds or
520 known shorthand: 'tab' 'press' 'long_press'
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700521 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800522 self.set_nocheck('power_key', press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700523
524
Ruben Zakarian47c1d4f2019-09-30 14:17:41 -0700525 def pwr_button(self, action='press'):
526 """Simulate a power button press.
527
528 @param action: str; could be press or could be release.
529 """
530 self.set_nocheck('pwr_button', action)
531
532
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700533 def lid_open(self):
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800534 """Simulate opening the lid and raise exception if all attempts fail"""
535 self.set('lid_open', 'yes')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700536
537
538 def lid_close(self):
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800539 """Simulate closing the lid and raise exception if all attempts fail
Craig Harrison48997262011-06-27 14:31:10 -0700540
541 Waits 6 seconds to ensure the device is fully asleep before returning.
542 """
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800543 self.set('lid_open', 'no')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700544 time.sleep(Servo.SLEEP_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700545
Ruben Zakarian47c1d4f2019-09-30 14:17:41 -0700546
547 def vbus_power_get(self):
548 """Get current vbus_power."""
549 return self.get('vbus_power')
550
551
Shelley Chenc26575a2015-09-18 10:56:16 -0700552 def volume_up(self, timeout=300):
Kevin Chenge448a8b2016-09-09 10:18:11 -0700553 """Simulate pushing the volume down button.
554
555 @param timeout: Timeout for setting the volume.
556 """
Shelley Chenc26575a2015-09-18 10:56:16 -0700557 self.set_get_all(['volume_up:yes',
558 'sleep:%.4f' % self.SERVO_KEY_PRESS_DELAY,
559 'volume_up:no'])
560 # we need to wait for commands to take effect before moving on
561 time_left = float(timeout)
562 while time_left > 0.0:
563 value = self.get('volume_up')
564 if value == 'no':
565 return
566 time.sleep(self.SHORT_DELAY)
567 time_left = time_left - self.SHORT_DELAY
568 raise error.TestFail("Failed setting volume_up to no")
569
570 def volume_down(self, timeout=300):
Kevin Chenge448a8b2016-09-09 10:18:11 -0700571 """Simulate pushing the volume down button.
572
573 @param timeout: Timeout for setting the volume.
574 """
Shelley Chenc26575a2015-09-18 10:56:16 -0700575 self.set_get_all(['volume_down:yes',
576 'sleep:%.4f' % self.SERVO_KEY_PRESS_DELAY,
577 'volume_down:no'])
578 # we need to wait for commands to take effect before moving on
579 time_left = float(timeout)
580 while time_left > 0.0:
581 value = self.get('volume_down')
582 if value == 'no':
583 return
584 time.sleep(self.SHORT_DELAY)
585 time_left = time_left - self.SHORT_DELAY
586 raise error.TestFail("Failed setting volume_down to no")
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700587
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800588 def ctrl_d(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800589 """Simulate Ctrl-d simultaneous button presses.
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800590
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800591 @param press_secs: int, float, str; time to press key in seconds or
592 known shorthand: 'tab' 'press' 'long_press'
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800593 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800594 self.set_nocheck('ctrl_d', press_secs)
Gediminas Ramanauskas9da80f22012-11-14 12:59:43 -0800595
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800596
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800597 def ctrl_u(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800598 """Simulate Ctrl-u simultaneous button presses.
599
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800600 @param press_secs: int, float, str; time to press key in seconds or
601 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800602 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800603 self.set_nocheck('ctrl_u', press_secs)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700604
605
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800606 def ctrl_enter(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800607 """Simulate Ctrl-enter simultaneous button presses.
608
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800609 @param press_secs: int, float, str; time to press key in seconds or
610 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800611 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800612 self.set_nocheck('ctrl_enter', press_secs)
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -0700613
614
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800615 def ctrl_key(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800616 """Simulate Enter key button press.
617
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800618 @param press_secs: int, float, str; time to press key in seconds or
619 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800620 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800621 self.set_nocheck('ctrl_key', press_secs)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700622
623
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800624 def enter_key(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800625 """Simulate Enter key button press.
626
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800627 @param press_secs: int, float, str; time to press key in seconds or
628 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800629 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800630 self.set_nocheck('enter_key', press_secs)
Todd Broch9753bd42012-03-21 10:15:08 -0700631
632
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800633 def refresh_key(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800634 """Simulate Refresh key (F3) button press.
635
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800636 @param press_secs: int, float, str; time to press key in seconds or
637 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800638 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800639 self.set_nocheck('refresh_key', press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700640
641
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800642 def ctrl_refresh_key(self, press_secs='tab'):
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800643 """Simulate Ctrl and Refresh (F3) simultaneous press.
644
645 This key combination is an alternative of Space key.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800646
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800647 @param press_secs: int, float, str; time to press key in seconds or
648 known shorthand: 'tab' 'press' 'long_press'
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800649 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800650 self.set_nocheck('ctrl_refresh_key', press_secs)
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800651
652
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800653 def imaginary_key(self, press_secs='tab'):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700654 """Simulate imaginary key button press.
655
656 Maps to a key that doesn't physically exist.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800657
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800658 @param press_secs: int, float, str; time to press key in seconds or
659 known shorthand: 'tab' 'press' 'long_press'
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700660 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800661 self.set_nocheck('imaginary_key', press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700662
663
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800664 def sysrq_x(self, press_secs='tab'):
Vincent Palatine7dc9282016-07-14 11:31:58 +0200665 """Simulate Alt VolumeUp X simulataneous press.
666
667 This key combination is the kernel system request (sysrq) X.
668
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800669 @param press_secs: int, float, str; time to press key in seconds or
670 known shorthand: 'tab' 'press' 'long_press'
Vincent Palatine7dc9282016-07-14 11:31:58 +0200671 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800672 self.set_nocheck('sysrq_x', press_secs)
Vincent Palatine7dc9282016-07-14 11:31:58 +0200673
674
Tom Wai-Hong Tam93b5c092015-05-14 02:50:43 +0800675 def toggle_recovery_switch(self):
676 """Toggle recovery switch on and off."""
677 self.enable_recovery_mode()
678 time.sleep(self.REC_TOGGLE_DELAY)
679 self.disable_recovery_mode()
680
681
Craig Harrison6b36b122011-06-28 17:58:43 -0700682 def enable_recovery_mode(self):
683 """Enable recovery mode on device."""
684 self.set('rec_mode', 'on')
685
686
687 def disable_recovery_mode(self):
688 """Disable recovery mode on device."""
689 self.set('rec_mode', 'off')
690
691
Tom Wai-Hong Tamf9ded092015-05-20 05:37:22 +0800692 def toggle_development_switch(self):
693 """Toggle development switch on and off."""
694 self.enable_development_mode()
695 time.sleep(self.DEV_TOGGLE_DELAY)
696 self.disable_development_mode()
697
698
Craig Harrison6b36b122011-06-28 17:58:43 -0700699 def enable_development_mode(self):
700 """Enable development mode on device."""
701 self.set('dev_mode', 'on')
702
703
704 def disable_development_mode(self):
705 """Disable development mode on device."""
706 self.set('dev_mode', 'off')
707
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700708 def boot_devmode(self):
709 """Boot a dev-mode device that is powered off."""
Tom Wai-Hong Tam23869102012-01-17 15:41:30 +0800710 self.power_short_press()
Craig Harrison48997262011-06-27 14:31:10 -0700711 self.pass_devmode()
712
713
714 def pass_devmode(self):
715 """Pass through boot screens in dev-mode."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700716 time.sleep(Servo.BOOT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700717 self.ctrl_d()
Chrome Bot9a1137d2011-07-19 14:35:00 -0700718 time.sleep(Servo.BOOT_DELAY)
Craig Harrison48997262011-06-27 14:31:10 -0700719
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700720
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800721 def get_board(self):
Wai-Hong Tam0f904002017-09-19 15:52:22 -0700722 """Get the board connected to servod."""
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800723 return self._server.get_board()
724
725
Wai-Hong Tam0f904002017-09-19 15:52:22 -0700726 def get_base_board(self):
727 """Get the board of the base connected to servod."""
Wai-Hong Tam7f6169e2017-09-29 09:23:48 -0700728 try:
729 return self._server.get_base_board()
730 except xmlrpclib.Fault as e:
731 # TODO(waihong): Remove the following compatibility check when
732 # the new versions of hdctools are deployed.
733 if 'not supported' in str(e):
734 logging.warning('The servod is too old that get_base_board '
735 'not supported.')
736 return ''
737 raise
Wai-Hong Tam0f904002017-09-19 15:52:22 -0700738
739
Wai-Hong Tamcc399ee2017-12-08 12:43:28 -0800740 def get_ec_active_copy(self):
741 """Get the active copy of the EC image."""
742 return self.get('ec_active_copy')
743
744
Todd Brochefe72cb2012-07-11 19:58:53 -0700745 def _get_xmlrpclib_exception(self, xmlexc):
746 """Get meaningful exception string from xmlrpc.
747
748 Args:
749 xmlexc: xmlrpclib.Fault object
750
751 xmlrpclib.Fault.faultString has the following format:
752
753 <type 'exception type'>:'actual error message'
754
755 Parse and return the real exception from the servod side instead of the
756 less meaningful one like,
757 <Fault 1: "<type 'exceptions.AttributeError'>:'tca6416' object has no
758 attribute 'hw_driver'">
759
760 Returns:
761 string of underlying exception raised in servod.
762 """
763 return re.sub('^.*>:', '', xmlexc.faultString)
764
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -0800765 def has_control(self, ctrl_name, prefix=''):
766 """Query servod server to determine if |ctrl_name| is a valid control.
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700767
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -0800768 @param ctrl_name Name of the control.
769 @param prefix: prefix to route control to correct servo device.
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700770
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -0800771 @returns: true if |ctrl_name| is a known control, false otherwise.
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700772 """
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -0800773 cltr_name = self._build_ctrl_name(ctrl_name, prefix)
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700774 try:
775 # If the control exists, doc() will work.
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -0800776 self._server.doc(ctrl_name)
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700777 return True
778 except xmlrpclib.Fault as e:
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -0800779 if re.search('No control %s' % ctrl_name,
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700780 self._get_xmlrpclib_exception(e)):
781 return False
782 raise e
Todd Brochefe72cb2012-07-11 19:58:53 -0700783
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -0800784 def _build_ctrl_name(self, ctrl_name, prefix):
785 """Helper to build the control name if a prefix is used.
786
787 @param ctrl_name Name of the control.
788 @param prefix: prefix to route control to correct servo device.
789
790 @returns: [|prefix|.]ctrl_name depending on whether prefix is non-empty.
791 """
792 assert ctrl_name
793 if prefix:
794 return '%s.%s' % (prefix, ctrl_name)
795 return ctrl_name
796
797 def get(self, ctrl_name, prefix=''):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700798 """Get the value of a gpio from Servod.
799
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -0800800 @param ctrl_name Name of the control.
801 @param prefix: prefix to route control to correct servo device.
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700802
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -0800803 @returns: server response to |ctrl_name| request.
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700804
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -0800805 @raise ControlUnavailableError: if |ctrl_name| not a known control.
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700806 @raise error.TestFail: for all other failures doing get().
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700807 """
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -0800808 cltr_name = self._build_ctrl_name(ctrl_name, prefix)
Todd Brochefe72cb2012-07-11 19:58:53 -0700809 try:
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -0800810 return self._server.get(ctrl_name)
Todd Brochefe72cb2012-07-11 19:58:53 -0700811 except xmlrpclib.Fault as e:
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700812 err_str = self._get_xmlrpclib_exception(e)
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -0800813 err_msg = "Getting '%s' :: %s" % (ctrl_name, err_str)
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700814 unknown_ctrl = re.findall(NO_CONTROL_RE, err_str)
815 if unknown_ctrl:
816 raise ControlUnavailableError('No control named %r' %
817 unknown_ctrl[0])
818 else:
819 logging.error(err_msg)
820 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700821
822
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -0800823 def set(self, ctrl_name, ctrl_value, prefix=''):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700824 """Set and check the value of a gpio using Servod.
825
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -0800826 @param ctrl_name: Name of the control.
827 @param ctrl_value: New setting for the control.
828 @param prefix: prefix to route control to correct servo device.
Dana Goyette7ff06c92019-10-11 13:38:03 -0700829 @raise error.TestFail: if the control value fails to change.
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700830 """
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -0800831 cltr_name = self._build_ctrl_name(ctrl_name, prefix)
832 self.set_nocheck(ctrl_name, ctrl_value)
Todd Brochcf7c6652012-02-24 13:03:59 -0800833 retry_count = Servo.GET_RETRY_MAX
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -0800834 actual_value = self.get(ctrl_name)
835 while ctrl_value != actual_value and retry_count:
836 logging.warning("%s != %s, retry %d", ctrl_name, ctrl_value,
Dana Goyette7ff06c92019-10-11 13:38:03 -0700837 retry_count)
Todd Brochcf7c6652012-02-24 13:03:59 -0800838 retry_count -= 1
839 time.sleep(Servo.SHORT_DELAY)
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -0800840 actual_value = self.get(ctrl_name)
Dana Goyette7ff06c92019-10-11 13:38:03 -0700841
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -0800842 if ctrl_value != actual_value:
Dana Goyette7ff06c92019-10-11 13:38:03 -0700843 raise error.TestFail(
844 'Servo failed to set %s to %s. Got %s.'
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -0800845 % (ctrl_name, ctrl_value, actual_value))
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700846
847
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -0800848 def set_nocheck(self, ctrl_name, ctrl_value, prefix=''):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700849 """Set the value of a gpio using Servod.
850
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -0800851 @param ctrl_name Name of the control.
852 @param ctrl_value New setting for the control.
853 @param prefix: prefix to route control to correct servo device.
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700854
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -0800855 @raise ControlUnavailableError: if |ctrl_name| not a known control.
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700856 @raise error.TestFail: for all other failures doing set().
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700857 """
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -0800858 cltr_name = self._build_ctrl_name(ctrl_name, prefix)
Ruben Rodriguez Buchillon1c0219c2019-06-25 12:30:44 -0700859 # The real danger here is to pass a None value through the xmlrpc.
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -0800860 assert ctrl_value is not None
861 logging.debug('Setting %s to %r', ctrl_name, ctrl_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700862 try:
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -0800863 self._server.set(ctrl_name, ctrl_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700864 except xmlrpclib.Fault as e:
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700865 err_str = self._get_xmlrpclib_exception(e)
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -0800866 err_msg = "Setting '%s' :: %s" % (ctrl_name, err_str)
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700867 unknown_ctrl = re.findall(NO_CONTROL_RE, err_str)
868 if unknown_ctrl:
869 raise ControlUnavailableError('No control named %r' %
870 unknown_ctrl[0])
871 else:
872 logging.error(err_msg)
873 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700874
875
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800876 def set_get_all(self, controls):
877 """Set &| get one or more control values.
878
879 @param controls: list of strings, controls to set &| get.
880
881 @raise: error.TestError in case error occurs setting/getting values.
882 """
883 rv = []
884 try:
Mary Ruthvendfa45342019-06-25 10:10:17 -0700885 logging.debug('Set/get all: %s', str(controls))
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800886 rv = self._server.set_get_all(controls)
887 except xmlrpclib.Fault as e:
888 # TODO(waihong): Remove the following backward compatibility when
889 # the new versions of hdctools are deployed.
890 if 'not supported' in str(e):
891 logging.warning('The servod is too old that set_get_all '
892 'not supported. Use set and get instead.')
893 for control in controls:
894 if ':' in control:
895 (name, value) = control.split(':')
896 if name == 'sleep':
897 time.sleep(float(value))
898 else:
899 self.set_nocheck(name, value)
900 rv.append(True)
901 else:
902 rv.append(self.get(name))
903 else:
904 err_msg = "Problem with '%s' :: %s" % \
905 (controls, self._get_xmlrpclib_exception(e))
906 raise error.TestFail(err_msg)
907 return rv
908
909
Jon Salzc88e5b62011-11-30 14:38:54 +0800910 # TODO(waihong) It may fail if multiple servo's are connected to the same
911 # host. Should look for a better way, like the USB serial name, to identify
912 # the USB device.
Simran Basi741b5d42012-05-18 11:27:15 -0700913 # TODO(sbasi) Remove this code from autoserv once firmware tests have been
914 # updated.
Kevin Chenga22c4a82016-10-07 14:13:25 -0700915 def probe_host_usb_dev(self, timeout=_USB_PROBE_TIMEOUT):
Jon Salzc88e5b62011-11-30 14:38:54 +0800916 """Probe the USB disk device plugged-in the servo from the host side.
917
Kevin Chengeb06fe72016-08-22 15:26:32 -0700918 It uses servod to discover if there is a usb device attached to it.
Jon Salzc88e5b62011-11-30 14:38:54 +0800919
Kevin Chenga22c4a82016-10-07 14:13:25 -0700920 @param timeout The timeout period when probing for the usb host device.
921
922 @return: String of USB disk path (e.g. '/dev/sdb') or None.
Jon Salzc88e5b62011-11-30 14:38:54 +0800923 """
Ruben Rodriguez Buchillon9a4bfc32018-10-16 20:46:25 +0800924 # Set up Servo's usb mux.
925 self.switch_usbkey('host')
Kevin Chenga22c4a82016-10-07 14:13:25 -0700926 return self._server.probe_host_usb_dev(timeout) or None
Jon Salzc88e5b62011-11-30 14:38:54 +0800927
928
Mike Truty49153d82012-08-21 22:27:30 -0500929 def image_to_servo_usb(self, image_path=None,
930 make_image_noninteractive=False):
931 """Install an image to the USB key plugged into the servo.
932
933 This method may copy any image to the servo USB key including a
934 recovery image or a test image. These images are frequently used
935 for test purposes such as restoring a corrupted image or conducting
936 an upgrade of ec/fw/kernel as part of a test of a specific image part.
937
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700938 @param image_path Path on the host to the recovery image.
939 @param make_image_noninteractive Make the recovery image
940 noninteractive, therefore the DUT
941 will reboot automatically after
942 installation.
Mike Truty49153d82012-08-21 22:27:30 -0500943 """
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700944 # We're about to start plugging/unplugging the USB key. We
945 # don't know the state of the DUT, or what it might choose
946 # to do to the device after hotplug. To avoid surprises,
947 # force the DUT to be off.
948 self._server.hwinit()
Ruben Rodriguez Buchillonb0e2a9f2019-08-12 12:40:44 -0700949 if self.has_control('init_keyboard'):
950 # This indicates the servod version does not
951 # have explicit keyboard initialization yet.
952 # Ignore this.
Ruben Rodriguez Buchillonbd3b74f2019-08-08 16:18:27 -0700953 # TODO(coconutruben): change this back to set() about a month
954 # after crrev.com/c/1586239 has been merged (or whenever that
955 # logic is in the labstation images).
956 self.set_nocheck('init_keyboard','on')
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700957 self._power_state.power_off()
Mike Truty49153d82012-08-21 22:27:30 -0500958
Mike Truty49153d82012-08-21 22:27:30 -0500959 if image_path:
Garry Wangd2656812019-08-07 17:29:16 -0700960 # Set up Servo's usb mux.
961 self.switch_usbkey('host')
Tom Wai-Hong Tam42f136d2012-10-26 11:11:23 +0800962 logging.info('Searching for usb device and copying image to it. '
963 'Please wait a few minutes...')
Mike Truty49153d82012-08-21 22:27:30 -0500964 if not self._server.download_image_to_usb(image_path):
965 logging.error('Failed to transfer requested image to USB. '
966 'Please take a look at Servo Logs.')
967 raise error.AutotestError('Download image to usb failed.')
968 if make_image_noninteractive:
969 logging.info('Making image noninteractive')
970 if not self._server.make_image_noninteractive():
971 logging.error('Failed to make image noninteractive. '
972 'Please take a look at Servo Logs.')
973
Kalin Stoyanovb3c11f32018-05-11 09:02:00 -0700974 def boot_in_recovery_mode(self):
975 """Boot host DUT in recovery mode."""
976 self._power_state.power_on(rec_mode=self._power_state.REC_ON)
977 self.switch_usbkey('dut')
978
Mike Truty49153d82012-08-21 22:27:30 -0500979
Simran Basi741b5d42012-05-18 11:27:15 -0700980 def install_recovery_image(self, image_path=None,
J. Richard Barnetteb6de7e32013-02-14 13:28:04 -0800981 make_image_noninteractive=False):
Dan Shic67f1332016-04-06 12:38:06 -0700982 """Install the recovery image specified by the path onto the DUT.
Jon Salzc88e5b62011-11-30 14:38:54 +0800983
984 This method uses google recovery mode to install a recovery image
985 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 +0800986 board specified by the usb_dev. If no image path is specified
Jon Salzc88e5b62011-11-30 14:38:54 +0800987 we use the recovery image already on the usb image.
988
Dan Shic67f1332016-04-06 12:38:06 -0700989 @param image_path: Path on the host to the recovery image.
990 @param make_image_noninteractive: Make the recovery image
991 noninteractive, therefore the DUT will reboot automatically
992 after installation.
Jon Salzc88e5b62011-11-30 14:38:54 +0800993 """
Mike Truty49153d82012-08-21 22:27:30 -0500994 self.image_to_servo_usb(image_path, make_image_noninteractive)
Garry Wangd2656812019-08-07 17:29:16 -0700995 # Give the DUT some time to power_off if we skip
996 # download image to usb. (crbug.com/982993)
997 if not image_path:
998 time.sleep(10)
Kalin Stoyanovb3c11f32018-05-11 09:02:00 -0700999 self.boot_in_recovery_mode()
Jon Salzc88e5b62011-11-30 14:38:54 +08001000
1001
Vadim Bendeburyb80ba592012-12-07 15:02:34 -08001002 def _scp_image(self, image_path):
1003 """Copy image to the servo host.
1004
1005 When programming a firmware image on the DUT, the image must be
1006 located on the host to which the servo device is connected. Sometimes
1007 servo is controlled by a remote host, in this case the image needs to
Dana Goyetted5a95542019-12-30 11:14:14 -08001008 be transferred to the remote host. This adds the servod port number, to
1009 make sure tests for different DUTs don't trample on each other's files.
Vadim Bendeburyb80ba592012-12-07 15:02:34 -08001010
1011 @param image_path: a string, name of the firmware image file to be
1012 transferred.
1013 @return: a string, full path name of the copied file on the remote.
1014 """
Mary Ruthven401a6012019-12-13 11:39:44 -08001015 name = os.path.basename(image_path)
Dana Goyetted5a95542019-12-30 11:14:14 -08001016 remote_name = 'dut_%s.%s' % (self._servo_host.servo_port, name)
Mary Ruthven401a6012019-12-13 11:39:44 -08001017 dest_path = os.path.join('/tmp', remote_name)
1018 logging.info('Copying %s to %s', name, dest_path)
Fang Deng5d518f42013-08-02 14:04:32 -07001019 self._servo_host.send_file(image_path, dest_path)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -08001020 return dest_path
1021
1022
Dan Shifecdaf42015-07-28 10:17:26 -07001023 def system(self, command, timeout=3600):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -07001024 """Execute the passed in command on the servod host.
1025
1026 @param command Command to be executed.
Dan Shifecdaf42015-07-28 10:17:26 -07001027 @param timeout Maximum number of seconds of runtime allowed. Default to
1028 1 hour.
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -07001029 """
Vadim Bendebury89ec24e2012-12-17 12:54:18 -08001030 logging.info('Will execute on servo host: %s', command)
Fang Deng5d518f42013-08-02 14:04:32 -07001031 self._servo_host.run(command, timeout=timeout)
Vadim Bendebury89ec24e2012-12-17 12:54:18 -08001032
1033
Dan Shifecdaf42015-07-28 10:17:26 -07001034 def system_output(self, command, timeout=3600,
Vadim Bendebury89ec24e2012-12-17 12:54:18 -08001035 ignore_status=False, args=()):
1036 """Execute the passed in command on the servod host, return stdout.
1037
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -07001038 @param command a string, the command to execute
1039 @param timeout an int, max number of seconds to wait til command
Dan Shifecdaf42015-07-28 10:17:26 -07001040 execution completes. Default to 1 hour.
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -07001041 @param ignore_status a Boolean, if true - ignore command's nonzero exit
Vadim Bendebury89ec24e2012-12-17 12:54:18 -08001042 status, otherwise an exception will be thrown
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -07001043 @param args a tuple of strings, each becoming a separate command line
Vadim Bendebury89ec24e2012-12-17 12:54:18 -08001044 parameter for the command
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -07001045 @return command's stdout as a string.
Vadim Bendebury89ec24e2012-12-17 12:54:18 -08001046 """
Fang Deng5d518f42013-08-02 14:04:32 -07001047 return self._servo_host.run(command, timeout=timeout,
1048 ignore_status=ignore_status,
1049 args=args).stdout.strip()
Vadim Bendeburyb80ba592012-12-07 15:02:34 -08001050
1051
Mary Ruthven38d90af2019-08-16 13:13:31 -07001052 def get_servo_version(self, active=False):
Dan Shia5fef052015-05-18 23:28:47 -07001053 """Get the version of the servo, e.g., servo_v2 or servo_v3.
1054
Mary Ruthven38d90af2019-08-16 13:13:31 -07001055 @param active: Only return the servo type with the active device.
Dan Shia5fef052015-05-18 23:28:47 -07001056 @return: The version of the servo.
1057
1058 """
Mary Ruthven38d90af2019-08-16 13:13:31 -07001059 servo_type = self._server.get_version()
1060 if '_and_' not in servo_type or not active:
1061 return servo_type
1062
1063 # If servo v4 is using ccd and servo micro, modify the servo type to
1064 # reflect the active device.
1065 active_device = self.get('active_v4_device')
1066 if active_device in servo_type:
1067 logging.info('%s is active', active_device)
1068 return 'servo_v4_with_' + active_device
1069
1070 logging.warn("%s is active even though it's not in servo type",
1071 active_device)
1072 return servo_type
Dan Shia5fef052015-05-18 23:28:47 -07001073
1074
Mary Ruthvena3c52b82019-10-09 14:09:08 -07001075 def get_main_servo_device(self):
1076 """Return the main servo device"""
Ruben Rodriguez Buchillonf3f63b52020-01-27 17:56:49 -08001077 return self._servo_type.split('_with_')[-1].split('_and_')[0]
Mary Ruthvena3c52b82019-10-09 14:09:08 -07001078
1079
1080 def enable_main_servo_device(self):
1081 """Make sure the main device has control of the dut."""
Mary Ruthven1409d9d2019-10-22 20:44:24 -07001082 # Cr50 detects servo using the EC uart. It doesn't work well if the
1083 # board doesn't use EC uart. The lab active_v4_device doesn't handle
1084 # this correctly. Check ec_uart_pty before trying to change the active
1085 # device.
1086 # TODO(crbug.com/1016842): reenable the setting the main device when
1087 # active device works on labstations.
1088 return
Mary Ruthvena3c52b82019-10-09 14:09:08 -07001089 if not self.has_control('active_v4_device'):
1090 return
1091 self.set('active_v4_device', self.get_main_servo_device())
1092
1093
Ruben Rodriguez Buchillon1823bad2019-09-30 17:04:34 -07001094 def main_device_is_ccd(self):
1095 """Whether the main servo device (no prefixes) is a ccd device."""
Mary Ruthven2724ee62019-07-16 11:16:59 -07001096 servo = self._server.get_version()
1097 return 'ccd_cr50' in servo and 'servo_micro' not in servo
1098
1099
Ruben Rodriguez Buchillon1823bad2019-09-30 17:04:34 -07001100 def main_device_is_flex(self):
1101 """Whether the main servo device (no prefixes) is a legacy device."""
1102 return not self.main_device_is_ccd()
1103
Ruben Rodriguez Buchillon82c94ad2019-09-30 14:49:25 -07001104
1105 def main_device_is_active(self):
1106 """Return whether the main device is the active device.
1107
1108 This is only relevant for a dual setup with ccd and legacy on the same
1109 DUT. The main device is the servo that has no prefix on its controls.
1110 This helper answers the question whether that device is also the
1111 active device or not.
1112 """
1113 # TODO(coconutruben): The current implementation of the dual setup only
1114 # ever has legacy as the main device. Therefore, it suffices to ask
1115 # whether the active device is ccd.
1116 if not self.dts_mode_is_valid():
1117 # Use dts support as a proxy to whether the servo setup could
1118 # support a dual role. Only those setups now support legacy and ccd.
1119 return True
1120 active_device = self.get('active_v4_device')
1121 return 'ccd_cr50' not in active_device
1122
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001123 def _initialize_programmer(self, rw_only=False):
1124 """Initialize the firmware programmer.
1125
1126 @param rw_only: True to initialize a programmer which only
1127 programs the RW portions.
1128 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -07001129 if self._programmer:
1130 return
1131 # Initialize firmware programmer
Ruben Rodriguez Buchillonf3f63b52020-01-27 17:56:49 -08001132 if self._servo_type.startswith('servo_v2'):
J. Richard Barnette8f19b392014-08-11 14:05:39 -07001133 self._programmer = firmware_programmer.ProgrammerV2(self)
Wai-Hong Tamc36c4d22016-09-09 10:39:45 -07001134 self._programmer_rw = firmware_programmer.ProgrammerV2RwOnly(self)
Kevin Chengdf2e29f2016-09-09 02:31:22 -07001135 # Both servo v3 and v4 use the same programming methods so just leverage
1136 # ProgrammerV3 for servo v4 as well.
Ruben Rodriguez Buchillonf3f63b52020-01-27 17:56:49 -08001137 elif (self._servo_type.startswith('servo_v3') or
1138 self._servo_type.startswith('servo_v4')):
Dan Shia5fef052015-05-18 23:28:47 -07001139 self._programmer = firmware_programmer.ProgrammerV3(self)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001140 self._programmer_rw = firmware_programmer.ProgrammerV3RwOnly(self)
J. Richard Barnette8f19b392014-08-11 14:05:39 -07001141 else:
1142 raise error.TestError(
1143 'No firmware programmer for servo version: %s' %
Ruben Rodriguez Buchillonf3f63b52020-01-27 17:56:49 -08001144 self._servo_type)
J. Richard Barnette8f19b392014-08-11 14:05:39 -07001145
1146
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001147 def program_bios(self, image, rw_only=False):
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -08001148 """Program bios on DUT with given image.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -08001149
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -08001150 @param image: a string, file name of the BIOS image to program
1151 on the DUT.
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001152 @param rw_only: True to only program the RW portion of BIOS.
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -08001153
Vadim Bendebury1a7ec632013-02-17 16:22:09 -08001154 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -07001155 self._initialize_programmer()
Ricky Liangc31aab32014-07-03 16:23:29 +08001156 if not self.is_localhost():
1157 image = self._scp_image(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001158 if rw_only:
1159 self._programmer_rw.program_bios(image)
1160 else:
1161 self._programmer.program_bios(image)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -08001162
1163
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001164 def program_ec(self, image, rw_only=False):
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -08001165 """Program ec on DUT with given image.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -08001166
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -08001167 @param image: a string, file name of the EC image to program
1168 on the DUT.
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001169 @param rw_only: True to only program the RW portion of EC.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -08001170
Vadim Bendebury1a7ec632013-02-17 16:22:09 -08001171 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -07001172 self._initialize_programmer()
Ricky Liangc31aab32014-07-03 16:23:29 +08001173 if not self.is_localhost():
1174 image = self._scp_image(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001175 if rw_only:
Congbin Guo42427612019-02-12 10:22:06 -08001176 self._programmer_rw.program_ec(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001177 else:
Congbin Guo42427612019-02-12 10:22:06 -08001178 self._programmer.program_ec(image)
1179
1180
Brent Peterson1cb623a2020-01-09 13:14:28 -08001181 def extract_ec_image(self, board, model, tarball_path):
1182 """Helper function to extract EC image from downloaded tarball.
Congbin Guo42427612019-02-12 10:22:06 -08001183
Shelley Chenac61d5a2019-06-24 15:35:46 -07001184 @param board: The DUT board name.
Congbin Guo42427612019-02-12 10:22:06 -08001185 @param model: The DUT model name.
1186 @param tarball_path: The path of the downloaded build tarball.
Justin TerAvest1d0c1bb2019-12-13 11:53:25 -07001187
Brent Peterson1cb623a2020-01-09 13:14:28 -08001188 @return: Path to extracted EC image.
1189 """
Justin TerAvest1d0c1bb2019-12-13 11:53:25 -07001190 # Best effort; try to retrieve the EC board from the version as
1191 # reported by the EC.
1192 ec_board = None
1193 try:
1194 ec_board = self.get('ec_board')
1195 except Exception as err:
1196 logging.info('Failed to get ec_board value; ignoring')
1197 pass
1198
Brent Peterson1cb623a2020-01-09 13:14:28 -08001199 # Array of candidates for EC image
1200 ec_image_candidates = ['ec.bin',
1201 '%s/ec.bin' % model,
Justin TerAvest1d0c1bb2019-12-13 11:53:25 -07001202 '%s/ec.bin' % board]
1203 if ec_board:
1204 ec_image_candidates.append('%s/ec.bin' % ec_board)
Congbin Guo42427612019-02-12 10:22:06 -08001205
Brent Peterson1cb623a2020-01-09 13:14:28 -08001206 # Extract EC image from tarball
1207 dest_dir = os.path.join(os.path.dirname(tarball_path), 'EC')
1208 ec_image = _extract_image_from_tarball(tarball_path, dest_dir,
1209 ec_image_candidates)
Congbin Guo42427612019-02-12 10:22:06 -08001210
Brent Peterson1cb623a2020-01-09 13:14:28 -08001211 # Return path to EC image
1212 return os.path.join(dest_dir, ec_image)
1213
1214
1215 def extract_bios_image(self, board, model, tarball_path):
1216 """Helper function to extract BIOS image from downloaded tarball.
1217
1218 @param board: The DUT board name.
1219 @param model: The DUT model name.
1220 @param tarball_path: The path of the downloaded build tarball.
1221
1222 @return: Path to extracted BIOS image.
1223 """
1224
1225 # Array of candidates for BIOS image
1226 bios_image_candidates = ['image.bin',
1227 'image-%s.bin' % model,
1228 'image-%s.bin' % board]
1229
1230 # Extract BIOS image from tarball
1231 dest_dir = os.path.join(os.path.dirname(tarball_path), 'BIOS')
1232 bios_image = _extract_image_from_tarball(tarball_path, dest_dir,
1233 bios_image_candidates)
1234
1235 # Return path to BIOS image
1236 return os.path.join(dest_dir, bios_image)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001237
Fang Dengafb88142013-05-30 17:44:31 -07001238
1239 def _switch_usbkey_power(self, power_state, detection_delay=False):
1240 """Switch usbkey power.
1241
1242 This function switches usbkey power by setting the value of
1243 'prtctl4_pwren'. If the power is already in the
1244 requested state, this function simply returns.
1245
1246 @param power_state: A string, 'on' or 'off'.
1247 @param detection_delay: A boolean value, if True, sleep
1248 for |USB_DETECTION_DELAY| after switching
1249 the power on.
1250 """
Kevin Chenga22c4a82016-10-07 14:13:25 -07001251 # TODO(kevcheng): Forgive me for this terrible hack. This is just to
1252 # handle beaglebones that haven't yet updated and have the
1253 # safe_switch_usbkey_power RPC. I'll remove this once all beaglebones
1254 # have been updated and also think about a better way to handle
1255 # situations like this.
1256 try:
1257 self._server.safe_switch_usbkey_power(power_state)
1258 except Exception:
1259 self.set('prtctl4_pwren', power_state)
Fang Dengafb88142013-05-30 17:44:31 -07001260 if power_state == 'off':
1261 time.sleep(self.USB_POWEROFF_DELAY)
1262 elif detection_delay:
1263 time.sleep(self.USB_DETECTION_DELAY)
1264
1265
1266 def switch_usbkey(self, usb_state):
1267 """Connect USB flash stick to either host or DUT, or turn USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001268
1269 This function switches the servo multiplexer to provide electrical
Fang Dengafb88142013-05-30 17:44:31 -07001270 connection between the USB port J3 and either host or DUT side. It
1271 can also be used to turn the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001272
Fang Dengafb88142013-05-30 17:44:31 -07001273 Switching to 'dut' or 'host' is accompanied by powercycling
1274 of the USB stick, because it sometimes gets wedged if the mux
1275 is switched while the stick power is on.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001276
Fang Dengafb88142013-05-30 17:44:31 -07001277 @param usb_state: A string, one of 'dut', 'host', or 'off'.
1278 'dut' and 'host' indicate which side the
1279 USB flash device is required to be connected to.
1280 'off' indicates turning the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001281
Fang Dengafb88142013-05-30 17:44:31 -07001282 @raise: error.TestError in case the parameter is not 'dut'
1283 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001284 """
Fang Dengafb88142013-05-30 17:44:31 -07001285 if self.get_usbkey_direction() == usb_state:
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001286 return
1287
Fang Dengafb88142013-05-30 17:44:31 -07001288 if usb_state == 'off':
Dan Shia5fef052015-05-18 23:28:47 -07001289 self._switch_usbkey_power('off')
1290 self._usb_state = usb_state
1291 return
Fang Dengafb88142013-05-30 17:44:31 -07001292 elif usb_state == 'host':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001293 mux_direction = 'servo_sees_usbkey'
Fang Dengafb88142013-05-30 17:44:31 -07001294 elif usb_state == 'dut':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001295 mux_direction = 'dut_sees_usbkey'
1296 else:
Fang Dengafb88142013-05-30 17:44:31 -07001297 raise error.TestError('Unknown USB state request: %s' % usb_state)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001298
Fang Dengafb88142013-05-30 17:44:31 -07001299 self._switch_usbkey_power('off')
Kevin Chenga22c4a82016-10-07 14:13:25 -07001300 # TODO(kevcheng): Forgive me for this terrible hack. This is just to
1301 # handle beaglebones that haven't yet updated and have the
1302 # safe_switch_usbkey RPC. I'll remove this once all beaglebones have
1303 # been updated and also think about a better way to handle situations
1304 # like this.
1305 try:
1306 self._server.safe_switch_usbkey(mux_direction)
1307 except Exception:
1308 self.set('usb_mux_sel1', mux_direction)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001309 time.sleep(self.USB_POWEROFF_DELAY)
Fang Dengafb88142013-05-30 17:44:31 -07001310 self._switch_usbkey_power('on', usb_state == 'host')
1311 self._usb_state = usb_state
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001312
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001313
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001314 def get_usbkey_direction(self):
Fang Dengafb88142013-05-30 17:44:31 -07001315 """Get which side USB is connected to or 'off' if usb power is off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001316
Fang Dengafb88142013-05-30 17:44:31 -07001317 @return: A string, one of 'dut', 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001318 """
Fang Dengafb88142013-05-30 17:44:31 -07001319 if not self._usb_state:
1320 if self.get('prtctl4_pwren') == 'off':
1321 self._usb_state = 'off'
1322 elif self.get('usb_mux_sel1').startswith('dut'):
1323 self._usb_state = 'dut'
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001324 else:
Fang Dengafb88142013-05-30 17:44:31 -07001325 self._usb_state = 'host'
1326 return self._usb_state
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001327
1328
Wai-Hong Tam60377262018-03-01 10:55:39 -08001329 def set_servo_v4_role(self, role):
1330 """Set the power role of servo v4, either 'src' or 'snk'.
1331
1332 It does nothing if not a servo v4.
1333
1334 @param role: Power role for DUT port on servo v4, either 'src' or 'snk'.
1335 """
Ruben Rodriguez Buchillonf3f63b52020-01-27 17:56:49 -08001336 if self._servo_type.startswith('servo_v4'):
Wai-Hong Tam60377262018-03-01 10:55:39 -08001337 value = self.get('servo_v4_role')
1338 if value != role:
1339 self.set_nocheck('servo_v4_role', role)
1340 else:
1341 logging.debug('Already in the role: %s.', role)
1342 else:
1343 logging.debug('Not a servo v4, unable to set role to %s.', role)
1344
1345
Ruben Rodriguez Buchillon5f7ee682019-09-30 12:50:09 -07001346 def supports_built_in_pd_control(self):
1347 """Return whether the servo type supports pd charging and control."""
Ruben Rodriguez Buchillonf3f63b52020-01-27 17:56:49 -08001348 if 'servo_v4' not in self._servo_type:
Ruben Rodriguez Buchillon5f7ee682019-09-30 12:50:09 -07001349 # Only servo v4 supports this feature.
Ruben Rodriguez Buchillonf3f63b52020-01-27 17:56:49 -08001350 logging.info('%r type does not support pd control.',
1351 self._servo_type)
Ruben Rodriguez Buchillon5f7ee682019-09-30 12:50:09 -07001352 return False
Ruben Rodriguez Buchillonf3f63b52020-01-27 17:56:49 -08001353 # On servo v4, it still needs to be the type-c version.
Ruben Rodriguez Buchillon5f7ee682019-09-30 12:50:09 -07001354 if not self.get('servo_v4_type') == 'type-c':
1355 logging.info('PD controls require a type-c servo v4.')
1356 return False
1357 # Lastly, one cannot really do anything without a plugged in charger.
1358 chg_port_mv = self.get('ppchg5_mv')
1359 if chg_port_mv < V4_CHG_ATTACHED_MIN_VOLTAGE_MV:
1360 logging.warn('It appears that no charger is plugged into servo v4. '
1361 'Charger port voltage: %dmV', chg_port_mv)
1362 return False
1363 logging.info('Charger port voltage: %dmV', chg_port_mv)
1364 return True
1365
Ruben Rodriguez Buchillon82c94ad2019-09-30 14:49:25 -07001366 def dts_mode_is_valid(self):
1367 """Return whether servo setup supports dts mode control for cr50."""
1368 if 'servo_v4' not in self._servo_type:
1369 # Only servo v4 supports this feature.
1370 logging.debug('%r type does not support dts mode control.',
1371 self._servo_type)
1372 return False
1373 # On servo v4, it still needs ot be the type-c version.
1374 if not 'type-c' == self.get('servo_v4_type'):
1375 logging.info('DTS controls require a type-c servo v4.')
1376 return False
1377 return True
1378
1379 def dts_mode_is_safe(self):
1380 """Return whether servo setup supports dts mode without losing access.
1381
1382 DTS mode control exists but the main device might go through ccd.
1383 In that case, it's only safe to control dts mode if the main device
1384 is legacy as otherwise the connection to the main device cuts out.
1385 """
1386 return self.dts_mode_is_valid() and self.main_device_is_flex()
1387
1388 def get_dts_mode(self):
1389 """Return servo dts mode.
1390
1391 @returns: on/off whether dts is on or off
1392 """
1393 if not self.dts_mode_is_valid():
1394 logging.info('Not a valid servo setup. Unable to get dts mode.')
1395 return
1396 return self.get('servo_v4_dts_mode')
1397
1398 def set_dts_mode(self, state):
1399 """Set servo dts mode to off or on.
Mary Ruthven739b2922019-08-22 11:16:06 -07001400
1401 It does nothing if not a servo v4. Disable the ccd watchdog if we're
1402 disabling dts mode. CCD will disconnect. The watchdog only allows CCD
1403 to disconnect for 10 seconds until it kills servod. Disable the
1404 watchdog, so CCD can stay disconnected indefinitely.
1405
1406 @param state: Set servo v4 dts mode 'off' or 'on'.
1407 """
Ruben Rodriguez Buchillon82c94ad2019-09-30 14:49:25 -07001408 if not self.dts_mode_is_valid():
1409 logging.info('Not a valid servo setup. Unable to set dts mode %s.',
1410 state)
Mary Ruthven739b2922019-08-22 11:16:06 -07001411 return
1412
1413 # TODO(mruthven): remove watchdog check once the labstation has been
1414 # updated to have support for modifying the watchdog.
Ruben Rodriguez Buchillonf3f63b52020-01-27 17:56:49 -08001415 set_watchdog = (self.has_control('watchdog') and
1416 'ccd' in self._servo_type)
Mary Ruthven739b2922019-08-22 11:16:06 -07001417 enable_watchdog = state == 'on'
1418
1419 if set_watchdog and not enable_watchdog:
1420 self.set_nocheck('watchdog_remove', 'ccd')
1421
1422 self.set_nocheck('servo_v4_dts_mode', state)
1423
1424 if set_watchdog and enable_watchdog:
1425 self.set_nocheck('watchdog_add', 'ccd')
1426
1427
Congbin Guofc3b8962019-03-22 17:38:46 -07001428 @property
1429 def uart_logs_dir(self):
1430 """Return the directory to save UART logs."""
1431 return self._uart.logs_dir if self._uart else ""
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001432
Congbin Guofc3b8962019-03-22 17:38:46 -07001433
1434 @uart_logs_dir.setter
1435 def uart_logs_dir(self, logs_dir):
1436 """Set directory to save UART logs.
1437
1438 @param logs_dir String of directory name."""
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001439 if self._uart:
Congbin Guofc3b8962019-03-22 17:38:46 -07001440 self._uart.logs_dir = logs_dir
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001441
1442
1443 def close(self):
1444 """Close the servo object."""
1445 if self._uart:
1446 self._uart.stop_capture()
Congbin Guofc3b8962019-03-22 17:38:46 -07001447 self._uart.dump()
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001448 self._uart = None