blob: 295bf3ec62ef609b473fc2f3351368918ebcb381 [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 """
Ravutappa951ca432019-05-15 09:52:52 -0700107 # TODO: warm_reset support has added to power_state.py. Once it
108 # available to labstation remove fallback method.
109 try:
110 self._servo.set_nocheck('power_state', 'warm_reset')
111 except error.TestFail as err:
112 logging.info("Fallback to warm_reset control method")
113 self._servo.set_get_all(['warm_reset:on',
J. Richard Barnette0c9c5882014-06-11 15:27:05 -0700114 'sleep:%.4f' % self._RESET_HOLD_TIME,
115 'warm_reset:off'])
116
J. Richard Barnette0c9c5882014-06-11 15:27:05 -0700117 def power_off(self):
118 """Force the DUT to power off.
119
120 The DUT is guaranteed to be off at the end of this call,
121 regardless of its previous state, provided that there is
122 working EC and boot firmware. There is no requirement for
123 working OS software.
124
125 """
126 self._servo.set_nocheck('power_state', 'off')
127
128 def power_on(self, rec_mode=REC_OFF):
129 """Force the DUT to power on.
130
131 Prior to calling this function, the DUT must be powered off,
132 e.g. with a call to `power_off()`.
133
134 At power on, recovery mode is set as specified by the
135 corresponding argument. When booting with recovery mode on, it
136 is the caller's responsibility to unplug/plug in a bootable
137 external storage device.
138
139 If the DUT requires a delay after powering on but before
140 processing inputs such as USB stick insertion, the delay is
141 handled by this method; the caller is not responsible for such
142 delays.
143
144 @param rec_mode Setting of recovery mode to be applied at
145 power on. default: REC_OFF aka 'off'
146
147 """
148 self._servo.set_nocheck('power_state', rec_mode)
149
150
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700151class _Uart(object):
Congbin Guo0f00be82019-04-18 17:51:14 -0700152 """Class to capture UART streams of CPU, EC, Cr50, etc."""
153 _UartToCapture = ('cpu', 'ec', 'cr50', 'servo_v4', 'servo_micro', 'usbpd')
154
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700155 def __init__(self, servo):
156 self._servo = servo
157 self._streams = []
Congbin Guo0f00be82019-04-18 17:51:14 -0700158 self.logs_dir = None
Congbin Guofe4d4e82019-04-18 17:51:14 -0700159
Congbin Guo0f00be82019-04-18 17:51:14 -0700160 def _start_stop_capture(self, uart, start):
161 """Helper function to start/stop capturing on specified UART.
162
163 @param uart: The UART name to start/stop capturing.
164 @param start: True to start capturing, otherwise stop.
165
Ruben Rodriguez Buchillonecb525f2019-09-03 14:52:14 -0700166 @returns True if the operation completes successfully.
167 False if the UART capturing is not supported or failed due to
168 an error.
Congbin Guo0f00be82019-04-18 17:51:14 -0700169 """
170 logging.debug('%s capturing %s UART.', 'Start' if start else 'Stop',
171 uart)
Ruben Rodriguez Buchillonb0e2a9f2019-08-12 12:40:44 -0700172 uart_cmd = '%s_uart_capture' % uart
Ruben Rodriguez Buchillonecb525f2019-09-03 14:52:14 -0700173 target_level = 'on' if start else 'off'
174 level = None
Ruben Rodriguez Buchillonb0e2a9f2019-08-12 12:40:44 -0700175 if self._servo.has_control(uart_cmd):
Ruben Rodriguez Buchillon9f485642019-08-21 14:32:22 -0700176 # Do our own implementation of set() here as not_applicable
177 # should also count as a valid control.
Ruben Rodriguez Buchillonecb525f2019-09-03 14:52:14 -0700178 logging.debug('Trying to set %s to %s.', uart_cmd, target_level)
179 try:
180 self._servo.set_nocheck(uart_cmd, target_level)
181 level = self._servo.get(uart_cmd)
182 except error.TestFail as e:
183 # Any sort of test failure here should not stop the test. This
184 # is just to capture more output. Log and move on.
185 logging.warning('Failed to set %s to %s. %s. Ignoring.',
186 uart_cmd, target_level, str(e))
187 if level == target_level:
188 logging.debug('Managed to set %s to %s.', uart_cmd, level)
189 else:
190 logging.debug('Failed to set %s to %s. Got %s.', uart_cmd,
191 target_level, level)
192 return level == target_level
Congbin Guo0f00be82019-04-18 17:51:14 -0700193
194 def start_capture(self):
195 """Start capturing UART streams."""
196 for uart in self._UartToCapture:
197 if self._start_stop_capture(uart, True):
198 self._streams.append(('%s_uart_stream' % uart, '%s_uart.log' %
199 uart))
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700200
Congbin Guofc3b8962019-03-22 17:38:46 -0700201 def dump(self):
202 """Dump UART streams to log files accordingly."""
Congbin Guo0f00be82019-04-18 17:51:14 -0700203 if not self.logs_dir:
Congbin Guofc3b8962019-03-22 17:38:46 -0700204 return
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700205
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700206 for stream, logfile in self._streams:
Congbin Guo0f00be82019-04-18 17:51:14 -0700207 logfile_fullname = os.path.join(self.logs_dir, logfile)
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700208 try:
209 content = self._servo.get(stream)
210 except Exception as err:
211 logging.warn('Failed to get UART log for %s: %s', stream, err)
212 continue
213
Kuang-che Wu12192fc2019-08-31 09:55:00 +0800214 if content == 'not_applicable':
215 logging.warn('%s is not applicable', stream)
216 continue
217
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700218 # The UART stream may contain non-printable characters, and servo
219 # returns it in string representation. We use `ast.leteral_eval`
220 # to revert it back.
221 with open(logfile_fullname, 'a') as fd:
Kuang-che Wu12192fc2019-08-31 09:55:00 +0800222 try:
223 fd.write(ast.literal_eval(content))
224 except ValueError:
225 logging.exception('Invalid value for %s: %r', stream,
226 content)
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700227
228 def stop_capture(self):
229 """Stop capturing UART streams."""
Congbin Guo0f00be82019-04-18 17:51:14 -0700230 for uart in self._UartToCapture:
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700231 try:
Congbin Guo0f00be82019-04-18 17:51:14 -0700232 self._start_stop_capture(uart, False)
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700233 except Exception as err:
234 logging.warn('Failed to stop UART logging for %s: %s', uart,
235 err)
236
237
J. Richard Barnette384056b2012-04-16 11:04:46 -0700238class Servo(object):
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700239
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700240 """Manages control of a Servo board.
241
242 Servo is a board developed by hardware group to aide in the debug and
243 control of various partner devices. Servo's features include the simulation
244 of pressing the power button, closing the lid, and pressing Ctrl-d. This
245 class manages setting up and communicating with a servo demon (servod)
246 process. It provides both high-level functions for common servo tasks and
247 low-level functions for directly setting and reading gpios.
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700248
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700249 """
250
Chrome Bot9a1137d2011-07-19 14:35:00 -0700251 # Power button press delays in seconds.
J. Richard Barnetted2e4cbd2012-06-29 12:18:40 -0700252 #
J. Richard Barnette5383f072012-07-26 17:35:40 -0700253 # The EC specification says that 8.0 seconds should be enough
254 # for the long power press. However, some platforms need a bit
255 # more time. Empirical testing has found these requirements:
256 # Alex: 8.2 seconds
257 # ZGB: 8.5 seconds
258 # The actual value is set to the largest known necessary value.
259 #
260 # TODO(jrbarnette) Being generous is the right thing to do for
261 # existing platforms, but if this code is to be used for
262 # qualification of new hardware, we should be less generous.
Chrome Bot9a1137d2011-07-19 14:35:00 -0700263 SHORT_DELAY = 0.1
J. Richard Barnette5383f072012-07-26 17:35:40 -0700264
Todd Broch31c82502011-08-29 08:14:39 -0700265 # Maximum number of times to re-read power button on release.
Todd Brochcf7c6652012-02-24 13:03:59 -0800266 GET_RETRY_MAX = 10
Chrome Bot9a1137d2011-07-19 14:35:00 -0700267
J. Richard Barnette5383f072012-07-26 17:35:40 -0700268 # Delays to deal with DUT state transitions.
Chrome Bot9a1137d2011-07-19 14:35:00 -0700269 SLEEP_DELAY = 6
270 BOOT_DELAY = 10
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700271
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700272 # Default minimum time interval between 'press' and 'release'
273 # keyboard events.
Vic Yang0aca1c22012-11-19 18:33:56 -0800274 SERVO_KEY_PRESS_DELAY = 0.1
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700275
Tom Wai-Hong Tam93b5c092015-05-14 02:50:43 +0800276 # Time to toggle recovery switch on and off.
277 REC_TOGGLE_DELAY = 0.1
278
Tom Wai-Hong Tamf9ded092015-05-20 05:37:22 +0800279 # Time to toggle development switch on and off.
280 DEV_TOGGLE_DELAY = 0.1
281
Jon Salzc88e5b62011-11-30 14:38:54 +0800282 # Time between an usb disk plugged-in and detected in the system.
283 USB_DETECTION_DELAY = 10
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800284 # Time to keep USB power off before and after USB mux direction is changed
285 USB_POWEROFF_DELAY = 2
Jon Salzc88e5b62011-11-30 14:38:54 +0800286
Simran Basib7850bb2013-07-24 12:33:42 -0700287 # Time to wait before timing out on servo initialization.
288 INIT_TIMEOUT_SECS = 10
Simran Basi59331342013-07-12 12:06:29 -0700289
J. Richard Barnette67ccb872012-04-19 16:34:56 -0700290
Ricky Liang0dd379c2014-04-23 16:29:08 +0800291 def __init__(self, servo_host, servo_serial=None):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700292 """Sets up the servo communication infrastructure.
293
Fang Deng5d518f42013-08-02 14:04:32 -0700294 @param servo_host: A ServoHost object representing
295 the host running servod.
Dana Goyettea2f00ea2019-06-26 16:14:12 -0700296 @type servo_host: autotest_lib.server.hosts.servo_host.ServoHost
Ricky Liang0dd379c2014-04-23 16:29:08 +0800297 @param servo_serial: Serial number of the servo board.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700298 """
Fang Deng5d518f42013-08-02 14:04:32 -0700299 # TODO(fdeng): crbug.com/298379
300 # We should move servo_host object out of servo object
301 # to minimize the dependencies on the rest of Autotest.
302 self._servo_host = servo_host
Ricky Liang0dd379c2014-04-23 16:29:08 +0800303 self._servo_serial = servo_serial
Richard Barnette180efe62016-12-02 23:20:44 +0000304 self._server = servo_host.get_servod_server_proxy()
J. Richard Barnette0c9c5882014-06-11 15:27:05 -0700305 self._power_state = _PowerStateController(self)
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700306 self._uart = _Uart(self)
Fang Dengafb88142013-05-30 17:44:31 -0700307 self._usb_state = None
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700308 self._programmer = None
Dana Goyette4dc0adc2019-05-06 14:51:53 -0700309 self._prev_log_inode = None
310 self._prev_log_size = 0
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800311
Ricky Liang0dd379c2014-04-23 16:29:08 +0800312 @property
313 def servo_serial(self):
314 """Returns the serial number of the servo board."""
315 return self._servo_serial
316
Dana Goyette0b6e6402019-10-04 11:09:24 -0700317 def rotate_servod_logs(self, filename=None, directory=None):
318 """Save the latest servod log into a local directory, then rotate logs.
Dana Goyette4dc0adc2019-05-06 14:51:53 -0700319
Dana Goyette0b6e6402019-10-04 11:09:24 -0700320 The files will be <filename>.DEBUG, <filename>.INFO, <filename>.WARNING,
321 or just <filename>.log if not using split level logging.
322
323 @param filename: local filename prefix (no file extension) to use.
324 If None, rotate log but don't save it.
325 @param directory: local directory to save logs into (if unset, use cwd)
Dana Goyette4dc0adc2019-05-06 14:51:53 -0700326 """
Dana Goyette0b6e6402019-10-04 11:09:24 -0700327 if self.is_localhost():
328 # Local servod usually runs without log-dir, so can't be collected.
329 # TODO(crbug.com/1011516): allow localhost when rotation is enabled
330 return
331
332 log_dir = '/var/log/servod_%s' % self._servo_host.servo_port
333
334 if filename:
Dana Goyettec8613342019-11-13 15:03:28 -0800335 logging.info("Saving servod logs: %s/%s.*", directory or '.',
336 filename)
Dana Goyette0b6e6402019-10-04 11:09:24 -0700337 # TODO(crrev.com/c/1793030): remove no-level case once CL is pushed
338 for level_name in ('', 'DEBUG', 'INFO', 'WARNING'):
339
340 remote_path = os.path.join(log_dir, 'latest')
341 if level_name:
342 remote_path += '.%s' % level_name
343
344 local_path = '%s.%s' % (filename, level_name or 'log')
345 if directory:
346 local_path = os.path.join(directory, local_path)
347
348 try:
349 self._servo_host.get_file(
350 remote_path, local_path, try_rsync=False)
351
352 except error.AutoservRunError as e:
353 result = e.result_obj
354 if result.exit_status != 0:
355 stderr = result.stderr.strip()
356
357 # File not existing is okay, but warn for anything else.
358 if 'no such' not in stderr.lower():
359 logging.warn(
360 "Couldn't retrieve servod log: %s",
361 stderr or '\n%s' % result)
362
363 try:
364 if os.stat(local_path).st_size == 0:
365 os.unlink(local_path)
366 except EnvironmentError:
367 pass
368
369 else:
370 # No filename given, so caller wants to discard the log lines.
371 # Remove the symlinks to prevent old log-dir links from being
372 # picked up multiple times when using servod without log-dir.
373 remote_path = os.path.join(log_dir, 'latest*')
374 self._servo_host.run(
375 "rm %s" % remote_path,
376 stderr_tee=None, ignore_status=True)
377
378 # Servod log rotation renames current log, then creates a new file with
379 # the old name: log.<date> -> log.<date>.1.tbz2 -> log.<date>.2.tbz2
380
381 # Must rotate after copying, or the copy would be the new, empty file.
382 try:
383 self.set_nocheck('rotate_servod_logs', 'yes')
384 except ControlUnavailableError as e:
Dana Goyettec8613342019-11-13 15:03:28 -0800385 # Missing control (possibly old servod)
Dana Goyette0b6e6402019-10-04 11:09:24 -0700386 logging.warn("Couldn't rotate servod logs: %s", str(e))
Dana Goyettec8613342019-11-13 15:03:28 -0800387 except error.TestFail:
388 # Control exists but gave an error; don't let it fail the test.
389 # The error is already logged in set_nocheck().
390 pass
Ricky Liang0dd379c2014-04-23 16:29:08 +0800391
Tom Wai-Hong Tam22ee0e52013-04-03 13:36:39 +0800392 def get_power_state_controller(self):
J. Richard Barnette75136b32013-03-26 13:38:44 -0700393 """Return the power state controller for this Servo.
394
395 The power state controller provides board-independent
396 interfaces for reset, power-on, power-off operations.
397
398 """
399 return self._power_state
400
Fang Deng5d518f42013-08-02 14:04:32 -0700401
Mary Ruthvena3c52b82019-10-09 14:09:08 -0700402 def initialize_dut(self, cold_reset=False, enable_main=True):
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700403 """Initializes a dut for testing purposes.
404
405 This sets various servo signals back to default values
406 appropriate for the target board. By default, if the DUT
407 is already on, it stays on. If the DUT is powered off
408 before initialization, its state afterward is unspecified.
409
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700410 Rationale: Basic initialization of servo sets the lid open,
411 when there is a lid. This operation won't affect powered on
412 units; however, setting the lid open may power on a unit
J. Richard Barnette75136b32013-03-26 13:38:44 -0700413 that's off, depending on the board type and previous state
414 of the device.
415
J. Richard Barnette4b6af0d2014-06-05 09:57:20 -0700416 If `cold_reset` is a true value, the DUT and its EC will be
417 reset, and the DUT rebooted in normal mode.
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700418
419 @param cold_reset If True, cold reset the device after
420 initialization.
Mary Ruthvena3c52b82019-10-09 14:09:08 -0700421 @param enable_main If True, make sure the main servo device has
422 control of the dut.
423
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700424 """
Mary Ruthvena3c52b82019-10-09 14:09:08 -0700425 if enable_main:
426 self.enable_main_servo_device()
427
Dana Goyettea2f00ea2019-06-26 16:14:12 -0700428 try:
429 self._server.hwinit()
430 except socket.error as e:
431 e.filename = '%s:%s' % (self._servo_host.hostname,
432 self._servo_host.servo_port)
433 raise
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700434 self.set('usb_mux_oe1', 'on')
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700435 self._usb_state = None
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700436 self.switch_usbkey('off')
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700437 self._uart.start_capture()
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700438 if cold_reset:
J. Richard Barnette4b6af0d2014-06-05 09:57:20 -0700439 self._power_state.reset()
J. Richard Barnette1777f5d2014-09-03 15:18:19 -0700440 logging.debug('Servo initialized, version is %s',
441 self._server.get_version())
Ruben Rodriguez Buchillonb0e2a9f2019-08-12 12:40:44 -0700442 if self.has_control('init_keyboard'):
443 # This indicates the servod version does not
444 # have explicit keyboard initialization yet.
445 # Ignore this.
Ruben Rodriguez Buchillon05298242019-05-20 11:29:06 -0700446 # TODO(coconutruben): change this back to set() about a month
447 # after crrev.com/c/1586239 has been merged (or whenever that
448 # logic is in the labstation images).
449 self.set_nocheck('init_keyboard','on')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700450
451
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800452 def is_localhost(self):
453 """Is the servod hosted locally?
454
455 Returns:
456 True if local hosted; otherwise, False.
457 """
Fang Deng5d518f42013-08-02 14:04:32 -0700458 return self._servo_host.is_localhost()
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800459
460
Mary Ruthvenecf12712019-06-26 17:36:21 -0700461 def get_os_version(self):
462 """Returns the chromeos release version."""
463 lsb_release_content = self.system_output('cat /etc/lsb-release',
464 ignore_status=True)
465 return lsbrelease_utils.get_chromeos_release_builder_path(
466 lsb_release_content=lsb_release_content)
467
468
Mary Ruthven83bb5952019-06-27 12:34:05 -0700469 def get_servod_version(self):
470 """Returns the servod version."""
471 result = self._servo_host.run('servod --version')
472 # TODO: use system_output once servod --version prints to stdout
473 stdout = result.stdout.strip()
474 return stdout if stdout else result.stderr.strip()
475
476
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700477 def power_long_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700478 """Simulate a long power button press."""
J. Richard Barnettef12bff22012-07-18 14:41:06 -0700479 # After a long power press, the EC may ignore the next power
480 # button press (at least on Alex). To guarantee that this
481 # won't happen, we need to allow the EC one second to
482 # collect itself.
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800483 # long_press is defined as 8.5s in servod
484 self.set_nocheck('power_key', 'long_press')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700485
486
487 def power_normal_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700488 """Simulate a normal power button press."""
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800489 # press is defined as 1.2s in servod
490 self.set_nocheck('power_key', 'press')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700491
492
493 def power_short_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700494 """Simulate a short power button press."""
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800495 # tab is defined as 0.2s in servod
496 self.set_nocheck('power_key', 'tab')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700497
498
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800499 def power_key(self, press_secs='tab'):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700500 """Simulate a power button press.
501
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800502 @param press_secs: int, float, str; time to press key in seconds or
503 known shorthand: 'tab' 'press' 'long_press'
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700504 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800505 self.set_nocheck('power_key', press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700506
507
Ruben Zakarian47c1d4f2019-09-30 14:17:41 -0700508 def pwr_button(self, action='press'):
509 """Simulate a power button press.
510
511 @param action: str; could be press or could be release.
512 """
513 self.set_nocheck('pwr_button', action)
514
515
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700516 def lid_open(self):
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800517 """Simulate opening the lid and raise exception if all attempts fail"""
518 self.set('lid_open', 'yes')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700519
520
521 def lid_close(self):
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800522 """Simulate closing the lid and raise exception if all attempts fail
Craig Harrison48997262011-06-27 14:31:10 -0700523
524 Waits 6 seconds to ensure the device is fully asleep before returning.
525 """
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800526 self.set('lid_open', 'no')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700527 time.sleep(Servo.SLEEP_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700528
Ruben Zakarian47c1d4f2019-09-30 14:17:41 -0700529
530 def vbus_power_get(self):
531 """Get current vbus_power."""
532 return self.get('vbus_power')
533
534
Shelley Chenc26575a2015-09-18 10:56:16 -0700535 def volume_up(self, timeout=300):
Kevin Chenge448a8b2016-09-09 10:18:11 -0700536 """Simulate pushing the volume down button.
537
538 @param timeout: Timeout for setting the volume.
539 """
Shelley Chenc26575a2015-09-18 10:56:16 -0700540 self.set_get_all(['volume_up:yes',
541 'sleep:%.4f' % self.SERVO_KEY_PRESS_DELAY,
542 'volume_up:no'])
543 # we need to wait for commands to take effect before moving on
544 time_left = float(timeout)
545 while time_left > 0.0:
546 value = self.get('volume_up')
547 if value == 'no':
548 return
549 time.sleep(self.SHORT_DELAY)
550 time_left = time_left - self.SHORT_DELAY
551 raise error.TestFail("Failed setting volume_up to no")
552
553 def volume_down(self, timeout=300):
Kevin Chenge448a8b2016-09-09 10:18:11 -0700554 """Simulate pushing the volume down button.
555
556 @param timeout: Timeout for setting the volume.
557 """
Shelley Chenc26575a2015-09-18 10:56:16 -0700558 self.set_get_all(['volume_down:yes',
559 'sleep:%.4f' % self.SERVO_KEY_PRESS_DELAY,
560 'volume_down:no'])
561 # we need to wait for commands to take effect before moving on
562 time_left = float(timeout)
563 while time_left > 0.0:
564 value = self.get('volume_down')
565 if value == 'no':
566 return
567 time.sleep(self.SHORT_DELAY)
568 time_left = time_left - self.SHORT_DELAY
569 raise error.TestFail("Failed setting volume_down to no")
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700570
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800571 def ctrl_d(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800572 """Simulate Ctrl-d simultaneous button presses.
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800573
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800574 @param press_secs: int, float, str; time to press key in seconds or
575 known shorthand: 'tab' 'press' 'long_press'
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800576 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800577 self.set_nocheck('ctrl_d', press_secs)
Gediminas Ramanauskas9da80f22012-11-14 12:59:43 -0800578
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800579
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800580 def ctrl_u(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800581 """Simulate Ctrl-u simultaneous button presses.
582
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800583 @param press_secs: int, float, str; time to press key in seconds or
584 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800585 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800586 self.set_nocheck('ctrl_u', press_secs)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700587
588
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800589 def ctrl_enter(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800590 """Simulate Ctrl-enter simultaneous button presses.
591
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800592 @param press_secs: int, float, str; time to press key in seconds or
593 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800594 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800595 self.set_nocheck('ctrl_enter', press_secs)
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -0700596
597
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800598 def ctrl_key(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800599 """Simulate Enter key button press.
600
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800601 @param press_secs: int, float, str; time to press key in seconds or
602 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800603 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800604 self.set_nocheck('ctrl_key', press_secs)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700605
606
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800607 def enter_key(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800608 """Simulate Enter key button press.
609
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800610 @param press_secs: int, float, str; time to press key in seconds or
611 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800612 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800613 self.set_nocheck('enter_key', press_secs)
Todd Broch9753bd42012-03-21 10:15:08 -0700614
615
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800616 def refresh_key(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800617 """Simulate Refresh key (F3) button press.
618
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800619 @param press_secs: int, float, str; time to press key in seconds or
620 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800621 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800622 self.set_nocheck('refresh_key', press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700623
624
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800625 def ctrl_refresh_key(self, press_secs='tab'):
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800626 """Simulate Ctrl and Refresh (F3) simultaneous press.
627
628 This key combination is an alternative of Space key.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800629
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800630 @param press_secs: int, float, str; time to press key in seconds or
631 known shorthand: 'tab' 'press' 'long_press'
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800632 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800633 self.set_nocheck('ctrl_refresh_key', press_secs)
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800634
635
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800636 def imaginary_key(self, press_secs='tab'):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700637 """Simulate imaginary key button press.
638
639 Maps to a key that doesn't physically exist.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800640
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800641 @param press_secs: int, float, str; time to press key in seconds or
642 known shorthand: 'tab' 'press' 'long_press'
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700643 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800644 self.set_nocheck('imaginary_key', press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700645
646
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800647 def sysrq_x(self, press_secs='tab'):
Vincent Palatine7dc9282016-07-14 11:31:58 +0200648 """Simulate Alt VolumeUp X simulataneous press.
649
650 This key combination is the kernel system request (sysrq) X.
651
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800652 @param press_secs: int, float, str; time to press key in seconds or
653 known shorthand: 'tab' 'press' 'long_press'
Vincent Palatine7dc9282016-07-14 11:31:58 +0200654 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800655 self.set_nocheck('sysrq_x', press_secs)
Vincent Palatine7dc9282016-07-14 11:31:58 +0200656
657
Tom Wai-Hong Tam93b5c092015-05-14 02:50:43 +0800658 def toggle_recovery_switch(self):
659 """Toggle recovery switch on and off."""
660 self.enable_recovery_mode()
661 time.sleep(self.REC_TOGGLE_DELAY)
662 self.disable_recovery_mode()
663
664
Craig Harrison6b36b122011-06-28 17:58:43 -0700665 def enable_recovery_mode(self):
666 """Enable recovery mode on device."""
667 self.set('rec_mode', 'on')
668
669
670 def disable_recovery_mode(self):
671 """Disable recovery mode on device."""
672 self.set('rec_mode', 'off')
673
674
Tom Wai-Hong Tamf9ded092015-05-20 05:37:22 +0800675 def toggle_development_switch(self):
676 """Toggle development switch on and off."""
677 self.enable_development_mode()
678 time.sleep(self.DEV_TOGGLE_DELAY)
679 self.disable_development_mode()
680
681
Craig Harrison6b36b122011-06-28 17:58:43 -0700682 def enable_development_mode(self):
683 """Enable development mode on device."""
684 self.set('dev_mode', 'on')
685
686
687 def disable_development_mode(self):
688 """Disable development mode on device."""
689 self.set('dev_mode', 'off')
690
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700691 def boot_devmode(self):
692 """Boot a dev-mode device that is powered off."""
Tom Wai-Hong Tam23869102012-01-17 15:41:30 +0800693 self.power_short_press()
Craig Harrison48997262011-06-27 14:31:10 -0700694 self.pass_devmode()
695
696
697 def pass_devmode(self):
698 """Pass through boot screens in dev-mode."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700699 time.sleep(Servo.BOOT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700700 self.ctrl_d()
Chrome Bot9a1137d2011-07-19 14:35:00 -0700701 time.sleep(Servo.BOOT_DELAY)
Craig Harrison48997262011-06-27 14:31:10 -0700702
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700703
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800704 def get_board(self):
Wai-Hong Tam0f904002017-09-19 15:52:22 -0700705 """Get the board connected to servod."""
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800706 return self._server.get_board()
707
708
Wai-Hong Tam0f904002017-09-19 15:52:22 -0700709 def get_base_board(self):
710 """Get the board of the base connected to servod."""
Wai-Hong Tam7f6169e2017-09-29 09:23:48 -0700711 try:
712 return self._server.get_base_board()
713 except xmlrpclib.Fault as e:
714 # TODO(waihong): Remove the following compatibility check when
715 # the new versions of hdctools are deployed.
716 if 'not supported' in str(e):
717 logging.warning('The servod is too old that get_base_board '
718 'not supported.')
719 return ''
720 raise
Wai-Hong Tam0f904002017-09-19 15:52:22 -0700721
722
Wai-Hong Tamcc399ee2017-12-08 12:43:28 -0800723 def get_ec_active_copy(self):
724 """Get the active copy of the EC image."""
725 return self.get('ec_active_copy')
726
727
Todd Brochefe72cb2012-07-11 19:58:53 -0700728 def _get_xmlrpclib_exception(self, xmlexc):
729 """Get meaningful exception string from xmlrpc.
730
731 Args:
732 xmlexc: xmlrpclib.Fault object
733
734 xmlrpclib.Fault.faultString has the following format:
735
736 <type 'exception type'>:'actual error message'
737
738 Parse and return the real exception from the servod side instead of the
739 less meaningful one like,
740 <Fault 1: "<type 'exceptions.AttributeError'>:'tca6416' object has no
741 attribute 'hw_driver'">
742
743 Returns:
744 string of underlying exception raised in servod.
745 """
746 return re.sub('^.*>:', '', xmlexc.faultString)
747
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700748 def has_control(self, control):
749 """Query servod server to determine if |control| is a valid control.
750
751 @param control: str, control name to query
752
753 @returns: true if |control| is a known control, false otherwise.
754 """
755 assert control
756 try:
757 # If the control exists, doc() will work.
758 self._server.doc(control)
759 return True
760 except xmlrpclib.Fault as e:
761 if re.search('No control %s' % control,
762 self._get_xmlrpclib_exception(e)):
763 return False
764 raise e
Todd Brochefe72cb2012-07-11 19:58:53 -0700765
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700766 def get(self, gpio_name):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700767 """Get the value of a gpio from Servod.
768
769 @param gpio_name Name of the gpio.
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700770
771 @returns: server response to |gpio_name| request.
772
773 @raise ControlUnavailableError: if |gpio_name| not a known control.
774 @raise error.TestFail: for all other failures doing get().
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700775 """
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700776 assert gpio_name
Todd Brochefe72cb2012-07-11 19:58:53 -0700777 try:
Simran Basi1cbc5f22013-07-17 14:23:58 -0700778 return self._server.get(gpio_name)
Todd Brochefe72cb2012-07-11 19:58:53 -0700779 except xmlrpclib.Fault as e:
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700780 err_str = self._get_xmlrpclib_exception(e)
781 err_msg = "Getting '%s' :: %s" % (gpio_name, err_str)
782 unknown_ctrl = re.findall(NO_CONTROL_RE, err_str)
783 if unknown_ctrl:
784 raise ControlUnavailableError('No control named %r' %
785 unknown_ctrl[0])
786 else:
787 logging.error(err_msg)
788 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700789
790
791 def set(self, gpio_name, gpio_value):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700792 """Set and check the value of a gpio using Servod.
793
794 @param gpio_name Name of the gpio.
795 @param gpio_value New setting for the gpio.
Dana Goyette7ff06c92019-10-11 13:38:03 -0700796 @raise error.TestFail: if the control value fails to change.
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700797 """
Chrome Bot9a1137d2011-07-19 14:35:00 -0700798 self.set_nocheck(gpio_name, gpio_value)
Todd Brochcf7c6652012-02-24 13:03:59 -0800799 retry_count = Servo.GET_RETRY_MAX
Dana Goyette7ff06c92019-10-11 13:38:03 -0700800 actual_value = self.get(gpio_name)
801 while gpio_value != actual_value and retry_count:
Ilja H. Friedel04be2bd2014-05-07 21:29:59 -0700802 logging.warning("%s != %s, retry %d", gpio_name, gpio_value,
Dana Goyette7ff06c92019-10-11 13:38:03 -0700803 retry_count)
Todd Brochcf7c6652012-02-24 13:03:59 -0800804 retry_count -= 1
805 time.sleep(Servo.SHORT_DELAY)
Dana Goyette7ff06c92019-10-11 13:38:03 -0700806 actual_value = self.get(gpio_name)
807
808 if gpio_value != actual_value:
809 raise error.TestFail(
810 'Servo failed to set %s to %s. Got %s.'
811 % (gpio_name, gpio_value, actual_value))
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700812
813
814 def set_nocheck(self, gpio_name, gpio_value):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700815 """Set the value of a gpio using Servod.
816
817 @param gpio_name Name of the gpio.
818 @param gpio_value New setting for the gpio.
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700819
820 @raise ControlUnavailableError: if |gpio_name| not a known control.
821 @raise error.TestFail: for all other failures doing set().
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700822 """
Ruben Rodriguez Buchillon1c0219c2019-06-25 12:30:44 -0700823 # The real danger here is to pass a None value through the xmlrpc.
824 assert gpio_name and gpio_value is not None
Mary Ruthvendfa45342019-06-25 10:10:17 -0700825 logging.debug('Setting %s to %r', gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700826 try:
Simran Basi1cbc5f22013-07-17 14:23:58 -0700827 self._server.set(gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700828 except xmlrpclib.Fault as e:
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700829 err_str = self._get_xmlrpclib_exception(e)
830 err_msg = "Setting '%s' :: %s" % (gpio_name, err_str)
831 unknown_ctrl = re.findall(NO_CONTROL_RE, err_str)
832 if unknown_ctrl:
833 raise ControlUnavailableError('No control named %r' %
834 unknown_ctrl[0])
835 else:
836 logging.error(err_msg)
837 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700838
839
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800840 def set_get_all(self, controls):
841 """Set &| get one or more control values.
842
843 @param controls: list of strings, controls to set &| get.
844
845 @raise: error.TestError in case error occurs setting/getting values.
846 """
847 rv = []
848 try:
Mary Ruthvendfa45342019-06-25 10:10:17 -0700849 logging.debug('Set/get all: %s', str(controls))
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800850 rv = self._server.set_get_all(controls)
851 except xmlrpclib.Fault as e:
852 # TODO(waihong): Remove the following backward compatibility when
853 # the new versions of hdctools are deployed.
854 if 'not supported' in str(e):
855 logging.warning('The servod is too old that set_get_all '
856 'not supported. Use set and get instead.')
857 for control in controls:
858 if ':' in control:
859 (name, value) = control.split(':')
860 if name == 'sleep':
861 time.sleep(float(value))
862 else:
863 self.set_nocheck(name, value)
864 rv.append(True)
865 else:
866 rv.append(self.get(name))
867 else:
868 err_msg = "Problem with '%s' :: %s" % \
869 (controls, self._get_xmlrpclib_exception(e))
870 raise error.TestFail(err_msg)
871 return rv
872
873
Jon Salzc88e5b62011-11-30 14:38:54 +0800874 # TODO(waihong) It may fail if multiple servo's are connected to the same
875 # host. Should look for a better way, like the USB serial name, to identify
876 # the USB device.
Simran Basi741b5d42012-05-18 11:27:15 -0700877 # TODO(sbasi) Remove this code from autoserv once firmware tests have been
878 # updated.
Kevin Chenga22c4a82016-10-07 14:13:25 -0700879 def probe_host_usb_dev(self, timeout=_USB_PROBE_TIMEOUT):
Jon Salzc88e5b62011-11-30 14:38:54 +0800880 """Probe the USB disk device plugged-in the servo from the host side.
881
Kevin Chengeb06fe72016-08-22 15:26:32 -0700882 It uses servod to discover if there is a usb device attached to it.
Jon Salzc88e5b62011-11-30 14:38:54 +0800883
Kevin Chenga22c4a82016-10-07 14:13:25 -0700884 @param timeout The timeout period when probing for the usb host device.
885
886 @return: String of USB disk path (e.g. '/dev/sdb') or None.
Jon Salzc88e5b62011-11-30 14:38:54 +0800887 """
Ruben Rodriguez Buchillon9a4bfc32018-10-16 20:46:25 +0800888 # Set up Servo's usb mux.
889 self.switch_usbkey('host')
Kevin Chenga22c4a82016-10-07 14:13:25 -0700890 return self._server.probe_host_usb_dev(timeout) or None
Jon Salzc88e5b62011-11-30 14:38:54 +0800891
892
Mike Truty49153d82012-08-21 22:27:30 -0500893 def image_to_servo_usb(self, image_path=None,
894 make_image_noninteractive=False):
895 """Install an image to the USB key plugged into the servo.
896
897 This method may copy any image to the servo USB key including a
898 recovery image or a test image. These images are frequently used
899 for test purposes such as restoring a corrupted image or conducting
900 an upgrade of ec/fw/kernel as part of a test of a specific image part.
901
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700902 @param image_path Path on the host to the recovery image.
903 @param make_image_noninteractive Make the recovery image
904 noninteractive, therefore the DUT
905 will reboot automatically after
906 installation.
Mike Truty49153d82012-08-21 22:27:30 -0500907 """
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700908 # We're about to start plugging/unplugging the USB key. We
909 # don't know the state of the DUT, or what it might choose
910 # to do to the device after hotplug. To avoid surprises,
911 # force the DUT to be off.
912 self._server.hwinit()
Ruben Rodriguez Buchillonb0e2a9f2019-08-12 12:40:44 -0700913 if self.has_control('init_keyboard'):
914 # This indicates the servod version does not
915 # have explicit keyboard initialization yet.
916 # Ignore this.
Ruben Rodriguez Buchillonbd3b74f2019-08-08 16:18:27 -0700917 # TODO(coconutruben): change this back to set() about a month
918 # after crrev.com/c/1586239 has been merged (or whenever that
919 # logic is in the labstation images).
920 self.set_nocheck('init_keyboard','on')
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700921 self._power_state.power_off()
Mike Truty49153d82012-08-21 22:27:30 -0500922
Mike Truty49153d82012-08-21 22:27:30 -0500923 if image_path:
Garry Wangd2656812019-08-07 17:29:16 -0700924 # Set up Servo's usb mux.
925 self.switch_usbkey('host')
Tom Wai-Hong Tam42f136d2012-10-26 11:11:23 +0800926 logging.info('Searching for usb device and copying image to it. '
927 'Please wait a few minutes...')
Mike Truty49153d82012-08-21 22:27:30 -0500928 if not self._server.download_image_to_usb(image_path):
929 logging.error('Failed to transfer requested image to USB. '
930 'Please take a look at Servo Logs.')
931 raise error.AutotestError('Download image to usb failed.')
932 if make_image_noninteractive:
933 logging.info('Making image noninteractive')
934 if not self._server.make_image_noninteractive():
935 logging.error('Failed to make image noninteractive. '
936 'Please take a look at Servo Logs.')
937
Kalin Stoyanovb3c11f32018-05-11 09:02:00 -0700938 def boot_in_recovery_mode(self):
939 """Boot host DUT in recovery mode."""
940 self._power_state.power_on(rec_mode=self._power_state.REC_ON)
941 self.switch_usbkey('dut')
942
Mike Truty49153d82012-08-21 22:27:30 -0500943
Simran Basi741b5d42012-05-18 11:27:15 -0700944 def install_recovery_image(self, image_path=None,
J. Richard Barnetteb6de7e32013-02-14 13:28:04 -0800945 make_image_noninteractive=False):
Dan Shic67f1332016-04-06 12:38:06 -0700946 """Install the recovery image specified by the path onto the DUT.
Jon Salzc88e5b62011-11-30 14:38:54 +0800947
948 This method uses google recovery mode to install a recovery image
949 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 +0800950 board specified by the usb_dev. If no image path is specified
Jon Salzc88e5b62011-11-30 14:38:54 +0800951 we use the recovery image already on the usb image.
952
Dan Shic67f1332016-04-06 12:38:06 -0700953 @param image_path: Path on the host to the recovery image.
954 @param make_image_noninteractive: Make the recovery image
955 noninteractive, therefore the DUT will reboot automatically
956 after installation.
Jon Salzc88e5b62011-11-30 14:38:54 +0800957 """
Mike Truty49153d82012-08-21 22:27:30 -0500958 self.image_to_servo_usb(image_path, make_image_noninteractive)
Garry Wangd2656812019-08-07 17:29:16 -0700959 # Give the DUT some time to power_off if we skip
960 # download image to usb. (crbug.com/982993)
961 if not image_path:
962 time.sleep(10)
Kalin Stoyanovb3c11f32018-05-11 09:02:00 -0700963 self.boot_in_recovery_mode()
Jon Salzc88e5b62011-11-30 14:38:54 +0800964
965
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800966 def _scp_image(self, image_path):
967 """Copy image to the servo host.
968
969 When programming a firmware image on the DUT, the image must be
970 located on the host to which the servo device is connected. Sometimes
971 servo is controlled by a remote host, in this case the image needs to
972 be transferred to the remote host.
973
974 @param image_path: a string, name of the firmware image file to be
975 transferred.
976 @return: a string, full path name of the copied file on the remote.
977 """
978
979 dest_path = os.path.join('/tmp', os.path.basename(image_path))
Fang Deng5d518f42013-08-02 14:04:32 -0700980 self._servo_host.send_file(image_path, dest_path)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800981 return dest_path
982
983
Dan Shifecdaf42015-07-28 10:17:26 -0700984 def system(self, command, timeout=3600):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700985 """Execute the passed in command on the servod host.
986
987 @param command Command to be executed.
Dan Shifecdaf42015-07-28 10:17:26 -0700988 @param timeout Maximum number of seconds of runtime allowed. Default to
989 1 hour.
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700990 """
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800991 logging.info('Will execute on servo host: %s', command)
Fang Deng5d518f42013-08-02 14:04:32 -0700992 self._servo_host.run(command, timeout=timeout)
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800993
994
Dan Shifecdaf42015-07-28 10:17:26 -0700995 def system_output(self, command, timeout=3600,
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800996 ignore_status=False, args=()):
997 """Execute the passed in command on the servod host, return stdout.
998
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700999 @param command a string, the command to execute
1000 @param timeout an int, max number of seconds to wait til command
Dan Shifecdaf42015-07-28 10:17:26 -07001001 execution completes. Default to 1 hour.
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -07001002 @param ignore_status a Boolean, if true - ignore command's nonzero exit
Vadim Bendebury89ec24e2012-12-17 12:54:18 -08001003 status, otherwise an exception will be thrown
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -07001004 @param args a tuple of strings, each becoming a separate command line
Vadim Bendebury89ec24e2012-12-17 12:54:18 -08001005 parameter for the command
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -07001006 @return command's stdout as a string.
Vadim Bendebury89ec24e2012-12-17 12:54:18 -08001007 """
Fang Deng5d518f42013-08-02 14:04:32 -07001008 return self._servo_host.run(command, timeout=timeout,
1009 ignore_status=ignore_status,
1010 args=args).stdout.strip()
Vadim Bendeburyb80ba592012-12-07 15:02:34 -08001011
1012
Mary Ruthven38d90af2019-08-16 13:13:31 -07001013 def get_servo_version(self, active=False):
Dan Shia5fef052015-05-18 23:28:47 -07001014 """Get the version of the servo, e.g., servo_v2 or servo_v3.
1015
Mary Ruthven38d90af2019-08-16 13:13:31 -07001016 @param active: Only return the servo type with the active device.
Dan Shia5fef052015-05-18 23:28:47 -07001017 @return: The version of the servo.
1018
1019 """
Mary Ruthven38d90af2019-08-16 13:13:31 -07001020 servo_type = self._server.get_version()
1021 if '_and_' not in servo_type or not active:
1022 return servo_type
1023
1024 # If servo v4 is using ccd and servo micro, modify the servo type to
1025 # reflect the active device.
1026 active_device = self.get('active_v4_device')
1027 if active_device in servo_type:
1028 logging.info('%s is active', active_device)
1029 return 'servo_v4_with_' + active_device
1030
1031 logging.warn("%s is active even though it's not in servo type",
1032 active_device)
1033 return servo_type
Dan Shia5fef052015-05-18 23:28:47 -07001034
1035
Mary Ruthvena3c52b82019-10-09 14:09:08 -07001036 def get_main_servo_device(self):
1037 """Return the main servo device"""
1038 servo_type = self.get_servo_version()
1039 return servo_type.split('_with_')[-1].split('_and_')[0]
1040
1041
1042 def enable_main_servo_device(self):
1043 """Make sure the main device has control of the dut."""
Mary Ruthven1409d9d2019-10-22 20:44:24 -07001044 # Cr50 detects servo using the EC uart. It doesn't work well if the
1045 # board doesn't use EC uart. The lab active_v4_device doesn't handle
1046 # this correctly. Check ec_uart_pty before trying to change the active
1047 # device.
1048 # TODO(crbug.com/1016842): reenable the setting the main device when
1049 # active device works on labstations.
1050 return
Mary Ruthvena3c52b82019-10-09 14:09:08 -07001051 if not self.has_control('active_v4_device'):
1052 return
1053 self.set('active_v4_device', self.get_main_servo_device())
1054
1055
Mary Ruthven2724ee62019-07-16 11:16:59 -07001056 def running_through_ccd(self):
1057 """Returns True if the setup is using ccd to run."""
1058 servo = self._server.get_version()
1059 return 'ccd_cr50' in servo and 'servo_micro' not in servo
1060
1061
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001062 def _initialize_programmer(self, rw_only=False):
1063 """Initialize the firmware programmer.
1064
1065 @param rw_only: True to initialize a programmer which only
1066 programs the RW portions.
1067 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -07001068 if self._programmer:
1069 return
1070 # Initialize firmware programmer
Dan Shia5fef052015-05-18 23:28:47 -07001071 servo_version = self.get_servo_version()
Dan Shi9cb0eec2014-06-03 09:04:50 -07001072 if servo_version.startswith('servo_v2'):
J. Richard Barnette8f19b392014-08-11 14:05:39 -07001073 self._programmer = firmware_programmer.ProgrammerV2(self)
Wai-Hong Tamc36c4d22016-09-09 10:39:45 -07001074 self._programmer_rw = firmware_programmer.ProgrammerV2RwOnly(self)
Kevin Chengdf2e29f2016-09-09 02:31:22 -07001075 # Both servo v3 and v4 use the same programming methods so just leverage
1076 # ProgrammerV3 for servo v4 as well.
1077 elif (servo_version.startswith('servo_v3') or
1078 servo_version.startswith('servo_v4')):
Dan Shia5fef052015-05-18 23:28:47 -07001079 self._programmer = firmware_programmer.ProgrammerV3(self)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001080 self._programmer_rw = firmware_programmer.ProgrammerV3RwOnly(self)
J. Richard Barnette8f19b392014-08-11 14:05:39 -07001081 else:
1082 raise error.TestError(
1083 'No firmware programmer for servo version: %s' %
Congbin Guo42427612019-02-12 10:22:06 -08001084 servo_version)
J. Richard Barnette8f19b392014-08-11 14:05:39 -07001085
1086
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001087 def program_bios(self, image, rw_only=False):
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -08001088 """Program bios on DUT with given image.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -08001089
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -08001090 @param image: a string, file name of the BIOS image to program
1091 on the DUT.
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001092 @param rw_only: True to only program the RW portion of BIOS.
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -08001093
Vadim Bendebury1a7ec632013-02-17 16:22:09 -08001094 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -07001095 self._initialize_programmer()
Ricky Liangc31aab32014-07-03 16:23:29 +08001096 if not self.is_localhost():
1097 image = self._scp_image(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001098 if rw_only:
1099 self._programmer_rw.program_bios(image)
1100 else:
1101 self._programmer.program_bios(image)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -08001102
1103
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001104 def program_ec(self, image, rw_only=False):
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -08001105 """Program ec on DUT with given image.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -08001106
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -08001107 @param image: a string, file name of the EC image to program
1108 on the DUT.
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001109 @param rw_only: True to only program the RW portion of EC.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -08001110
Vadim Bendebury1a7ec632013-02-17 16:22:09 -08001111 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -07001112 self._initialize_programmer()
Ricky Liangc31aab32014-07-03 16:23:29 +08001113 if not self.is_localhost():
1114 image = self._scp_image(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001115 if rw_only:
Congbin Guo42427612019-02-12 10:22:06 -08001116 self._programmer_rw.program_ec(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001117 else:
Congbin Guo42427612019-02-12 10:22:06 -08001118 self._programmer.program_ec(image)
1119
1120
1121 def _reprogram(self, tarball_path, firmware_name, image_candidates,
1122 rw_only):
1123 """Helper function to reprogram firmware for EC or BIOS.
1124
1125 @param tarball_path: The path of the downloaded build tarball.
1126 @param: firmware_name: either 'EC' or 'BIOS'.
1127 @param image_candidates: A tuple of the paths of image candidates.
1128 @param rw_only: True to only install firmware to its RW portions. Keep
1129 the RO portions unchanged.
1130
1131 @raise: TestError if cannot extract firmware from the tarball.
1132 """
Mary Ruthven771b2012019-08-19 12:18:49 -07001133 dest_dir = os.path.join(os.path.dirname(tarball_path), firmware_name)
1134 # Create the firmware_name subdirectory if it doesn't exist.
1135 if not os.path.exists(dest_dir):
1136 os.mkdir(dest_dir)
Congbin Guo42427612019-02-12 10:22:06 -08001137 image = _extract_image_from_tarball(tarball_path, dest_dir,
1138 image_candidates)
1139 if not image:
1140 if firmware_name == 'EC':
1141 logging.info('Not a Chrome EC, ignore re-programming it')
1142 return
1143 else:
1144 raise error.TestError('Failed to extract the %s image from '
1145 'tarball' % firmware_name)
1146
Namyoon Woo0e623622019-10-08 16:02:50 -07001147 # Extract subsidiary binaries for EC
1148 if firmware_name == 'EC':
1149 # Find a monitor binary for NPCX_UUT chip type, if any.
1150 mon_candidates = [ w.replace('ec.bin', 'npcx_monitor.bin')
1151 for w in image_candidates ]
1152 _extract_image_from_tarball(tarball_path, dest_dir, mon_candidates)
1153
Congbin Guo42427612019-02-12 10:22:06 -08001154 logging.info('Will re-program %s %snow', firmware_name,
1155 'RW ' if rw_only else '')
1156
1157 if firmware_name == 'EC':
1158 self.program_ec(os.path.join(dest_dir, image), rw_only)
1159 else:
1160 self.program_bios(os.path.join(dest_dir, image), rw_only)
1161
1162
Shelley Chenac61d5a2019-06-24 15:35:46 -07001163 def program_firmware(self, board, model, tarball_path, rw_only=False):
Congbin Guo42427612019-02-12 10:22:06 -08001164 """Program firmware (EC, if applied, and BIOS) of the DUT.
1165
Shelley Chenac61d5a2019-06-24 15:35:46 -07001166 @param board: The DUT board name.
Congbin Guo42427612019-02-12 10:22:06 -08001167 @param model: The DUT model name.
1168 @param tarball_path: The path of the downloaded build tarball.
1169 @param rw_only: True to only install firmware to its RW portions. Keep
1170 the RO portions unchanged.
1171 """
Shelley Chenac61d5a2019-06-24 15:35:46 -07001172 ap_image_candidates = ('image.bin', 'image-%s.bin' % model,
1173 'image-%s.bin' % board)
1174 ec_image_candidates = ('ec.bin', '%s/ec.bin' % model,
1175 '%s/ec.bin' % board)
Congbin Guo42427612019-02-12 10:22:06 -08001176
1177 self._reprogram(tarball_path, 'EC', ec_image_candidates, rw_only)
1178 self._reprogram(tarball_path, 'BIOS', ap_image_candidates, rw_only)
1179
1180 self.get_power_state_controller().reset()
1181 time.sleep(Servo.BOOT_DELAY)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001182
Fang Dengafb88142013-05-30 17:44:31 -07001183
1184 def _switch_usbkey_power(self, power_state, detection_delay=False):
1185 """Switch usbkey power.
1186
1187 This function switches usbkey power by setting the value of
1188 'prtctl4_pwren'. If the power is already in the
1189 requested state, this function simply returns.
1190
1191 @param power_state: A string, 'on' or 'off'.
1192 @param detection_delay: A boolean value, if True, sleep
1193 for |USB_DETECTION_DELAY| after switching
1194 the power on.
1195 """
Kevin Chenga22c4a82016-10-07 14:13:25 -07001196 # TODO(kevcheng): Forgive me for this terrible hack. This is just to
1197 # handle beaglebones that haven't yet updated and have the
1198 # safe_switch_usbkey_power RPC. I'll remove this once all beaglebones
1199 # have been updated and also think about a better way to handle
1200 # situations like this.
1201 try:
1202 self._server.safe_switch_usbkey_power(power_state)
1203 except Exception:
1204 self.set('prtctl4_pwren', power_state)
Fang Dengafb88142013-05-30 17:44:31 -07001205 if power_state == 'off':
1206 time.sleep(self.USB_POWEROFF_DELAY)
1207 elif detection_delay:
1208 time.sleep(self.USB_DETECTION_DELAY)
1209
1210
1211 def switch_usbkey(self, usb_state):
1212 """Connect USB flash stick to either host or DUT, or turn USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001213
1214 This function switches the servo multiplexer to provide electrical
Fang Dengafb88142013-05-30 17:44:31 -07001215 connection between the USB port J3 and either host or DUT side. It
1216 can also be used to turn the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001217
Fang Dengafb88142013-05-30 17:44:31 -07001218 Switching to 'dut' or 'host' is accompanied by powercycling
1219 of the USB stick, because it sometimes gets wedged if the mux
1220 is switched while the stick power is on.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001221
Fang Dengafb88142013-05-30 17:44:31 -07001222 @param usb_state: A string, one of 'dut', 'host', or 'off'.
1223 'dut' and 'host' indicate which side the
1224 USB flash device is required to be connected to.
1225 'off' indicates turning the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001226
Fang Dengafb88142013-05-30 17:44:31 -07001227 @raise: error.TestError in case the parameter is not 'dut'
1228 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001229 """
Fang Dengafb88142013-05-30 17:44:31 -07001230 if self.get_usbkey_direction() == usb_state:
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001231 return
1232
Fang Dengafb88142013-05-30 17:44:31 -07001233 if usb_state == 'off':
Dan Shia5fef052015-05-18 23:28:47 -07001234 self._switch_usbkey_power('off')
1235 self._usb_state = usb_state
1236 return
Fang Dengafb88142013-05-30 17:44:31 -07001237 elif usb_state == 'host':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001238 mux_direction = 'servo_sees_usbkey'
Fang Dengafb88142013-05-30 17:44:31 -07001239 elif usb_state == 'dut':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001240 mux_direction = 'dut_sees_usbkey'
1241 else:
Fang Dengafb88142013-05-30 17:44:31 -07001242 raise error.TestError('Unknown USB state request: %s' % usb_state)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001243
Fang Dengafb88142013-05-30 17:44:31 -07001244 self._switch_usbkey_power('off')
Kevin Chenga22c4a82016-10-07 14:13:25 -07001245 # TODO(kevcheng): Forgive me for this terrible hack. This is just to
1246 # handle beaglebones that haven't yet updated and have the
1247 # safe_switch_usbkey RPC. I'll remove this once all beaglebones have
1248 # been updated and also think about a better way to handle situations
1249 # like this.
1250 try:
1251 self._server.safe_switch_usbkey(mux_direction)
1252 except Exception:
1253 self.set('usb_mux_sel1', mux_direction)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001254 time.sleep(self.USB_POWEROFF_DELAY)
Fang Dengafb88142013-05-30 17:44:31 -07001255 self._switch_usbkey_power('on', usb_state == 'host')
1256 self._usb_state = usb_state
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001257
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001258
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001259 def get_usbkey_direction(self):
Fang Dengafb88142013-05-30 17:44:31 -07001260 """Get which side USB is connected to or 'off' if usb power is off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001261
Fang Dengafb88142013-05-30 17:44:31 -07001262 @return: A string, one of 'dut', 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001263 """
Fang Dengafb88142013-05-30 17:44:31 -07001264 if not self._usb_state:
1265 if self.get('prtctl4_pwren') == 'off':
1266 self._usb_state = 'off'
1267 elif self.get('usb_mux_sel1').startswith('dut'):
1268 self._usb_state = 'dut'
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001269 else:
Fang Dengafb88142013-05-30 17:44:31 -07001270 self._usb_state = 'host'
1271 return self._usb_state
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001272
1273
Wai-Hong Tam60377262018-03-01 10:55:39 -08001274 def set_servo_v4_role(self, role):
1275 """Set the power role of servo v4, either 'src' or 'snk'.
1276
1277 It does nothing if not a servo v4.
1278
1279 @param role: Power role for DUT port on servo v4, either 'src' or 'snk'.
1280 """
1281 servo_version = self.get_servo_version()
1282 if servo_version.startswith('servo_v4'):
1283 value = self.get('servo_v4_role')
1284 if value != role:
1285 self.set_nocheck('servo_v4_role', role)
1286 else:
1287 logging.debug('Already in the role: %s.', role)
1288 else:
1289 logging.debug('Not a servo v4, unable to set role to %s.', role)
1290
1291
Mary Ruthven739b2922019-08-22 11:16:06 -07001292 def set_servo_v4_dts_mode(self, state):
1293 """Set servo v4 dts mode to off or on.
1294
1295 It does nothing if not a servo v4. Disable the ccd watchdog if we're
1296 disabling dts mode. CCD will disconnect. The watchdog only allows CCD
1297 to disconnect for 10 seconds until it kills servod. Disable the
1298 watchdog, so CCD can stay disconnected indefinitely.
1299
1300 @param state: Set servo v4 dts mode 'off' or 'on'.
1301 """
1302 servo_version = self.get_servo_version()
1303 if not servo_version.startswith('servo_v4'):
1304 logging.debug('Not a servo v4, unable to set dts mode %s.', state)
1305 return
1306
1307 # TODO(mruthven): remove watchdog check once the labstation has been
1308 # updated to have support for modifying the watchdog.
1309 set_watchdog = self.has_control('watchdog') and 'ccd' in servo_version
1310 enable_watchdog = state == 'on'
1311
1312 if set_watchdog and not enable_watchdog:
1313 self.set_nocheck('watchdog_remove', 'ccd')
1314
1315 self.set_nocheck('servo_v4_dts_mode', state)
1316
1317 if set_watchdog and enable_watchdog:
1318 self.set_nocheck('watchdog_add', 'ccd')
1319
1320
Congbin Guofc3b8962019-03-22 17:38:46 -07001321 @property
1322 def uart_logs_dir(self):
1323 """Return the directory to save UART logs."""
1324 return self._uart.logs_dir if self._uart else ""
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001325
Congbin Guofc3b8962019-03-22 17:38:46 -07001326
1327 @uart_logs_dir.setter
1328 def uart_logs_dir(self, logs_dir):
1329 """Set directory to save UART logs.
1330
1331 @param logs_dir String of directory name."""
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001332 if self._uart:
Congbin Guofc3b8962019-03-22 17:38:46 -07001333 self._uart.logs_dir = logs_dir
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001334
1335
1336 def close(self):
1337 """Close the servo object."""
1338 if self._uart:
1339 self._uart.stop_capture()
Congbin Guofc3b8962019-03-22 17:38:46 -07001340 self._uart.dump()
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001341 self._uart = None