blob: 7c878f989260330e8d27c60b17a3d196480f2260 [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
29class ControlUnavailableError(error.TestFail):
30 """Custom error class to indicate a control is unavailable on servod."""
31 pass
32
33
Congbin Guo42427612019-02-12 10:22:06 -080034def _extract_image_from_tarball(tarball, dest_dir, image_candidates):
35 """Try extracting the image_candidates from the tarball.
36
37 @param tarball: The path of the tarball.
38 @param dest_path: The path of the destination.
39 @param image_candidates: A tuple of the paths of image candidates.
40
41 @return: The first path from the image candidates, which succeeds, or None
42 if all the image candidates fail.
43 """
44 for image in image_candidates:
45 status = server_utils.system(
46 ('tar xf %s -C %s %s' % (tarball, dest_dir, image)),
47 timeout=60, ignore_status=True)
48 if status == 0:
49 return image
50 return None
51
52
J. Richard Barnette0c9c5882014-06-11 15:27:05 -070053class _PowerStateController(object):
54
55 """Class to provide board-specific power operations.
56
57 This class is responsible for "power on" and "power off"
58 operations that can operate without making assumptions in
59 advance about board state. It offers an interface that
60 abstracts out the different sequences required for different
61 board types.
62
63 """
64
65 # Constants acceptable to be passed for the `rec_mode` parameter
66 # to power_on().
67 #
68 # REC_ON: Boot the DUT in recovery mode, i.e. boot from USB or
69 # SD card.
70 # REC_OFF: Boot in normal mode, i.e. boot from internal storage.
71
72 REC_ON = 'rec'
73 REC_OFF = 'on'
Shelley Chen65938622018-05-16 07:45:54 -070074 REC_ON_FORCE_MRC = 'rec_force_mrc'
J. Richard Barnette0c9c5882014-06-11 15:27:05 -070075
76 # Delay in seconds needed between asserting and de-asserting
77 # warm reset.
78 _RESET_HOLD_TIME = 0.5
79
80 def __init__(self, servo):
81 """Initialize the power state control.
82
83 @param servo Servo object providing the underlying `set` and `get`
84 methods for the target controls.
85
86 """
87 self._servo = servo
88
89 def reset(self):
90 """Force the DUT to reset.
91
92 The DUT is guaranteed to be on at the end of this call,
93 regardless of its previous state, provided that there is
94 working OS software. This also guarantees that the EC has
95 been restarted.
96
97 """
98 self._servo.set_nocheck('power_state', 'reset')
99
100 def warm_reset(self):
101 """Apply warm reset to the DUT.
102
103 This asserts, then de-asserts the 'warm_reset' signal.
104 Generally, this causes the board to restart.
105
106 """
107 self._servo.set_get_all(['warm_reset:on',
108 'sleep:%.4f' % self._RESET_HOLD_TIME,
109 'warm_reset:off'])
110
J. Richard Barnette0c9c5882014-06-11 15:27:05 -0700111 def power_off(self):
112 """Force the DUT to power off.
113
114 The DUT is guaranteed to be off at the end of this call,
115 regardless of its previous state, provided that there is
116 working EC and boot firmware. There is no requirement for
117 working OS software.
118
119 """
120 self._servo.set_nocheck('power_state', 'off')
121
122 def power_on(self, rec_mode=REC_OFF):
123 """Force the DUT to power on.
124
125 Prior to calling this function, the DUT must be powered off,
126 e.g. with a call to `power_off()`.
127
128 At power on, recovery mode is set as specified by the
129 corresponding argument. When booting with recovery mode on, it
130 is the caller's responsibility to unplug/plug in a bootable
131 external storage device.
132
133 If the DUT requires a delay after powering on but before
134 processing inputs such as USB stick insertion, the delay is
135 handled by this method; the caller is not responsible for such
136 delays.
137
138 @param rec_mode Setting of recovery mode to be applied at
139 power on. default: REC_OFF aka 'off'
140
141 """
142 self._servo.set_nocheck('power_state', rec_mode)
143
144
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700145class _Uart(object):
Congbin Guo0f00be82019-04-18 17:51:14 -0700146 """Class to capture UART streams of CPU, EC, Cr50, etc."""
147 _UartToCapture = ('cpu', 'ec', 'cr50', 'servo_v4', 'servo_micro', 'usbpd')
148
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700149 def __init__(self, servo):
150 self._servo = servo
151 self._streams = []
Congbin Guo0f00be82019-04-18 17:51:14 -0700152 self.logs_dir = None
Congbin Guofe4d4e82019-04-18 17:51:14 -0700153
Congbin Guo0f00be82019-04-18 17:51:14 -0700154 def _start_stop_capture(self, uart, start):
155 """Helper function to start/stop capturing on specified UART.
156
157 @param uart: The UART name to start/stop capturing.
158 @param start: True to start capturing, otherwise stop.
159
160 @returns True if the operation completes successfully. False if the UART
161 capturing is not supported or failed due to an error.
162 """
163 logging.debug('%s capturing %s UART.', 'Start' if start else 'Stop',
164 uart)
Congbin Guo39dc6f22019-04-23 23:01:21 +0000165 try:
Congbin Guo0f00be82019-04-18 17:51:14 -0700166 self._servo.set('%s_uart_capture' % uart,
167 'on' if start else 'off')
Congbin Guo39dc6f22019-04-23 23:01:21 +0000168 except error.TestFail as err:
169 if 'No control named' in str(err):
Congbin Guo0f00be82019-04-18 17:51:14 -0700170 logging.debug("The servod doesn't support %s_uart_capture.",
171 uart)
172 else:
173 logging.debug("Can't caputre UART for %s: %s", uart, err)
174 return False
175
176 return True
177
178 def start_capture(self):
179 """Start capturing UART streams."""
180 for uart in self._UartToCapture:
181 if self._start_stop_capture(uart, True):
182 self._streams.append(('%s_uart_stream' % uart, '%s_uart.log' %
183 uart))
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700184
Congbin Guofc3b8962019-03-22 17:38:46 -0700185 def dump(self):
186 """Dump UART streams to log files accordingly."""
Congbin Guo0f00be82019-04-18 17:51:14 -0700187 if not self.logs_dir:
Congbin Guofc3b8962019-03-22 17:38:46 -0700188 return
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700189
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700190 for stream, logfile in self._streams:
Congbin Guo0f00be82019-04-18 17:51:14 -0700191 logfile_fullname = os.path.join(self.logs_dir, logfile)
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700192 try:
193 content = self._servo.get(stream)
194 except Exception as err:
195 logging.warn('Failed to get UART log for %s: %s', stream, err)
196 continue
197
198 # The UART stream may contain non-printable characters, and servo
199 # returns it in string representation. We use `ast.leteral_eval`
200 # to revert it back.
201 with open(logfile_fullname, 'a') as fd:
202 fd.write(ast.literal_eval(content))
203
204 def stop_capture(self):
205 """Stop capturing UART streams."""
Congbin Guo0f00be82019-04-18 17:51:14 -0700206 for uart in self._UartToCapture:
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700207 try:
Congbin Guo0f00be82019-04-18 17:51:14 -0700208 self._start_stop_capture(uart, False)
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700209 except Exception as err:
210 logging.warn('Failed to stop UART logging for %s: %s', uart,
211 err)
212
213
J. Richard Barnette384056b2012-04-16 11:04:46 -0700214class Servo(object):
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700215
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700216 """Manages control of a Servo board.
217
218 Servo is a board developed by hardware group to aide in the debug and
219 control of various partner devices. Servo's features include the simulation
220 of pressing the power button, closing the lid, and pressing Ctrl-d. This
221 class manages setting up and communicating with a servo demon (servod)
222 process. It provides both high-level functions for common servo tasks and
223 low-level functions for directly setting and reading gpios.
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700224
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700225 """
226
Chrome Bot9a1137d2011-07-19 14:35:00 -0700227 # Power button press delays in seconds.
J. Richard Barnetted2e4cbd2012-06-29 12:18:40 -0700228 #
J. Richard Barnette5383f072012-07-26 17:35:40 -0700229 # The EC specification says that 8.0 seconds should be enough
230 # for the long power press. However, some platforms need a bit
231 # more time. Empirical testing has found these requirements:
232 # Alex: 8.2 seconds
233 # ZGB: 8.5 seconds
234 # The actual value is set to the largest known necessary value.
235 #
236 # TODO(jrbarnette) Being generous is the right thing to do for
237 # existing platforms, but if this code is to be used for
238 # qualification of new hardware, we should be less generous.
Chrome Bot9a1137d2011-07-19 14:35:00 -0700239 SHORT_DELAY = 0.1
J. Richard Barnette5383f072012-07-26 17:35:40 -0700240
Todd Broch31c82502011-08-29 08:14:39 -0700241 # Maximum number of times to re-read power button on release.
Todd Brochcf7c6652012-02-24 13:03:59 -0800242 GET_RETRY_MAX = 10
Chrome Bot9a1137d2011-07-19 14:35:00 -0700243
J. Richard Barnette5383f072012-07-26 17:35:40 -0700244 # Delays to deal with DUT state transitions.
Chrome Bot9a1137d2011-07-19 14:35:00 -0700245 SLEEP_DELAY = 6
246 BOOT_DELAY = 10
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700247
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700248 # Default minimum time interval between 'press' and 'release'
249 # keyboard events.
Vic Yang0aca1c22012-11-19 18:33:56 -0800250 SERVO_KEY_PRESS_DELAY = 0.1
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700251
Tom Wai-Hong Tam93b5c092015-05-14 02:50:43 +0800252 # Time to toggle recovery switch on and off.
253 REC_TOGGLE_DELAY = 0.1
254
Tom Wai-Hong Tamf9ded092015-05-20 05:37:22 +0800255 # Time to toggle development switch on and off.
256 DEV_TOGGLE_DELAY = 0.1
257
Jon Salzc88e5b62011-11-30 14:38:54 +0800258 # Time between an usb disk plugged-in and detected in the system.
259 USB_DETECTION_DELAY = 10
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800260 # Time to keep USB power off before and after USB mux direction is changed
261 USB_POWEROFF_DELAY = 2
Jon Salzc88e5b62011-11-30 14:38:54 +0800262
Simran Basib7850bb2013-07-24 12:33:42 -0700263 # Time to wait before timing out on servo initialization.
264 INIT_TIMEOUT_SECS = 10
Simran Basi59331342013-07-12 12:06:29 -0700265
J. Richard Barnette67ccb872012-04-19 16:34:56 -0700266
Ricky Liang0dd379c2014-04-23 16:29:08 +0800267 def __init__(self, servo_host, servo_serial=None):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700268 """Sets up the servo communication infrastructure.
269
Fang Deng5d518f42013-08-02 14:04:32 -0700270 @param servo_host: A ServoHost object representing
271 the host running servod.
Dana Goyettea2f00ea2019-06-26 16:14:12 -0700272 @type servo_host: autotest_lib.server.hosts.servo_host.ServoHost
Ricky Liang0dd379c2014-04-23 16:29:08 +0800273 @param servo_serial: Serial number of the servo board.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700274 """
Fang Deng5d518f42013-08-02 14:04:32 -0700275 # TODO(fdeng): crbug.com/298379
276 # We should move servo_host object out of servo object
277 # to minimize the dependencies on the rest of Autotest.
278 self._servo_host = servo_host
Ricky Liang0dd379c2014-04-23 16:29:08 +0800279 self._servo_serial = servo_serial
Richard Barnette180efe62016-12-02 23:20:44 +0000280 self._server = servo_host.get_servod_server_proxy()
J. Richard Barnette0c9c5882014-06-11 15:27:05 -0700281 self._power_state = _PowerStateController(self)
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700282 self._uart = _Uart(self)
Fang Dengafb88142013-05-30 17:44:31 -0700283 self._usb_state = None
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700284 self._programmer = None
Dana Goyette4dc0adc2019-05-06 14:51:53 -0700285 self._prev_log_inode = None
286 self._prev_log_size = 0
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800287
Ricky Liang0dd379c2014-04-23 16:29:08 +0800288 @property
289 def servo_serial(self):
290 """Returns the serial number of the servo board."""
291 return self._servo_serial
292
Dana Goyette4dc0adc2019-05-06 14:51:53 -0700293 def fetch_servod_log(self, filename=None, skip_old=False):
294 """Save the servod log into the given local file.
295
296 @param filename: save the contents into a file with the given name.
297 @param skip_old: if True, skip past the old data in the log file.
298 @type filename: str
299 @type skip_old: bool
300 @rtype: None
301 """
302 return self._servo_host.fetch_servod_log(filename, skip_old)
Ricky Liang0dd379c2014-04-23 16:29:08 +0800303
Tom Wai-Hong Tam22ee0e52013-04-03 13:36:39 +0800304 def get_power_state_controller(self):
J. Richard Barnette75136b32013-03-26 13:38:44 -0700305 """Return the power state controller for this Servo.
306
307 The power state controller provides board-independent
308 interfaces for reset, power-on, power-off operations.
309
310 """
311 return self._power_state
312
Fang Deng5d518f42013-08-02 14:04:32 -0700313
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700314 def initialize_dut(self, cold_reset=False):
315 """Initializes a dut for testing purposes.
316
317 This sets various servo signals back to default values
318 appropriate for the target board. By default, if the DUT
319 is already on, it stays on. If the DUT is powered off
320 before initialization, its state afterward is unspecified.
321
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700322 Rationale: Basic initialization of servo sets the lid open,
323 when there is a lid. This operation won't affect powered on
324 units; however, setting the lid open may power on a unit
J. Richard Barnette75136b32013-03-26 13:38:44 -0700325 that's off, depending on the board type and previous state
326 of the device.
327
J. Richard Barnette4b6af0d2014-06-05 09:57:20 -0700328 If `cold_reset` is a true value, the DUT and its EC will be
329 reset, and the DUT rebooted in normal mode.
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700330
331 @param cold_reset If True, cold reset the device after
332 initialization.
333 """
Dana Goyettea2f00ea2019-06-26 16:14:12 -0700334 try:
335 self._server.hwinit()
336 except socket.error as e:
337 e.filename = '%s:%s' % (self._servo_host.hostname,
338 self._servo_host.servo_port)
339 raise
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700340 self.set('usb_mux_oe1', 'on')
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700341 self._usb_state = None
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700342 self.switch_usbkey('off')
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700343 self._uart.start_capture()
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700344 if cold_reset:
J. Richard Barnette4b6af0d2014-06-05 09:57:20 -0700345 self._power_state.reset()
J. Richard Barnette1777f5d2014-09-03 15:18:19 -0700346 logging.debug('Servo initialized, version is %s',
347 self._server.get_version())
Ruben Rodriguez Buchillon7121b092019-04-29 12:39:34 -0700348 try:
Ruben Rodriguez Buchillon05298242019-05-20 11:29:06 -0700349 # TODO(coconutruben): change this back to set() about a month
350 # after crrev.com/c/1586239 has been merged (or whenever that
351 # logic is in the labstation images).
352 self.set_nocheck('init_keyboard','on')
Ruben Rodriguez Buchillon7121b092019-04-29 12:39:34 -0700353 except error.TestFail as err:
354 if 'No control named' in str(err):
355 # This indicates the servod version does not
356 # have explicit keyboard initialization yet.
357 # Ignore this.
358 pass
359 else:
360 raise err
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700361
362
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800363 def is_localhost(self):
364 """Is the servod hosted locally?
365
366 Returns:
367 True if local hosted; otherwise, False.
368 """
Fang Deng5d518f42013-08-02 14:04:32 -0700369 return self._servo_host.is_localhost()
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800370
371
Mary Ruthvenecf12712019-06-26 17:36:21 -0700372 def get_os_version(self):
373 """Returns the chromeos release version."""
374 lsb_release_content = self.system_output('cat /etc/lsb-release',
375 ignore_status=True)
376 return lsbrelease_utils.get_chromeos_release_builder_path(
377 lsb_release_content=lsb_release_content)
378
379
Mary Ruthven83bb5952019-06-27 12:34:05 -0700380 def get_servod_version(self):
381 """Returns the servod version."""
382 result = self._servo_host.run('servod --version')
383 # TODO: use system_output once servod --version prints to stdout
384 stdout = result.stdout.strip()
385 return stdout if stdout else result.stderr.strip()
386
387
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700388 def power_long_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700389 """Simulate a long power button press."""
J. Richard Barnettef12bff22012-07-18 14:41:06 -0700390 # After a long power press, the EC may ignore the next power
391 # button press (at least on Alex). To guarantee that this
392 # won't happen, we need to allow the EC one second to
393 # collect itself.
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800394 # long_press is defined as 8.5s in servod
395 self.set_nocheck('power_key', 'long_press')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700396
397
398 def power_normal_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700399 """Simulate a normal power button press."""
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800400 # press is defined as 1.2s in servod
401 self.set_nocheck('power_key', 'press')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700402
403
404 def power_short_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700405 """Simulate a short power button press."""
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800406 # tab is defined as 0.2s in servod
407 self.set_nocheck('power_key', 'tab')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700408
409
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800410 def power_key(self, press_secs='tab'):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700411 """Simulate a power button press.
412
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800413 @param press_secs: int, float, str; time to press key in seconds or
414 known shorthand: 'tab' 'press' 'long_press'
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700415 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800416 self.set_nocheck('power_key', press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700417
418
419 def lid_open(self):
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800420 """Simulate opening the lid and raise exception if all attempts fail"""
421 self.set('lid_open', 'yes')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700422
423
424 def lid_close(self):
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800425 """Simulate closing the lid and raise exception if all attempts fail
Craig Harrison48997262011-06-27 14:31:10 -0700426
427 Waits 6 seconds to ensure the device is fully asleep before returning.
428 """
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800429 self.set('lid_open', 'no')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700430 time.sleep(Servo.SLEEP_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700431
Shelley Chenc26575a2015-09-18 10:56:16 -0700432 def volume_up(self, timeout=300):
Kevin Chenge448a8b2016-09-09 10:18:11 -0700433 """Simulate pushing the volume down button.
434
435 @param timeout: Timeout for setting the volume.
436 """
Shelley Chenc26575a2015-09-18 10:56:16 -0700437 self.set_get_all(['volume_up:yes',
438 'sleep:%.4f' % self.SERVO_KEY_PRESS_DELAY,
439 'volume_up:no'])
440 # we need to wait for commands to take effect before moving on
441 time_left = float(timeout)
442 while time_left > 0.0:
443 value = self.get('volume_up')
444 if value == 'no':
445 return
446 time.sleep(self.SHORT_DELAY)
447 time_left = time_left - self.SHORT_DELAY
448 raise error.TestFail("Failed setting volume_up to no")
449
450 def volume_down(self, timeout=300):
Kevin Chenge448a8b2016-09-09 10:18:11 -0700451 """Simulate pushing the volume down button.
452
453 @param timeout: Timeout for setting the volume.
454 """
Shelley Chenc26575a2015-09-18 10:56:16 -0700455 self.set_get_all(['volume_down:yes',
456 'sleep:%.4f' % self.SERVO_KEY_PRESS_DELAY,
457 'volume_down:no'])
458 # we need to wait for commands to take effect before moving on
459 time_left = float(timeout)
460 while time_left > 0.0:
461 value = self.get('volume_down')
462 if value == 'no':
463 return
464 time.sleep(self.SHORT_DELAY)
465 time_left = time_left - self.SHORT_DELAY
466 raise error.TestFail("Failed setting volume_down to no")
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700467
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800468 def ctrl_d(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800469 """Simulate Ctrl-d simultaneous button presses.
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800470
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800471 @param press_secs: int, float, str; time to press key in seconds or
472 known shorthand: 'tab' 'press' 'long_press'
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800473 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800474 self.set_nocheck('ctrl_d', press_secs)
Gediminas Ramanauskas9da80f22012-11-14 12:59:43 -0800475
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800476
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800477 def ctrl_u(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800478 """Simulate Ctrl-u simultaneous button presses.
479
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800480 @param press_secs: int, float, str; time to press key in seconds or
481 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800482 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800483 self.set_nocheck('ctrl_u', press_secs)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700484
485
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800486 def ctrl_enter(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800487 """Simulate Ctrl-enter simultaneous button presses.
488
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800489 @param press_secs: int, float, str; time to press key in seconds or
490 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800491 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800492 self.set_nocheck('ctrl_enter', press_secs)
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -0700493
494
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800495 def ctrl_key(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800496 """Simulate Enter key button press.
497
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800498 @param press_secs: int, float, str; time to press key in seconds or
499 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800500 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800501 self.set_nocheck('ctrl_key', press_secs)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700502
503
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800504 def enter_key(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800505 """Simulate Enter key button press.
506
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800507 @param press_secs: int, float, str; time to press key in seconds or
508 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800509 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800510 self.set_nocheck('enter_key', press_secs)
Todd Broch9753bd42012-03-21 10:15:08 -0700511
512
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800513 def refresh_key(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800514 """Simulate Refresh key (F3) button press.
515
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800516 @param press_secs: int, float, str; time to press key in seconds or
517 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800518 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800519 self.set_nocheck('refresh_key', press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700520
521
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800522 def ctrl_refresh_key(self, press_secs='tab'):
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800523 """Simulate Ctrl and Refresh (F3) simultaneous press.
524
525 This key combination is an alternative of Space key.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800526
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800527 @param press_secs: int, float, str; time to press key in seconds or
528 known shorthand: 'tab' 'press' 'long_press'
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800529 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800530 self.set_nocheck('ctrl_refresh_key', press_secs)
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800531
532
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800533 def imaginary_key(self, press_secs='tab'):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700534 """Simulate imaginary key button press.
535
536 Maps to a key that doesn't physically exist.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800537
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800538 @param press_secs: int, float, str; time to press key in seconds or
539 known shorthand: 'tab' 'press' 'long_press'
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700540 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800541 self.set_nocheck('imaginary_key', press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700542
543
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800544 def sysrq_x(self, press_secs='tab'):
Vincent Palatine7dc9282016-07-14 11:31:58 +0200545 """Simulate Alt VolumeUp X simulataneous press.
546
547 This key combination is the kernel system request (sysrq) X.
548
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800549 @param press_secs: int, float, str; time to press key in seconds or
550 known shorthand: 'tab' 'press' 'long_press'
Vincent Palatine7dc9282016-07-14 11:31:58 +0200551 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800552 self.set_nocheck('sysrq_x', press_secs)
Vincent Palatine7dc9282016-07-14 11:31:58 +0200553
554
Tom Wai-Hong Tam93b5c092015-05-14 02:50:43 +0800555 def toggle_recovery_switch(self):
556 """Toggle recovery switch on and off."""
557 self.enable_recovery_mode()
558 time.sleep(self.REC_TOGGLE_DELAY)
559 self.disable_recovery_mode()
560
561
Craig Harrison6b36b122011-06-28 17:58:43 -0700562 def enable_recovery_mode(self):
563 """Enable recovery mode on device."""
564 self.set('rec_mode', 'on')
565
566
567 def disable_recovery_mode(self):
568 """Disable recovery mode on device."""
569 self.set('rec_mode', 'off')
570
571
Tom Wai-Hong Tamf9ded092015-05-20 05:37:22 +0800572 def toggle_development_switch(self):
573 """Toggle development switch on and off."""
574 self.enable_development_mode()
575 time.sleep(self.DEV_TOGGLE_DELAY)
576 self.disable_development_mode()
577
578
Craig Harrison6b36b122011-06-28 17:58:43 -0700579 def enable_development_mode(self):
580 """Enable development mode on device."""
581 self.set('dev_mode', 'on')
582
583
584 def disable_development_mode(self):
585 """Disable development mode on device."""
586 self.set('dev_mode', 'off')
587
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700588 def boot_devmode(self):
589 """Boot a dev-mode device that is powered off."""
Tom Wai-Hong Tam23869102012-01-17 15:41:30 +0800590 self.power_short_press()
Craig Harrison48997262011-06-27 14:31:10 -0700591 self.pass_devmode()
592
593
594 def pass_devmode(self):
595 """Pass through boot screens in dev-mode."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700596 time.sleep(Servo.BOOT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700597 self.ctrl_d()
Chrome Bot9a1137d2011-07-19 14:35:00 -0700598 time.sleep(Servo.BOOT_DELAY)
Craig Harrison48997262011-06-27 14:31:10 -0700599
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700600
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800601 def get_board(self):
Wai-Hong Tam0f904002017-09-19 15:52:22 -0700602 """Get the board connected to servod."""
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800603 return self._server.get_board()
604
605
Wai-Hong Tam0f904002017-09-19 15:52:22 -0700606 def get_base_board(self):
607 """Get the board of the base connected to servod."""
Wai-Hong Tam7f6169e2017-09-29 09:23:48 -0700608 try:
609 return self._server.get_base_board()
610 except xmlrpclib.Fault as e:
611 # TODO(waihong): Remove the following compatibility check when
612 # the new versions of hdctools are deployed.
613 if 'not supported' in str(e):
614 logging.warning('The servod is too old that get_base_board '
615 'not supported.')
616 return ''
617 raise
Wai-Hong Tam0f904002017-09-19 15:52:22 -0700618
619
Wai-Hong Tamcc399ee2017-12-08 12:43:28 -0800620 def get_ec_active_copy(self):
621 """Get the active copy of the EC image."""
622 return self.get('ec_active_copy')
623
624
Todd Brochefe72cb2012-07-11 19:58:53 -0700625 def _get_xmlrpclib_exception(self, xmlexc):
626 """Get meaningful exception string from xmlrpc.
627
628 Args:
629 xmlexc: xmlrpclib.Fault object
630
631 xmlrpclib.Fault.faultString has the following format:
632
633 <type 'exception type'>:'actual error message'
634
635 Parse and return the real exception from the servod side instead of the
636 less meaningful one like,
637 <Fault 1: "<type 'exceptions.AttributeError'>:'tca6416' object has no
638 attribute 'hw_driver'">
639
640 Returns:
641 string of underlying exception raised in servod.
642 """
643 return re.sub('^.*>:', '', xmlexc.faultString)
644
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700645 def has_control(self, control):
646 """Query servod server to determine if |control| is a valid control.
647
648 @param control: str, control name to query
649
650 @returns: true if |control| is a known control, false otherwise.
651 """
652 assert control
653 try:
654 # If the control exists, doc() will work.
655 self._server.doc(control)
656 return True
657 except xmlrpclib.Fault as e:
658 if re.search('No control %s' % control,
659 self._get_xmlrpclib_exception(e)):
660 return False
661 raise e
Todd Brochefe72cb2012-07-11 19:58:53 -0700662
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700663 def get(self, gpio_name):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700664 """Get the value of a gpio from Servod.
665
666 @param gpio_name Name of the gpio.
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700667
668 @returns: server response to |gpio_name| request.
669
670 @raise ControlUnavailableError: if |gpio_name| not a known control.
671 @raise error.TestFail: for all other failures doing get().
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700672 """
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700673 assert gpio_name
Todd Brochefe72cb2012-07-11 19:58:53 -0700674 try:
Simran Basi1cbc5f22013-07-17 14:23:58 -0700675 return self._server.get(gpio_name)
Todd Brochefe72cb2012-07-11 19:58:53 -0700676 except xmlrpclib.Fault as e:
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700677 err_str = self._get_xmlrpclib_exception(e)
678 err_msg = "Getting '%s' :: %s" % (gpio_name, err_str)
679 unknown_ctrl = re.findall(NO_CONTROL_RE, err_str)
680 if unknown_ctrl:
681 raise ControlUnavailableError('No control named %r' %
682 unknown_ctrl[0])
683 else:
684 logging.error(err_msg)
685 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700686
687
688 def set(self, gpio_name, gpio_value):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700689 """Set and check the value of a gpio using Servod.
690
691 @param gpio_name Name of the gpio.
692 @param gpio_value New setting for the gpio.
693 """
Chrome Bot9a1137d2011-07-19 14:35:00 -0700694 self.set_nocheck(gpio_name, gpio_value)
Todd Brochcf7c6652012-02-24 13:03:59 -0800695 retry_count = Servo.GET_RETRY_MAX
696 while gpio_value != self.get(gpio_name) and retry_count:
Ilja H. Friedel04be2bd2014-05-07 21:29:59 -0700697 logging.warning("%s != %s, retry %d", gpio_name, gpio_value,
Todd Brochcf7c6652012-02-24 13:03:59 -0800698 retry_count)
699 retry_count -= 1
700 time.sleep(Servo.SHORT_DELAY)
701 if not retry_count:
702 assert gpio_value == self.get(gpio_name), \
703 'Servo failed to set %s to %s' % (gpio_name, gpio_value)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700704
705
706 def set_nocheck(self, gpio_name, gpio_value):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700707 """Set the value of a gpio using Servod.
708
709 @param gpio_name Name of the gpio.
710 @param gpio_value New setting for the gpio.
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700711
712 @raise ControlUnavailableError: if |gpio_name| not a known control.
713 @raise error.TestFail: for all other failures doing set().
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700714 """
Ruben Rodriguez Buchillon1c0219c2019-06-25 12:30:44 -0700715 # The real danger here is to pass a None value through the xmlrpc.
716 assert gpio_name and gpio_value is not None
Mary Ruthvendfa45342019-06-25 10:10:17 -0700717 logging.debug('Setting %s to %r', gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700718 try:
Simran Basi1cbc5f22013-07-17 14:23:58 -0700719 self._server.set(gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700720 except xmlrpclib.Fault as e:
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700721 err_str = self._get_xmlrpclib_exception(e)
722 err_msg = "Setting '%s' :: %s" % (gpio_name, err_str)
723 unknown_ctrl = re.findall(NO_CONTROL_RE, err_str)
724 if unknown_ctrl:
725 raise ControlUnavailableError('No control named %r' %
726 unknown_ctrl[0])
727 else:
728 logging.error(err_msg)
729 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700730
731
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800732 def set_get_all(self, controls):
733 """Set &| get one or more control values.
734
735 @param controls: list of strings, controls to set &| get.
736
737 @raise: error.TestError in case error occurs setting/getting values.
738 """
739 rv = []
740 try:
Mary Ruthvendfa45342019-06-25 10:10:17 -0700741 logging.debug('Set/get all: %s', str(controls))
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800742 rv = self._server.set_get_all(controls)
743 except xmlrpclib.Fault as e:
744 # TODO(waihong): Remove the following backward compatibility when
745 # the new versions of hdctools are deployed.
746 if 'not supported' in str(e):
747 logging.warning('The servod is too old that set_get_all '
748 'not supported. Use set and get instead.')
749 for control in controls:
750 if ':' in control:
751 (name, value) = control.split(':')
752 if name == 'sleep':
753 time.sleep(float(value))
754 else:
755 self.set_nocheck(name, value)
756 rv.append(True)
757 else:
758 rv.append(self.get(name))
759 else:
760 err_msg = "Problem with '%s' :: %s" % \
761 (controls, self._get_xmlrpclib_exception(e))
762 raise error.TestFail(err_msg)
763 return rv
764
765
Jon Salzc88e5b62011-11-30 14:38:54 +0800766 # TODO(waihong) It may fail if multiple servo's are connected to the same
767 # host. Should look for a better way, like the USB serial name, to identify
768 # the USB device.
Simran Basi741b5d42012-05-18 11:27:15 -0700769 # TODO(sbasi) Remove this code from autoserv once firmware tests have been
770 # updated.
Kevin Chenga22c4a82016-10-07 14:13:25 -0700771 def probe_host_usb_dev(self, timeout=_USB_PROBE_TIMEOUT):
Jon Salzc88e5b62011-11-30 14:38:54 +0800772 """Probe the USB disk device plugged-in the servo from the host side.
773
Kevin Chengeb06fe72016-08-22 15:26:32 -0700774 It uses servod to discover if there is a usb device attached to it.
Jon Salzc88e5b62011-11-30 14:38:54 +0800775
Kevin Chenga22c4a82016-10-07 14:13:25 -0700776 @param timeout The timeout period when probing for the usb host device.
777
778 @return: String of USB disk path (e.g. '/dev/sdb') or None.
Jon Salzc88e5b62011-11-30 14:38:54 +0800779 """
Ruben Rodriguez Buchillon9a4bfc32018-10-16 20:46:25 +0800780 # Set up Servo's usb mux.
781 self.switch_usbkey('host')
Kevin Chenga22c4a82016-10-07 14:13:25 -0700782 return self._server.probe_host_usb_dev(timeout) or None
Jon Salzc88e5b62011-11-30 14:38:54 +0800783
784
Mike Truty49153d82012-08-21 22:27:30 -0500785 def image_to_servo_usb(self, image_path=None,
786 make_image_noninteractive=False):
787 """Install an image to the USB key plugged into the servo.
788
789 This method may copy any image to the servo USB key including a
790 recovery image or a test image. These images are frequently used
791 for test purposes such as restoring a corrupted image or conducting
792 an upgrade of ec/fw/kernel as part of a test of a specific image part.
793
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700794 @param image_path Path on the host to the recovery image.
795 @param make_image_noninteractive Make the recovery image
796 noninteractive, therefore the DUT
797 will reboot automatically after
798 installation.
Mike Truty49153d82012-08-21 22:27:30 -0500799 """
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700800 # We're about to start plugging/unplugging the USB key. We
801 # don't know the state of the DUT, or what it might choose
802 # to do to the device after hotplug. To avoid surprises,
803 # force the DUT to be off.
804 self._server.hwinit()
Ruben Rodriguez Buchillonbd3b74f2019-08-08 16:18:27 -0700805 try:
806 # TODO(coconutruben): change this back to set() about a month
807 # after crrev.com/c/1586239 has been merged (or whenever that
808 # logic is in the labstation images).
809 self.set_nocheck('init_keyboard','on')
810 except error.TestFail as err:
811 if 'No control named' in str(err):
812 # This indicates the servod version does not
813 # have explicit keyboard initialization yet.
814 # Ignore this.
815 pass
816 else:
817 raise err
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700818 self._power_state.power_off()
Mike Truty49153d82012-08-21 22:27:30 -0500819
Mike Truty49153d82012-08-21 22:27:30 -0500820 if image_path:
Garry Wangd2656812019-08-07 17:29:16 -0700821 # Set up Servo's usb mux.
822 self.switch_usbkey('host')
Tom Wai-Hong Tam42f136d2012-10-26 11:11:23 +0800823 logging.info('Searching for usb device and copying image to it. '
824 'Please wait a few minutes...')
Mike Truty49153d82012-08-21 22:27:30 -0500825 if not self._server.download_image_to_usb(image_path):
826 logging.error('Failed to transfer requested image to USB. '
827 'Please take a look at Servo Logs.')
828 raise error.AutotestError('Download image to usb failed.')
829 if make_image_noninteractive:
830 logging.info('Making image noninteractive')
831 if not self._server.make_image_noninteractive():
832 logging.error('Failed to make image noninteractive. '
833 'Please take a look at Servo Logs.')
834
Kalin Stoyanovb3c11f32018-05-11 09:02:00 -0700835 def boot_in_recovery_mode(self):
836 """Boot host DUT in recovery mode."""
837 self._power_state.power_on(rec_mode=self._power_state.REC_ON)
838 self.switch_usbkey('dut')
839
Mike Truty49153d82012-08-21 22:27:30 -0500840
Simran Basi741b5d42012-05-18 11:27:15 -0700841 def install_recovery_image(self, image_path=None,
J. Richard Barnetteb6de7e32013-02-14 13:28:04 -0800842 make_image_noninteractive=False):
Dan Shic67f1332016-04-06 12:38:06 -0700843 """Install the recovery image specified by the path onto the DUT.
Jon Salzc88e5b62011-11-30 14:38:54 +0800844
845 This method uses google recovery mode to install a recovery image
846 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 +0800847 board specified by the usb_dev. If no image path is specified
Jon Salzc88e5b62011-11-30 14:38:54 +0800848 we use the recovery image already on the usb image.
849
Dan Shic67f1332016-04-06 12:38:06 -0700850 @param image_path: Path on the host to the recovery image.
851 @param make_image_noninteractive: Make the recovery image
852 noninteractive, therefore the DUT will reboot automatically
853 after installation.
Jon Salzc88e5b62011-11-30 14:38:54 +0800854 """
Mike Truty49153d82012-08-21 22:27:30 -0500855 self.image_to_servo_usb(image_path, make_image_noninteractive)
Garry Wangd2656812019-08-07 17:29:16 -0700856 # Give the DUT some time to power_off if we skip
857 # download image to usb. (crbug.com/982993)
858 if not image_path:
859 time.sleep(10)
Kalin Stoyanovb3c11f32018-05-11 09:02:00 -0700860 self.boot_in_recovery_mode()
Jon Salzc88e5b62011-11-30 14:38:54 +0800861
862
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800863 def _scp_image(self, image_path):
864 """Copy image to the servo host.
865
866 When programming a firmware image on the DUT, the image must be
867 located on the host to which the servo device is connected. Sometimes
868 servo is controlled by a remote host, in this case the image needs to
869 be transferred to the remote host.
870
871 @param image_path: a string, name of the firmware image file to be
872 transferred.
873 @return: a string, full path name of the copied file on the remote.
874 """
875
876 dest_path = os.path.join('/tmp', os.path.basename(image_path))
Fang Deng5d518f42013-08-02 14:04:32 -0700877 self._servo_host.send_file(image_path, dest_path)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800878 return dest_path
879
880
Dan Shifecdaf42015-07-28 10:17:26 -0700881 def system(self, command, timeout=3600):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700882 """Execute the passed in command on the servod host.
883
884 @param command Command to be executed.
Dan Shifecdaf42015-07-28 10:17:26 -0700885 @param timeout Maximum number of seconds of runtime allowed. Default to
886 1 hour.
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700887 """
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800888 logging.info('Will execute on servo host: %s', command)
Fang Deng5d518f42013-08-02 14:04:32 -0700889 self._servo_host.run(command, timeout=timeout)
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800890
891
Dan Shifecdaf42015-07-28 10:17:26 -0700892 def system_output(self, command, timeout=3600,
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800893 ignore_status=False, args=()):
894 """Execute the passed in command on the servod host, return stdout.
895
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700896 @param command a string, the command to execute
897 @param timeout an int, max number of seconds to wait til command
Dan Shifecdaf42015-07-28 10:17:26 -0700898 execution completes. Default to 1 hour.
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700899 @param ignore_status a Boolean, if true - ignore command's nonzero exit
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800900 status, otherwise an exception will be thrown
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700901 @param args a tuple of strings, each becoming a separate command line
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800902 parameter for the command
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700903 @return command's stdout as a string.
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800904 """
Fang Deng5d518f42013-08-02 14:04:32 -0700905 return self._servo_host.run(command, timeout=timeout,
906 ignore_status=ignore_status,
907 args=args).stdout.strip()
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800908
909
Mary Ruthven38d90af2019-08-16 13:13:31 -0700910 def get_servo_version(self, active=False):
Dan Shia5fef052015-05-18 23:28:47 -0700911 """Get the version of the servo, e.g., servo_v2 or servo_v3.
912
Mary Ruthven38d90af2019-08-16 13:13:31 -0700913 @param active: Only return the servo type with the active device.
Dan Shia5fef052015-05-18 23:28:47 -0700914 @return: The version of the servo.
915
916 """
Mary Ruthven38d90af2019-08-16 13:13:31 -0700917 servo_type = self._server.get_version()
918 if '_and_' not in servo_type or not active:
919 return servo_type
920
921 # If servo v4 is using ccd and servo micro, modify the servo type to
922 # reflect the active device.
923 active_device = self.get('active_v4_device')
924 if active_device in servo_type:
925 logging.info('%s is active', active_device)
926 return 'servo_v4_with_' + active_device
927
928 logging.warn("%s is active even though it's not in servo type",
929 active_device)
930 return servo_type
Dan Shia5fef052015-05-18 23:28:47 -0700931
932
Mary Ruthven2724ee62019-07-16 11:16:59 -0700933 def running_through_ccd(self):
934 """Returns True if the setup is using ccd to run."""
935 servo = self._server.get_version()
936 return 'ccd_cr50' in servo and 'servo_micro' not in servo
937
938
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800939 def _initialize_programmer(self, rw_only=False):
940 """Initialize the firmware programmer.
941
942 @param rw_only: True to initialize a programmer which only
943 programs the RW portions.
944 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700945 if self._programmer:
946 return
947 # Initialize firmware programmer
Dan Shia5fef052015-05-18 23:28:47 -0700948 servo_version = self.get_servo_version()
Dan Shi9cb0eec2014-06-03 09:04:50 -0700949 if servo_version.startswith('servo_v2'):
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700950 self._programmer = firmware_programmer.ProgrammerV2(self)
Wai-Hong Tamc36c4d22016-09-09 10:39:45 -0700951 self._programmer_rw = firmware_programmer.ProgrammerV2RwOnly(self)
Kevin Chengdf2e29f2016-09-09 02:31:22 -0700952 # Both servo v3 and v4 use the same programming methods so just leverage
953 # ProgrammerV3 for servo v4 as well.
954 elif (servo_version.startswith('servo_v3') or
955 servo_version.startswith('servo_v4')):
Dan Shia5fef052015-05-18 23:28:47 -0700956 self._programmer = firmware_programmer.ProgrammerV3(self)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800957 self._programmer_rw = firmware_programmer.ProgrammerV3RwOnly(self)
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700958 else:
959 raise error.TestError(
960 'No firmware programmer for servo version: %s' %
Congbin Guo42427612019-02-12 10:22:06 -0800961 servo_version)
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700962
963
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800964 def program_bios(self, image, rw_only=False):
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800965 """Program bios on DUT with given image.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800966
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800967 @param image: a string, file name of the BIOS image to program
968 on the DUT.
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800969 @param rw_only: True to only program the RW portion of BIOS.
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800970
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800971 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700972 self._initialize_programmer()
Ricky Liangc31aab32014-07-03 16:23:29 +0800973 if not self.is_localhost():
974 image = self._scp_image(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800975 if rw_only:
976 self._programmer_rw.program_bios(image)
977 else:
978 self._programmer.program_bios(image)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800979
980
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800981 def program_ec(self, image, rw_only=False):
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800982 """Program ec on DUT with given image.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800983
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800984 @param image: a string, file name of the EC image to program
985 on the DUT.
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800986 @param rw_only: True to only program the RW portion of EC.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800987
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800988 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700989 self._initialize_programmer()
Ricky Liangc31aab32014-07-03 16:23:29 +0800990 if not self.is_localhost():
991 image = self._scp_image(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800992 if rw_only:
Congbin Guo42427612019-02-12 10:22:06 -0800993 self._programmer_rw.program_ec(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800994 else:
Congbin Guo42427612019-02-12 10:22:06 -0800995 self._programmer.program_ec(image)
996
997
998 def _reprogram(self, tarball_path, firmware_name, image_candidates,
999 rw_only):
1000 """Helper function to reprogram firmware for EC or BIOS.
1001
1002 @param tarball_path: The path of the downloaded build tarball.
1003 @param: firmware_name: either 'EC' or 'BIOS'.
1004 @param image_candidates: A tuple of the paths of image candidates.
1005 @param rw_only: True to only install firmware to its RW portions. Keep
1006 the RO portions unchanged.
1007
1008 @raise: TestError if cannot extract firmware from the tarball.
1009 """
Mary Ruthven771b2012019-08-19 12:18:49 -07001010 dest_dir = os.path.join(os.path.dirname(tarball_path), firmware_name)
1011 # Create the firmware_name subdirectory if it doesn't exist.
1012 if not os.path.exists(dest_dir):
1013 os.mkdir(dest_dir)
Congbin Guo42427612019-02-12 10:22:06 -08001014 image = _extract_image_from_tarball(tarball_path, dest_dir,
1015 image_candidates)
1016 if not image:
1017 if firmware_name == 'EC':
1018 logging.info('Not a Chrome EC, ignore re-programming it')
1019 return
1020 else:
1021 raise error.TestError('Failed to extract the %s image from '
1022 'tarball' % firmware_name)
1023
1024 logging.info('Will re-program %s %snow', firmware_name,
1025 'RW ' if rw_only else '')
1026
1027 if firmware_name == 'EC':
1028 self.program_ec(os.path.join(dest_dir, image), rw_only)
1029 else:
1030 self.program_bios(os.path.join(dest_dir, image), rw_only)
1031
1032
Shelley Chenac61d5a2019-06-24 15:35:46 -07001033 def program_firmware(self, board, model, tarball_path, rw_only=False):
Congbin Guo42427612019-02-12 10:22:06 -08001034 """Program firmware (EC, if applied, and BIOS) of the DUT.
1035
Shelley Chenac61d5a2019-06-24 15:35:46 -07001036 @param board: The DUT board name.
Congbin Guo42427612019-02-12 10:22:06 -08001037 @param model: The DUT model name.
1038 @param tarball_path: The path of the downloaded build tarball.
1039 @param rw_only: True to only install firmware to its RW portions. Keep
1040 the RO portions unchanged.
1041 """
Shelley Chenac61d5a2019-06-24 15:35:46 -07001042 ap_image_candidates = ('image.bin', 'image-%s.bin' % model,
1043 'image-%s.bin' % board)
1044 ec_image_candidates = ('ec.bin', '%s/ec.bin' % model,
1045 '%s/ec.bin' % board)
Congbin Guo42427612019-02-12 10:22:06 -08001046
1047 self._reprogram(tarball_path, 'EC', ec_image_candidates, rw_only)
1048 self._reprogram(tarball_path, 'BIOS', ap_image_candidates, rw_only)
1049
1050 self.get_power_state_controller().reset()
1051 time.sleep(Servo.BOOT_DELAY)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001052
Fang Dengafb88142013-05-30 17:44:31 -07001053
1054 def _switch_usbkey_power(self, power_state, detection_delay=False):
1055 """Switch usbkey power.
1056
1057 This function switches usbkey power by setting the value of
1058 'prtctl4_pwren'. If the power is already in the
1059 requested state, this function simply returns.
1060
1061 @param power_state: A string, 'on' or 'off'.
1062 @param detection_delay: A boolean value, if True, sleep
1063 for |USB_DETECTION_DELAY| after switching
1064 the power on.
1065 """
Kevin Chenga22c4a82016-10-07 14:13:25 -07001066 # TODO(kevcheng): Forgive me for this terrible hack. This is just to
1067 # handle beaglebones that haven't yet updated and have the
1068 # safe_switch_usbkey_power RPC. I'll remove this once all beaglebones
1069 # have been updated and also think about a better way to handle
1070 # situations like this.
1071 try:
1072 self._server.safe_switch_usbkey_power(power_state)
1073 except Exception:
1074 self.set('prtctl4_pwren', power_state)
Fang Dengafb88142013-05-30 17:44:31 -07001075 if power_state == 'off':
1076 time.sleep(self.USB_POWEROFF_DELAY)
1077 elif detection_delay:
1078 time.sleep(self.USB_DETECTION_DELAY)
1079
1080
1081 def switch_usbkey(self, usb_state):
1082 """Connect USB flash stick to either host or DUT, or turn USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001083
1084 This function switches the servo multiplexer to provide electrical
Fang Dengafb88142013-05-30 17:44:31 -07001085 connection between the USB port J3 and either host or DUT side. It
1086 can also be used to turn the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001087
Fang Dengafb88142013-05-30 17:44:31 -07001088 Switching to 'dut' or 'host' is accompanied by powercycling
1089 of the USB stick, because it sometimes gets wedged if the mux
1090 is switched while the stick power is on.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001091
Fang Dengafb88142013-05-30 17:44:31 -07001092 @param usb_state: A string, one of 'dut', 'host', or 'off'.
1093 'dut' and 'host' indicate which side the
1094 USB flash device is required to be connected to.
1095 'off' indicates turning the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001096
Fang Dengafb88142013-05-30 17:44:31 -07001097 @raise: error.TestError in case the parameter is not 'dut'
1098 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001099 """
Fang Dengafb88142013-05-30 17:44:31 -07001100 if self.get_usbkey_direction() == usb_state:
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001101 return
1102
Fang Dengafb88142013-05-30 17:44:31 -07001103 if usb_state == 'off':
Dan Shia5fef052015-05-18 23:28:47 -07001104 self._switch_usbkey_power('off')
1105 self._usb_state = usb_state
1106 return
Fang Dengafb88142013-05-30 17:44:31 -07001107 elif usb_state == 'host':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001108 mux_direction = 'servo_sees_usbkey'
Fang Dengafb88142013-05-30 17:44:31 -07001109 elif usb_state == 'dut':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001110 mux_direction = 'dut_sees_usbkey'
1111 else:
Fang Dengafb88142013-05-30 17:44:31 -07001112 raise error.TestError('Unknown USB state request: %s' % usb_state)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001113
Fang Dengafb88142013-05-30 17:44:31 -07001114 self._switch_usbkey_power('off')
Kevin Chenga22c4a82016-10-07 14:13:25 -07001115 # TODO(kevcheng): Forgive me for this terrible hack. This is just to
1116 # handle beaglebones that haven't yet updated and have the
1117 # safe_switch_usbkey RPC. I'll remove this once all beaglebones have
1118 # been updated and also think about a better way to handle situations
1119 # like this.
1120 try:
1121 self._server.safe_switch_usbkey(mux_direction)
1122 except Exception:
1123 self.set('usb_mux_sel1', mux_direction)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001124 time.sleep(self.USB_POWEROFF_DELAY)
Fang Dengafb88142013-05-30 17:44:31 -07001125 self._switch_usbkey_power('on', usb_state == 'host')
1126 self._usb_state = usb_state
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001127
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001128
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001129 def get_usbkey_direction(self):
Fang Dengafb88142013-05-30 17:44:31 -07001130 """Get which side USB is connected to or 'off' if usb power is off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001131
Fang Dengafb88142013-05-30 17:44:31 -07001132 @return: A string, one of 'dut', 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001133 """
Fang Dengafb88142013-05-30 17:44:31 -07001134 if not self._usb_state:
1135 if self.get('prtctl4_pwren') == 'off':
1136 self._usb_state = 'off'
1137 elif self.get('usb_mux_sel1').startswith('dut'):
1138 self._usb_state = 'dut'
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001139 else:
Fang Dengafb88142013-05-30 17:44:31 -07001140 self._usb_state = 'host'
1141 return self._usb_state
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001142
1143
Wai-Hong Tam60377262018-03-01 10:55:39 -08001144 def set_servo_v4_role(self, role):
1145 """Set the power role of servo v4, either 'src' or 'snk'.
1146
1147 It does nothing if not a servo v4.
1148
1149 @param role: Power role for DUT port on servo v4, either 'src' or 'snk'.
1150 """
1151 servo_version = self.get_servo_version()
1152 if servo_version.startswith('servo_v4'):
1153 value = self.get('servo_v4_role')
1154 if value != role:
1155 self.set_nocheck('servo_v4_role', role)
1156 else:
1157 logging.debug('Already in the role: %s.', role)
1158 else:
1159 logging.debug('Not a servo v4, unable to set role to %s.', role)
1160
1161
Mary Ruthven739b2922019-08-22 11:16:06 -07001162 def set_servo_v4_dts_mode(self, state):
1163 """Set servo v4 dts mode to off or on.
1164
1165 It does nothing if not a servo v4. Disable the ccd watchdog if we're
1166 disabling dts mode. CCD will disconnect. The watchdog only allows CCD
1167 to disconnect for 10 seconds until it kills servod. Disable the
1168 watchdog, so CCD can stay disconnected indefinitely.
1169
1170 @param state: Set servo v4 dts mode 'off' or 'on'.
1171 """
1172 servo_version = self.get_servo_version()
1173 if not servo_version.startswith('servo_v4'):
1174 logging.debug('Not a servo v4, unable to set dts mode %s.', state)
1175 return
1176
1177 # TODO(mruthven): remove watchdog check once the labstation has been
1178 # updated to have support for modifying the watchdog.
1179 set_watchdog = self.has_control('watchdog') and 'ccd' in servo_version
1180 enable_watchdog = state == 'on'
1181
1182 if set_watchdog and not enable_watchdog:
1183 self.set_nocheck('watchdog_remove', 'ccd')
1184
1185 self.set_nocheck('servo_v4_dts_mode', state)
1186
1187 if set_watchdog and enable_watchdog:
1188 self.set_nocheck('watchdog_add', 'ccd')
1189
1190
Congbin Guofc3b8962019-03-22 17:38:46 -07001191 @property
1192 def uart_logs_dir(self):
1193 """Return the directory to save UART logs."""
1194 return self._uart.logs_dir if self._uart else ""
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001195
Congbin Guofc3b8962019-03-22 17:38:46 -07001196
1197 @uart_logs_dir.setter
1198 def uart_logs_dir(self, logs_dir):
1199 """Set directory to save UART logs.
1200
1201 @param logs_dir String of directory name."""
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001202 if self._uart:
Congbin Guofc3b8962019-03-22 17:38:46 -07001203 self._uart.logs_dir = logs_dir
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001204
1205
1206 def close(self):
1207 """Close the servo object."""
1208 if self._uart:
1209 self._uart.stop_capture()
Congbin Guofc3b8962019-03-22 17:38:46 -07001210 self._uart.dump()
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001211 self._uart = None