blob: 4373dfe0d704827c41307a46d50b1b7befa08bb1 [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 Barnette8f19b392014-08-11 14:05:39 -0700447 self.set('usb_mux_oe1', 'on')
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700448 self._usb_state = None
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700449 self.switch_usbkey('off')
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700450 self._uart.start_capture()
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700451 if cold_reset:
J. Richard Barnette4b6af0d2014-06-05 09:57:20 -0700452 self._power_state.reset()
J. Richard Barnette1777f5d2014-09-03 15:18:19 -0700453 logging.debug('Servo initialized, version is %s',
454 self._server.get_version())
Ruben Rodriguez Buchillonb0e2a9f2019-08-12 12:40:44 -0700455 if self.has_control('init_keyboard'):
456 # This indicates the servod version does not
457 # have explicit keyboard initialization yet.
458 # Ignore this.
Ruben Rodriguez Buchillon05298242019-05-20 11:29:06 -0700459 # TODO(coconutruben): change this back to set() about a month
460 # after crrev.com/c/1586239 has been merged (or whenever that
461 # logic is in the labstation images).
462 self.set_nocheck('init_keyboard','on')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700463
464
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800465 def is_localhost(self):
466 """Is the servod hosted locally?
467
468 Returns:
469 True if local hosted; otherwise, False.
470 """
Fang Deng5d518f42013-08-02 14:04:32 -0700471 return self._servo_host.is_localhost()
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800472
473
Mary Ruthvenecf12712019-06-26 17:36:21 -0700474 def get_os_version(self):
475 """Returns the chromeos release version."""
476 lsb_release_content = self.system_output('cat /etc/lsb-release',
477 ignore_status=True)
478 return lsbrelease_utils.get_chromeos_release_builder_path(
479 lsb_release_content=lsb_release_content)
480
481
Mary Ruthven83bb5952019-06-27 12:34:05 -0700482 def get_servod_version(self):
483 """Returns the servod version."""
484 result = self._servo_host.run('servod --version')
485 # TODO: use system_output once servod --version prints to stdout
486 stdout = result.stdout.strip()
487 return stdout if stdout else result.stderr.strip()
488
489
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700490 def power_long_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700491 """Simulate a long power button press."""
J. Richard Barnettef12bff22012-07-18 14:41:06 -0700492 # After a long power press, the EC may ignore the next power
493 # button press (at least on Alex). To guarantee that this
494 # won't happen, we need to allow the EC one second to
495 # collect itself.
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800496 # long_press is defined as 8.5s in servod
497 self.set_nocheck('power_key', 'long_press')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700498
499
500 def power_normal_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700501 """Simulate a normal power button press."""
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800502 # press is defined as 1.2s in servod
503 self.set_nocheck('power_key', 'press')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700504
505
506 def power_short_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700507 """Simulate a short power button press."""
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800508 # tab is defined as 0.2s in servod
509 self.set_nocheck('power_key', 'tab')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700510
511
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800512 def power_key(self, press_secs='tab'):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700513 """Simulate a power button press.
514
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800515 @param press_secs: int, float, str; time to press key in seconds or
516 known shorthand: 'tab' 'press' 'long_press'
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700517 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800518 self.set_nocheck('power_key', press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700519
520
Ruben Zakarian47c1d4f2019-09-30 14:17:41 -0700521 def pwr_button(self, action='press'):
522 """Simulate a power button press.
523
524 @param action: str; could be press or could be release.
525 """
526 self.set_nocheck('pwr_button', action)
527
528
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700529 def lid_open(self):
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800530 """Simulate opening the lid and raise exception if all attempts fail"""
531 self.set('lid_open', 'yes')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700532
533
534 def lid_close(self):
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800535 """Simulate closing the lid and raise exception if all attempts fail
Craig Harrison48997262011-06-27 14:31:10 -0700536
537 Waits 6 seconds to ensure the device is fully asleep before returning.
538 """
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800539 self.set('lid_open', 'no')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700540 time.sleep(Servo.SLEEP_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700541
Ruben Zakarian47c1d4f2019-09-30 14:17:41 -0700542
543 def vbus_power_get(self):
544 """Get current vbus_power."""
545 return self.get('vbus_power')
546
547
Shelley Chenc26575a2015-09-18 10:56:16 -0700548 def volume_up(self, timeout=300):
Kevin Chenge448a8b2016-09-09 10:18:11 -0700549 """Simulate pushing the volume down button.
550
551 @param timeout: Timeout for setting the volume.
552 """
Shelley Chenc26575a2015-09-18 10:56:16 -0700553 self.set_get_all(['volume_up:yes',
554 'sleep:%.4f' % self.SERVO_KEY_PRESS_DELAY,
555 'volume_up:no'])
556 # we need to wait for commands to take effect before moving on
557 time_left = float(timeout)
558 while time_left > 0.0:
559 value = self.get('volume_up')
560 if value == 'no':
561 return
562 time.sleep(self.SHORT_DELAY)
563 time_left = time_left - self.SHORT_DELAY
564 raise error.TestFail("Failed setting volume_up to no")
565
566 def volume_down(self, timeout=300):
Kevin Chenge448a8b2016-09-09 10:18:11 -0700567 """Simulate pushing the volume down button.
568
569 @param timeout: Timeout for setting the volume.
570 """
Shelley Chenc26575a2015-09-18 10:56:16 -0700571 self.set_get_all(['volume_down:yes',
572 'sleep:%.4f' % self.SERVO_KEY_PRESS_DELAY,
573 'volume_down:no'])
574 # we need to wait for commands to take effect before moving on
575 time_left = float(timeout)
576 while time_left > 0.0:
577 value = self.get('volume_down')
578 if value == 'no':
579 return
580 time.sleep(self.SHORT_DELAY)
581 time_left = time_left - self.SHORT_DELAY
582 raise error.TestFail("Failed setting volume_down to no")
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700583
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800584 def ctrl_d(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800585 """Simulate Ctrl-d simultaneous button presses.
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800586
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800587 @param press_secs: int, float, str; time to press key in seconds or
588 known shorthand: 'tab' 'press' 'long_press'
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800589 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800590 self.set_nocheck('ctrl_d', press_secs)
Gediminas Ramanauskas9da80f22012-11-14 12:59:43 -0800591
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800592
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800593 def ctrl_u(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800594 """Simulate Ctrl-u simultaneous button presses.
595
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800596 @param press_secs: int, float, str; time to press key in seconds or
597 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800598 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800599 self.set_nocheck('ctrl_u', press_secs)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700600
601
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800602 def ctrl_enter(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800603 """Simulate Ctrl-enter simultaneous button presses.
604
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800605 @param press_secs: int, float, str; time to press key in seconds or
606 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800607 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800608 self.set_nocheck('ctrl_enter', press_secs)
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -0700609
610
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800611 def ctrl_key(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800612 """Simulate Enter key button press.
613
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800614 @param press_secs: int, float, str; time to press key in seconds or
615 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800616 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800617 self.set_nocheck('ctrl_key', press_secs)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700618
619
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800620 def enter_key(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800621 """Simulate Enter key button press.
622
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800623 @param press_secs: int, float, str; time to press key in seconds or
624 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800625 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800626 self.set_nocheck('enter_key', press_secs)
Todd Broch9753bd42012-03-21 10:15:08 -0700627
628
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800629 def refresh_key(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800630 """Simulate Refresh key (F3) button press.
631
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800632 @param press_secs: int, float, str; time to press key in seconds or
633 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800634 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800635 self.set_nocheck('refresh_key', press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700636
637
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800638 def ctrl_refresh_key(self, press_secs='tab'):
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800639 """Simulate Ctrl and Refresh (F3) simultaneous press.
640
641 This key combination is an alternative of Space key.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800642
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800643 @param press_secs: int, float, str; time to press key in seconds or
644 known shorthand: 'tab' 'press' 'long_press'
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800645 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800646 self.set_nocheck('ctrl_refresh_key', press_secs)
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800647
648
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800649 def imaginary_key(self, press_secs='tab'):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700650 """Simulate imaginary key button press.
651
652 Maps to a key that doesn't physically exist.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800653
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800654 @param press_secs: int, float, str; time to press key in seconds or
655 known shorthand: 'tab' 'press' 'long_press'
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700656 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800657 self.set_nocheck('imaginary_key', press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700658
659
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800660 def sysrq_x(self, press_secs='tab'):
Vincent Palatine7dc9282016-07-14 11:31:58 +0200661 """Simulate Alt VolumeUp X simulataneous press.
662
663 This key combination is the kernel system request (sysrq) X.
664
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800665 @param press_secs: int, float, str; time to press key in seconds or
666 known shorthand: 'tab' 'press' 'long_press'
Vincent Palatine7dc9282016-07-14 11:31:58 +0200667 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800668 self.set_nocheck('sysrq_x', press_secs)
Vincent Palatine7dc9282016-07-14 11:31:58 +0200669
670
Tom Wai-Hong Tam93b5c092015-05-14 02:50:43 +0800671 def toggle_recovery_switch(self):
672 """Toggle recovery switch on and off."""
673 self.enable_recovery_mode()
674 time.sleep(self.REC_TOGGLE_DELAY)
675 self.disable_recovery_mode()
676
677
Craig Harrison6b36b122011-06-28 17:58:43 -0700678 def enable_recovery_mode(self):
679 """Enable recovery mode on device."""
680 self.set('rec_mode', 'on')
681
682
683 def disable_recovery_mode(self):
684 """Disable recovery mode on device."""
685 self.set('rec_mode', 'off')
686
687
Tom Wai-Hong Tamf9ded092015-05-20 05:37:22 +0800688 def toggle_development_switch(self):
689 """Toggle development switch on and off."""
690 self.enable_development_mode()
691 time.sleep(self.DEV_TOGGLE_DELAY)
692 self.disable_development_mode()
693
694
Craig Harrison6b36b122011-06-28 17:58:43 -0700695 def enable_development_mode(self):
696 """Enable development mode on device."""
697 self.set('dev_mode', 'on')
698
699
700 def disable_development_mode(self):
701 """Disable development mode on device."""
702 self.set('dev_mode', 'off')
703
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700704 def boot_devmode(self):
705 """Boot a dev-mode device that is powered off."""
Tom Wai-Hong Tam23869102012-01-17 15:41:30 +0800706 self.power_short_press()
Craig Harrison48997262011-06-27 14:31:10 -0700707 self.pass_devmode()
708
709
710 def pass_devmode(self):
711 """Pass through boot screens in dev-mode."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700712 time.sleep(Servo.BOOT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700713 self.ctrl_d()
Chrome Bot9a1137d2011-07-19 14:35:00 -0700714 time.sleep(Servo.BOOT_DELAY)
Craig Harrison48997262011-06-27 14:31:10 -0700715
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700716
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800717 def get_board(self):
Wai-Hong Tam0f904002017-09-19 15:52:22 -0700718 """Get the board connected to servod."""
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800719 return self._server.get_board()
720
721
Wai-Hong Tam0f904002017-09-19 15:52:22 -0700722 def get_base_board(self):
723 """Get the board of the base connected to servod."""
Wai-Hong Tam7f6169e2017-09-29 09:23:48 -0700724 try:
725 return self._server.get_base_board()
726 except xmlrpclib.Fault as e:
727 # TODO(waihong): Remove the following compatibility check when
728 # the new versions of hdctools are deployed.
729 if 'not supported' in str(e):
730 logging.warning('The servod is too old that get_base_board '
731 'not supported.')
732 return ''
733 raise
Wai-Hong Tam0f904002017-09-19 15:52:22 -0700734
735
Wai-Hong Tamcc399ee2017-12-08 12:43:28 -0800736 def get_ec_active_copy(self):
737 """Get the active copy of the EC image."""
738 return self.get('ec_active_copy')
739
740
Todd Brochefe72cb2012-07-11 19:58:53 -0700741 def _get_xmlrpclib_exception(self, xmlexc):
742 """Get meaningful exception string from xmlrpc.
743
744 Args:
745 xmlexc: xmlrpclib.Fault object
746
747 xmlrpclib.Fault.faultString has the following format:
748
749 <type 'exception type'>:'actual error message'
750
751 Parse and return the real exception from the servod side instead of the
752 less meaningful one like,
753 <Fault 1: "<type 'exceptions.AttributeError'>:'tca6416' object has no
754 attribute 'hw_driver'">
755
756 Returns:
757 string of underlying exception raised in servod.
758 """
759 return re.sub('^.*>:', '', xmlexc.faultString)
760
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -0800761 def has_control(self, ctrl_name, prefix=''):
762 """Query servod server to determine if |ctrl_name| is a valid control.
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700763
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -0800764 @param ctrl_name Name of the control.
765 @param prefix: prefix to route control to correct servo device.
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700766
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -0800767 @returns: true if |ctrl_name| is a known control, false otherwise.
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700768 """
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -0800769 cltr_name = self._build_ctrl_name(ctrl_name, prefix)
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700770 try:
771 # If the control exists, doc() will work.
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -0800772 self._server.doc(ctrl_name)
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700773 return True
774 except xmlrpclib.Fault as e:
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -0800775 if re.search('No control %s' % ctrl_name,
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700776 self._get_xmlrpclib_exception(e)):
777 return False
778 raise e
Todd Brochefe72cb2012-07-11 19:58:53 -0700779
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -0800780 def _build_ctrl_name(self, ctrl_name, prefix):
781 """Helper to build the control name if a prefix is used.
782
783 @param ctrl_name Name of the control.
784 @param prefix: prefix to route control to correct servo device.
785
786 @returns: [|prefix|.]ctrl_name depending on whether prefix is non-empty.
787 """
788 assert ctrl_name
789 if prefix:
790 return '%s.%s' % (prefix, ctrl_name)
791 return ctrl_name
792
793 def get(self, ctrl_name, prefix=''):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700794 """Get the value of a gpio from Servod.
795
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -0800796 @param ctrl_name Name of the control.
797 @param prefix: prefix to route control to correct servo device.
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700798
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -0800799 @returns: server response to |ctrl_name| request.
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700800
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -0800801 @raise ControlUnavailableError: if |ctrl_name| not a known control.
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700802 @raise error.TestFail: for all other failures doing get().
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700803 """
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -0800804 cltr_name = self._build_ctrl_name(ctrl_name, prefix)
Todd Brochefe72cb2012-07-11 19:58:53 -0700805 try:
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -0800806 return self._server.get(ctrl_name)
Todd Brochefe72cb2012-07-11 19:58:53 -0700807 except xmlrpclib.Fault as e:
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700808 err_str = self._get_xmlrpclib_exception(e)
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -0800809 err_msg = "Getting '%s' :: %s" % (ctrl_name, err_str)
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700810 unknown_ctrl = re.findall(NO_CONTROL_RE, err_str)
811 if unknown_ctrl:
812 raise ControlUnavailableError('No control named %r' %
813 unknown_ctrl[0])
814 else:
815 logging.error(err_msg)
816 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700817
818
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -0800819 def set(self, ctrl_name, ctrl_value, prefix=''):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700820 """Set and check the value of a gpio using Servod.
821
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -0800822 @param ctrl_name: Name of the control.
823 @param ctrl_value: New setting for the control.
824 @param prefix: prefix to route control to correct servo device.
Dana Goyette7ff06c92019-10-11 13:38:03 -0700825 @raise error.TestFail: if the control value fails to change.
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700826 """
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -0800827 cltr_name = self._build_ctrl_name(ctrl_name, prefix)
828 self.set_nocheck(ctrl_name, ctrl_value)
Todd Brochcf7c6652012-02-24 13:03:59 -0800829 retry_count = Servo.GET_RETRY_MAX
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -0800830 actual_value = self.get(ctrl_name)
831 while ctrl_value != actual_value and retry_count:
832 logging.warning("%s != %s, retry %d", ctrl_name, ctrl_value,
Dana Goyette7ff06c92019-10-11 13:38:03 -0700833 retry_count)
Todd Brochcf7c6652012-02-24 13:03:59 -0800834 retry_count -= 1
835 time.sleep(Servo.SHORT_DELAY)
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -0800836 actual_value = self.get(ctrl_name)
Dana Goyette7ff06c92019-10-11 13:38:03 -0700837
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -0800838 if ctrl_value != actual_value:
Dana Goyette7ff06c92019-10-11 13:38:03 -0700839 raise error.TestFail(
840 'Servo failed to set %s to %s. Got %s.'
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -0800841 % (ctrl_name, ctrl_value, actual_value))
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700842
843
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -0800844 def set_nocheck(self, ctrl_name, ctrl_value, prefix=''):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700845 """Set the value of a gpio using Servod.
846
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -0800847 @param ctrl_name Name of the control.
848 @param ctrl_value New setting for the control.
849 @param prefix: prefix to route control to correct servo device.
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700850
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -0800851 @raise ControlUnavailableError: if |ctrl_name| not a known control.
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700852 @raise error.TestFail: for all other failures doing set().
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700853 """
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -0800854 cltr_name = self._build_ctrl_name(ctrl_name, prefix)
Ruben Rodriguez Buchillon1c0219c2019-06-25 12:30:44 -0700855 # The real danger here is to pass a None value through the xmlrpc.
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -0800856 assert ctrl_value is not None
857 logging.debug('Setting %s to %r', ctrl_name, ctrl_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700858 try:
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -0800859 self._server.set(ctrl_name, ctrl_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700860 except xmlrpclib.Fault as e:
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700861 err_str = self._get_xmlrpclib_exception(e)
Ruben Rodriguez Buchillon5d3ced02020-02-04 14:56:35 -0800862 err_msg = "Setting '%s' :: %s" % (ctrl_name, err_str)
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700863 unknown_ctrl = re.findall(NO_CONTROL_RE, err_str)
864 if unknown_ctrl:
865 raise ControlUnavailableError('No control named %r' %
866 unknown_ctrl[0])
867 else:
868 logging.error(err_msg)
869 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700870
871
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800872 def set_get_all(self, controls):
873 """Set &| get one or more control values.
874
875 @param controls: list of strings, controls to set &| get.
876
877 @raise: error.TestError in case error occurs setting/getting values.
878 """
879 rv = []
880 try:
Mary Ruthvendfa45342019-06-25 10:10:17 -0700881 logging.debug('Set/get all: %s', str(controls))
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800882 rv = self._server.set_get_all(controls)
883 except xmlrpclib.Fault as e:
884 # TODO(waihong): Remove the following backward compatibility when
885 # the new versions of hdctools are deployed.
886 if 'not supported' in str(e):
887 logging.warning('The servod is too old that set_get_all '
888 'not supported. Use set and get instead.')
889 for control in controls:
890 if ':' in control:
891 (name, value) = control.split(':')
892 if name == 'sleep':
893 time.sleep(float(value))
894 else:
895 self.set_nocheck(name, value)
896 rv.append(True)
897 else:
898 rv.append(self.get(name))
899 else:
900 err_msg = "Problem with '%s' :: %s" % \
901 (controls, self._get_xmlrpclib_exception(e))
902 raise error.TestFail(err_msg)
903 return rv
904
905
Jon Salzc88e5b62011-11-30 14:38:54 +0800906 # TODO(waihong) It may fail if multiple servo's are connected to the same
907 # host. Should look for a better way, like the USB serial name, to identify
908 # the USB device.
Simran Basi741b5d42012-05-18 11:27:15 -0700909 # TODO(sbasi) Remove this code from autoserv once firmware tests have been
910 # updated.
Kevin Chenga22c4a82016-10-07 14:13:25 -0700911 def probe_host_usb_dev(self, timeout=_USB_PROBE_TIMEOUT):
Jon Salzc88e5b62011-11-30 14:38:54 +0800912 """Probe the USB disk device plugged-in the servo from the host side.
913
Kevin Chengeb06fe72016-08-22 15:26:32 -0700914 It uses servod to discover if there is a usb device attached to it.
Jon Salzc88e5b62011-11-30 14:38:54 +0800915
Kevin Chenga22c4a82016-10-07 14:13:25 -0700916 @param timeout The timeout period when probing for the usb host device.
917
918 @return: String of USB disk path (e.g. '/dev/sdb') or None.
Jon Salzc88e5b62011-11-30 14:38:54 +0800919 """
Ruben Rodriguez Buchillon9a4bfc32018-10-16 20:46:25 +0800920 # Set up Servo's usb mux.
921 self.switch_usbkey('host')
Kevin Chenga22c4a82016-10-07 14:13:25 -0700922 return self._server.probe_host_usb_dev(timeout) or None
Jon Salzc88e5b62011-11-30 14:38:54 +0800923
924
Mike Truty49153d82012-08-21 22:27:30 -0500925 def image_to_servo_usb(self, image_path=None,
926 make_image_noninteractive=False):
927 """Install an image to the USB key plugged into the servo.
928
929 This method may copy any image to the servo USB key including a
930 recovery image or a test image. These images are frequently used
931 for test purposes such as restoring a corrupted image or conducting
932 an upgrade of ec/fw/kernel as part of a test of a specific image part.
933
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700934 @param image_path Path on the host to the recovery image.
935 @param make_image_noninteractive Make the recovery image
936 noninteractive, therefore the DUT
937 will reboot automatically after
938 installation.
Mike Truty49153d82012-08-21 22:27:30 -0500939 """
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700940 # We're about to start plugging/unplugging the USB key. We
941 # don't know the state of the DUT, or what it might choose
942 # to do to the device after hotplug. To avoid surprises,
943 # force the DUT to be off.
944 self._server.hwinit()
Ruben Rodriguez Buchillonb0e2a9f2019-08-12 12:40:44 -0700945 if self.has_control('init_keyboard'):
946 # This indicates the servod version does not
947 # have explicit keyboard initialization yet.
948 # Ignore this.
Ruben Rodriguez Buchillonbd3b74f2019-08-08 16:18:27 -0700949 # TODO(coconutruben): change this back to set() about a month
950 # after crrev.com/c/1586239 has been merged (or whenever that
951 # logic is in the labstation images).
952 self.set_nocheck('init_keyboard','on')
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700953 self._power_state.power_off()
Mike Truty49153d82012-08-21 22:27:30 -0500954
Mike Truty49153d82012-08-21 22:27:30 -0500955 if image_path:
Garry Wangd2656812019-08-07 17:29:16 -0700956 # Set up Servo's usb mux.
957 self.switch_usbkey('host')
Tom Wai-Hong Tam42f136d2012-10-26 11:11:23 +0800958 logging.info('Searching for usb device and copying image to it. '
959 'Please wait a few minutes...')
Mike Truty49153d82012-08-21 22:27:30 -0500960 if not self._server.download_image_to_usb(image_path):
961 logging.error('Failed to transfer requested image to USB. '
962 'Please take a look at Servo Logs.')
963 raise error.AutotestError('Download image to usb failed.')
964 if make_image_noninteractive:
965 logging.info('Making image noninteractive')
966 if not self._server.make_image_noninteractive():
967 logging.error('Failed to make image noninteractive. '
968 'Please take a look at Servo Logs.')
969
Kalin Stoyanovb3c11f32018-05-11 09:02:00 -0700970 def boot_in_recovery_mode(self):
971 """Boot host DUT in recovery mode."""
972 self._power_state.power_on(rec_mode=self._power_state.REC_ON)
973 self.switch_usbkey('dut')
974
Mike Truty49153d82012-08-21 22:27:30 -0500975
Simran Basi741b5d42012-05-18 11:27:15 -0700976 def install_recovery_image(self, image_path=None,
J. Richard Barnetteb6de7e32013-02-14 13:28:04 -0800977 make_image_noninteractive=False):
Dan Shic67f1332016-04-06 12:38:06 -0700978 """Install the recovery image specified by the path onto the DUT.
Jon Salzc88e5b62011-11-30 14:38:54 +0800979
980 This method uses google recovery mode to install a recovery image
981 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 +0800982 board specified by the usb_dev. If no image path is specified
Jon Salzc88e5b62011-11-30 14:38:54 +0800983 we use the recovery image already on the usb image.
984
Dan Shic67f1332016-04-06 12:38:06 -0700985 @param image_path: Path on the host to the recovery image.
986 @param make_image_noninteractive: Make the recovery image
987 noninteractive, therefore the DUT will reboot automatically
988 after installation.
Jon Salzc88e5b62011-11-30 14:38:54 +0800989 """
Mike Truty49153d82012-08-21 22:27:30 -0500990 self.image_to_servo_usb(image_path, make_image_noninteractive)
Garry Wangd2656812019-08-07 17:29:16 -0700991 # Give the DUT some time to power_off if we skip
992 # download image to usb. (crbug.com/982993)
993 if not image_path:
994 time.sleep(10)
Kalin Stoyanovb3c11f32018-05-11 09:02:00 -0700995 self.boot_in_recovery_mode()
Jon Salzc88e5b62011-11-30 14:38:54 +0800996
997
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800998 def _scp_image(self, image_path):
999 """Copy image to the servo host.
1000
1001 When programming a firmware image on the DUT, the image must be
1002 located on the host to which the servo device is connected. Sometimes
1003 servo is controlled by a remote host, in this case the image needs to
Dana Goyetted5a95542019-12-30 11:14:14 -08001004 be transferred to the remote host. This adds the servod port number, to
1005 make sure tests for different DUTs don't trample on each other's files.
Vadim Bendeburyb80ba592012-12-07 15:02:34 -08001006
1007 @param image_path: a string, name of the firmware image file to be
1008 transferred.
1009 @return: a string, full path name of the copied file on the remote.
1010 """
Mary Ruthven401a6012019-12-13 11:39:44 -08001011 name = os.path.basename(image_path)
Dana Goyetted5a95542019-12-30 11:14:14 -08001012 remote_name = 'dut_%s.%s' % (self._servo_host.servo_port, name)
Mary Ruthven401a6012019-12-13 11:39:44 -08001013 dest_path = os.path.join('/tmp', remote_name)
1014 logging.info('Copying %s to %s', name, dest_path)
Fang Deng5d518f42013-08-02 14:04:32 -07001015 self._servo_host.send_file(image_path, dest_path)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -08001016 return dest_path
1017
1018
Dan Shifecdaf42015-07-28 10:17:26 -07001019 def system(self, command, timeout=3600):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -07001020 """Execute the passed in command on the servod host.
1021
1022 @param command Command to be executed.
Dan Shifecdaf42015-07-28 10:17:26 -07001023 @param timeout Maximum number of seconds of runtime allowed. Default to
1024 1 hour.
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -07001025 """
Vadim Bendebury89ec24e2012-12-17 12:54:18 -08001026 logging.info('Will execute on servo host: %s', command)
Fang Deng5d518f42013-08-02 14:04:32 -07001027 self._servo_host.run(command, timeout=timeout)
Vadim Bendebury89ec24e2012-12-17 12:54:18 -08001028
1029
Dan Shifecdaf42015-07-28 10:17:26 -07001030 def system_output(self, command, timeout=3600,
Vadim Bendebury89ec24e2012-12-17 12:54:18 -08001031 ignore_status=False, args=()):
1032 """Execute the passed in command on the servod host, return stdout.
1033
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -07001034 @param command a string, the command to execute
1035 @param timeout an int, max number of seconds to wait til command
Dan Shifecdaf42015-07-28 10:17:26 -07001036 execution completes. Default to 1 hour.
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -07001037 @param ignore_status a Boolean, if true - ignore command's nonzero exit
Vadim Bendebury89ec24e2012-12-17 12:54:18 -08001038 status, otherwise an exception will be thrown
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -07001039 @param args a tuple of strings, each becoming a separate command line
Vadim Bendebury89ec24e2012-12-17 12:54:18 -08001040 parameter for the command
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -07001041 @return command's stdout as a string.
Vadim Bendebury89ec24e2012-12-17 12:54:18 -08001042 """
Fang Deng5d518f42013-08-02 14:04:32 -07001043 return self._servo_host.run(command, timeout=timeout,
1044 ignore_status=ignore_status,
1045 args=args).stdout.strip()
Vadim Bendeburyb80ba592012-12-07 15:02:34 -08001046
1047
Mary Ruthven38d90af2019-08-16 13:13:31 -07001048 def get_servo_version(self, active=False):
Dan Shia5fef052015-05-18 23:28:47 -07001049 """Get the version of the servo, e.g., servo_v2 or servo_v3.
1050
Mary Ruthven38d90af2019-08-16 13:13:31 -07001051 @param active: Only return the servo type with the active device.
Dan Shia5fef052015-05-18 23:28:47 -07001052 @return: The version of the servo.
1053
1054 """
Mary Ruthven38d90af2019-08-16 13:13:31 -07001055 servo_type = self._server.get_version()
1056 if '_and_' not in servo_type or not active:
1057 return servo_type
1058
1059 # If servo v4 is using ccd and servo micro, modify the servo type to
1060 # reflect the active device.
1061 active_device = self.get('active_v4_device')
1062 if active_device in servo_type:
1063 logging.info('%s is active', active_device)
1064 return 'servo_v4_with_' + active_device
1065
1066 logging.warn("%s is active even though it's not in servo type",
1067 active_device)
1068 return servo_type
Dan Shia5fef052015-05-18 23:28:47 -07001069
1070
Mary Ruthvena3c52b82019-10-09 14:09:08 -07001071 def get_main_servo_device(self):
1072 """Return the main servo device"""
Ruben Rodriguez Buchillonf3f63b52020-01-27 17:56:49 -08001073 return self._servo_type.split('_with_')[-1].split('_and_')[0]
Mary Ruthvena3c52b82019-10-09 14:09:08 -07001074
1075
1076 def enable_main_servo_device(self):
1077 """Make sure the main device has control of the dut."""
Mary Ruthven1409d9d2019-10-22 20:44:24 -07001078 # Cr50 detects servo using the EC uart. It doesn't work well if the
1079 # board doesn't use EC uart. The lab active_v4_device doesn't handle
1080 # this correctly. Check ec_uart_pty before trying to change the active
1081 # device.
1082 # TODO(crbug.com/1016842): reenable the setting the main device when
1083 # active device works on labstations.
1084 return
Mary Ruthvena3c52b82019-10-09 14:09:08 -07001085 if not self.has_control('active_v4_device'):
1086 return
1087 self.set('active_v4_device', self.get_main_servo_device())
1088
1089
Ruben Rodriguez Buchillon1823bad2019-09-30 17:04:34 -07001090 def main_device_is_ccd(self):
1091 """Whether the main servo device (no prefixes) is a ccd device."""
Mary Ruthven2724ee62019-07-16 11:16:59 -07001092 servo = self._server.get_version()
1093 return 'ccd_cr50' in servo and 'servo_micro' not in servo
1094
1095
Ruben Rodriguez Buchillon1823bad2019-09-30 17:04:34 -07001096 def main_device_is_flex(self):
1097 """Whether the main servo device (no prefixes) is a legacy device."""
1098 return not self.main_device_is_ccd()
1099
Ruben Rodriguez Buchillon82c94ad2019-09-30 14:49:25 -07001100
1101 def main_device_is_active(self):
1102 """Return whether the main device is the active device.
1103
1104 This is only relevant for a dual setup with ccd and legacy on the same
1105 DUT. The main device is the servo that has no prefix on its controls.
1106 This helper answers the question whether that device is also the
1107 active device or not.
1108 """
1109 # TODO(coconutruben): The current implementation of the dual setup only
1110 # ever has legacy as the main device. Therefore, it suffices to ask
1111 # whether the active device is ccd.
1112 if not self.dts_mode_is_valid():
1113 # Use dts support as a proxy to whether the servo setup could
1114 # support a dual role. Only those setups now support legacy and ccd.
1115 return True
1116 active_device = self.get('active_v4_device')
1117 return 'ccd_cr50' not in active_device
1118
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001119 def _initialize_programmer(self, rw_only=False):
1120 """Initialize the firmware programmer.
1121
1122 @param rw_only: True to initialize a programmer which only
1123 programs the RW portions.
1124 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -07001125 if self._programmer:
1126 return
1127 # Initialize firmware programmer
Ruben Rodriguez Buchillonf3f63b52020-01-27 17:56:49 -08001128 if self._servo_type.startswith('servo_v2'):
J. Richard Barnette8f19b392014-08-11 14:05:39 -07001129 self._programmer = firmware_programmer.ProgrammerV2(self)
Wai-Hong Tamc36c4d22016-09-09 10:39:45 -07001130 self._programmer_rw = firmware_programmer.ProgrammerV2RwOnly(self)
Kevin Chengdf2e29f2016-09-09 02:31:22 -07001131 # Both servo v3 and v4 use the same programming methods so just leverage
1132 # ProgrammerV3 for servo v4 as well.
Ruben Rodriguez Buchillonf3f63b52020-01-27 17:56:49 -08001133 elif (self._servo_type.startswith('servo_v3') or
1134 self._servo_type.startswith('servo_v4')):
Dan Shia5fef052015-05-18 23:28:47 -07001135 self._programmer = firmware_programmer.ProgrammerV3(self)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001136 self._programmer_rw = firmware_programmer.ProgrammerV3RwOnly(self)
J. Richard Barnette8f19b392014-08-11 14:05:39 -07001137 else:
1138 raise error.TestError(
1139 'No firmware programmer for servo version: %s' %
Ruben Rodriguez Buchillonf3f63b52020-01-27 17:56:49 -08001140 self._servo_type)
J. Richard Barnette8f19b392014-08-11 14:05:39 -07001141
1142
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001143 def program_bios(self, image, rw_only=False):
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -08001144 """Program bios on DUT with given image.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -08001145
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -08001146 @param image: a string, file name of the BIOS image to program
1147 on the DUT.
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001148 @param rw_only: True to only program the RW portion of BIOS.
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -08001149
Vadim Bendebury1a7ec632013-02-17 16:22:09 -08001150 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -07001151 self._initialize_programmer()
Ricky Liangc31aab32014-07-03 16:23:29 +08001152 if not self.is_localhost():
1153 image = self._scp_image(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001154 if rw_only:
1155 self._programmer_rw.program_bios(image)
1156 else:
1157 self._programmer.program_bios(image)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -08001158
1159
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001160 def program_ec(self, image, rw_only=False):
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -08001161 """Program ec on DUT with given image.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -08001162
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -08001163 @param image: a string, file name of the EC image to program
1164 on the DUT.
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001165 @param rw_only: True to only program the RW portion of EC.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -08001166
Vadim Bendebury1a7ec632013-02-17 16:22:09 -08001167 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -07001168 self._initialize_programmer()
Ricky Liangc31aab32014-07-03 16:23:29 +08001169 if not self.is_localhost():
1170 image = self._scp_image(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001171 if rw_only:
Congbin Guo42427612019-02-12 10:22:06 -08001172 self._programmer_rw.program_ec(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001173 else:
Congbin Guo42427612019-02-12 10:22:06 -08001174 self._programmer.program_ec(image)
1175
1176
Brent Peterson1cb623a2020-01-09 13:14:28 -08001177 def extract_ec_image(self, board, model, tarball_path):
1178 """Helper function to extract EC image from downloaded tarball.
Congbin Guo42427612019-02-12 10:22:06 -08001179
Shelley Chenac61d5a2019-06-24 15:35:46 -07001180 @param board: The DUT board name.
Congbin Guo42427612019-02-12 10:22:06 -08001181 @param model: The DUT model name.
1182 @param tarball_path: The path of the downloaded build tarball.
Justin TerAvest1d0c1bb2019-12-13 11:53:25 -07001183
Brent Peterson1cb623a2020-01-09 13:14:28 -08001184 @return: Path to extracted EC image.
1185 """
Justin TerAvest1d0c1bb2019-12-13 11:53:25 -07001186 # Best effort; try to retrieve the EC board from the version as
1187 # reported by the EC.
1188 ec_board = None
1189 try:
1190 ec_board = self.get('ec_board')
1191 except Exception as err:
1192 logging.info('Failed to get ec_board value; ignoring')
1193 pass
1194
Brent Peterson1cb623a2020-01-09 13:14:28 -08001195 # Array of candidates for EC image
1196 ec_image_candidates = ['ec.bin',
1197 '%s/ec.bin' % model,
Justin TerAvest1d0c1bb2019-12-13 11:53:25 -07001198 '%s/ec.bin' % board]
1199 if ec_board:
1200 ec_image_candidates.append('%s/ec.bin' % ec_board)
Congbin Guo42427612019-02-12 10:22:06 -08001201
Brent Peterson1cb623a2020-01-09 13:14:28 -08001202 # Extract EC image from tarball
1203 dest_dir = os.path.join(os.path.dirname(tarball_path), 'EC')
1204 ec_image = _extract_image_from_tarball(tarball_path, dest_dir,
1205 ec_image_candidates)
Congbin Guo42427612019-02-12 10:22:06 -08001206
Brent Peterson1cb623a2020-01-09 13:14:28 -08001207 # Return path to EC image
1208 return os.path.join(dest_dir, ec_image)
1209
1210
1211 def extract_bios_image(self, board, model, tarball_path):
1212 """Helper function to extract BIOS image from downloaded tarball.
1213
1214 @param board: The DUT board name.
1215 @param model: The DUT model name.
1216 @param tarball_path: The path of the downloaded build tarball.
1217
1218 @return: Path to extracted BIOS image.
1219 """
1220
1221 # Array of candidates for BIOS image
1222 bios_image_candidates = ['image.bin',
1223 'image-%s.bin' % model,
1224 'image-%s.bin' % board]
1225
1226 # Extract BIOS image from tarball
1227 dest_dir = os.path.join(os.path.dirname(tarball_path), 'BIOS')
1228 bios_image = _extract_image_from_tarball(tarball_path, dest_dir,
1229 bios_image_candidates)
1230
1231 # Return path to BIOS image
1232 return os.path.join(dest_dir, bios_image)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001233
Fang Dengafb88142013-05-30 17:44:31 -07001234
1235 def _switch_usbkey_power(self, power_state, detection_delay=False):
1236 """Switch usbkey power.
1237
1238 This function switches usbkey power by setting the value of
1239 'prtctl4_pwren'. If the power is already in the
1240 requested state, this function simply returns.
1241
1242 @param power_state: A string, 'on' or 'off'.
1243 @param detection_delay: A boolean value, if True, sleep
1244 for |USB_DETECTION_DELAY| after switching
1245 the power on.
1246 """
Kevin Chenga22c4a82016-10-07 14:13:25 -07001247 # TODO(kevcheng): Forgive me for this terrible hack. This is just to
1248 # handle beaglebones that haven't yet updated and have the
1249 # safe_switch_usbkey_power RPC. I'll remove this once all beaglebones
1250 # have been updated and also think about a better way to handle
1251 # situations like this.
1252 try:
1253 self._server.safe_switch_usbkey_power(power_state)
1254 except Exception:
1255 self.set('prtctl4_pwren', power_state)
Fang Dengafb88142013-05-30 17:44:31 -07001256 if power_state == 'off':
1257 time.sleep(self.USB_POWEROFF_DELAY)
1258 elif detection_delay:
1259 time.sleep(self.USB_DETECTION_DELAY)
1260
1261
1262 def switch_usbkey(self, usb_state):
1263 """Connect USB flash stick to either host or DUT, or turn USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001264
1265 This function switches the servo multiplexer to provide electrical
Fang Dengafb88142013-05-30 17:44:31 -07001266 connection between the USB port J3 and either host or DUT side. It
1267 can also be used to turn the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001268
Fang Dengafb88142013-05-30 17:44:31 -07001269 Switching to 'dut' or 'host' is accompanied by powercycling
1270 of the USB stick, because it sometimes gets wedged if the mux
1271 is switched while the stick power is on.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001272
Fang Dengafb88142013-05-30 17:44:31 -07001273 @param usb_state: A string, one of 'dut', 'host', or 'off'.
1274 'dut' and 'host' indicate which side the
1275 USB flash device is required to be connected to.
1276 'off' indicates turning the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001277
Fang Dengafb88142013-05-30 17:44:31 -07001278 @raise: error.TestError in case the parameter is not 'dut'
1279 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001280 """
Fang Dengafb88142013-05-30 17:44:31 -07001281 if self.get_usbkey_direction() == usb_state:
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001282 return
1283
Fang Dengafb88142013-05-30 17:44:31 -07001284 if usb_state == 'off':
Dan Shia5fef052015-05-18 23:28:47 -07001285 self._switch_usbkey_power('off')
1286 self._usb_state = usb_state
1287 return
Fang Dengafb88142013-05-30 17:44:31 -07001288 elif usb_state == 'host':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001289 mux_direction = 'servo_sees_usbkey'
Fang Dengafb88142013-05-30 17:44:31 -07001290 elif usb_state == 'dut':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001291 mux_direction = 'dut_sees_usbkey'
1292 else:
Fang Dengafb88142013-05-30 17:44:31 -07001293 raise error.TestError('Unknown USB state request: %s' % usb_state)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001294
Fang Dengafb88142013-05-30 17:44:31 -07001295 self._switch_usbkey_power('off')
Kevin Chenga22c4a82016-10-07 14:13:25 -07001296 # TODO(kevcheng): Forgive me for this terrible hack. This is just to
1297 # handle beaglebones that haven't yet updated and have the
1298 # safe_switch_usbkey RPC. I'll remove this once all beaglebones have
1299 # been updated and also think about a better way to handle situations
1300 # like this.
1301 try:
1302 self._server.safe_switch_usbkey(mux_direction)
1303 except Exception:
1304 self.set('usb_mux_sel1', mux_direction)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001305 time.sleep(self.USB_POWEROFF_DELAY)
Fang Dengafb88142013-05-30 17:44:31 -07001306 self._switch_usbkey_power('on', usb_state == 'host')
1307 self._usb_state = usb_state
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001308
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001309
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001310 def get_usbkey_direction(self):
Fang Dengafb88142013-05-30 17:44:31 -07001311 """Get which side USB is connected to or 'off' if usb power is off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001312
Fang Dengafb88142013-05-30 17:44:31 -07001313 @return: A string, one of 'dut', 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001314 """
Fang Dengafb88142013-05-30 17:44:31 -07001315 if not self._usb_state:
1316 if self.get('prtctl4_pwren') == 'off':
1317 self._usb_state = 'off'
1318 elif self.get('usb_mux_sel1').startswith('dut'):
1319 self._usb_state = 'dut'
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001320 else:
Fang Dengafb88142013-05-30 17:44:31 -07001321 self._usb_state = 'host'
1322 return self._usb_state
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001323
1324
Wai-Hong Tam60377262018-03-01 10:55:39 -08001325 def set_servo_v4_role(self, role):
1326 """Set the power role of servo v4, either 'src' or 'snk'.
1327
1328 It does nothing if not a servo v4.
1329
1330 @param role: Power role for DUT port on servo v4, either 'src' or 'snk'.
1331 """
Ruben Rodriguez Buchillonf3f63b52020-01-27 17:56:49 -08001332 if self._servo_type.startswith('servo_v4'):
Wai-Hong Tam60377262018-03-01 10:55:39 -08001333 value = self.get('servo_v4_role')
1334 if value != role:
1335 self.set_nocheck('servo_v4_role', role)
1336 else:
1337 logging.debug('Already in the role: %s.', role)
1338 else:
1339 logging.debug('Not a servo v4, unable to set role to %s.', role)
1340
1341
Ruben Rodriguez Buchillon5f7ee682019-09-30 12:50:09 -07001342 def supports_built_in_pd_control(self):
1343 """Return whether the servo type supports pd charging and control."""
Ruben Rodriguez Buchillonf3f63b52020-01-27 17:56:49 -08001344 if 'servo_v4' not in self._servo_type:
Ruben Rodriguez Buchillon5f7ee682019-09-30 12:50:09 -07001345 # Only servo v4 supports this feature.
Ruben Rodriguez Buchillonf3f63b52020-01-27 17:56:49 -08001346 logging.info('%r type does not support pd control.',
1347 self._servo_type)
Ruben Rodriguez Buchillon5f7ee682019-09-30 12:50:09 -07001348 return False
Ruben Rodriguez Buchillonf3f63b52020-01-27 17:56:49 -08001349 # On servo v4, it still needs to be the type-c version.
Ruben Rodriguez Buchillon5f7ee682019-09-30 12:50:09 -07001350 if not self.get('servo_v4_type') == 'type-c':
1351 logging.info('PD controls require a type-c servo v4.')
1352 return False
1353 # Lastly, one cannot really do anything without a plugged in charger.
1354 chg_port_mv = self.get('ppchg5_mv')
1355 if chg_port_mv < V4_CHG_ATTACHED_MIN_VOLTAGE_MV:
1356 logging.warn('It appears that no charger is plugged into servo v4. '
1357 'Charger port voltage: %dmV', chg_port_mv)
1358 return False
1359 logging.info('Charger port voltage: %dmV', chg_port_mv)
1360 return True
1361
Ruben Rodriguez Buchillon82c94ad2019-09-30 14:49:25 -07001362 def dts_mode_is_valid(self):
1363 """Return whether servo setup supports dts mode control for cr50."""
1364 if 'servo_v4' not in self._servo_type:
1365 # Only servo v4 supports this feature.
1366 logging.debug('%r type does not support dts mode control.',
1367 self._servo_type)
1368 return False
1369 # On servo v4, it still needs ot be the type-c version.
1370 if not 'type-c' == self.get('servo_v4_type'):
1371 logging.info('DTS controls require a type-c servo v4.')
1372 return False
1373 return True
1374
1375 def dts_mode_is_safe(self):
1376 """Return whether servo setup supports dts mode without losing access.
1377
1378 DTS mode control exists but the main device might go through ccd.
1379 In that case, it's only safe to control dts mode if the main device
1380 is legacy as otherwise the connection to the main device cuts out.
1381 """
1382 return self.dts_mode_is_valid() and self.main_device_is_flex()
1383
1384 def get_dts_mode(self):
1385 """Return servo dts mode.
1386
1387 @returns: on/off whether dts is on or off
1388 """
1389 if not self.dts_mode_is_valid():
1390 logging.info('Not a valid servo setup. Unable to get dts mode.')
1391 return
1392 return self.get('servo_v4_dts_mode')
1393
1394 def set_dts_mode(self, state):
1395 """Set servo dts mode to off or on.
Mary Ruthven739b2922019-08-22 11:16:06 -07001396
1397 It does nothing if not a servo v4. Disable the ccd watchdog if we're
1398 disabling dts mode. CCD will disconnect. The watchdog only allows CCD
1399 to disconnect for 10 seconds until it kills servod. Disable the
1400 watchdog, so CCD can stay disconnected indefinitely.
1401
1402 @param state: Set servo v4 dts mode 'off' or 'on'.
1403 """
Ruben Rodriguez Buchillon82c94ad2019-09-30 14:49:25 -07001404 if not self.dts_mode_is_valid():
1405 logging.info('Not a valid servo setup. Unable to set dts mode %s.',
1406 state)
Mary Ruthven739b2922019-08-22 11:16:06 -07001407 return
1408
1409 # TODO(mruthven): remove watchdog check once the labstation has been
1410 # updated to have support for modifying the watchdog.
Ruben Rodriguez Buchillonf3f63b52020-01-27 17:56:49 -08001411 set_watchdog = (self.has_control('watchdog') and
1412 'ccd' in self._servo_type)
Mary Ruthven739b2922019-08-22 11:16:06 -07001413 enable_watchdog = state == 'on'
1414
1415 if set_watchdog and not enable_watchdog:
1416 self.set_nocheck('watchdog_remove', 'ccd')
1417
1418 self.set_nocheck('servo_v4_dts_mode', state)
1419
1420 if set_watchdog and enable_watchdog:
1421 self.set_nocheck('watchdog_add', 'ccd')
1422
1423
Congbin Guofc3b8962019-03-22 17:38:46 -07001424 @property
1425 def uart_logs_dir(self):
1426 """Return the directory to save UART logs."""
1427 return self._uart.logs_dir if self._uart else ""
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001428
Congbin Guofc3b8962019-03-22 17:38:46 -07001429
1430 @uart_logs_dir.setter
1431 def uart_logs_dir(self, logs_dir):
1432 """Set directory to save UART logs.
1433
1434 @param logs_dir String of directory name."""
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001435 if self._uart:
Congbin Guofc3b8962019-03-22 17:38:46 -07001436 self._uart.logs_dir = logs_dir
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001437
1438
1439 def close(self):
1440 """Close the servo object."""
1441 if self._uart:
1442 self._uart.stop_capture()
Congbin Guofc3b8962019-03-22 17:38:46 -07001443 self._uart.dump()
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001444 self._uart = None