blob: e07680195c73785f0b2721eb132a82584a86ff4f [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
Congbin Guo42427612019-02-12 10:22:06 -080026def _extract_image_from_tarball(tarball, dest_dir, image_candidates):
27 """Try extracting the image_candidates from the tarball.
28
29 @param tarball: The path of the tarball.
30 @param dest_path: The path of the destination.
31 @param image_candidates: A tuple of the paths of image candidates.
32
33 @return: The first path from the image candidates, which succeeds, or None
34 if all the image candidates fail.
35 """
36 for image in image_candidates:
37 status = server_utils.system(
38 ('tar xf %s -C %s %s' % (tarball, dest_dir, image)),
39 timeout=60, ignore_status=True)
40 if status == 0:
41 return image
42 return None
43
44
J. Richard Barnette0c9c5882014-06-11 15:27:05 -070045class _PowerStateController(object):
46
47 """Class to provide board-specific power operations.
48
49 This class is responsible for "power on" and "power off"
50 operations that can operate without making assumptions in
51 advance about board state. It offers an interface that
52 abstracts out the different sequences required for different
53 board types.
54
55 """
56
57 # Constants acceptable to be passed for the `rec_mode` parameter
58 # to power_on().
59 #
60 # REC_ON: Boot the DUT in recovery mode, i.e. boot from USB or
61 # SD card.
62 # REC_OFF: Boot in normal mode, i.e. boot from internal storage.
63
64 REC_ON = 'rec'
65 REC_OFF = 'on'
Shelley Chen65938622018-05-16 07:45:54 -070066 REC_ON_FORCE_MRC = 'rec_force_mrc'
J. Richard Barnette0c9c5882014-06-11 15:27:05 -070067
68 # Delay in seconds needed between asserting and de-asserting
69 # warm reset.
70 _RESET_HOLD_TIME = 0.5
71
72 def __init__(self, servo):
73 """Initialize the power state control.
74
75 @param servo Servo object providing the underlying `set` and `get`
76 methods for the target controls.
77
78 """
79 self._servo = servo
80
81 def reset(self):
82 """Force the DUT to reset.
83
84 The DUT is guaranteed to be on at the end of this call,
85 regardless of its previous state, provided that there is
86 working OS software. This also guarantees that the EC has
87 been restarted.
88
89 """
90 self._servo.set_nocheck('power_state', 'reset')
91
92 def warm_reset(self):
93 """Apply warm reset to the DUT.
94
95 This asserts, then de-asserts the 'warm_reset' signal.
96 Generally, this causes the board to restart.
97
98 """
99 self._servo.set_get_all(['warm_reset:on',
100 'sleep:%.4f' % self._RESET_HOLD_TIME,
101 'warm_reset:off'])
102
J. Richard Barnette0c9c5882014-06-11 15:27:05 -0700103 def power_off(self):
104 """Force the DUT to power off.
105
106 The DUT is guaranteed to be off at the end of this call,
107 regardless of its previous state, provided that there is
108 working EC and boot firmware. There is no requirement for
109 working OS software.
110
111 """
112 self._servo.set_nocheck('power_state', 'off')
113
114 def power_on(self, rec_mode=REC_OFF):
115 """Force the DUT to power on.
116
117 Prior to calling this function, the DUT must be powered off,
118 e.g. with a call to `power_off()`.
119
120 At power on, recovery mode is set as specified by the
121 corresponding argument. When booting with recovery mode on, it
122 is the caller's responsibility to unplug/plug in a bootable
123 external storage device.
124
125 If the DUT requires a delay after powering on but before
126 processing inputs such as USB stick insertion, the delay is
127 handled by this method; the caller is not responsible for such
128 delays.
129
130 @param rec_mode Setting of recovery mode to be applied at
131 power on. default: REC_OFF aka 'off'
132
133 """
134 self._servo.set_nocheck('power_state', rec_mode)
135
136
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700137class _Uart(object):
Congbin Guo0f00be82019-04-18 17:51:14 -0700138 """Class to capture UART streams of CPU, EC, Cr50, etc."""
139 _UartToCapture = ('cpu', 'ec', 'cr50', 'servo_v4', 'servo_micro', 'usbpd')
140
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700141 def __init__(self, servo):
142 self._servo = servo
143 self._streams = []
Congbin Guo0f00be82019-04-18 17:51:14 -0700144 self.logs_dir = None
Congbin Guofe4d4e82019-04-18 17:51:14 -0700145
Congbin Guo0f00be82019-04-18 17:51:14 -0700146 def _start_stop_capture(self, uart, start):
147 """Helper function to start/stop capturing on specified UART.
148
149 @param uart: The UART name to start/stop capturing.
150 @param start: True to start capturing, otherwise stop.
151
152 @returns True if the operation completes successfully. False if the UART
153 capturing is not supported or failed due to an error.
154 """
155 logging.debug('%s capturing %s UART.', 'Start' if start else 'Stop',
156 uart)
Congbin Guo39dc6f22019-04-23 23:01:21 +0000157 try:
Congbin Guo0f00be82019-04-18 17:51:14 -0700158 self._servo.set('%s_uart_capture' % uart,
159 'on' if start else 'off')
Congbin Guo39dc6f22019-04-23 23:01:21 +0000160 except error.TestFail as err:
161 if 'No control named' in str(err):
Congbin Guo0f00be82019-04-18 17:51:14 -0700162 logging.debug("The servod doesn't support %s_uart_capture.",
163 uart)
164 else:
165 logging.debug("Can't caputre UART for %s: %s", uart, err)
166 return False
167
168 return True
169
170 def start_capture(self):
171 """Start capturing UART streams."""
172 for uart in self._UartToCapture:
173 if self._start_stop_capture(uart, True):
174 self._streams.append(('%s_uart_stream' % uart, '%s_uart.log' %
175 uart))
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700176
Congbin Guofc3b8962019-03-22 17:38:46 -0700177 def dump(self):
178 """Dump UART streams to log files accordingly."""
Congbin Guo0f00be82019-04-18 17:51:14 -0700179 if not self.logs_dir:
Congbin Guofc3b8962019-03-22 17:38:46 -0700180 return
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700181
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700182 for stream, logfile in self._streams:
Congbin Guo0f00be82019-04-18 17:51:14 -0700183 logfile_fullname = os.path.join(self.logs_dir, logfile)
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700184 try:
185 content = self._servo.get(stream)
186 except Exception as err:
187 logging.warn('Failed to get UART log for %s: %s', stream, err)
188 continue
189
190 # The UART stream may contain non-printable characters, and servo
191 # returns it in string representation. We use `ast.leteral_eval`
192 # to revert it back.
193 with open(logfile_fullname, 'a') as fd:
194 fd.write(ast.literal_eval(content))
195
196 def stop_capture(self):
197 """Stop capturing UART streams."""
Congbin Guo0f00be82019-04-18 17:51:14 -0700198 for uart in self._UartToCapture:
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700199 try:
Congbin Guo0f00be82019-04-18 17:51:14 -0700200 self._start_stop_capture(uart, False)
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700201 except Exception as err:
202 logging.warn('Failed to stop UART logging for %s: %s', uart,
203 err)
204
205
J. Richard Barnette384056b2012-04-16 11:04:46 -0700206class Servo(object):
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700207
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700208 """Manages control of a Servo board.
209
210 Servo is a board developed by hardware group to aide in the debug and
211 control of various partner devices. Servo's features include the simulation
212 of pressing the power button, closing the lid, and pressing Ctrl-d. This
213 class manages setting up and communicating with a servo demon (servod)
214 process. It provides both high-level functions for common servo tasks and
215 low-level functions for directly setting and reading gpios.
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700216
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700217 """
218
Chrome Bot9a1137d2011-07-19 14:35:00 -0700219 # Power button press delays in seconds.
J. Richard Barnetted2e4cbd2012-06-29 12:18:40 -0700220 #
J. Richard Barnette5383f072012-07-26 17:35:40 -0700221 # The EC specification says that 8.0 seconds should be enough
222 # for the long power press. However, some platforms need a bit
223 # more time. Empirical testing has found these requirements:
224 # Alex: 8.2 seconds
225 # ZGB: 8.5 seconds
226 # The actual value is set to the largest known necessary value.
227 #
228 # TODO(jrbarnette) Being generous is the right thing to do for
229 # existing platforms, but if this code is to be used for
230 # qualification of new hardware, we should be less generous.
Chrome Bot9a1137d2011-07-19 14:35:00 -0700231 SHORT_DELAY = 0.1
J. Richard Barnette5383f072012-07-26 17:35:40 -0700232
Todd Broch31c82502011-08-29 08:14:39 -0700233 # Maximum number of times to re-read power button on release.
Todd Brochcf7c6652012-02-24 13:03:59 -0800234 GET_RETRY_MAX = 10
Chrome Bot9a1137d2011-07-19 14:35:00 -0700235
J. Richard Barnette5383f072012-07-26 17:35:40 -0700236 # Delays to deal with DUT state transitions.
Chrome Bot9a1137d2011-07-19 14:35:00 -0700237 SLEEP_DELAY = 6
238 BOOT_DELAY = 10
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700239
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700240 # Default minimum time interval between 'press' and 'release'
241 # keyboard events.
Vic Yang0aca1c22012-11-19 18:33:56 -0800242 SERVO_KEY_PRESS_DELAY = 0.1
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700243
Tom Wai-Hong Tam93b5c092015-05-14 02:50:43 +0800244 # Time to toggle recovery switch on and off.
245 REC_TOGGLE_DELAY = 0.1
246
Tom Wai-Hong Tamf9ded092015-05-20 05:37:22 +0800247 # Time to toggle development switch on and off.
248 DEV_TOGGLE_DELAY = 0.1
249
Jon Salzc88e5b62011-11-30 14:38:54 +0800250 # Time between an usb disk plugged-in and detected in the system.
251 USB_DETECTION_DELAY = 10
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800252 # Time to keep USB power off before and after USB mux direction is changed
253 USB_POWEROFF_DELAY = 2
Jon Salzc88e5b62011-11-30 14:38:54 +0800254
Simran Basib7850bb2013-07-24 12:33:42 -0700255 # Time to wait before timing out on servo initialization.
256 INIT_TIMEOUT_SECS = 10
Simran Basi59331342013-07-12 12:06:29 -0700257
J. Richard Barnette67ccb872012-04-19 16:34:56 -0700258
Ricky Liang0dd379c2014-04-23 16:29:08 +0800259 def __init__(self, servo_host, servo_serial=None):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700260 """Sets up the servo communication infrastructure.
261
Fang Deng5d518f42013-08-02 14:04:32 -0700262 @param servo_host: A ServoHost object representing
263 the host running servod.
Dana Goyettea2f00ea2019-06-26 16:14:12 -0700264 @type servo_host: autotest_lib.server.hosts.servo_host.ServoHost
Ricky Liang0dd379c2014-04-23 16:29:08 +0800265 @param servo_serial: Serial number of the servo board.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700266 """
Fang Deng5d518f42013-08-02 14:04:32 -0700267 # TODO(fdeng): crbug.com/298379
268 # We should move servo_host object out of servo object
269 # to minimize the dependencies on the rest of Autotest.
270 self._servo_host = servo_host
Ricky Liang0dd379c2014-04-23 16:29:08 +0800271 self._servo_serial = servo_serial
Richard Barnette180efe62016-12-02 23:20:44 +0000272 self._server = servo_host.get_servod_server_proxy()
J. Richard Barnette0c9c5882014-06-11 15:27:05 -0700273 self._power_state = _PowerStateController(self)
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700274 self._uart = _Uart(self)
Fang Dengafb88142013-05-30 17:44:31 -0700275 self._usb_state = None
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700276 self._programmer = None
Dana Goyette4dc0adc2019-05-06 14:51:53 -0700277 self._prev_log_inode = None
278 self._prev_log_size = 0
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800279
Ricky Liang0dd379c2014-04-23 16:29:08 +0800280 @property
281 def servo_serial(self):
282 """Returns the serial number of the servo board."""
283 return self._servo_serial
284
Dana Goyette4dc0adc2019-05-06 14:51:53 -0700285 def fetch_servod_log(self, filename=None, skip_old=False):
286 """Save the servod log into the given local file.
287
288 @param filename: save the contents into a file with the given name.
289 @param skip_old: if True, skip past the old data in the log file.
290 @type filename: str
291 @type skip_old: bool
292 @rtype: None
293 """
294 return self._servo_host.fetch_servod_log(filename, skip_old)
Ricky Liang0dd379c2014-04-23 16:29:08 +0800295
Tom Wai-Hong Tam22ee0e52013-04-03 13:36:39 +0800296 def get_power_state_controller(self):
J. Richard Barnette75136b32013-03-26 13:38:44 -0700297 """Return the power state controller for this Servo.
298
299 The power state controller provides board-independent
300 interfaces for reset, power-on, power-off operations.
301
302 """
303 return self._power_state
304
Fang Deng5d518f42013-08-02 14:04:32 -0700305
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700306 def initialize_dut(self, cold_reset=False):
307 """Initializes a dut for testing purposes.
308
309 This sets various servo signals back to default values
310 appropriate for the target board. By default, if the DUT
311 is already on, it stays on. If the DUT is powered off
312 before initialization, its state afterward is unspecified.
313
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700314 Rationale: Basic initialization of servo sets the lid open,
315 when there is a lid. This operation won't affect powered on
316 units; however, setting the lid open may power on a unit
J. Richard Barnette75136b32013-03-26 13:38:44 -0700317 that's off, depending on the board type and previous state
318 of the device.
319
J. Richard Barnette4b6af0d2014-06-05 09:57:20 -0700320 If `cold_reset` is a true value, the DUT and its EC will be
321 reset, and the DUT rebooted in normal mode.
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700322
323 @param cold_reset If True, cold reset the device after
324 initialization.
325 """
Dana Goyettea2f00ea2019-06-26 16:14:12 -0700326 try:
327 self._server.hwinit()
328 except socket.error as e:
329 e.filename = '%s:%s' % (self._servo_host.hostname,
330 self._servo_host.servo_port)
331 raise
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700332 self.set('usb_mux_oe1', 'on')
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700333 self._usb_state = None
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700334 self.switch_usbkey('off')
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700335 self._uart.start_capture()
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700336 if cold_reset:
J. Richard Barnette4b6af0d2014-06-05 09:57:20 -0700337 self._power_state.reset()
J. Richard Barnette1777f5d2014-09-03 15:18:19 -0700338 logging.debug('Servo initialized, version is %s',
339 self._server.get_version())
Ruben Rodriguez Buchillon7121b092019-04-29 12:39:34 -0700340 try:
Ruben Rodriguez Buchillon05298242019-05-20 11:29:06 -0700341 # TODO(coconutruben): change this back to set() about a month
342 # after crrev.com/c/1586239 has been merged (or whenever that
343 # logic is in the labstation images).
344 self.set_nocheck('init_keyboard','on')
Ruben Rodriguez Buchillon7121b092019-04-29 12:39:34 -0700345 except error.TestFail as err:
346 if 'No control named' in str(err):
347 # This indicates the servod version does not
348 # have explicit keyboard initialization yet.
349 # Ignore this.
350 pass
351 else:
352 raise err
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700353
354
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800355 def is_localhost(self):
356 """Is the servod hosted locally?
357
358 Returns:
359 True if local hosted; otherwise, False.
360 """
Fang Deng5d518f42013-08-02 14:04:32 -0700361 return self._servo_host.is_localhost()
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800362
363
Mary Ruthvenecf12712019-06-26 17:36:21 -0700364 def get_os_version(self):
365 """Returns the chromeos release version."""
366 lsb_release_content = self.system_output('cat /etc/lsb-release',
367 ignore_status=True)
368 return lsbrelease_utils.get_chromeos_release_builder_path(
369 lsb_release_content=lsb_release_content)
370
371
Mary Ruthven83bb5952019-06-27 12:34:05 -0700372 def get_servod_version(self):
373 """Returns the servod version."""
374 result = self._servo_host.run('servod --version')
375 # TODO: use system_output once servod --version prints to stdout
376 stdout = result.stdout.strip()
377 return stdout if stdout else result.stderr.strip()
378
379
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700380 def power_long_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700381 """Simulate a long power button press."""
J. Richard Barnettef12bff22012-07-18 14:41:06 -0700382 # After a long power press, the EC may ignore the next power
383 # button press (at least on Alex). To guarantee that this
384 # won't happen, we need to allow the EC one second to
385 # collect itself.
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800386 # long_press is defined as 8.5s in servod
387 self.set_nocheck('power_key', 'long_press')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700388
389
390 def power_normal_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700391 """Simulate a normal power button press."""
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800392 # press is defined as 1.2s in servod
393 self.set_nocheck('power_key', 'press')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700394
395
396 def power_short_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700397 """Simulate a short power button press."""
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800398 # tab is defined as 0.2s in servod
399 self.set_nocheck('power_key', 'tab')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700400
401
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800402 def power_key(self, press_secs='tab'):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700403 """Simulate a power button press.
404
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800405 @param press_secs: int, float, str; time to press key in seconds or
406 known shorthand: 'tab' 'press' 'long_press'
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700407 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800408 self.set_nocheck('power_key', press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700409
410
411 def lid_open(self):
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800412 """Simulate opening the lid and raise exception if all attempts fail"""
413 self.set('lid_open', 'yes')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700414
415
416 def lid_close(self):
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800417 """Simulate closing the lid and raise exception if all attempts fail
Craig Harrison48997262011-06-27 14:31:10 -0700418
419 Waits 6 seconds to ensure the device is fully asleep before returning.
420 """
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800421 self.set('lid_open', 'no')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700422 time.sleep(Servo.SLEEP_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700423
Shelley Chenc26575a2015-09-18 10:56:16 -0700424 def volume_up(self, timeout=300):
Kevin Chenge448a8b2016-09-09 10:18:11 -0700425 """Simulate pushing the volume down button.
426
427 @param timeout: Timeout for setting the volume.
428 """
Shelley Chenc26575a2015-09-18 10:56:16 -0700429 self.set_get_all(['volume_up:yes',
430 'sleep:%.4f' % self.SERVO_KEY_PRESS_DELAY,
431 'volume_up:no'])
432 # we need to wait for commands to take effect before moving on
433 time_left = float(timeout)
434 while time_left > 0.0:
435 value = self.get('volume_up')
436 if value == 'no':
437 return
438 time.sleep(self.SHORT_DELAY)
439 time_left = time_left - self.SHORT_DELAY
440 raise error.TestFail("Failed setting volume_up to no")
441
442 def volume_down(self, timeout=300):
Kevin Chenge448a8b2016-09-09 10:18:11 -0700443 """Simulate pushing the volume down button.
444
445 @param timeout: Timeout for setting the volume.
446 """
Shelley Chenc26575a2015-09-18 10:56:16 -0700447 self.set_get_all(['volume_down:yes',
448 'sleep:%.4f' % self.SERVO_KEY_PRESS_DELAY,
449 'volume_down:no'])
450 # we need to wait for commands to take effect before moving on
451 time_left = float(timeout)
452 while time_left > 0.0:
453 value = self.get('volume_down')
454 if value == 'no':
455 return
456 time.sleep(self.SHORT_DELAY)
457 time_left = time_left - self.SHORT_DELAY
458 raise error.TestFail("Failed setting volume_down to no")
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700459
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800460 def ctrl_d(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800461 """Simulate Ctrl-d simultaneous button presses.
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800462
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800463 @param press_secs: int, float, str; time to press key in seconds or
464 known shorthand: 'tab' 'press' 'long_press'
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800465 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800466 self.set_nocheck('ctrl_d', press_secs)
Gediminas Ramanauskas9da80f22012-11-14 12:59:43 -0800467
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800468
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800469 def ctrl_u(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800470 """Simulate Ctrl-u simultaneous button presses.
471
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800472 @param press_secs: int, float, str; time to press key in seconds or
473 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800474 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800475 self.set_nocheck('ctrl_u', press_secs)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700476
477
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800478 def ctrl_enter(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800479 """Simulate Ctrl-enter simultaneous button presses.
480
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800481 @param press_secs: int, float, str; time to press key in seconds or
482 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800483 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800484 self.set_nocheck('ctrl_enter', press_secs)
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -0700485
486
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800487 def ctrl_key(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800488 """Simulate Enter key button press.
489
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800490 @param press_secs: int, float, str; time to press key in seconds or
491 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800492 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800493 self.set_nocheck('ctrl_key', press_secs)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700494
495
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800496 def enter_key(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800497 """Simulate Enter key button press.
498
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800499 @param press_secs: int, float, str; time to press key in seconds or
500 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800501 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800502 self.set_nocheck('enter_key', press_secs)
Todd Broch9753bd42012-03-21 10:15:08 -0700503
504
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800505 def refresh_key(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800506 """Simulate Refresh key (F3) button press.
507
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800508 @param press_secs: int, float, str; time to press key in seconds or
509 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800510 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800511 self.set_nocheck('refresh_key', press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700512
513
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800514 def ctrl_refresh_key(self, press_secs='tab'):
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800515 """Simulate Ctrl and Refresh (F3) simultaneous press.
516
517 This key combination is an alternative of Space key.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800518
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800519 @param press_secs: int, float, str; time to press key in seconds or
520 known shorthand: 'tab' 'press' 'long_press'
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800521 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800522 self.set_nocheck('ctrl_refresh_key', press_secs)
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800523
524
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800525 def imaginary_key(self, press_secs='tab'):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700526 """Simulate imaginary key button press.
527
528 Maps to a key that doesn't physically exist.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800529
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800530 @param press_secs: int, float, str; time to press key in seconds or
531 known shorthand: 'tab' 'press' 'long_press'
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700532 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800533 self.set_nocheck('imaginary_key', press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700534
535
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800536 def sysrq_x(self, press_secs='tab'):
Vincent Palatine7dc9282016-07-14 11:31:58 +0200537 """Simulate Alt VolumeUp X simulataneous press.
538
539 This key combination is the kernel system request (sysrq) X.
540
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800541 @param press_secs: int, float, str; time to press key in seconds or
542 known shorthand: 'tab' 'press' 'long_press'
Vincent Palatine7dc9282016-07-14 11:31:58 +0200543 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800544 self.set_nocheck('sysrq_x', press_secs)
Vincent Palatine7dc9282016-07-14 11:31:58 +0200545
546
Tom Wai-Hong Tam93b5c092015-05-14 02:50:43 +0800547 def toggle_recovery_switch(self):
548 """Toggle recovery switch on and off."""
549 self.enable_recovery_mode()
550 time.sleep(self.REC_TOGGLE_DELAY)
551 self.disable_recovery_mode()
552
553
Craig Harrison6b36b122011-06-28 17:58:43 -0700554 def enable_recovery_mode(self):
555 """Enable recovery mode on device."""
556 self.set('rec_mode', 'on')
557
558
559 def disable_recovery_mode(self):
560 """Disable recovery mode on device."""
561 self.set('rec_mode', 'off')
562
563
Tom Wai-Hong Tamf9ded092015-05-20 05:37:22 +0800564 def toggle_development_switch(self):
565 """Toggle development switch on and off."""
566 self.enable_development_mode()
567 time.sleep(self.DEV_TOGGLE_DELAY)
568 self.disable_development_mode()
569
570
Craig Harrison6b36b122011-06-28 17:58:43 -0700571 def enable_development_mode(self):
572 """Enable development mode on device."""
573 self.set('dev_mode', 'on')
574
575
576 def disable_development_mode(self):
577 """Disable development mode on device."""
578 self.set('dev_mode', 'off')
579
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700580 def boot_devmode(self):
581 """Boot a dev-mode device that is powered off."""
Tom Wai-Hong Tam23869102012-01-17 15:41:30 +0800582 self.power_short_press()
Craig Harrison48997262011-06-27 14:31:10 -0700583 self.pass_devmode()
584
585
586 def pass_devmode(self):
587 """Pass through boot screens in dev-mode."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700588 time.sleep(Servo.BOOT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700589 self.ctrl_d()
Chrome Bot9a1137d2011-07-19 14:35:00 -0700590 time.sleep(Servo.BOOT_DELAY)
Craig Harrison48997262011-06-27 14:31:10 -0700591
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700592
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800593 def get_board(self):
Wai-Hong Tam0f904002017-09-19 15:52:22 -0700594 """Get the board connected to servod."""
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800595 return self._server.get_board()
596
597
Wai-Hong Tam0f904002017-09-19 15:52:22 -0700598 def get_base_board(self):
599 """Get the board of the base connected to servod."""
Wai-Hong Tam7f6169e2017-09-29 09:23:48 -0700600 try:
601 return self._server.get_base_board()
602 except xmlrpclib.Fault as e:
603 # TODO(waihong): Remove the following compatibility check when
604 # the new versions of hdctools are deployed.
605 if 'not supported' in str(e):
606 logging.warning('The servod is too old that get_base_board '
607 'not supported.')
608 return ''
609 raise
Wai-Hong Tam0f904002017-09-19 15:52:22 -0700610
611
Wai-Hong Tamcc399ee2017-12-08 12:43:28 -0800612 def get_ec_active_copy(self):
613 """Get the active copy of the EC image."""
614 return self.get('ec_active_copy')
615
616
Todd Brochefe72cb2012-07-11 19:58:53 -0700617 def _get_xmlrpclib_exception(self, xmlexc):
618 """Get meaningful exception string from xmlrpc.
619
620 Args:
621 xmlexc: xmlrpclib.Fault object
622
623 xmlrpclib.Fault.faultString has the following format:
624
625 <type 'exception type'>:'actual error message'
626
627 Parse and return the real exception from the servod side instead of the
628 less meaningful one like,
629 <Fault 1: "<type 'exceptions.AttributeError'>:'tca6416' object has no
630 attribute 'hw_driver'">
631
632 Returns:
633 string of underlying exception raised in servod.
634 """
635 return re.sub('^.*>:', '', xmlexc.faultString)
636
637
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700638 def get(self, gpio_name):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700639 """Get the value of a gpio from Servod.
640
641 @param gpio_name Name of the gpio.
642 """
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700643 assert gpio_name
Todd Brochefe72cb2012-07-11 19:58:53 -0700644 try:
Simran Basi1cbc5f22013-07-17 14:23:58 -0700645 return self._server.get(gpio_name)
Todd Brochefe72cb2012-07-11 19:58:53 -0700646 except xmlrpclib.Fault as e:
647 err_msg = "Getting '%s' :: %s" % \
648 (gpio_name, self._get_xmlrpclib_exception(e))
649 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700650
651
652 def set(self, gpio_name, gpio_value):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700653 """Set and check the value of a gpio using Servod.
654
655 @param gpio_name Name of the gpio.
656 @param gpio_value New setting for the gpio.
657 """
Chrome Bot9a1137d2011-07-19 14:35:00 -0700658 self.set_nocheck(gpio_name, gpio_value)
Todd Brochcf7c6652012-02-24 13:03:59 -0800659 retry_count = Servo.GET_RETRY_MAX
660 while gpio_value != self.get(gpio_name) and retry_count:
Ilja H. Friedel04be2bd2014-05-07 21:29:59 -0700661 logging.warning("%s != %s, retry %d", gpio_name, gpio_value,
Todd Brochcf7c6652012-02-24 13:03:59 -0800662 retry_count)
663 retry_count -= 1
664 time.sleep(Servo.SHORT_DELAY)
665 if not retry_count:
666 assert gpio_value == self.get(gpio_name), \
667 'Servo failed to set %s to %s' % (gpio_name, gpio_value)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700668
669
670 def set_nocheck(self, gpio_name, gpio_value):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700671 """Set the value of a gpio using Servod.
672
673 @param gpio_name Name of the gpio.
674 @param gpio_value New setting for the gpio.
675 """
Ruben Rodriguez Buchillon1c0219c2019-06-25 12:30:44 -0700676 # The real danger here is to pass a None value through the xmlrpc.
677 assert gpio_name and gpio_value is not None
Mary Ruthvendfa45342019-06-25 10:10:17 -0700678 logging.debug('Setting %s to %r', gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700679 try:
Simran Basi1cbc5f22013-07-17 14:23:58 -0700680 self._server.set(gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700681 except xmlrpclib.Fault as e:
Mary Ruthven73d3a352018-05-10 15:35:20 -0700682 err_msg = "Setting '%s' to %r :: %s" % \
Todd Brochefe72cb2012-07-11 19:58:53 -0700683 (gpio_name, gpio_value, self._get_xmlrpclib_exception(e))
684 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700685
686
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800687 def set_get_all(self, controls):
688 """Set &| get one or more control values.
689
690 @param controls: list of strings, controls to set &| get.
691
692 @raise: error.TestError in case error occurs setting/getting values.
693 """
694 rv = []
695 try:
Mary Ruthvendfa45342019-06-25 10:10:17 -0700696 logging.debug('Set/get all: %s', str(controls))
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800697 rv = self._server.set_get_all(controls)
698 except xmlrpclib.Fault as e:
699 # TODO(waihong): Remove the following backward compatibility when
700 # the new versions of hdctools are deployed.
701 if 'not supported' in str(e):
702 logging.warning('The servod is too old that set_get_all '
703 'not supported. Use set and get instead.')
704 for control in controls:
705 if ':' in control:
706 (name, value) = control.split(':')
707 if name == 'sleep':
708 time.sleep(float(value))
709 else:
710 self.set_nocheck(name, value)
711 rv.append(True)
712 else:
713 rv.append(self.get(name))
714 else:
715 err_msg = "Problem with '%s' :: %s" % \
716 (controls, self._get_xmlrpclib_exception(e))
717 raise error.TestFail(err_msg)
718 return rv
719
720
Jon Salzc88e5b62011-11-30 14:38:54 +0800721 # TODO(waihong) It may fail if multiple servo's are connected to the same
722 # host. Should look for a better way, like the USB serial name, to identify
723 # the USB device.
Simran Basi741b5d42012-05-18 11:27:15 -0700724 # TODO(sbasi) Remove this code from autoserv once firmware tests have been
725 # updated.
Kevin Chenga22c4a82016-10-07 14:13:25 -0700726 def probe_host_usb_dev(self, timeout=_USB_PROBE_TIMEOUT):
Jon Salzc88e5b62011-11-30 14:38:54 +0800727 """Probe the USB disk device plugged-in the servo from the host side.
728
Kevin Chengeb06fe72016-08-22 15:26:32 -0700729 It uses servod to discover if there is a usb device attached to it.
Jon Salzc88e5b62011-11-30 14:38:54 +0800730
Kevin Chenga22c4a82016-10-07 14:13:25 -0700731 @param timeout The timeout period when probing for the usb host device.
732
733 @return: String of USB disk path (e.g. '/dev/sdb') or None.
Jon Salzc88e5b62011-11-30 14:38:54 +0800734 """
Ruben Rodriguez Buchillon9a4bfc32018-10-16 20:46:25 +0800735 # Set up Servo's usb mux.
736 self.switch_usbkey('host')
Kevin Chenga22c4a82016-10-07 14:13:25 -0700737 return self._server.probe_host_usb_dev(timeout) or None
Jon Salzc88e5b62011-11-30 14:38:54 +0800738
739
Mike Truty49153d82012-08-21 22:27:30 -0500740 def image_to_servo_usb(self, image_path=None,
741 make_image_noninteractive=False):
742 """Install an image to the USB key plugged into the servo.
743
744 This method may copy any image to the servo USB key including a
745 recovery image or a test image. These images are frequently used
746 for test purposes such as restoring a corrupted image or conducting
747 an upgrade of ec/fw/kernel as part of a test of a specific image part.
748
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700749 @param image_path Path on the host to the recovery image.
750 @param make_image_noninteractive Make the recovery image
751 noninteractive, therefore the DUT
752 will reboot automatically after
753 installation.
Mike Truty49153d82012-08-21 22:27:30 -0500754 """
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700755 # We're about to start plugging/unplugging the USB key. We
756 # don't know the state of the DUT, or what it might choose
757 # to do to the device after hotplug. To avoid surprises,
758 # force the DUT to be off.
759 self._server.hwinit()
Ruben Rodriguez Buchillonbd3b74f2019-08-08 16:18:27 -0700760 try:
761 # TODO(coconutruben): change this back to set() about a month
762 # after crrev.com/c/1586239 has been merged (or whenever that
763 # logic is in the labstation images).
764 self.set_nocheck('init_keyboard','on')
765 except error.TestFail as err:
766 if 'No control named' in str(err):
767 # This indicates the servod version does not
768 # have explicit keyboard initialization yet.
769 # Ignore this.
770 pass
771 else:
772 raise err
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700773 self._power_state.power_off()
Mike Truty49153d82012-08-21 22:27:30 -0500774
Mike Truty49153d82012-08-21 22:27:30 -0500775 if image_path:
Garry Wangd2656812019-08-07 17:29:16 -0700776 # Set up Servo's usb mux.
777 self.switch_usbkey('host')
Tom Wai-Hong Tam42f136d2012-10-26 11:11:23 +0800778 logging.info('Searching for usb device and copying image to it. '
779 'Please wait a few minutes...')
Mike Truty49153d82012-08-21 22:27:30 -0500780 if not self._server.download_image_to_usb(image_path):
781 logging.error('Failed to transfer requested image to USB. '
782 'Please take a look at Servo Logs.')
783 raise error.AutotestError('Download image to usb failed.')
784 if make_image_noninteractive:
785 logging.info('Making image noninteractive')
786 if not self._server.make_image_noninteractive():
787 logging.error('Failed to make image noninteractive. '
788 'Please take a look at Servo Logs.')
789
Kalin Stoyanovb3c11f32018-05-11 09:02:00 -0700790 def boot_in_recovery_mode(self):
791 """Boot host DUT in recovery mode."""
792 self._power_state.power_on(rec_mode=self._power_state.REC_ON)
793 self.switch_usbkey('dut')
794
Mike Truty49153d82012-08-21 22:27:30 -0500795
Simran Basi741b5d42012-05-18 11:27:15 -0700796 def install_recovery_image(self, image_path=None,
J. Richard Barnetteb6de7e32013-02-14 13:28:04 -0800797 make_image_noninteractive=False):
Dan Shic67f1332016-04-06 12:38:06 -0700798 """Install the recovery image specified by the path onto the DUT.
Jon Salzc88e5b62011-11-30 14:38:54 +0800799
800 This method uses google recovery mode to install a recovery image
801 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 +0800802 board specified by the usb_dev. If no image path is specified
Jon Salzc88e5b62011-11-30 14:38:54 +0800803 we use the recovery image already on the usb image.
804
Dan Shic67f1332016-04-06 12:38:06 -0700805 @param image_path: Path on the host to the recovery image.
806 @param make_image_noninteractive: Make the recovery image
807 noninteractive, therefore the DUT will reboot automatically
808 after installation.
Jon Salzc88e5b62011-11-30 14:38:54 +0800809 """
Mike Truty49153d82012-08-21 22:27:30 -0500810 self.image_to_servo_usb(image_path, make_image_noninteractive)
Garry Wangd2656812019-08-07 17:29:16 -0700811 # Give the DUT some time to power_off if we skip
812 # download image to usb. (crbug.com/982993)
813 if not image_path:
814 time.sleep(10)
Kalin Stoyanovb3c11f32018-05-11 09:02:00 -0700815 self.boot_in_recovery_mode()
Jon Salzc88e5b62011-11-30 14:38:54 +0800816
817
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800818 def _scp_image(self, image_path):
819 """Copy image to the servo host.
820
821 When programming a firmware image on the DUT, the image must be
822 located on the host to which the servo device is connected. Sometimes
823 servo is controlled by a remote host, in this case the image needs to
824 be transferred to the remote host.
825
826 @param image_path: a string, name of the firmware image file to be
827 transferred.
828 @return: a string, full path name of the copied file on the remote.
829 """
830
831 dest_path = os.path.join('/tmp', os.path.basename(image_path))
Fang Deng5d518f42013-08-02 14:04:32 -0700832 self._servo_host.send_file(image_path, dest_path)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800833 return dest_path
834
835
Dan Shifecdaf42015-07-28 10:17:26 -0700836 def system(self, command, timeout=3600):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700837 """Execute the passed in command on the servod host.
838
839 @param command Command to be executed.
Dan Shifecdaf42015-07-28 10:17:26 -0700840 @param timeout Maximum number of seconds of runtime allowed. Default to
841 1 hour.
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700842 """
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800843 logging.info('Will execute on servo host: %s', command)
Fang Deng5d518f42013-08-02 14:04:32 -0700844 self._servo_host.run(command, timeout=timeout)
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800845
846
Dan Shifecdaf42015-07-28 10:17:26 -0700847 def system_output(self, command, timeout=3600,
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800848 ignore_status=False, args=()):
849 """Execute the passed in command on the servod host, return stdout.
850
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700851 @param command a string, the command to execute
852 @param timeout an int, max number of seconds to wait til command
Dan Shifecdaf42015-07-28 10:17:26 -0700853 execution completes. Default to 1 hour.
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700854 @param ignore_status a Boolean, if true - ignore command's nonzero exit
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800855 status, otherwise an exception will be thrown
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700856 @param args a tuple of strings, each becoming a separate command line
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800857 parameter for the command
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700858 @return command's stdout as a string.
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800859 """
Fang Deng5d518f42013-08-02 14:04:32 -0700860 return self._servo_host.run(command, timeout=timeout,
861 ignore_status=ignore_status,
862 args=args).stdout.strip()
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800863
864
Dan Shia5fef052015-05-18 23:28:47 -0700865 def get_servo_version(self):
866 """Get the version of the servo, e.g., servo_v2 or servo_v3.
867
868 @return: The version of the servo.
869
870 """
871 return self._server.get_version()
872
873
Mary Ruthven2724ee62019-07-16 11:16:59 -0700874 def running_through_ccd(self):
875 """Returns True if the setup is using ccd to run."""
876 servo = self._server.get_version()
877 return 'ccd_cr50' in servo and 'servo_micro' not in servo
878
879
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800880 def _initialize_programmer(self, rw_only=False):
881 """Initialize the firmware programmer.
882
883 @param rw_only: True to initialize a programmer which only
884 programs the RW portions.
885 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700886 if self._programmer:
887 return
888 # Initialize firmware programmer
Dan Shia5fef052015-05-18 23:28:47 -0700889 servo_version = self.get_servo_version()
Dan Shi9cb0eec2014-06-03 09:04:50 -0700890 if servo_version.startswith('servo_v2'):
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700891 self._programmer = firmware_programmer.ProgrammerV2(self)
Wai-Hong Tamc36c4d22016-09-09 10:39:45 -0700892 self._programmer_rw = firmware_programmer.ProgrammerV2RwOnly(self)
Kevin Chengdf2e29f2016-09-09 02:31:22 -0700893 # Both servo v3 and v4 use the same programming methods so just leverage
894 # ProgrammerV3 for servo v4 as well.
895 elif (servo_version.startswith('servo_v3') or
896 servo_version.startswith('servo_v4')):
Dan Shia5fef052015-05-18 23:28:47 -0700897 self._programmer = firmware_programmer.ProgrammerV3(self)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800898 self._programmer_rw = firmware_programmer.ProgrammerV3RwOnly(self)
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700899 else:
900 raise error.TestError(
901 'No firmware programmer for servo version: %s' %
Congbin Guo42427612019-02-12 10:22:06 -0800902 servo_version)
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700903
904
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800905 def program_bios(self, image, rw_only=False):
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800906 """Program bios on DUT with given image.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800907
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800908 @param image: a string, file name of the BIOS image to program
909 on the DUT.
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800910 @param rw_only: True to only program the RW portion of BIOS.
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800911
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800912 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700913 self._initialize_programmer()
Ricky Liangc31aab32014-07-03 16:23:29 +0800914 if not self.is_localhost():
915 image = self._scp_image(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800916 if rw_only:
917 self._programmer_rw.program_bios(image)
918 else:
919 self._programmer.program_bios(image)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800920
921
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800922 def program_ec(self, image, rw_only=False):
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800923 """Program ec on DUT with given image.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800924
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800925 @param image: a string, file name of the EC image to program
926 on the DUT.
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800927 @param rw_only: True to only program the RW portion of EC.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800928
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800929 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700930 self._initialize_programmer()
Ricky Liangc31aab32014-07-03 16:23:29 +0800931 if not self.is_localhost():
932 image = self._scp_image(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800933 if rw_only:
Congbin Guo42427612019-02-12 10:22:06 -0800934 self._programmer_rw.program_ec(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800935 else:
Congbin Guo42427612019-02-12 10:22:06 -0800936 self._programmer.program_ec(image)
937
938
939 def _reprogram(self, tarball_path, firmware_name, image_candidates,
940 rw_only):
941 """Helper function to reprogram firmware for EC or BIOS.
942
943 @param tarball_path: The path of the downloaded build tarball.
944 @param: firmware_name: either 'EC' or 'BIOS'.
945 @param image_candidates: A tuple of the paths of image candidates.
946 @param rw_only: True to only install firmware to its RW portions. Keep
947 the RO portions unchanged.
948
949 @raise: TestError if cannot extract firmware from the tarball.
950 """
951 dest_dir = os.path.dirname(tarball_path)
952 image = _extract_image_from_tarball(tarball_path, dest_dir,
953 image_candidates)
954 if not image:
955 if firmware_name == 'EC':
956 logging.info('Not a Chrome EC, ignore re-programming it')
957 return
958 else:
959 raise error.TestError('Failed to extract the %s image from '
960 'tarball' % firmware_name)
961
962 logging.info('Will re-program %s %snow', firmware_name,
963 'RW ' if rw_only else '')
964
965 if firmware_name == 'EC':
966 self.program_ec(os.path.join(dest_dir, image), rw_only)
967 else:
968 self.program_bios(os.path.join(dest_dir, image), rw_only)
969
970
Shelley Chenac61d5a2019-06-24 15:35:46 -0700971 def program_firmware(self, board, model, tarball_path, rw_only=False):
Congbin Guo42427612019-02-12 10:22:06 -0800972 """Program firmware (EC, if applied, and BIOS) of the DUT.
973
Shelley Chenac61d5a2019-06-24 15:35:46 -0700974 @param board: The DUT board name.
Congbin Guo42427612019-02-12 10:22:06 -0800975 @param model: The DUT model name.
976 @param tarball_path: The path of the downloaded build tarball.
977 @param rw_only: True to only install firmware to its RW portions. Keep
978 the RO portions unchanged.
979 """
Shelley Chenac61d5a2019-06-24 15:35:46 -0700980 ap_image_candidates = ('image.bin', 'image-%s.bin' % model,
981 'image-%s.bin' % board)
982 ec_image_candidates = ('ec.bin', '%s/ec.bin' % model,
983 '%s/ec.bin' % board)
Congbin Guo42427612019-02-12 10:22:06 -0800984
985 self._reprogram(tarball_path, 'EC', ec_image_candidates, rw_only)
986 self._reprogram(tarball_path, 'BIOS', ap_image_candidates, rw_only)
987
988 self.get_power_state_controller().reset()
989 time.sleep(Servo.BOOT_DELAY)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800990
Fang Dengafb88142013-05-30 17:44:31 -0700991
992 def _switch_usbkey_power(self, power_state, detection_delay=False):
993 """Switch usbkey power.
994
995 This function switches usbkey power by setting the value of
996 'prtctl4_pwren'. If the power is already in the
997 requested state, this function simply returns.
998
999 @param power_state: A string, 'on' or 'off'.
1000 @param detection_delay: A boolean value, if True, sleep
1001 for |USB_DETECTION_DELAY| after switching
1002 the power on.
1003 """
Kevin Chenga22c4a82016-10-07 14:13:25 -07001004 # TODO(kevcheng): Forgive me for this terrible hack. This is just to
1005 # handle beaglebones that haven't yet updated and have the
1006 # safe_switch_usbkey_power RPC. I'll remove this once all beaglebones
1007 # have been updated and also think about a better way to handle
1008 # situations like this.
1009 try:
1010 self._server.safe_switch_usbkey_power(power_state)
1011 except Exception:
1012 self.set('prtctl4_pwren', power_state)
Fang Dengafb88142013-05-30 17:44:31 -07001013 if power_state == 'off':
1014 time.sleep(self.USB_POWEROFF_DELAY)
1015 elif detection_delay:
1016 time.sleep(self.USB_DETECTION_DELAY)
1017
1018
1019 def switch_usbkey(self, usb_state):
1020 """Connect USB flash stick to either host or DUT, or turn USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001021
1022 This function switches the servo multiplexer to provide electrical
Fang Dengafb88142013-05-30 17:44:31 -07001023 connection between the USB port J3 and either host or DUT side. It
1024 can also be used to turn the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001025
Fang Dengafb88142013-05-30 17:44:31 -07001026 Switching to 'dut' or 'host' is accompanied by powercycling
1027 of the USB stick, because it sometimes gets wedged if the mux
1028 is switched while the stick power is on.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001029
Fang Dengafb88142013-05-30 17:44:31 -07001030 @param usb_state: A string, one of 'dut', 'host', or 'off'.
1031 'dut' and 'host' indicate which side the
1032 USB flash device is required to be connected to.
1033 'off' indicates turning the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001034
Fang Dengafb88142013-05-30 17:44:31 -07001035 @raise: error.TestError in case the parameter is not 'dut'
1036 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001037 """
Fang Dengafb88142013-05-30 17:44:31 -07001038 if self.get_usbkey_direction() == usb_state:
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001039 return
1040
Fang Dengafb88142013-05-30 17:44:31 -07001041 if usb_state == 'off':
Dan Shia5fef052015-05-18 23:28:47 -07001042 self._switch_usbkey_power('off')
1043 self._usb_state = usb_state
1044 return
Fang Dengafb88142013-05-30 17:44:31 -07001045 elif usb_state == 'host':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001046 mux_direction = 'servo_sees_usbkey'
Fang Dengafb88142013-05-30 17:44:31 -07001047 elif usb_state == 'dut':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001048 mux_direction = 'dut_sees_usbkey'
1049 else:
Fang Dengafb88142013-05-30 17:44:31 -07001050 raise error.TestError('Unknown USB state request: %s' % usb_state)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001051
Fang Dengafb88142013-05-30 17:44:31 -07001052 self._switch_usbkey_power('off')
Kevin Chenga22c4a82016-10-07 14:13:25 -07001053 # TODO(kevcheng): Forgive me for this terrible hack. This is just to
1054 # handle beaglebones that haven't yet updated and have the
1055 # safe_switch_usbkey RPC. I'll remove this once all beaglebones have
1056 # been updated and also think about a better way to handle situations
1057 # like this.
1058 try:
1059 self._server.safe_switch_usbkey(mux_direction)
1060 except Exception:
1061 self.set('usb_mux_sel1', mux_direction)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001062 time.sleep(self.USB_POWEROFF_DELAY)
Fang Dengafb88142013-05-30 17:44:31 -07001063 self._switch_usbkey_power('on', usb_state == 'host')
1064 self._usb_state = usb_state
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001065
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001066
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001067 def get_usbkey_direction(self):
Fang Dengafb88142013-05-30 17:44:31 -07001068 """Get which side USB is connected to or 'off' if usb power is off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001069
Fang Dengafb88142013-05-30 17:44:31 -07001070 @return: A string, one of 'dut', 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001071 """
Fang Dengafb88142013-05-30 17:44:31 -07001072 if not self._usb_state:
1073 if self.get('prtctl4_pwren') == 'off':
1074 self._usb_state = 'off'
1075 elif self.get('usb_mux_sel1').startswith('dut'):
1076 self._usb_state = 'dut'
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001077 else:
Fang Dengafb88142013-05-30 17:44:31 -07001078 self._usb_state = 'host'
1079 return self._usb_state
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001080
1081
Wai-Hong Tam60377262018-03-01 10:55:39 -08001082 def set_servo_v4_role(self, role):
1083 """Set the power role of servo v4, either 'src' or 'snk'.
1084
1085 It does nothing if not a servo v4.
1086
1087 @param role: Power role for DUT port on servo v4, either 'src' or 'snk'.
1088 """
1089 servo_version = self.get_servo_version()
1090 if servo_version.startswith('servo_v4'):
1091 value = self.get('servo_v4_role')
1092 if value != role:
1093 self.set_nocheck('servo_v4_role', role)
1094 else:
1095 logging.debug('Already in the role: %s.', role)
1096 else:
1097 logging.debug('Not a servo v4, unable to set role to %s.', role)
1098
1099
Congbin Guofc3b8962019-03-22 17:38:46 -07001100 @property
1101 def uart_logs_dir(self):
1102 """Return the directory to save UART logs."""
1103 return self._uart.logs_dir if self._uart else ""
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001104
Congbin Guofc3b8962019-03-22 17:38:46 -07001105
1106 @uart_logs_dir.setter
1107 def uart_logs_dir(self, logs_dir):
1108 """Set directory to save UART logs.
1109
1110 @param logs_dir String of directory name."""
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001111 if self._uart:
Congbin Guofc3b8962019-03-22 17:38:46 -07001112 self._uart.logs_dir = logs_dir
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001113
1114
1115 def close(self):
1116 """Close the servo object."""
1117 if self._uart:
1118 self._uart.stop_capture()
Congbin Guofc3b8962019-03-22 17:38:46 -07001119 self._uart.dump()
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001120 self._uart = None