blob: 581a30709073fcfa8d83578ca9088b1c11fa694b [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)
Ruben Rodriguez Buchillonb0e2a9f2019-08-12 12:40:44 -0700165 uart_cmd = '%s_uart_capture' % uart
166 if self._servo.has_control(uart_cmd):
167 self._servo.set(uart_cmd, 'on' if start else 'off')
168 return True
169 return False
Congbin Guo0f00be82019-04-18 17:51:14 -0700170
171 def start_capture(self):
172 """Start capturing UART streams."""
173 for uart in self._UartToCapture:
174 if self._start_stop_capture(uart, True):
175 self._streams.append(('%s_uart_stream' % uart, '%s_uart.log' %
176 uart))
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700177
Congbin Guofc3b8962019-03-22 17:38:46 -0700178 def dump(self):
179 """Dump UART streams to log files accordingly."""
Congbin Guo0f00be82019-04-18 17:51:14 -0700180 if not self.logs_dir:
Congbin Guofc3b8962019-03-22 17:38:46 -0700181 return
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700182
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700183 for stream, logfile in self._streams:
Congbin Guo0f00be82019-04-18 17:51:14 -0700184 logfile_fullname = os.path.join(self.logs_dir, logfile)
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700185 try:
186 content = self._servo.get(stream)
187 except Exception as err:
188 logging.warn('Failed to get UART log for %s: %s', stream, err)
189 continue
190
191 # The UART stream may contain non-printable characters, and servo
192 # returns it in string representation. We use `ast.leteral_eval`
193 # to revert it back.
194 with open(logfile_fullname, 'a') as fd:
195 fd.write(ast.literal_eval(content))
196
197 def stop_capture(self):
198 """Stop capturing UART streams."""
Congbin Guo0f00be82019-04-18 17:51:14 -0700199 for uart in self._UartToCapture:
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700200 try:
Congbin Guo0f00be82019-04-18 17:51:14 -0700201 self._start_stop_capture(uart, False)
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700202 except Exception as err:
203 logging.warn('Failed to stop UART logging for %s: %s', uart,
204 err)
205
206
J. Richard Barnette384056b2012-04-16 11:04:46 -0700207class Servo(object):
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700208
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700209 """Manages control of a Servo board.
210
211 Servo is a board developed by hardware group to aide in the debug and
212 control of various partner devices. Servo's features include the simulation
213 of pressing the power button, closing the lid, and pressing Ctrl-d. This
214 class manages setting up and communicating with a servo demon (servod)
215 process. It provides both high-level functions for common servo tasks and
216 low-level functions for directly setting and reading gpios.
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700217
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700218 """
219
Chrome Bot9a1137d2011-07-19 14:35:00 -0700220 # Power button press delays in seconds.
J. Richard Barnetted2e4cbd2012-06-29 12:18:40 -0700221 #
J. Richard Barnette5383f072012-07-26 17:35:40 -0700222 # The EC specification says that 8.0 seconds should be enough
223 # for the long power press. However, some platforms need a bit
224 # more time. Empirical testing has found these requirements:
225 # Alex: 8.2 seconds
226 # ZGB: 8.5 seconds
227 # The actual value is set to the largest known necessary value.
228 #
229 # TODO(jrbarnette) Being generous is the right thing to do for
230 # existing platforms, but if this code is to be used for
231 # qualification of new hardware, we should be less generous.
Chrome Bot9a1137d2011-07-19 14:35:00 -0700232 SHORT_DELAY = 0.1
J. Richard Barnette5383f072012-07-26 17:35:40 -0700233
Todd Broch31c82502011-08-29 08:14:39 -0700234 # Maximum number of times to re-read power button on release.
Todd Brochcf7c6652012-02-24 13:03:59 -0800235 GET_RETRY_MAX = 10
Chrome Bot9a1137d2011-07-19 14:35:00 -0700236
J. Richard Barnette5383f072012-07-26 17:35:40 -0700237 # Delays to deal with DUT state transitions.
Chrome Bot9a1137d2011-07-19 14:35:00 -0700238 SLEEP_DELAY = 6
239 BOOT_DELAY = 10
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700240
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700241 # Default minimum time interval between 'press' and 'release'
242 # keyboard events.
Vic Yang0aca1c22012-11-19 18:33:56 -0800243 SERVO_KEY_PRESS_DELAY = 0.1
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700244
Tom Wai-Hong Tam93b5c092015-05-14 02:50:43 +0800245 # Time to toggle recovery switch on and off.
246 REC_TOGGLE_DELAY = 0.1
247
Tom Wai-Hong Tamf9ded092015-05-20 05:37:22 +0800248 # Time to toggle development switch on and off.
249 DEV_TOGGLE_DELAY = 0.1
250
Jon Salzc88e5b62011-11-30 14:38:54 +0800251 # Time between an usb disk plugged-in and detected in the system.
252 USB_DETECTION_DELAY = 10
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800253 # Time to keep USB power off before and after USB mux direction is changed
254 USB_POWEROFF_DELAY = 2
Jon Salzc88e5b62011-11-30 14:38:54 +0800255
Simran Basib7850bb2013-07-24 12:33:42 -0700256 # Time to wait before timing out on servo initialization.
257 INIT_TIMEOUT_SECS = 10
Simran Basi59331342013-07-12 12:06:29 -0700258
J. Richard Barnette67ccb872012-04-19 16:34:56 -0700259
Ricky Liang0dd379c2014-04-23 16:29:08 +0800260 def __init__(self, servo_host, servo_serial=None):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700261 """Sets up the servo communication infrastructure.
262
Fang Deng5d518f42013-08-02 14:04:32 -0700263 @param servo_host: A ServoHost object representing
264 the host running servod.
Dana Goyettea2f00ea2019-06-26 16:14:12 -0700265 @type servo_host: autotest_lib.server.hosts.servo_host.ServoHost
Ricky Liang0dd379c2014-04-23 16:29:08 +0800266 @param servo_serial: Serial number of the servo board.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700267 """
Fang Deng5d518f42013-08-02 14:04:32 -0700268 # TODO(fdeng): crbug.com/298379
269 # We should move servo_host object out of servo object
270 # to minimize the dependencies on the rest of Autotest.
271 self._servo_host = servo_host
Ricky Liang0dd379c2014-04-23 16:29:08 +0800272 self._servo_serial = servo_serial
Richard Barnette180efe62016-12-02 23:20:44 +0000273 self._server = servo_host.get_servod_server_proxy()
J. Richard Barnette0c9c5882014-06-11 15:27:05 -0700274 self._power_state = _PowerStateController(self)
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700275 self._uart = _Uart(self)
Fang Dengafb88142013-05-30 17:44:31 -0700276 self._usb_state = None
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700277 self._programmer = None
Dana Goyette4dc0adc2019-05-06 14:51:53 -0700278 self._prev_log_inode = None
279 self._prev_log_size = 0
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800280
Ricky Liang0dd379c2014-04-23 16:29:08 +0800281 @property
282 def servo_serial(self):
283 """Returns the serial number of the servo board."""
284 return self._servo_serial
285
Dana Goyette4dc0adc2019-05-06 14:51:53 -0700286 def fetch_servod_log(self, filename=None, skip_old=False):
287 """Save the servod log into the given local file.
288
289 @param filename: save the contents into a file with the given name.
290 @param skip_old: if True, skip past the old data in the log file.
291 @type filename: str
292 @type skip_old: bool
293 @rtype: None
294 """
295 return self._servo_host.fetch_servod_log(filename, skip_old)
Ricky Liang0dd379c2014-04-23 16:29:08 +0800296
Tom Wai-Hong Tam22ee0e52013-04-03 13:36:39 +0800297 def get_power_state_controller(self):
J. Richard Barnette75136b32013-03-26 13:38:44 -0700298 """Return the power state controller for this Servo.
299
300 The power state controller provides board-independent
301 interfaces for reset, power-on, power-off operations.
302
303 """
304 return self._power_state
305
Fang Deng5d518f42013-08-02 14:04:32 -0700306
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700307 def initialize_dut(self, cold_reset=False):
308 """Initializes a dut for testing purposes.
309
310 This sets various servo signals back to default values
311 appropriate for the target board. By default, if the DUT
312 is already on, it stays on. If the DUT is powered off
313 before initialization, its state afterward is unspecified.
314
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700315 Rationale: Basic initialization of servo sets the lid open,
316 when there is a lid. This operation won't affect powered on
317 units; however, setting the lid open may power on a unit
J. Richard Barnette75136b32013-03-26 13:38:44 -0700318 that's off, depending on the board type and previous state
319 of the device.
320
J. Richard Barnette4b6af0d2014-06-05 09:57:20 -0700321 If `cold_reset` is a true value, the DUT and its EC will be
322 reset, and the DUT rebooted in normal mode.
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700323
324 @param cold_reset If True, cold reset the device after
325 initialization.
326 """
Dana Goyettea2f00ea2019-06-26 16:14:12 -0700327 try:
328 self._server.hwinit()
329 except socket.error as e:
330 e.filename = '%s:%s' % (self._servo_host.hostname,
331 self._servo_host.servo_port)
332 raise
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700333 self.set('usb_mux_oe1', 'on')
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700334 self._usb_state = None
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700335 self.switch_usbkey('off')
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700336 self._uart.start_capture()
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700337 if cold_reset:
J. Richard Barnette4b6af0d2014-06-05 09:57:20 -0700338 self._power_state.reset()
J. Richard Barnette1777f5d2014-09-03 15:18:19 -0700339 logging.debug('Servo initialized, version is %s',
340 self._server.get_version())
Ruben Rodriguez Buchillonb0e2a9f2019-08-12 12:40:44 -0700341 if self.has_control('init_keyboard'):
342 # This indicates the servod version does not
343 # have explicit keyboard initialization yet.
344 # Ignore this.
Ruben Rodriguez Buchillon05298242019-05-20 11:29:06 -0700345 # TODO(coconutruben): change this back to set() about a month
346 # after crrev.com/c/1586239 has been merged (or whenever that
347 # logic is in the labstation images).
348 self.set_nocheck('init_keyboard','on')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700349
350
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800351 def is_localhost(self):
352 """Is the servod hosted locally?
353
354 Returns:
355 True if local hosted; otherwise, False.
356 """
Fang Deng5d518f42013-08-02 14:04:32 -0700357 return self._servo_host.is_localhost()
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800358
359
Mary Ruthvenecf12712019-06-26 17:36:21 -0700360 def get_os_version(self):
361 """Returns the chromeos release version."""
362 lsb_release_content = self.system_output('cat /etc/lsb-release',
363 ignore_status=True)
364 return lsbrelease_utils.get_chromeos_release_builder_path(
365 lsb_release_content=lsb_release_content)
366
367
Mary Ruthven83bb5952019-06-27 12:34:05 -0700368 def get_servod_version(self):
369 """Returns the servod version."""
370 result = self._servo_host.run('servod --version')
371 # TODO: use system_output once servod --version prints to stdout
372 stdout = result.stdout.strip()
373 return stdout if stdout else result.stderr.strip()
374
375
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700376 def power_long_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700377 """Simulate a long power button press."""
J. Richard Barnettef12bff22012-07-18 14:41:06 -0700378 # After a long power press, the EC may ignore the next power
379 # button press (at least on Alex). To guarantee that this
380 # won't happen, we need to allow the EC one second to
381 # collect itself.
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800382 # long_press is defined as 8.5s in servod
383 self.set_nocheck('power_key', 'long_press')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700384
385
386 def power_normal_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700387 """Simulate a normal power button press."""
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800388 # press is defined as 1.2s in servod
389 self.set_nocheck('power_key', 'press')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700390
391
392 def power_short_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700393 """Simulate a short power button press."""
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800394 # tab is defined as 0.2s in servod
395 self.set_nocheck('power_key', 'tab')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700396
397
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800398 def power_key(self, press_secs='tab'):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700399 """Simulate a power button press.
400
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800401 @param press_secs: int, float, str; time to press key in seconds or
402 known shorthand: 'tab' 'press' 'long_press'
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700403 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800404 self.set_nocheck('power_key', press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700405
406
407 def lid_open(self):
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800408 """Simulate opening the lid and raise exception if all attempts fail"""
409 self.set('lid_open', 'yes')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700410
411
412 def lid_close(self):
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800413 """Simulate closing the lid and raise exception if all attempts fail
Craig Harrison48997262011-06-27 14:31:10 -0700414
415 Waits 6 seconds to ensure the device is fully asleep before returning.
416 """
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800417 self.set('lid_open', 'no')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700418 time.sleep(Servo.SLEEP_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700419
Shelley Chenc26575a2015-09-18 10:56:16 -0700420 def volume_up(self, timeout=300):
Kevin Chenge448a8b2016-09-09 10:18:11 -0700421 """Simulate pushing the volume down button.
422
423 @param timeout: Timeout for setting the volume.
424 """
Shelley Chenc26575a2015-09-18 10:56:16 -0700425 self.set_get_all(['volume_up:yes',
426 'sleep:%.4f' % self.SERVO_KEY_PRESS_DELAY,
427 'volume_up:no'])
428 # we need to wait for commands to take effect before moving on
429 time_left = float(timeout)
430 while time_left > 0.0:
431 value = self.get('volume_up')
432 if value == 'no':
433 return
434 time.sleep(self.SHORT_DELAY)
435 time_left = time_left - self.SHORT_DELAY
436 raise error.TestFail("Failed setting volume_up to no")
437
438 def volume_down(self, timeout=300):
Kevin Chenge448a8b2016-09-09 10:18:11 -0700439 """Simulate pushing the volume down button.
440
441 @param timeout: Timeout for setting the volume.
442 """
Shelley Chenc26575a2015-09-18 10:56:16 -0700443 self.set_get_all(['volume_down:yes',
444 'sleep:%.4f' % self.SERVO_KEY_PRESS_DELAY,
445 'volume_down:no'])
446 # we need to wait for commands to take effect before moving on
447 time_left = float(timeout)
448 while time_left > 0.0:
449 value = self.get('volume_down')
450 if value == 'no':
451 return
452 time.sleep(self.SHORT_DELAY)
453 time_left = time_left - self.SHORT_DELAY
454 raise error.TestFail("Failed setting volume_down to no")
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700455
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800456 def ctrl_d(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800457 """Simulate Ctrl-d simultaneous button presses.
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800458
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800459 @param press_secs: int, float, str; time to press key in seconds or
460 known shorthand: 'tab' 'press' 'long_press'
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800461 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800462 self.set_nocheck('ctrl_d', press_secs)
Gediminas Ramanauskas9da80f22012-11-14 12:59:43 -0800463
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800464
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800465 def ctrl_u(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800466 """Simulate Ctrl-u simultaneous button presses.
467
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800468 @param press_secs: int, float, str; time to press key in seconds or
469 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800470 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800471 self.set_nocheck('ctrl_u', press_secs)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700472
473
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800474 def ctrl_enter(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800475 """Simulate Ctrl-enter simultaneous button presses.
476
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800477 @param press_secs: int, float, str; time to press key in seconds or
478 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800479 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800480 self.set_nocheck('ctrl_enter', press_secs)
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -0700481
482
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800483 def ctrl_key(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800484 """Simulate Enter key button press.
485
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800486 @param press_secs: int, float, str; time to press key in seconds or
487 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800488 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800489 self.set_nocheck('ctrl_key', press_secs)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700490
491
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800492 def enter_key(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800493 """Simulate Enter key button press.
494
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800495 @param press_secs: int, float, str; time to press key in seconds or
496 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800497 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800498 self.set_nocheck('enter_key', press_secs)
Todd Broch9753bd42012-03-21 10:15:08 -0700499
500
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800501 def refresh_key(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800502 """Simulate Refresh key (F3) button press.
503
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800504 @param press_secs: int, float, str; time to press key in seconds or
505 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800506 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800507 self.set_nocheck('refresh_key', press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700508
509
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800510 def ctrl_refresh_key(self, press_secs='tab'):
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800511 """Simulate Ctrl and Refresh (F3) simultaneous press.
512
513 This key combination is an alternative of Space key.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800514
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'
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800517 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800518 self.set_nocheck('ctrl_refresh_key', press_secs)
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800519
520
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800521 def imaginary_key(self, press_secs='tab'):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700522 """Simulate imaginary key button press.
523
524 Maps to a key that doesn't physically exist.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800525
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800526 @param press_secs: int, float, str; time to press key in seconds or
527 known shorthand: 'tab' 'press' 'long_press'
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700528 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800529 self.set_nocheck('imaginary_key', press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700530
531
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800532 def sysrq_x(self, press_secs='tab'):
Vincent Palatine7dc9282016-07-14 11:31:58 +0200533 """Simulate Alt VolumeUp X simulataneous press.
534
535 This key combination is the kernel system request (sysrq) X.
536
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800537 @param press_secs: int, float, str; time to press key in seconds or
538 known shorthand: 'tab' 'press' 'long_press'
Vincent Palatine7dc9282016-07-14 11:31:58 +0200539 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800540 self.set_nocheck('sysrq_x', press_secs)
Vincent Palatine7dc9282016-07-14 11:31:58 +0200541
542
Tom Wai-Hong Tam93b5c092015-05-14 02:50:43 +0800543 def toggle_recovery_switch(self):
544 """Toggle recovery switch on and off."""
545 self.enable_recovery_mode()
546 time.sleep(self.REC_TOGGLE_DELAY)
547 self.disable_recovery_mode()
548
549
Craig Harrison6b36b122011-06-28 17:58:43 -0700550 def enable_recovery_mode(self):
551 """Enable recovery mode on device."""
552 self.set('rec_mode', 'on')
553
554
555 def disable_recovery_mode(self):
556 """Disable recovery mode on device."""
557 self.set('rec_mode', 'off')
558
559
Tom Wai-Hong Tamf9ded092015-05-20 05:37:22 +0800560 def toggle_development_switch(self):
561 """Toggle development switch on and off."""
562 self.enable_development_mode()
563 time.sleep(self.DEV_TOGGLE_DELAY)
564 self.disable_development_mode()
565
566
Craig Harrison6b36b122011-06-28 17:58:43 -0700567 def enable_development_mode(self):
568 """Enable development mode on device."""
569 self.set('dev_mode', 'on')
570
571
572 def disable_development_mode(self):
573 """Disable development mode on device."""
574 self.set('dev_mode', 'off')
575
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700576 def boot_devmode(self):
577 """Boot a dev-mode device that is powered off."""
Tom Wai-Hong Tam23869102012-01-17 15:41:30 +0800578 self.power_short_press()
Craig Harrison48997262011-06-27 14:31:10 -0700579 self.pass_devmode()
580
581
582 def pass_devmode(self):
583 """Pass through boot screens in dev-mode."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700584 time.sleep(Servo.BOOT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700585 self.ctrl_d()
Chrome Bot9a1137d2011-07-19 14:35:00 -0700586 time.sleep(Servo.BOOT_DELAY)
Craig Harrison48997262011-06-27 14:31:10 -0700587
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700588
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800589 def get_board(self):
Wai-Hong Tam0f904002017-09-19 15:52:22 -0700590 """Get the board connected to servod."""
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800591 return self._server.get_board()
592
593
Wai-Hong Tam0f904002017-09-19 15:52:22 -0700594 def get_base_board(self):
595 """Get the board of the base connected to servod."""
Wai-Hong Tam7f6169e2017-09-29 09:23:48 -0700596 try:
597 return self._server.get_base_board()
598 except xmlrpclib.Fault as e:
599 # TODO(waihong): Remove the following compatibility check when
600 # the new versions of hdctools are deployed.
601 if 'not supported' in str(e):
602 logging.warning('The servod is too old that get_base_board '
603 'not supported.')
604 return ''
605 raise
Wai-Hong Tam0f904002017-09-19 15:52:22 -0700606
607
Wai-Hong Tamcc399ee2017-12-08 12:43:28 -0800608 def get_ec_active_copy(self):
609 """Get the active copy of the EC image."""
610 return self.get('ec_active_copy')
611
612
Todd Brochefe72cb2012-07-11 19:58:53 -0700613 def _get_xmlrpclib_exception(self, xmlexc):
614 """Get meaningful exception string from xmlrpc.
615
616 Args:
617 xmlexc: xmlrpclib.Fault object
618
619 xmlrpclib.Fault.faultString has the following format:
620
621 <type 'exception type'>:'actual error message'
622
623 Parse and return the real exception from the servod side instead of the
624 less meaningful one like,
625 <Fault 1: "<type 'exceptions.AttributeError'>:'tca6416' object has no
626 attribute 'hw_driver'">
627
628 Returns:
629 string of underlying exception raised in servod.
630 """
631 return re.sub('^.*>:', '', xmlexc.faultString)
632
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700633 def has_control(self, control):
634 """Query servod server to determine if |control| is a valid control.
635
636 @param control: str, control name to query
637
638 @returns: true if |control| is a known control, false otherwise.
639 """
640 assert control
641 try:
642 # If the control exists, doc() will work.
643 self._server.doc(control)
644 return True
645 except xmlrpclib.Fault as e:
646 if re.search('No control %s' % control,
647 self._get_xmlrpclib_exception(e)):
648 return False
649 raise e
Todd Brochefe72cb2012-07-11 19:58:53 -0700650
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700651 def get(self, gpio_name):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700652 """Get the value of a gpio from Servod.
653
654 @param gpio_name Name of the gpio.
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700655
656 @returns: server response to |gpio_name| request.
657
658 @raise ControlUnavailableError: if |gpio_name| not a known control.
659 @raise error.TestFail: for all other failures doing get().
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700660 """
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700661 assert gpio_name
Todd Brochefe72cb2012-07-11 19:58:53 -0700662 try:
Simran Basi1cbc5f22013-07-17 14:23:58 -0700663 return self._server.get(gpio_name)
Todd Brochefe72cb2012-07-11 19:58:53 -0700664 except xmlrpclib.Fault as e:
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700665 err_str = self._get_xmlrpclib_exception(e)
666 err_msg = "Getting '%s' :: %s" % (gpio_name, err_str)
667 unknown_ctrl = re.findall(NO_CONTROL_RE, err_str)
668 if unknown_ctrl:
669 raise ControlUnavailableError('No control named %r' %
670 unknown_ctrl[0])
671 else:
672 logging.error(err_msg)
673 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700674
675
676 def set(self, gpio_name, gpio_value):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700677 """Set and check the value of a gpio using Servod.
678
679 @param gpio_name Name of the gpio.
680 @param gpio_value New setting for the gpio.
681 """
Chrome Bot9a1137d2011-07-19 14:35:00 -0700682 self.set_nocheck(gpio_name, gpio_value)
Todd Brochcf7c6652012-02-24 13:03:59 -0800683 retry_count = Servo.GET_RETRY_MAX
684 while gpio_value != self.get(gpio_name) and retry_count:
Ilja H. Friedel04be2bd2014-05-07 21:29:59 -0700685 logging.warning("%s != %s, retry %d", gpio_name, gpio_value,
Todd Brochcf7c6652012-02-24 13:03:59 -0800686 retry_count)
687 retry_count -= 1
688 time.sleep(Servo.SHORT_DELAY)
689 if not retry_count:
690 assert gpio_value == self.get(gpio_name), \
691 'Servo failed to set %s to %s' % (gpio_name, gpio_value)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700692
693
694 def set_nocheck(self, gpio_name, gpio_value):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700695 """Set the value of a gpio using Servod.
696
697 @param gpio_name Name of the gpio.
698 @param gpio_value New setting for the gpio.
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700699
700 @raise ControlUnavailableError: if |gpio_name| not a known control.
701 @raise error.TestFail: for all other failures doing set().
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700702 """
Ruben Rodriguez Buchillon1c0219c2019-06-25 12:30:44 -0700703 # The real danger here is to pass a None value through the xmlrpc.
704 assert gpio_name and gpio_value is not None
Mary Ruthvendfa45342019-06-25 10:10:17 -0700705 logging.debug('Setting %s to %r', gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700706 try:
Simran Basi1cbc5f22013-07-17 14:23:58 -0700707 self._server.set(gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700708 except xmlrpclib.Fault as e:
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700709 err_str = self._get_xmlrpclib_exception(e)
710 err_msg = "Setting '%s' :: %s" % (gpio_name, err_str)
711 unknown_ctrl = re.findall(NO_CONTROL_RE, err_str)
712 if unknown_ctrl:
713 raise ControlUnavailableError('No control named %r' %
714 unknown_ctrl[0])
715 else:
716 logging.error(err_msg)
717 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700718
719
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800720 def set_get_all(self, controls):
721 """Set &| get one or more control values.
722
723 @param controls: list of strings, controls to set &| get.
724
725 @raise: error.TestError in case error occurs setting/getting values.
726 """
727 rv = []
728 try:
Mary Ruthvendfa45342019-06-25 10:10:17 -0700729 logging.debug('Set/get all: %s', str(controls))
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800730 rv = self._server.set_get_all(controls)
731 except xmlrpclib.Fault as e:
732 # TODO(waihong): Remove the following backward compatibility when
733 # the new versions of hdctools are deployed.
734 if 'not supported' in str(e):
735 logging.warning('The servod is too old that set_get_all '
736 'not supported. Use set and get instead.')
737 for control in controls:
738 if ':' in control:
739 (name, value) = control.split(':')
740 if name == 'sleep':
741 time.sleep(float(value))
742 else:
743 self.set_nocheck(name, value)
744 rv.append(True)
745 else:
746 rv.append(self.get(name))
747 else:
748 err_msg = "Problem with '%s' :: %s" % \
749 (controls, self._get_xmlrpclib_exception(e))
750 raise error.TestFail(err_msg)
751 return rv
752
753
Jon Salzc88e5b62011-11-30 14:38:54 +0800754 # TODO(waihong) It may fail if multiple servo's are connected to the same
755 # host. Should look for a better way, like the USB serial name, to identify
756 # the USB device.
Simran Basi741b5d42012-05-18 11:27:15 -0700757 # TODO(sbasi) Remove this code from autoserv once firmware tests have been
758 # updated.
Kevin Chenga22c4a82016-10-07 14:13:25 -0700759 def probe_host_usb_dev(self, timeout=_USB_PROBE_TIMEOUT):
Jon Salzc88e5b62011-11-30 14:38:54 +0800760 """Probe the USB disk device plugged-in the servo from the host side.
761
Kevin Chengeb06fe72016-08-22 15:26:32 -0700762 It uses servod to discover if there is a usb device attached to it.
Jon Salzc88e5b62011-11-30 14:38:54 +0800763
Kevin Chenga22c4a82016-10-07 14:13:25 -0700764 @param timeout The timeout period when probing for the usb host device.
765
766 @return: String of USB disk path (e.g. '/dev/sdb') or None.
Jon Salzc88e5b62011-11-30 14:38:54 +0800767 """
Ruben Rodriguez Buchillon9a4bfc32018-10-16 20:46:25 +0800768 # Set up Servo's usb mux.
769 self.switch_usbkey('host')
Kevin Chenga22c4a82016-10-07 14:13:25 -0700770 return self._server.probe_host_usb_dev(timeout) or None
Jon Salzc88e5b62011-11-30 14:38:54 +0800771
772
Mike Truty49153d82012-08-21 22:27:30 -0500773 def image_to_servo_usb(self, image_path=None,
774 make_image_noninteractive=False):
775 """Install an image to the USB key plugged into the servo.
776
777 This method may copy any image to the servo USB key including a
778 recovery image or a test image. These images are frequently used
779 for test purposes such as restoring a corrupted image or conducting
780 an upgrade of ec/fw/kernel as part of a test of a specific image part.
781
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700782 @param image_path Path on the host to the recovery image.
783 @param make_image_noninteractive Make the recovery image
784 noninteractive, therefore the DUT
785 will reboot automatically after
786 installation.
Mike Truty49153d82012-08-21 22:27:30 -0500787 """
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700788 # We're about to start plugging/unplugging the USB key. We
789 # don't know the state of the DUT, or what it might choose
790 # to do to the device after hotplug. To avoid surprises,
791 # force the DUT to be off.
792 self._server.hwinit()
Ruben Rodriguez Buchillonb0e2a9f2019-08-12 12:40:44 -0700793 if self.has_control('init_keyboard'):
794 # This indicates the servod version does not
795 # have explicit keyboard initialization yet.
796 # Ignore this.
Ruben Rodriguez Buchillonbd3b74f2019-08-08 16:18:27 -0700797 # TODO(coconutruben): change this back to set() about a month
798 # after crrev.com/c/1586239 has been merged (or whenever that
799 # logic is in the labstation images).
800 self.set_nocheck('init_keyboard','on')
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700801 self._power_state.power_off()
Mike Truty49153d82012-08-21 22:27:30 -0500802
Mike Truty49153d82012-08-21 22:27:30 -0500803 if image_path:
Garry Wangd2656812019-08-07 17:29:16 -0700804 # Set up Servo's usb mux.
805 self.switch_usbkey('host')
Tom Wai-Hong Tam42f136d2012-10-26 11:11:23 +0800806 logging.info('Searching for usb device and copying image to it. '
807 'Please wait a few minutes...')
Mike Truty49153d82012-08-21 22:27:30 -0500808 if not self._server.download_image_to_usb(image_path):
809 logging.error('Failed to transfer requested image to USB. '
810 'Please take a look at Servo Logs.')
811 raise error.AutotestError('Download image to usb failed.')
812 if make_image_noninteractive:
813 logging.info('Making image noninteractive')
814 if not self._server.make_image_noninteractive():
815 logging.error('Failed to make image noninteractive. '
816 'Please take a look at Servo Logs.')
817
Kalin Stoyanovb3c11f32018-05-11 09:02:00 -0700818 def boot_in_recovery_mode(self):
819 """Boot host DUT in recovery mode."""
820 self._power_state.power_on(rec_mode=self._power_state.REC_ON)
821 self.switch_usbkey('dut')
822
Mike Truty49153d82012-08-21 22:27:30 -0500823
Simran Basi741b5d42012-05-18 11:27:15 -0700824 def install_recovery_image(self, image_path=None,
J. Richard Barnetteb6de7e32013-02-14 13:28:04 -0800825 make_image_noninteractive=False):
Dan Shic67f1332016-04-06 12:38:06 -0700826 """Install the recovery image specified by the path onto the DUT.
Jon Salzc88e5b62011-11-30 14:38:54 +0800827
828 This method uses google recovery mode to install a recovery image
829 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 +0800830 board specified by the usb_dev. If no image path is specified
Jon Salzc88e5b62011-11-30 14:38:54 +0800831 we use the recovery image already on the usb image.
832
Dan Shic67f1332016-04-06 12:38:06 -0700833 @param image_path: Path on the host to the recovery image.
834 @param make_image_noninteractive: Make the recovery image
835 noninteractive, therefore the DUT will reboot automatically
836 after installation.
Jon Salzc88e5b62011-11-30 14:38:54 +0800837 """
Mike Truty49153d82012-08-21 22:27:30 -0500838 self.image_to_servo_usb(image_path, make_image_noninteractive)
Garry Wangd2656812019-08-07 17:29:16 -0700839 # Give the DUT some time to power_off if we skip
840 # download image to usb. (crbug.com/982993)
841 if not image_path:
842 time.sleep(10)
Kalin Stoyanovb3c11f32018-05-11 09:02:00 -0700843 self.boot_in_recovery_mode()
Jon Salzc88e5b62011-11-30 14:38:54 +0800844
845
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800846 def _scp_image(self, image_path):
847 """Copy image to the servo host.
848
849 When programming a firmware image on the DUT, the image must be
850 located on the host to which the servo device is connected. Sometimes
851 servo is controlled by a remote host, in this case the image needs to
852 be transferred to the remote host.
853
854 @param image_path: a string, name of the firmware image file to be
855 transferred.
856 @return: a string, full path name of the copied file on the remote.
857 """
858
859 dest_path = os.path.join('/tmp', os.path.basename(image_path))
Fang Deng5d518f42013-08-02 14:04:32 -0700860 self._servo_host.send_file(image_path, dest_path)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800861 return dest_path
862
863
Dan Shifecdaf42015-07-28 10:17:26 -0700864 def system(self, command, timeout=3600):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700865 """Execute the passed in command on the servod host.
866
867 @param command Command to be executed.
Dan Shifecdaf42015-07-28 10:17:26 -0700868 @param timeout Maximum number of seconds of runtime allowed. Default to
869 1 hour.
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700870 """
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800871 logging.info('Will execute on servo host: %s', command)
Fang Deng5d518f42013-08-02 14:04:32 -0700872 self._servo_host.run(command, timeout=timeout)
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800873
874
Dan Shifecdaf42015-07-28 10:17:26 -0700875 def system_output(self, command, timeout=3600,
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800876 ignore_status=False, args=()):
877 """Execute the passed in command on the servod host, return stdout.
878
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700879 @param command a string, the command to execute
880 @param timeout an int, max number of seconds to wait til command
Dan Shifecdaf42015-07-28 10:17:26 -0700881 execution completes. Default to 1 hour.
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700882 @param ignore_status a Boolean, if true - ignore command's nonzero exit
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800883 status, otherwise an exception will be thrown
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700884 @param args a tuple of strings, each becoming a separate command line
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800885 parameter for the command
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700886 @return command's stdout as a string.
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800887 """
Fang Deng5d518f42013-08-02 14:04:32 -0700888 return self._servo_host.run(command, timeout=timeout,
889 ignore_status=ignore_status,
890 args=args).stdout.strip()
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800891
892
Mary Ruthven38d90af2019-08-16 13:13:31 -0700893 def get_servo_version(self, active=False):
Dan Shia5fef052015-05-18 23:28:47 -0700894 """Get the version of the servo, e.g., servo_v2 or servo_v3.
895
Mary Ruthven38d90af2019-08-16 13:13:31 -0700896 @param active: Only return the servo type with the active device.
Dan Shia5fef052015-05-18 23:28:47 -0700897 @return: The version of the servo.
898
899 """
Mary Ruthven38d90af2019-08-16 13:13:31 -0700900 servo_type = self._server.get_version()
901 if '_and_' not in servo_type or not active:
902 return servo_type
903
904 # If servo v4 is using ccd and servo micro, modify the servo type to
905 # reflect the active device.
906 active_device = self.get('active_v4_device')
907 if active_device in servo_type:
908 logging.info('%s is active', active_device)
909 return 'servo_v4_with_' + active_device
910
911 logging.warn("%s is active even though it's not in servo type",
912 active_device)
913 return servo_type
Dan Shia5fef052015-05-18 23:28:47 -0700914
915
Mary Ruthven2724ee62019-07-16 11:16:59 -0700916 def running_through_ccd(self):
917 """Returns True if the setup is using ccd to run."""
918 servo = self._server.get_version()
919 return 'ccd_cr50' in servo and 'servo_micro' not in servo
920
921
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800922 def _initialize_programmer(self, rw_only=False):
923 """Initialize the firmware programmer.
924
925 @param rw_only: True to initialize a programmer which only
926 programs the RW portions.
927 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700928 if self._programmer:
929 return
930 # Initialize firmware programmer
Dan Shia5fef052015-05-18 23:28:47 -0700931 servo_version = self.get_servo_version()
Dan Shi9cb0eec2014-06-03 09:04:50 -0700932 if servo_version.startswith('servo_v2'):
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700933 self._programmer = firmware_programmer.ProgrammerV2(self)
Wai-Hong Tamc36c4d22016-09-09 10:39:45 -0700934 self._programmer_rw = firmware_programmer.ProgrammerV2RwOnly(self)
Kevin Chengdf2e29f2016-09-09 02:31:22 -0700935 # Both servo v3 and v4 use the same programming methods so just leverage
936 # ProgrammerV3 for servo v4 as well.
937 elif (servo_version.startswith('servo_v3') or
938 servo_version.startswith('servo_v4')):
Dan Shia5fef052015-05-18 23:28:47 -0700939 self._programmer = firmware_programmer.ProgrammerV3(self)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800940 self._programmer_rw = firmware_programmer.ProgrammerV3RwOnly(self)
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700941 else:
942 raise error.TestError(
943 'No firmware programmer for servo version: %s' %
Congbin Guo42427612019-02-12 10:22:06 -0800944 servo_version)
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700945
946
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800947 def program_bios(self, image, rw_only=False):
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800948 """Program bios on DUT with given image.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800949
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800950 @param image: a string, file name of the BIOS image to program
951 on the DUT.
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800952 @param rw_only: True to only program the RW portion of BIOS.
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800953
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800954 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700955 self._initialize_programmer()
Ricky Liangc31aab32014-07-03 16:23:29 +0800956 if not self.is_localhost():
957 image = self._scp_image(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800958 if rw_only:
959 self._programmer_rw.program_bios(image)
960 else:
961 self._programmer.program_bios(image)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800962
963
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800964 def program_ec(self, image, rw_only=False):
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800965 """Program ec 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 EC 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 EC.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -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:
Congbin Guo42427612019-02-12 10:22:06 -0800976 self._programmer_rw.program_ec(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800977 else:
Congbin Guo42427612019-02-12 10:22:06 -0800978 self._programmer.program_ec(image)
979
980
981 def _reprogram(self, tarball_path, firmware_name, image_candidates,
982 rw_only):
983 """Helper function to reprogram firmware for EC or BIOS.
984
985 @param tarball_path: The path of the downloaded build tarball.
986 @param: firmware_name: either 'EC' or 'BIOS'.
987 @param image_candidates: A tuple of the paths of image candidates.
988 @param rw_only: True to only install firmware to its RW portions. Keep
989 the RO portions unchanged.
990
991 @raise: TestError if cannot extract firmware from the tarball.
992 """
Mary Ruthven771b2012019-08-19 12:18:49 -0700993 dest_dir = os.path.join(os.path.dirname(tarball_path), firmware_name)
994 # Create the firmware_name subdirectory if it doesn't exist.
995 if not os.path.exists(dest_dir):
996 os.mkdir(dest_dir)
Congbin Guo42427612019-02-12 10:22:06 -0800997 image = _extract_image_from_tarball(tarball_path, dest_dir,
998 image_candidates)
999 if not image:
1000 if firmware_name == 'EC':
1001 logging.info('Not a Chrome EC, ignore re-programming it')
1002 return
1003 else:
1004 raise error.TestError('Failed to extract the %s image from '
1005 'tarball' % firmware_name)
1006
1007 logging.info('Will re-program %s %snow', firmware_name,
1008 'RW ' if rw_only else '')
1009
1010 if firmware_name == 'EC':
1011 self.program_ec(os.path.join(dest_dir, image), rw_only)
1012 else:
1013 self.program_bios(os.path.join(dest_dir, image), rw_only)
1014
1015
Shelley Chenac61d5a2019-06-24 15:35:46 -07001016 def program_firmware(self, board, model, tarball_path, rw_only=False):
Congbin Guo42427612019-02-12 10:22:06 -08001017 """Program firmware (EC, if applied, and BIOS) of the DUT.
1018
Shelley Chenac61d5a2019-06-24 15:35:46 -07001019 @param board: The DUT board name.
Congbin Guo42427612019-02-12 10:22:06 -08001020 @param model: The DUT model name.
1021 @param tarball_path: The path of the downloaded build tarball.
1022 @param rw_only: True to only install firmware to its RW portions. Keep
1023 the RO portions unchanged.
1024 """
Shelley Chenac61d5a2019-06-24 15:35:46 -07001025 ap_image_candidates = ('image.bin', 'image-%s.bin' % model,
1026 'image-%s.bin' % board)
1027 ec_image_candidates = ('ec.bin', '%s/ec.bin' % model,
1028 '%s/ec.bin' % board)
Congbin Guo42427612019-02-12 10:22:06 -08001029
1030 self._reprogram(tarball_path, 'EC', ec_image_candidates, rw_only)
1031 self._reprogram(tarball_path, 'BIOS', ap_image_candidates, rw_only)
1032
1033 self.get_power_state_controller().reset()
1034 time.sleep(Servo.BOOT_DELAY)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001035
Fang Dengafb88142013-05-30 17:44:31 -07001036
1037 def _switch_usbkey_power(self, power_state, detection_delay=False):
1038 """Switch usbkey power.
1039
1040 This function switches usbkey power by setting the value of
1041 'prtctl4_pwren'. If the power is already in the
1042 requested state, this function simply returns.
1043
1044 @param power_state: A string, 'on' or 'off'.
1045 @param detection_delay: A boolean value, if True, sleep
1046 for |USB_DETECTION_DELAY| after switching
1047 the power on.
1048 """
Kevin Chenga22c4a82016-10-07 14:13:25 -07001049 # TODO(kevcheng): Forgive me for this terrible hack. This is just to
1050 # handle beaglebones that haven't yet updated and have the
1051 # safe_switch_usbkey_power RPC. I'll remove this once all beaglebones
1052 # have been updated and also think about a better way to handle
1053 # situations like this.
1054 try:
1055 self._server.safe_switch_usbkey_power(power_state)
1056 except Exception:
1057 self.set('prtctl4_pwren', power_state)
Fang Dengafb88142013-05-30 17:44:31 -07001058 if power_state == 'off':
1059 time.sleep(self.USB_POWEROFF_DELAY)
1060 elif detection_delay:
1061 time.sleep(self.USB_DETECTION_DELAY)
1062
1063
1064 def switch_usbkey(self, usb_state):
1065 """Connect USB flash stick to either host or DUT, or turn USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001066
1067 This function switches the servo multiplexer to provide electrical
Fang Dengafb88142013-05-30 17:44:31 -07001068 connection between the USB port J3 and either host or DUT side. It
1069 can also be used to turn the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001070
Fang Dengafb88142013-05-30 17:44:31 -07001071 Switching to 'dut' or 'host' is accompanied by powercycling
1072 of the USB stick, because it sometimes gets wedged if the mux
1073 is switched while the stick power is on.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001074
Fang Dengafb88142013-05-30 17:44:31 -07001075 @param usb_state: A string, one of 'dut', 'host', or 'off'.
1076 'dut' and 'host' indicate which side the
1077 USB flash device is required to be connected to.
1078 'off' indicates turning the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001079
Fang Dengafb88142013-05-30 17:44:31 -07001080 @raise: error.TestError in case the parameter is not 'dut'
1081 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001082 """
Fang Dengafb88142013-05-30 17:44:31 -07001083 if self.get_usbkey_direction() == usb_state:
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001084 return
1085
Fang Dengafb88142013-05-30 17:44:31 -07001086 if usb_state == 'off':
Dan Shia5fef052015-05-18 23:28:47 -07001087 self._switch_usbkey_power('off')
1088 self._usb_state = usb_state
1089 return
Fang Dengafb88142013-05-30 17:44:31 -07001090 elif usb_state == 'host':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001091 mux_direction = 'servo_sees_usbkey'
Fang Dengafb88142013-05-30 17:44:31 -07001092 elif usb_state == 'dut':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001093 mux_direction = 'dut_sees_usbkey'
1094 else:
Fang Dengafb88142013-05-30 17:44:31 -07001095 raise error.TestError('Unknown USB state request: %s' % usb_state)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001096
Fang Dengafb88142013-05-30 17:44:31 -07001097 self._switch_usbkey_power('off')
Kevin Chenga22c4a82016-10-07 14:13:25 -07001098 # TODO(kevcheng): Forgive me for this terrible hack. This is just to
1099 # handle beaglebones that haven't yet updated and have the
1100 # safe_switch_usbkey RPC. I'll remove this once all beaglebones have
1101 # been updated and also think about a better way to handle situations
1102 # like this.
1103 try:
1104 self._server.safe_switch_usbkey(mux_direction)
1105 except Exception:
1106 self.set('usb_mux_sel1', mux_direction)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001107 time.sleep(self.USB_POWEROFF_DELAY)
Fang Dengafb88142013-05-30 17:44:31 -07001108 self._switch_usbkey_power('on', usb_state == 'host')
1109 self._usb_state = usb_state
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001110
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001111
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001112 def get_usbkey_direction(self):
Fang Dengafb88142013-05-30 17:44:31 -07001113 """Get which side USB is connected to or 'off' if usb power is off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001114
Fang Dengafb88142013-05-30 17:44:31 -07001115 @return: A string, one of 'dut', 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001116 """
Fang Dengafb88142013-05-30 17:44:31 -07001117 if not self._usb_state:
1118 if self.get('prtctl4_pwren') == 'off':
1119 self._usb_state = 'off'
1120 elif self.get('usb_mux_sel1').startswith('dut'):
1121 self._usb_state = 'dut'
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001122 else:
Fang Dengafb88142013-05-30 17:44:31 -07001123 self._usb_state = 'host'
1124 return self._usb_state
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001125
1126
Wai-Hong Tam60377262018-03-01 10:55:39 -08001127 def set_servo_v4_role(self, role):
1128 """Set the power role of servo v4, either 'src' or 'snk'.
1129
1130 It does nothing if not a servo v4.
1131
1132 @param role: Power role for DUT port on servo v4, either 'src' or 'snk'.
1133 """
1134 servo_version = self.get_servo_version()
1135 if servo_version.startswith('servo_v4'):
1136 value = self.get('servo_v4_role')
1137 if value != role:
1138 self.set_nocheck('servo_v4_role', role)
1139 else:
1140 logging.debug('Already in the role: %s.', role)
1141 else:
1142 logging.debug('Not a servo v4, unable to set role to %s.', role)
1143
1144
Mary Ruthven739b2922019-08-22 11:16:06 -07001145 def set_servo_v4_dts_mode(self, state):
1146 """Set servo v4 dts mode to off or on.
1147
1148 It does nothing if not a servo v4. Disable the ccd watchdog if we're
1149 disabling dts mode. CCD will disconnect. The watchdog only allows CCD
1150 to disconnect for 10 seconds until it kills servod. Disable the
1151 watchdog, so CCD can stay disconnected indefinitely.
1152
1153 @param state: Set servo v4 dts mode 'off' or 'on'.
1154 """
1155 servo_version = self.get_servo_version()
1156 if not servo_version.startswith('servo_v4'):
1157 logging.debug('Not a servo v4, unable to set dts mode %s.', state)
1158 return
1159
1160 # TODO(mruthven): remove watchdog check once the labstation has been
1161 # updated to have support for modifying the watchdog.
1162 set_watchdog = self.has_control('watchdog') and 'ccd' in servo_version
1163 enable_watchdog = state == 'on'
1164
1165 if set_watchdog and not enable_watchdog:
1166 self.set_nocheck('watchdog_remove', 'ccd')
1167
1168 self.set_nocheck('servo_v4_dts_mode', state)
1169
1170 if set_watchdog and enable_watchdog:
1171 self.set_nocheck('watchdog_add', 'ccd')
1172
1173
Congbin Guofc3b8962019-03-22 17:38:46 -07001174 @property
1175 def uart_logs_dir(self):
1176 """Return the directory to save UART logs."""
1177 return self._uart.logs_dir if self._uart else ""
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001178
Congbin Guofc3b8962019-03-22 17:38:46 -07001179
1180 @uart_logs_dir.setter
1181 def uart_logs_dir(self, logs_dir):
1182 """Set directory to save UART logs.
1183
1184 @param logs_dir String of directory name."""
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001185 if self._uart:
Congbin Guofc3b8962019-03-22 17:38:46 -07001186 self._uart.logs_dir = logs_dir
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001187
1188
1189 def close(self):
1190 """Close the servo object."""
1191 if self._uart:
1192 self._uart.stop_capture()
Congbin Guofc3b8962019-03-22 17:38:46 -07001193 self._uart.dump()
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001194 self._uart = None