blob: 2908a780ab50717cb227a45cf24af16d1e45dd36 [file] [log] [blame]
J. Richard Barnette384056b2012-04-16 11:04:46 -07001# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -07002# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4#
5# Expects to be run in an environment with sudo and no interactive password
6# prompt, such as within the Chromium OS development chroot.
7
Congbin Guoa1f9cba2018-07-03 11:36:59 -07008import ast
9import logging
Vadim Bendeburyb80ba592012-12-07 15:02:34 -080010import os
Congbin Guoa1f9cba2018-07-03 11:36:59 -070011import re
Dana Goyettea2f00ea2019-06-26 16:14:12 -070012import socket
Congbin Guoa1f9cba2018-07-03 11:36:59 -070013import time
14import xmlrpclib
Vadim Bendeburyb80ba592012-12-07 15:02:34 -080015
Simran Basi741b5d42012-05-18 11:27:15 -070016from autotest_lib.client.common_lib import error
Mary Ruthvenecf12712019-06-26 17:36:21 -070017from autotest_lib.client.common_lib import lsbrelease_utils
Congbin Guo42427612019-02-12 10:22:06 -080018from autotest_lib.server import utils as server_utils
Ricky Liangc31aab32014-07-03 16:23:29 +080019from autotest_lib.server.cros.servo import firmware_programmer
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070020
Kevin Chenga22c4a82016-10-07 14:13:25 -070021# Time to wait when probing for a usb device, it takes on avg 17 seconds
22# to do a full probe.
23_USB_PROBE_TIMEOUT = 40
24
J. Richard Barnette41320ee2013-03-11 13:00:13 -070025
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -070026# Regex to match XMLRPC errors due to a servod control not existing.
27NO_CONTROL_RE = re.compile(r'No control named (\w*\.?\w*)')
28
Ruben Rodriguez Buchillon5f7ee682019-09-30 12:50:09 -070029
30# The minimum voltage on the charger port on servo v4 that is expected. This is
31# to query whether a charger is plugged into servo v4 and thus pd control
32# capabilities can be used.
33V4_CHG_ATTACHED_MIN_VOLTAGE_MV = 4400
34
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -070035class ControlUnavailableError(error.TestFail):
36 """Custom error class to indicate a control is unavailable on servod."""
37 pass
38
39
Congbin Guo42427612019-02-12 10:22:06 -080040def _extract_image_from_tarball(tarball, dest_dir, image_candidates):
41 """Try extracting the image_candidates from the tarball.
42
43 @param tarball: The path of the tarball.
44 @param dest_path: The path of the destination.
45 @param image_candidates: A tuple of the paths of image candidates.
46
47 @return: The first path from the image candidates, which succeeds, or None
48 if all the image candidates fail.
49 """
Brent Peterson1cb623a2020-01-09 13:14:28 -080050
51 # Create the firmware_name subdirectory if it doesn't exist
52 if not os.path.exists(dest_dir):
53 os.mkdir(dest_dir)
54
55 # Try to extract image candidates from tarball
Congbin Guo42427612019-02-12 10:22:06 -080056 for image in image_candidates:
57 status = server_utils.system(
58 ('tar xf %s -C %s %s' % (tarball, dest_dir, image)),
59 timeout=60, ignore_status=True)
60 if status == 0:
61 return image
62 return None
63
64
J. Richard Barnette0c9c5882014-06-11 15:27:05 -070065class _PowerStateController(object):
66
67 """Class to provide board-specific power operations.
68
69 This class is responsible for "power on" and "power off"
70 operations that can operate without making assumptions in
71 advance about board state. It offers an interface that
72 abstracts out the different sequences required for different
73 board types.
74
75 """
76
77 # Constants acceptable to be passed for the `rec_mode` parameter
78 # to power_on().
79 #
80 # REC_ON: Boot the DUT in recovery mode, i.e. boot from USB or
81 # SD card.
82 # REC_OFF: Boot in normal mode, i.e. boot from internal storage.
83
84 REC_ON = 'rec'
85 REC_OFF = 'on'
Shelley Chen65938622018-05-16 07:45:54 -070086 REC_ON_FORCE_MRC = 'rec_force_mrc'
J. Richard Barnette0c9c5882014-06-11 15:27:05 -070087
88 # Delay in seconds needed between asserting and de-asserting
89 # warm reset.
90 _RESET_HOLD_TIME = 0.5
91
92 def __init__(self, servo):
93 """Initialize the power state control.
94
95 @param servo Servo object providing the underlying `set` and `get`
96 methods for the target controls.
97
98 """
99 self._servo = servo
100
101 def reset(self):
102 """Force the DUT to reset.
103
104 The DUT is guaranteed to be on at the end of this call,
105 regardless of its previous state, provided that there is
106 working OS software. This also guarantees that the EC has
107 been restarted.
108
109 """
110 self._servo.set_nocheck('power_state', 'reset')
111
112 def warm_reset(self):
113 """Apply warm reset to the DUT.
114
115 This asserts, then de-asserts the 'warm_reset' signal.
116 Generally, this causes the board to restart.
117
118 """
Ravutappa951ca432019-05-15 09:52:52 -0700119 # TODO: warm_reset support has added to power_state.py. Once it
120 # available to labstation remove fallback method.
121 try:
122 self._servo.set_nocheck('power_state', 'warm_reset')
123 except error.TestFail as err:
124 logging.info("Fallback to warm_reset control method")
125 self._servo.set_get_all(['warm_reset:on',
J. Richard Barnette0c9c5882014-06-11 15:27:05 -0700126 'sleep:%.4f' % self._RESET_HOLD_TIME,
127 'warm_reset:off'])
128
J. Richard Barnette0c9c5882014-06-11 15:27:05 -0700129 def power_off(self):
130 """Force the DUT to power off.
131
132 The DUT is guaranteed to be off at the end of this call,
133 regardless of its previous state, provided that there is
134 working EC and boot firmware. There is no requirement for
135 working OS software.
136
137 """
138 self._servo.set_nocheck('power_state', 'off')
139
140 def power_on(self, rec_mode=REC_OFF):
141 """Force the DUT to power on.
142
143 Prior to calling this function, the DUT must be powered off,
144 e.g. with a call to `power_off()`.
145
146 At power on, recovery mode is set as specified by the
147 corresponding argument. When booting with recovery mode on, it
148 is the caller's responsibility to unplug/plug in a bootable
149 external storage device.
150
151 If the DUT requires a delay after powering on but before
152 processing inputs such as USB stick insertion, the delay is
153 handled by this method; the caller is not responsible for such
154 delays.
155
156 @param rec_mode Setting of recovery mode to be applied at
157 power on. default: REC_OFF aka 'off'
158
159 """
160 self._servo.set_nocheck('power_state', rec_mode)
161
162
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700163class _Uart(object):
Congbin Guo0f00be82019-04-18 17:51:14 -0700164 """Class to capture UART streams of CPU, EC, Cr50, etc."""
165 _UartToCapture = ('cpu', 'ec', 'cr50', 'servo_v4', 'servo_micro', 'usbpd')
166
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700167 def __init__(self, servo):
168 self._servo = servo
169 self._streams = []
Congbin Guo0f00be82019-04-18 17:51:14 -0700170 self.logs_dir = None
Congbin Guofe4d4e82019-04-18 17:51:14 -0700171
Congbin Guo0f00be82019-04-18 17:51:14 -0700172 def _start_stop_capture(self, uart, start):
173 """Helper function to start/stop capturing on specified UART.
174
175 @param uart: The UART name to start/stop capturing.
176 @param start: True to start capturing, otherwise stop.
177
Ruben Rodriguez Buchillonecb525f2019-09-03 14:52:14 -0700178 @returns True if the operation completes successfully.
179 False if the UART capturing is not supported or failed due to
180 an error.
Congbin Guo0f00be82019-04-18 17:51:14 -0700181 """
182 logging.debug('%s capturing %s UART.', 'Start' if start else 'Stop',
183 uart)
Ruben Rodriguez Buchillonb0e2a9f2019-08-12 12:40:44 -0700184 uart_cmd = '%s_uart_capture' % uart
Ruben Rodriguez Buchillonecb525f2019-09-03 14:52:14 -0700185 target_level = 'on' if start else 'off'
186 level = None
Ruben Rodriguez Buchillonb0e2a9f2019-08-12 12:40:44 -0700187 if self._servo.has_control(uart_cmd):
Ruben Rodriguez Buchillon9f485642019-08-21 14:32:22 -0700188 # Do our own implementation of set() here as not_applicable
189 # should also count as a valid control.
Ruben Rodriguez Buchillonecb525f2019-09-03 14:52:14 -0700190 logging.debug('Trying to set %s to %s.', uart_cmd, target_level)
191 try:
192 self._servo.set_nocheck(uart_cmd, target_level)
193 level = self._servo.get(uart_cmd)
194 except error.TestFail as e:
195 # Any sort of test failure here should not stop the test. This
196 # is just to capture more output. Log and move on.
197 logging.warning('Failed to set %s to %s. %s. Ignoring.',
198 uart_cmd, target_level, str(e))
199 if level == target_level:
200 logging.debug('Managed to set %s to %s.', uart_cmd, level)
201 else:
202 logging.debug('Failed to set %s to %s. Got %s.', uart_cmd,
203 target_level, level)
204 return level == target_level
Congbin Guo0f00be82019-04-18 17:51:14 -0700205
206 def start_capture(self):
207 """Start capturing UART streams."""
208 for uart in self._UartToCapture:
209 if self._start_stop_capture(uart, True):
210 self._streams.append(('%s_uart_stream' % uart, '%s_uart.log' %
211 uart))
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700212
Congbin Guofc3b8962019-03-22 17:38:46 -0700213 def dump(self):
214 """Dump UART streams to log files accordingly."""
Congbin Guo0f00be82019-04-18 17:51:14 -0700215 if not self.logs_dir:
Congbin Guofc3b8962019-03-22 17:38:46 -0700216 return
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700217
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700218 for stream, logfile in self._streams:
Congbin Guo0f00be82019-04-18 17:51:14 -0700219 logfile_fullname = os.path.join(self.logs_dir, logfile)
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700220 try:
221 content = self._servo.get(stream)
222 except Exception as err:
223 logging.warn('Failed to get UART log for %s: %s', stream, err)
224 continue
225
Kuang-che Wu12192fc2019-08-31 09:55:00 +0800226 if content == 'not_applicable':
227 logging.warn('%s is not applicable', stream)
228 continue
229
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700230 # The UART stream may contain non-printable characters, and servo
231 # returns it in string representation. We use `ast.leteral_eval`
232 # to revert it back.
233 with open(logfile_fullname, 'a') as fd:
Kuang-che Wu12192fc2019-08-31 09:55:00 +0800234 try:
235 fd.write(ast.literal_eval(content))
236 except ValueError:
237 logging.exception('Invalid value for %s: %r', stream,
238 content)
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700239
240 def stop_capture(self):
241 """Stop capturing UART streams."""
Congbin Guo0f00be82019-04-18 17:51:14 -0700242 for uart in self._UartToCapture:
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700243 try:
Congbin Guo0f00be82019-04-18 17:51:14 -0700244 self._start_stop_capture(uart, False)
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700245 except Exception as err:
246 logging.warn('Failed to stop UART logging for %s: %s', uart,
247 err)
248
249
J. Richard Barnette384056b2012-04-16 11:04:46 -0700250class Servo(object):
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700251
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700252 """Manages control of a Servo board.
253
254 Servo is a board developed by hardware group to aide in the debug and
255 control of various partner devices. Servo's features include the simulation
256 of pressing the power button, closing the lid, and pressing Ctrl-d. This
257 class manages setting up and communicating with a servo demon (servod)
258 process. It provides both high-level functions for common servo tasks and
259 low-level functions for directly setting and reading gpios.
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700260
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700261 """
262
Chrome Bot9a1137d2011-07-19 14:35:00 -0700263 # Power button press delays in seconds.
J. Richard Barnetted2e4cbd2012-06-29 12:18:40 -0700264 #
J. Richard Barnette5383f072012-07-26 17:35:40 -0700265 # The EC specification says that 8.0 seconds should be enough
266 # for the long power press. However, some platforms need a bit
267 # more time. Empirical testing has found these requirements:
268 # Alex: 8.2 seconds
269 # ZGB: 8.5 seconds
270 # The actual value is set to the largest known necessary value.
271 #
272 # TODO(jrbarnette) Being generous is the right thing to do for
273 # existing platforms, but if this code is to be used for
274 # qualification of new hardware, we should be less generous.
Chrome Bot9a1137d2011-07-19 14:35:00 -0700275 SHORT_DELAY = 0.1
J. Richard Barnette5383f072012-07-26 17:35:40 -0700276
Todd Broch31c82502011-08-29 08:14:39 -0700277 # Maximum number of times to re-read power button on release.
Todd Brochcf7c6652012-02-24 13:03:59 -0800278 GET_RETRY_MAX = 10
Chrome Bot9a1137d2011-07-19 14:35:00 -0700279
J. Richard Barnette5383f072012-07-26 17:35:40 -0700280 # Delays to deal with DUT state transitions.
Chrome Bot9a1137d2011-07-19 14:35:00 -0700281 SLEEP_DELAY = 6
282 BOOT_DELAY = 10
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700283
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700284 # Default minimum time interval between 'press' and 'release'
285 # keyboard events.
Vic Yang0aca1c22012-11-19 18:33:56 -0800286 SERVO_KEY_PRESS_DELAY = 0.1
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700287
Tom Wai-Hong Tam93b5c092015-05-14 02:50:43 +0800288 # Time to toggle recovery switch on and off.
289 REC_TOGGLE_DELAY = 0.1
290
Tom Wai-Hong Tamf9ded092015-05-20 05:37:22 +0800291 # Time to toggle development switch on and off.
292 DEV_TOGGLE_DELAY = 0.1
293
Jon Salzc88e5b62011-11-30 14:38:54 +0800294 # Time between an usb disk plugged-in and detected in the system.
295 USB_DETECTION_DELAY = 10
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800296 # Time to keep USB power off before and after USB mux direction is changed
297 USB_POWEROFF_DELAY = 2
Jon Salzc88e5b62011-11-30 14:38:54 +0800298
Simran Basib7850bb2013-07-24 12:33:42 -0700299 # Time to wait before timing out on servo initialization.
300 INIT_TIMEOUT_SECS = 10
Simran Basi59331342013-07-12 12:06:29 -0700301
J. Richard Barnette67ccb872012-04-19 16:34:56 -0700302
Ricky Liang0dd379c2014-04-23 16:29:08 +0800303 def __init__(self, servo_host, servo_serial=None):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700304 """Sets up the servo communication infrastructure.
305
Fang Deng5d518f42013-08-02 14:04:32 -0700306 @param servo_host: A ServoHost object representing
307 the host running servod.
Dana Goyettea2f00ea2019-06-26 16:14:12 -0700308 @type servo_host: autotest_lib.server.hosts.servo_host.ServoHost
Ricky Liang0dd379c2014-04-23 16:29:08 +0800309 @param servo_serial: Serial number of the servo board.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700310 """
Fang Deng5d518f42013-08-02 14:04:32 -0700311 # TODO(fdeng): crbug.com/298379
312 # We should move servo_host object out of servo object
313 # to minimize the dependencies on the rest of Autotest.
314 self._servo_host = servo_host
Ricky Liang0dd379c2014-04-23 16:29:08 +0800315 self._servo_serial = servo_serial
Richard Barnette180efe62016-12-02 23:20:44 +0000316 self._server = servo_host.get_servod_server_proxy()
Ruben Rodriguez Buchillonf3f63b52020-01-27 17:56:49 -0800317 self._servo_type = self.get_servo_version()
J. Richard Barnette0c9c5882014-06-11 15:27:05 -0700318 self._power_state = _PowerStateController(self)
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700319 self._uart = _Uart(self)
Fang Dengafb88142013-05-30 17:44:31 -0700320 self._usb_state = None
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700321 self._programmer = None
Dana Goyette4dc0adc2019-05-06 14:51:53 -0700322 self._prev_log_inode = None
323 self._prev_log_size = 0
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800324
Ricky Liang0dd379c2014-04-23 16:29:08 +0800325 @property
326 def servo_serial(self):
327 """Returns the serial number of the servo board."""
328 return self._servo_serial
329
Dana Goyette0b6e6402019-10-04 11:09:24 -0700330 def rotate_servod_logs(self, filename=None, directory=None):
331 """Save the latest servod log into a local directory, then rotate logs.
Dana Goyette4dc0adc2019-05-06 14:51:53 -0700332
Dana Goyette0b6e6402019-10-04 11:09:24 -0700333 The files will be <filename>.DEBUG, <filename>.INFO, <filename>.WARNING,
334 or just <filename>.log if not using split level logging.
335
336 @param filename: local filename prefix (no file extension) to use.
337 If None, rotate log but don't save it.
338 @param directory: local directory to save logs into (if unset, use cwd)
Dana Goyette4dc0adc2019-05-06 14:51:53 -0700339 """
Dana Goyette0b6e6402019-10-04 11:09:24 -0700340 if self.is_localhost():
341 # Local servod usually runs without log-dir, so can't be collected.
342 # TODO(crbug.com/1011516): allow localhost when rotation is enabled
343 return
344
345 log_dir = '/var/log/servod_%s' % self._servo_host.servo_port
346
347 if filename:
Dana Goyettec8613342019-11-13 15:03:28 -0800348 logging.info("Saving servod logs: %s/%s.*", directory or '.',
349 filename)
Dana Goyette0b6e6402019-10-04 11:09:24 -0700350 # TODO(crrev.com/c/1793030): remove no-level case once CL is pushed
351 for level_name in ('', 'DEBUG', 'INFO', 'WARNING'):
352
353 remote_path = os.path.join(log_dir, 'latest')
354 if level_name:
355 remote_path += '.%s' % level_name
356
357 local_path = '%s.%s' % (filename, level_name or 'log')
358 if directory:
359 local_path = os.path.join(directory, local_path)
360
361 try:
362 self._servo_host.get_file(
363 remote_path, local_path, try_rsync=False)
364
365 except error.AutoservRunError as e:
366 result = e.result_obj
367 if result.exit_status != 0:
368 stderr = result.stderr.strip()
369
370 # File not existing is okay, but warn for anything else.
371 if 'no such' not in stderr.lower():
372 logging.warn(
373 "Couldn't retrieve servod log: %s",
374 stderr or '\n%s' % result)
375
376 try:
377 if os.stat(local_path).st_size == 0:
378 os.unlink(local_path)
379 except EnvironmentError:
380 pass
381
382 else:
383 # No filename given, so caller wants to discard the log lines.
384 # Remove the symlinks to prevent old log-dir links from being
385 # picked up multiple times when using servod without log-dir.
386 remote_path = os.path.join(log_dir, 'latest*')
387 self._servo_host.run(
388 "rm %s" % remote_path,
389 stderr_tee=None, ignore_status=True)
390
391 # Servod log rotation renames current log, then creates a new file with
392 # the old name: log.<date> -> log.<date>.1.tbz2 -> log.<date>.2.tbz2
393
394 # Must rotate after copying, or the copy would be the new, empty file.
395 try:
396 self.set_nocheck('rotate_servod_logs', 'yes')
397 except ControlUnavailableError as e:
Dana Goyettec8613342019-11-13 15:03:28 -0800398 # Missing control (possibly old servod)
Dana Goyette0b6e6402019-10-04 11:09:24 -0700399 logging.warn("Couldn't rotate servod logs: %s", str(e))
Dana Goyettec8613342019-11-13 15:03:28 -0800400 except error.TestFail:
401 # Control exists but gave an error; don't let it fail the test.
402 # The error is already logged in set_nocheck().
403 pass
Ricky Liang0dd379c2014-04-23 16:29:08 +0800404
Tom Wai-Hong Tam22ee0e52013-04-03 13:36:39 +0800405 def get_power_state_controller(self):
J. Richard Barnette75136b32013-03-26 13:38:44 -0700406 """Return the power state controller for this Servo.
407
408 The power state controller provides board-independent
409 interfaces for reset, power-on, power-off operations.
410
411 """
412 return self._power_state
413
Fang Deng5d518f42013-08-02 14:04:32 -0700414
Mary Ruthvena3c52b82019-10-09 14:09:08 -0700415 def initialize_dut(self, cold_reset=False, enable_main=True):
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700416 """Initializes a dut for testing purposes.
417
418 This sets various servo signals back to default values
419 appropriate for the target board. By default, if the DUT
420 is already on, it stays on. If the DUT is powered off
421 before initialization, its state afterward is unspecified.
422
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700423 Rationale: Basic initialization of servo sets the lid open,
424 when there is a lid. This operation won't affect powered on
425 units; however, setting the lid open may power on a unit
J. Richard Barnette75136b32013-03-26 13:38:44 -0700426 that's off, depending on the board type and previous state
427 of the device.
428
J. Richard Barnette4b6af0d2014-06-05 09:57:20 -0700429 If `cold_reset` is a true value, the DUT and its EC will be
430 reset, and the DUT rebooted in normal mode.
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700431
432 @param cold_reset If True, cold reset the device after
433 initialization.
Mary Ruthvena3c52b82019-10-09 14:09:08 -0700434 @param enable_main If True, make sure the main servo device has
435 control of the dut.
436
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700437 """
Mary Ruthvena3c52b82019-10-09 14:09:08 -0700438 if enable_main:
439 self.enable_main_servo_device()
440
Dana Goyettea2f00ea2019-06-26 16:14:12 -0700441 try:
442 self._server.hwinit()
443 except socket.error as e:
444 e.filename = '%s:%s' % (self._servo_host.hostname,
445 self._servo_host.servo_port)
446 raise
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700447 self.set('usb_mux_oe1', 'on')
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700448 self._usb_state = None
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700449 self.switch_usbkey('off')
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700450 self._uart.start_capture()
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700451 if cold_reset:
J. Richard Barnette4b6af0d2014-06-05 09:57:20 -0700452 self._power_state.reset()
J. Richard Barnette1777f5d2014-09-03 15:18:19 -0700453 logging.debug('Servo initialized, version is %s',
454 self._server.get_version())
Ruben Rodriguez Buchillonb0e2a9f2019-08-12 12:40:44 -0700455 if self.has_control('init_keyboard'):
456 # This indicates the servod version does not
457 # have explicit keyboard initialization yet.
458 # Ignore this.
Ruben Rodriguez Buchillon05298242019-05-20 11:29:06 -0700459 # TODO(coconutruben): change this back to set() about a month
460 # after crrev.com/c/1586239 has been merged (or whenever that
461 # logic is in the labstation images).
462 self.set_nocheck('init_keyboard','on')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700463
464
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800465 def is_localhost(self):
466 """Is the servod hosted locally?
467
468 Returns:
469 True if local hosted; otherwise, False.
470 """
Fang Deng5d518f42013-08-02 14:04:32 -0700471 return self._servo_host.is_localhost()
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800472
473
Mary Ruthvenecf12712019-06-26 17:36:21 -0700474 def get_os_version(self):
475 """Returns the chromeos release version."""
476 lsb_release_content = self.system_output('cat /etc/lsb-release',
477 ignore_status=True)
478 return lsbrelease_utils.get_chromeos_release_builder_path(
479 lsb_release_content=lsb_release_content)
480
481
Mary Ruthven83bb5952019-06-27 12:34:05 -0700482 def get_servod_version(self):
483 """Returns the servod version."""
484 result = self._servo_host.run('servod --version')
485 # TODO: use system_output once servod --version prints to stdout
486 stdout = result.stdout.strip()
487 return stdout if stdout else result.stderr.strip()
488
489
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700490 def power_long_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700491 """Simulate a long power button press."""
J. Richard Barnettef12bff22012-07-18 14:41:06 -0700492 # After a long power press, the EC may ignore the next power
493 # button press (at least on Alex). To guarantee that this
494 # won't happen, we need to allow the EC one second to
495 # collect itself.
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800496 # long_press is defined as 8.5s in servod
497 self.set_nocheck('power_key', 'long_press')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700498
499
500 def power_normal_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700501 """Simulate a normal power button press."""
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800502 # press is defined as 1.2s in servod
503 self.set_nocheck('power_key', 'press')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700504
505
506 def power_short_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700507 """Simulate a short power button press."""
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800508 # tab is defined as 0.2s in servod
509 self.set_nocheck('power_key', 'tab')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700510
511
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800512 def power_key(self, press_secs='tab'):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700513 """Simulate a power button press.
514
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800515 @param press_secs: int, float, str; time to press key in seconds or
516 known shorthand: 'tab' 'press' 'long_press'
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700517 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800518 self.set_nocheck('power_key', press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700519
520
Ruben Zakarian47c1d4f2019-09-30 14:17:41 -0700521 def pwr_button(self, action='press'):
522 """Simulate a power button press.
523
524 @param action: str; could be press or could be release.
525 """
526 self.set_nocheck('pwr_button', action)
527
528
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700529 def lid_open(self):
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800530 """Simulate opening the lid and raise exception if all attempts fail"""
531 self.set('lid_open', 'yes')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700532
533
534 def lid_close(self):
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800535 """Simulate closing the lid and raise exception if all attempts fail
Craig Harrison48997262011-06-27 14:31:10 -0700536
537 Waits 6 seconds to ensure the device is fully asleep before returning.
538 """
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800539 self.set('lid_open', 'no')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700540 time.sleep(Servo.SLEEP_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700541
Ruben Zakarian47c1d4f2019-09-30 14:17:41 -0700542
543 def vbus_power_get(self):
544 """Get current vbus_power."""
545 return self.get('vbus_power')
546
547
Shelley Chenc26575a2015-09-18 10:56:16 -0700548 def volume_up(self, timeout=300):
Kevin Chenge448a8b2016-09-09 10:18:11 -0700549 """Simulate pushing the volume down button.
550
551 @param timeout: Timeout for setting the volume.
552 """
Shelley Chenc26575a2015-09-18 10:56:16 -0700553 self.set_get_all(['volume_up:yes',
554 'sleep:%.4f' % self.SERVO_KEY_PRESS_DELAY,
555 'volume_up:no'])
556 # we need to wait for commands to take effect before moving on
557 time_left = float(timeout)
558 while time_left > 0.0:
559 value = self.get('volume_up')
560 if value == 'no':
561 return
562 time.sleep(self.SHORT_DELAY)
563 time_left = time_left - self.SHORT_DELAY
564 raise error.TestFail("Failed setting volume_up to no")
565
566 def volume_down(self, timeout=300):
Kevin Chenge448a8b2016-09-09 10:18:11 -0700567 """Simulate pushing the volume down button.
568
569 @param timeout: Timeout for setting the volume.
570 """
Shelley Chenc26575a2015-09-18 10:56:16 -0700571 self.set_get_all(['volume_down:yes',
572 'sleep:%.4f' % self.SERVO_KEY_PRESS_DELAY,
573 'volume_down:no'])
574 # we need to wait for commands to take effect before moving on
575 time_left = float(timeout)
576 while time_left > 0.0:
577 value = self.get('volume_down')
578 if value == 'no':
579 return
580 time.sleep(self.SHORT_DELAY)
581 time_left = time_left - self.SHORT_DELAY
582 raise error.TestFail("Failed setting volume_down to no")
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700583
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800584 def ctrl_d(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800585 """Simulate Ctrl-d simultaneous button presses.
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800586
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800587 @param press_secs: int, float, str; time to press key in seconds or
588 known shorthand: 'tab' 'press' 'long_press'
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800589 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800590 self.set_nocheck('ctrl_d', press_secs)
Gediminas Ramanauskas9da80f22012-11-14 12:59:43 -0800591
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800592
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800593 def ctrl_u(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800594 """Simulate Ctrl-u simultaneous button presses.
595
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800596 @param press_secs: int, float, str; time to press key in seconds or
597 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800598 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800599 self.set_nocheck('ctrl_u', press_secs)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700600
601
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800602 def ctrl_enter(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800603 """Simulate Ctrl-enter simultaneous button presses.
604
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800605 @param press_secs: int, float, str; time to press key in seconds or
606 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800607 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800608 self.set_nocheck('ctrl_enter', press_secs)
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -0700609
610
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800611 def ctrl_key(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800612 """Simulate Enter key button press.
613
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800614 @param press_secs: int, float, str; time to press key in seconds or
615 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800616 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800617 self.set_nocheck('ctrl_key', press_secs)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700618
619
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800620 def enter_key(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800621 """Simulate Enter key button press.
622
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800623 @param press_secs: int, float, str; time to press key in seconds or
624 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800625 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800626 self.set_nocheck('enter_key', press_secs)
Todd Broch9753bd42012-03-21 10:15:08 -0700627
628
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800629 def refresh_key(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800630 """Simulate Refresh key (F3) button press.
631
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800632 @param press_secs: int, float, str; time to press key in seconds or
633 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800634 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800635 self.set_nocheck('refresh_key', press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700636
637
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800638 def ctrl_refresh_key(self, press_secs='tab'):
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800639 """Simulate Ctrl and Refresh (F3) simultaneous press.
640
641 This key combination is an alternative of Space key.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800642
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800643 @param press_secs: int, float, str; time to press key in seconds or
644 known shorthand: 'tab' 'press' 'long_press'
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800645 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800646 self.set_nocheck('ctrl_refresh_key', press_secs)
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800647
648
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800649 def imaginary_key(self, press_secs='tab'):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700650 """Simulate imaginary key button press.
651
652 Maps to a key that doesn't physically exist.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800653
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800654 @param press_secs: int, float, str; time to press key in seconds or
655 known shorthand: 'tab' 'press' 'long_press'
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700656 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800657 self.set_nocheck('imaginary_key', press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700658
659
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800660 def sysrq_x(self, press_secs='tab'):
Vincent Palatine7dc9282016-07-14 11:31:58 +0200661 """Simulate Alt VolumeUp X simulataneous press.
662
663 This key combination is the kernel system request (sysrq) X.
664
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800665 @param press_secs: int, float, str; time to press key in seconds or
666 known shorthand: 'tab' 'press' 'long_press'
Vincent Palatine7dc9282016-07-14 11:31:58 +0200667 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800668 self.set_nocheck('sysrq_x', press_secs)
Vincent Palatine7dc9282016-07-14 11:31:58 +0200669
670
Tom Wai-Hong Tam93b5c092015-05-14 02:50:43 +0800671 def toggle_recovery_switch(self):
672 """Toggle recovery switch on and off."""
673 self.enable_recovery_mode()
674 time.sleep(self.REC_TOGGLE_DELAY)
675 self.disable_recovery_mode()
676
677
Craig Harrison6b36b122011-06-28 17:58:43 -0700678 def enable_recovery_mode(self):
679 """Enable recovery mode on device."""
680 self.set('rec_mode', 'on')
681
682
683 def disable_recovery_mode(self):
684 """Disable recovery mode on device."""
685 self.set('rec_mode', 'off')
686
687
Tom Wai-Hong Tamf9ded092015-05-20 05:37:22 +0800688 def toggle_development_switch(self):
689 """Toggle development switch on and off."""
690 self.enable_development_mode()
691 time.sleep(self.DEV_TOGGLE_DELAY)
692 self.disable_development_mode()
693
694
Craig Harrison6b36b122011-06-28 17:58:43 -0700695 def enable_development_mode(self):
696 """Enable development mode on device."""
697 self.set('dev_mode', 'on')
698
699
700 def disable_development_mode(self):
701 """Disable development mode on device."""
702 self.set('dev_mode', 'off')
703
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700704 def boot_devmode(self):
705 """Boot a dev-mode device that is powered off."""
Tom Wai-Hong Tam23869102012-01-17 15:41:30 +0800706 self.power_short_press()
Craig Harrison48997262011-06-27 14:31:10 -0700707 self.pass_devmode()
708
709
710 def pass_devmode(self):
711 """Pass through boot screens in dev-mode."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700712 time.sleep(Servo.BOOT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700713 self.ctrl_d()
Chrome Bot9a1137d2011-07-19 14:35:00 -0700714 time.sleep(Servo.BOOT_DELAY)
Craig Harrison48997262011-06-27 14:31:10 -0700715
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700716
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800717 def get_board(self):
Wai-Hong Tam0f904002017-09-19 15:52:22 -0700718 """Get the board connected to servod."""
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800719 return self._server.get_board()
720
721
Wai-Hong Tam0f904002017-09-19 15:52:22 -0700722 def get_base_board(self):
723 """Get the board of the base connected to servod."""
Wai-Hong Tam7f6169e2017-09-29 09:23:48 -0700724 try:
725 return self._server.get_base_board()
726 except xmlrpclib.Fault as e:
727 # TODO(waihong): Remove the following compatibility check when
728 # the new versions of hdctools are deployed.
729 if 'not supported' in str(e):
730 logging.warning('The servod is too old that get_base_board '
731 'not supported.')
732 return ''
733 raise
Wai-Hong Tam0f904002017-09-19 15:52:22 -0700734
735
Wai-Hong Tamcc399ee2017-12-08 12:43:28 -0800736 def get_ec_active_copy(self):
737 """Get the active copy of the EC image."""
738 return self.get('ec_active_copy')
739
740
Todd Brochefe72cb2012-07-11 19:58:53 -0700741 def _get_xmlrpclib_exception(self, xmlexc):
742 """Get meaningful exception string from xmlrpc.
743
744 Args:
745 xmlexc: xmlrpclib.Fault object
746
747 xmlrpclib.Fault.faultString has the following format:
748
749 <type 'exception type'>:'actual error message'
750
751 Parse and return the real exception from the servod side instead of the
752 less meaningful one like,
753 <Fault 1: "<type 'exceptions.AttributeError'>:'tca6416' object has no
754 attribute 'hw_driver'">
755
756 Returns:
757 string of underlying exception raised in servod.
758 """
759 return re.sub('^.*>:', '', xmlexc.faultString)
760
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700761 def has_control(self, control):
762 """Query servod server to determine if |control| is a valid control.
763
764 @param control: str, control name to query
765
766 @returns: true if |control| is a known control, false otherwise.
767 """
768 assert control
769 try:
770 # If the control exists, doc() will work.
771 self._server.doc(control)
772 return True
773 except xmlrpclib.Fault as e:
774 if re.search('No control %s' % control,
775 self._get_xmlrpclib_exception(e)):
776 return False
777 raise e
Todd Brochefe72cb2012-07-11 19:58:53 -0700778
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700779 def get(self, gpio_name):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700780 """Get the value of a gpio from Servod.
781
782 @param gpio_name Name of the gpio.
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700783
784 @returns: server response to |gpio_name| request.
785
786 @raise ControlUnavailableError: if |gpio_name| not a known control.
787 @raise error.TestFail: for all other failures doing get().
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700788 """
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700789 assert gpio_name
Todd Brochefe72cb2012-07-11 19:58:53 -0700790 try:
Simran Basi1cbc5f22013-07-17 14:23:58 -0700791 return self._server.get(gpio_name)
Todd Brochefe72cb2012-07-11 19:58:53 -0700792 except xmlrpclib.Fault as e:
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700793 err_str = self._get_xmlrpclib_exception(e)
794 err_msg = "Getting '%s' :: %s" % (gpio_name, err_str)
795 unknown_ctrl = re.findall(NO_CONTROL_RE, err_str)
796 if unknown_ctrl:
797 raise ControlUnavailableError('No control named %r' %
798 unknown_ctrl[0])
799 else:
800 logging.error(err_msg)
801 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700802
803
804 def set(self, gpio_name, gpio_value):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700805 """Set and check the value of a gpio using Servod.
806
807 @param gpio_name Name of the gpio.
808 @param gpio_value New setting for the gpio.
Dana Goyette7ff06c92019-10-11 13:38:03 -0700809 @raise error.TestFail: if the control value fails to change.
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700810 """
Chrome Bot9a1137d2011-07-19 14:35:00 -0700811 self.set_nocheck(gpio_name, gpio_value)
Todd Brochcf7c6652012-02-24 13:03:59 -0800812 retry_count = Servo.GET_RETRY_MAX
Dana Goyette7ff06c92019-10-11 13:38:03 -0700813 actual_value = self.get(gpio_name)
814 while gpio_value != actual_value and retry_count:
Ilja H. Friedel04be2bd2014-05-07 21:29:59 -0700815 logging.warning("%s != %s, retry %d", gpio_name, gpio_value,
Dana Goyette7ff06c92019-10-11 13:38:03 -0700816 retry_count)
Todd Brochcf7c6652012-02-24 13:03:59 -0800817 retry_count -= 1
818 time.sleep(Servo.SHORT_DELAY)
Dana Goyette7ff06c92019-10-11 13:38:03 -0700819 actual_value = self.get(gpio_name)
820
821 if gpio_value != actual_value:
822 raise error.TestFail(
823 'Servo failed to set %s to %s. Got %s.'
824 % (gpio_name, gpio_value, actual_value))
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700825
826
827 def set_nocheck(self, gpio_name, gpio_value):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700828 """Set the value of a gpio using Servod.
829
830 @param gpio_name Name of the gpio.
831 @param gpio_value New setting for the gpio.
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700832
833 @raise ControlUnavailableError: if |gpio_name| not a known control.
834 @raise error.TestFail: for all other failures doing set().
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700835 """
Ruben Rodriguez Buchillon1c0219c2019-06-25 12:30:44 -0700836 # The real danger here is to pass a None value through the xmlrpc.
837 assert gpio_name and gpio_value is not None
Mary Ruthvendfa45342019-06-25 10:10:17 -0700838 logging.debug('Setting %s to %r', gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700839 try:
Simran Basi1cbc5f22013-07-17 14:23:58 -0700840 self._server.set(gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700841 except xmlrpclib.Fault as e:
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700842 err_str = self._get_xmlrpclib_exception(e)
843 err_msg = "Setting '%s' :: %s" % (gpio_name, err_str)
844 unknown_ctrl = re.findall(NO_CONTROL_RE, err_str)
845 if unknown_ctrl:
846 raise ControlUnavailableError('No control named %r' %
847 unknown_ctrl[0])
848 else:
849 logging.error(err_msg)
850 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700851
852
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800853 def set_get_all(self, controls):
854 """Set &| get one or more control values.
855
856 @param controls: list of strings, controls to set &| get.
857
858 @raise: error.TestError in case error occurs setting/getting values.
859 """
860 rv = []
861 try:
Mary Ruthvendfa45342019-06-25 10:10:17 -0700862 logging.debug('Set/get all: %s', str(controls))
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800863 rv = self._server.set_get_all(controls)
864 except xmlrpclib.Fault as e:
865 # TODO(waihong): Remove the following backward compatibility when
866 # the new versions of hdctools are deployed.
867 if 'not supported' in str(e):
868 logging.warning('The servod is too old that set_get_all '
869 'not supported. Use set and get instead.')
870 for control in controls:
871 if ':' in control:
872 (name, value) = control.split(':')
873 if name == 'sleep':
874 time.sleep(float(value))
875 else:
876 self.set_nocheck(name, value)
877 rv.append(True)
878 else:
879 rv.append(self.get(name))
880 else:
881 err_msg = "Problem with '%s' :: %s" % \
882 (controls, self._get_xmlrpclib_exception(e))
883 raise error.TestFail(err_msg)
884 return rv
885
886
Jon Salzc88e5b62011-11-30 14:38:54 +0800887 # TODO(waihong) It may fail if multiple servo's are connected to the same
888 # host. Should look for a better way, like the USB serial name, to identify
889 # the USB device.
Simran Basi741b5d42012-05-18 11:27:15 -0700890 # TODO(sbasi) Remove this code from autoserv once firmware tests have been
891 # updated.
Kevin Chenga22c4a82016-10-07 14:13:25 -0700892 def probe_host_usb_dev(self, timeout=_USB_PROBE_TIMEOUT):
Jon Salzc88e5b62011-11-30 14:38:54 +0800893 """Probe the USB disk device plugged-in the servo from the host side.
894
Kevin Chengeb06fe72016-08-22 15:26:32 -0700895 It uses servod to discover if there is a usb device attached to it.
Jon Salzc88e5b62011-11-30 14:38:54 +0800896
Kevin Chenga22c4a82016-10-07 14:13:25 -0700897 @param timeout The timeout period when probing for the usb host device.
898
899 @return: String of USB disk path (e.g. '/dev/sdb') or None.
Jon Salzc88e5b62011-11-30 14:38:54 +0800900 """
Ruben Rodriguez Buchillon9a4bfc32018-10-16 20:46:25 +0800901 # Set up Servo's usb mux.
902 self.switch_usbkey('host')
Kevin Chenga22c4a82016-10-07 14:13:25 -0700903 return self._server.probe_host_usb_dev(timeout) or None
Jon Salzc88e5b62011-11-30 14:38:54 +0800904
905
Mike Truty49153d82012-08-21 22:27:30 -0500906 def image_to_servo_usb(self, image_path=None,
907 make_image_noninteractive=False):
908 """Install an image to the USB key plugged into the servo.
909
910 This method may copy any image to the servo USB key including a
911 recovery image or a test image. These images are frequently used
912 for test purposes such as restoring a corrupted image or conducting
913 an upgrade of ec/fw/kernel as part of a test of a specific image part.
914
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700915 @param image_path Path on the host to the recovery image.
916 @param make_image_noninteractive Make the recovery image
917 noninteractive, therefore the DUT
918 will reboot automatically after
919 installation.
Mike Truty49153d82012-08-21 22:27:30 -0500920 """
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700921 # We're about to start plugging/unplugging the USB key. We
922 # don't know the state of the DUT, or what it might choose
923 # to do to the device after hotplug. To avoid surprises,
924 # force the DUT to be off.
925 self._server.hwinit()
Ruben Rodriguez Buchillonb0e2a9f2019-08-12 12:40:44 -0700926 if self.has_control('init_keyboard'):
927 # This indicates the servod version does not
928 # have explicit keyboard initialization yet.
929 # Ignore this.
Ruben Rodriguez Buchillonbd3b74f2019-08-08 16:18:27 -0700930 # TODO(coconutruben): change this back to set() about a month
931 # after crrev.com/c/1586239 has been merged (or whenever that
932 # logic is in the labstation images).
933 self.set_nocheck('init_keyboard','on')
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700934 self._power_state.power_off()
Mike Truty49153d82012-08-21 22:27:30 -0500935
Mike Truty49153d82012-08-21 22:27:30 -0500936 if image_path:
Garry Wangd2656812019-08-07 17:29:16 -0700937 # Set up Servo's usb mux.
938 self.switch_usbkey('host')
Tom Wai-Hong Tam42f136d2012-10-26 11:11:23 +0800939 logging.info('Searching for usb device and copying image to it. '
940 'Please wait a few minutes...')
Mike Truty49153d82012-08-21 22:27:30 -0500941 if not self._server.download_image_to_usb(image_path):
942 logging.error('Failed to transfer requested image to USB. '
943 'Please take a look at Servo Logs.')
944 raise error.AutotestError('Download image to usb failed.')
945 if make_image_noninteractive:
946 logging.info('Making image noninteractive')
947 if not self._server.make_image_noninteractive():
948 logging.error('Failed to make image noninteractive. '
949 'Please take a look at Servo Logs.')
950
Kalin Stoyanovb3c11f32018-05-11 09:02:00 -0700951 def boot_in_recovery_mode(self):
952 """Boot host DUT in recovery mode."""
953 self._power_state.power_on(rec_mode=self._power_state.REC_ON)
954 self.switch_usbkey('dut')
955
Mike Truty49153d82012-08-21 22:27:30 -0500956
Simran Basi741b5d42012-05-18 11:27:15 -0700957 def install_recovery_image(self, image_path=None,
J. Richard Barnetteb6de7e32013-02-14 13:28:04 -0800958 make_image_noninteractive=False):
Dan Shic67f1332016-04-06 12:38:06 -0700959 """Install the recovery image specified by the path onto the DUT.
Jon Salzc88e5b62011-11-30 14:38:54 +0800960
961 This method uses google recovery mode to install a recovery image
962 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 +0800963 board specified by the usb_dev. If no image path is specified
Jon Salzc88e5b62011-11-30 14:38:54 +0800964 we use the recovery image already on the usb image.
965
Dan Shic67f1332016-04-06 12:38:06 -0700966 @param image_path: Path on the host to the recovery image.
967 @param make_image_noninteractive: Make the recovery image
968 noninteractive, therefore the DUT will reboot automatically
969 after installation.
Jon Salzc88e5b62011-11-30 14:38:54 +0800970 """
Mike Truty49153d82012-08-21 22:27:30 -0500971 self.image_to_servo_usb(image_path, make_image_noninteractive)
Garry Wangd2656812019-08-07 17:29:16 -0700972 # Give the DUT some time to power_off if we skip
973 # download image to usb. (crbug.com/982993)
974 if not image_path:
975 time.sleep(10)
Kalin Stoyanovb3c11f32018-05-11 09:02:00 -0700976 self.boot_in_recovery_mode()
Jon Salzc88e5b62011-11-30 14:38:54 +0800977
978
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800979 def _scp_image(self, image_path):
980 """Copy image to the servo host.
981
982 When programming a firmware image on the DUT, the image must be
983 located on the host to which the servo device is connected. Sometimes
984 servo is controlled by a remote host, in this case the image needs to
Dana Goyetted5a95542019-12-30 11:14:14 -0800985 be transferred to the remote host. This adds the servod port number, to
986 make sure tests for different DUTs don't trample on each other's files.
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800987
988 @param image_path: a string, name of the firmware image file to be
989 transferred.
990 @return: a string, full path name of the copied file on the remote.
991 """
Mary Ruthven401a6012019-12-13 11:39:44 -0800992 name = os.path.basename(image_path)
Dana Goyetted5a95542019-12-30 11:14:14 -0800993 remote_name = 'dut_%s.%s' % (self._servo_host.servo_port, name)
Mary Ruthven401a6012019-12-13 11:39:44 -0800994 dest_path = os.path.join('/tmp', remote_name)
995 logging.info('Copying %s to %s', name, dest_path)
Fang Deng5d518f42013-08-02 14:04:32 -0700996 self._servo_host.send_file(image_path, dest_path)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800997 return dest_path
998
999
Dan Shifecdaf42015-07-28 10:17:26 -07001000 def system(self, command, timeout=3600):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -07001001 """Execute the passed in command on the servod host.
1002
1003 @param command Command to be executed.
Dan Shifecdaf42015-07-28 10:17:26 -07001004 @param timeout Maximum number of seconds of runtime allowed. Default to
1005 1 hour.
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -07001006 """
Vadim Bendebury89ec24e2012-12-17 12:54:18 -08001007 logging.info('Will execute on servo host: %s', command)
Fang Deng5d518f42013-08-02 14:04:32 -07001008 self._servo_host.run(command, timeout=timeout)
Vadim Bendebury89ec24e2012-12-17 12:54:18 -08001009
1010
Dan Shifecdaf42015-07-28 10:17:26 -07001011 def system_output(self, command, timeout=3600,
Vadim Bendebury89ec24e2012-12-17 12:54:18 -08001012 ignore_status=False, args=()):
1013 """Execute the passed in command on the servod host, return stdout.
1014
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -07001015 @param command a string, the command to execute
1016 @param timeout an int, max number of seconds to wait til command
Dan Shifecdaf42015-07-28 10:17:26 -07001017 execution completes. Default to 1 hour.
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -07001018 @param ignore_status a Boolean, if true - ignore command's nonzero exit
Vadim Bendebury89ec24e2012-12-17 12:54:18 -08001019 status, otherwise an exception will be thrown
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -07001020 @param args a tuple of strings, each becoming a separate command line
Vadim Bendebury89ec24e2012-12-17 12:54:18 -08001021 parameter for the command
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -07001022 @return command's stdout as a string.
Vadim Bendebury89ec24e2012-12-17 12:54:18 -08001023 """
Fang Deng5d518f42013-08-02 14:04:32 -07001024 return self._servo_host.run(command, timeout=timeout,
1025 ignore_status=ignore_status,
1026 args=args).stdout.strip()
Vadim Bendeburyb80ba592012-12-07 15:02:34 -08001027
1028
Mary Ruthven38d90af2019-08-16 13:13:31 -07001029 def get_servo_version(self, active=False):
Dan Shia5fef052015-05-18 23:28:47 -07001030 """Get the version of the servo, e.g., servo_v2 or servo_v3.
1031
Mary Ruthven38d90af2019-08-16 13:13:31 -07001032 @param active: Only return the servo type with the active device.
Dan Shia5fef052015-05-18 23:28:47 -07001033 @return: The version of the servo.
1034
1035 """
Mary Ruthven38d90af2019-08-16 13:13:31 -07001036 servo_type = self._server.get_version()
1037 if '_and_' not in servo_type or not active:
1038 return servo_type
1039
1040 # If servo v4 is using ccd and servo micro, modify the servo type to
1041 # reflect the active device.
1042 active_device = self.get('active_v4_device')
1043 if active_device in servo_type:
1044 logging.info('%s is active', active_device)
1045 return 'servo_v4_with_' + active_device
1046
1047 logging.warn("%s is active even though it's not in servo type",
1048 active_device)
1049 return servo_type
Dan Shia5fef052015-05-18 23:28:47 -07001050
1051
Mary Ruthvena3c52b82019-10-09 14:09:08 -07001052 def get_main_servo_device(self):
1053 """Return the main servo device"""
Ruben Rodriguez Buchillonf3f63b52020-01-27 17:56:49 -08001054 return self._servo_type.split('_with_')[-1].split('_and_')[0]
Mary Ruthvena3c52b82019-10-09 14:09:08 -07001055
1056
1057 def enable_main_servo_device(self):
1058 """Make sure the main device has control of the dut."""
Mary Ruthven1409d9d2019-10-22 20:44:24 -07001059 # Cr50 detects servo using the EC uart. It doesn't work well if the
1060 # board doesn't use EC uart. The lab active_v4_device doesn't handle
1061 # this correctly. Check ec_uart_pty before trying to change the active
1062 # device.
1063 # TODO(crbug.com/1016842): reenable the setting the main device when
1064 # active device works on labstations.
1065 return
Mary Ruthvena3c52b82019-10-09 14:09:08 -07001066 if not self.has_control('active_v4_device'):
1067 return
1068 self.set('active_v4_device', self.get_main_servo_device())
1069
1070
Ruben Rodriguez Buchillon1823bad2019-09-30 17:04:34 -07001071 def main_device_is_ccd(self):
1072 """Whether the main servo device (no prefixes) is a ccd device."""
Mary Ruthven2724ee62019-07-16 11:16:59 -07001073 servo = self._server.get_version()
1074 return 'ccd_cr50' in servo and 'servo_micro' not in servo
1075
1076
Ruben Rodriguez Buchillon1823bad2019-09-30 17:04:34 -07001077 def main_device_is_flex(self):
1078 """Whether the main servo device (no prefixes) is a legacy device."""
1079 return not self.main_device_is_ccd()
1080
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001081 def _initialize_programmer(self, rw_only=False):
1082 """Initialize the firmware programmer.
1083
1084 @param rw_only: True to initialize a programmer which only
1085 programs the RW portions.
1086 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -07001087 if self._programmer:
1088 return
1089 # Initialize firmware programmer
Ruben Rodriguez Buchillonf3f63b52020-01-27 17:56:49 -08001090 if self._servo_type.startswith('servo_v2'):
J. Richard Barnette8f19b392014-08-11 14:05:39 -07001091 self._programmer = firmware_programmer.ProgrammerV2(self)
Wai-Hong Tamc36c4d22016-09-09 10:39:45 -07001092 self._programmer_rw = firmware_programmer.ProgrammerV2RwOnly(self)
Kevin Chengdf2e29f2016-09-09 02:31:22 -07001093 # Both servo v3 and v4 use the same programming methods so just leverage
1094 # ProgrammerV3 for servo v4 as well.
Ruben Rodriguez Buchillonf3f63b52020-01-27 17:56:49 -08001095 elif (self._servo_type.startswith('servo_v3') or
1096 self._servo_type.startswith('servo_v4')):
Dan Shia5fef052015-05-18 23:28:47 -07001097 self._programmer = firmware_programmer.ProgrammerV3(self)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001098 self._programmer_rw = firmware_programmer.ProgrammerV3RwOnly(self)
J. Richard Barnette8f19b392014-08-11 14:05:39 -07001099 else:
1100 raise error.TestError(
1101 'No firmware programmer for servo version: %s' %
Ruben Rodriguez Buchillonf3f63b52020-01-27 17:56:49 -08001102 self._servo_type)
J. Richard Barnette8f19b392014-08-11 14:05:39 -07001103
1104
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001105 def program_bios(self, image, rw_only=False):
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -08001106 """Program bios on DUT with given image.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -08001107
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -08001108 @param image: a string, file name of the BIOS image to program
1109 on the DUT.
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001110 @param rw_only: True to only program the RW portion of BIOS.
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -08001111
Vadim Bendebury1a7ec632013-02-17 16:22:09 -08001112 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -07001113 self._initialize_programmer()
Ricky Liangc31aab32014-07-03 16:23:29 +08001114 if not self.is_localhost():
1115 image = self._scp_image(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001116 if rw_only:
1117 self._programmer_rw.program_bios(image)
1118 else:
1119 self._programmer.program_bios(image)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -08001120
1121
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001122 def program_ec(self, image, rw_only=False):
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -08001123 """Program ec on DUT with given image.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -08001124
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -08001125 @param image: a string, file name of the EC image to program
1126 on the DUT.
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001127 @param rw_only: True to only program the RW portion of EC.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -08001128
Vadim Bendebury1a7ec632013-02-17 16:22:09 -08001129 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -07001130 self._initialize_programmer()
Ricky Liangc31aab32014-07-03 16:23:29 +08001131 if not self.is_localhost():
1132 image = self._scp_image(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001133 if rw_only:
Congbin Guo42427612019-02-12 10:22:06 -08001134 self._programmer_rw.program_ec(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001135 else:
Congbin Guo42427612019-02-12 10:22:06 -08001136 self._programmer.program_ec(image)
1137
1138
Brent Peterson1cb623a2020-01-09 13:14:28 -08001139 def extract_ec_image(self, board, model, tarball_path):
1140 """Helper function to extract EC image from downloaded tarball.
Congbin Guo42427612019-02-12 10:22:06 -08001141
Shelley Chenac61d5a2019-06-24 15:35:46 -07001142 @param board: The DUT board name.
Congbin Guo42427612019-02-12 10:22:06 -08001143 @param model: The DUT model name.
1144 @param tarball_path: The path of the downloaded build tarball.
Justin TerAvest1d0c1bb2019-12-13 11:53:25 -07001145
Brent Peterson1cb623a2020-01-09 13:14:28 -08001146 @return: Path to extracted EC image.
1147 """
Justin TerAvest1d0c1bb2019-12-13 11:53:25 -07001148 # Best effort; try to retrieve the EC board from the version as
1149 # reported by the EC.
1150 ec_board = None
1151 try:
1152 ec_board = self.get('ec_board')
1153 except Exception as err:
1154 logging.info('Failed to get ec_board value; ignoring')
1155 pass
1156
Brent Peterson1cb623a2020-01-09 13:14:28 -08001157 # Array of candidates for EC image
1158 ec_image_candidates = ['ec.bin',
1159 '%s/ec.bin' % model,
Justin TerAvest1d0c1bb2019-12-13 11:53:25 -07001160 '%s/ec.bin' % board]
1161 if ec_board:
1162 ec_image_candidates.append('%s/ec.bin' % ec_board)
Congbin Guo42427612019-02-12 10:22:06 -08001163
Brent Peterson1cb623a2020-01-09 13:14:28 -08001164 # Extract EC image from tarball
1165 dest_dir = os.path.join(os.path.dirname(tarball_path), 'EC')
1166 ec_image = _extract_image_from_tarball(tarball_path, dest_dir,
1167 ec_image_candidates)
Congbin Guo42427612019-02-12 10:22:06 -08001168
Brent Peterson1cb623a2020-01-09 13:14:28 -08001169 # Return path to EC image
1170 return os.path.join(dest_dir, ec_image)
1171
1172
1173 def extract_bios_image(self, board, model, tarball_path):
1174 """Helper function to extract BIOS image from downloaded tarball.
1175
1176 @param board: The DUT board name.
1177 @param model: The DUT model name.
1178 @param tarball_path: The path of the downloaded build tarball.
1179
1180 @return: Path to extracted BIOS image.
1181 """
1182
1183 # Array of candidates for BIOS image
1184 bios_image_candidates = ['image.bin',
1185 'image-%s.bin' % model,
1186 'image-%s.bin' % board]
1187
1188 # Extract BIOS image from tarball
1189 dest_dir = os.path.join(os.path.dirname(tarball_path), 'BIOS')
1190 bios_image = _extract_image_from_tarball(tarball_path, dest_dir,
1191 bios_image_candidates)
1192
1193 # Return path to BIOS image
1194 return os.path.join(dest_dir, bios_image)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001195
Fang Dengafb88142013-05-30 17:44:31 -07001196
1197 def _switch_usbkey_power(self, power_state, detection_delay=False):
1198 """Switch usbkey power.
1199
1200 This function switches usbkey power by setting the value of
1201 'prtctl4_pwren'. If the power is already in the
1202 requested state, this function simply returns.
1203
1204 @param power_state: A string, 'on' or 'off'.
1205 @param detection_delay: A boolean value, if True, sleep
1206 for |USB_DETECTION_DELAY| after switching
1207 the power on.
1208 """
Kevin Chenga22c4a82016-10-07 14:13:25 -07001209 # TODO(kevcheng): Forgive me for this terrible hack. This is just to
1210 # handle beaglebones that haven't yet updated and have the
1211 # safe_switch_usbkey_power RPC. I'll remove this once all beaglebones
1212 # have been updated and also think about a better way to handle
1213 # situations like this.
1214 try:
1215 self._server.safe_switch_usbkey_power(power_state)
1216 except Exception:
1217 self.set('prtctl4_pwren', power_state)
Fang Dengafb88142013-05-30 17:44:31 -07001218 if power_state == 'off':
1219 time.sleep(self.USB_POWEROFF_DELAY)
1220 elif detection_delay:
1221 time.sleep(self.USB_DETECTION_DELAY)
1222
1223
1224 def switch_usbkey(self, usb_state):
1225 """Connect USB flash stick to either host or DUT, or turn USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001226
1227 This function switches the servo multiplexer to provide electrical
Fang Dengafb88142013-05-30 17:44:31 -07001228 connection between the USB port J3 and either host or DUT side. It
1229 can also be used to turn the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001230
Fang Dengafb88142013-05-30 17:44:31 -07001231 Switching to 'dut' or 'host' is accompanied by powercycling
1232 of the USB stick, because it sometimes gets wedged if the mux
1233 is switched while the stick power is on.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001234
Fang Dengafb88142013-05-30 17:44:31 -07001235 @param usb_state: A string, one of 'dut', 'host', or 'off'.
1236 'dut' and 'host' indicate which side the
1237 USB flash device is required to be connected to.
1238 'off' indicates turning the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001239
Fang Dengafb88142013-05-30 17:44:31 -07001240 @raise: error.TestError in case the parameter is not 'dut'
1241 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001242 """
Fang Dengafb88142013-05-30 17:44:31 -07001243 if self.get_usbkey_direction() == usb_state:
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001244 return
1245
Fang Dengafb88142013-05-30 17:44:31 -07001246 if usb_state == 'off':
Dan Shia5fef052015-05-18 23:28:47 -07001247 self._switch_usbkey_power('off')
1248 self._usb_state = usb_state
1249 return
Fang Dengafb88142013-05-30 17:44:31 -07001250 elif usb_state == 'host':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001251 mux_direction = 'servo_sees_usbkey'
Fang Dengafb88142013-05-30 17:44:31 -07001252 elif usb_state == 'dut':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001253 mux_direction = 'dut_sees_usbkey'
1254 else:
Fang Dengafb88142013-05-30 17:44:31 -07001255 raise error.TestError('Unknown USB state request: %s' % usb_state)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001256
Fang Dengafb88142013-05-30 17:44:31 -07001257 self._switch_usbkey_power('off')
Kevin Chenga22c4a82016-10-07 14:13:25 -07001258 # TODO(kevcheng): Forgive me for this terrible hack. This is just to
1259 # handle beaglebones that haven't yet updated and have the
1260 # safe_switch_usbkey RPC. I'll remove this once all beaglebones have
1261 # been updated and also think about a better way to handle situations
1262 # like this.
1263 try:
1264 self._server.safe_switch_usbkey(mux_direction)
1265 except Exception:
1266 self.set('usb_mux_sel1', mux_direction)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001267 time.sleep(self.USB_POWEROFF_DELAY)
Fang Dengafb88142013-05-30 17:44:31 -07001268 self._switch_usbkey_power('on', usb_state == 'host')
1269 self._usb_state = usb_state
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001270
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001271
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001272 def get_usbkey_direction(self):
Fang Dengafb88142013-05-30 17:44:31 -07001273 """Get which side USB is connected to or 'off' if usb power is off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001274
Fang Dengafb88142013-05-30 17:44:31 -07001275 @return: A string, one of 'dut', 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001276 """
Fang Dengafb88142013-05-30 17:44:31 -07001277 if not self._usb_state:
1278 if self.get('prtctl4_pwren') == 'off':
1279 self._usb_state = 'off'
1280 elif self.get('usb_mux_sel1').startswith('dut'):
1281 self._usb_state = 'dut'
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001282 else:
Fang Dengafb88142013-05-30 17:44:31 -07001283 self._usb_state = 'host'
1284 return self._usb_state
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001285
1286
Wai-Hong Tam60377262018-03-01 10:55:39 -08001287 def set_servo_v4_role(self, role):
1288 """Set the power role of servo v4, either 'src' or 'snk'.
1289
1290 It does nothing if not a servo v4.
1291
1292 @param role: Power role for DUT port on servo v4, either 'src' or 'snk'.
1293 """
Ruben Rodriguez Buchillonf3f63b52020-01-27 17:56:49 -08001294 if self._servo_type.startswith('servo_v4'):
Wai-Hong Tam60377262018-03-01 10:55:39 -08001295 value = self.get('servo_v4_role')
1296 if value != role:
1297 self.set_nocheck('servo_v4_role', role)
1298 else:
1299 logging.debug('Already in the role: %s.', role)
1300 else:
1301 logging.debug('Not a servo v4, unable to set role to %s.', role)
1302
1303
Ruben Rodriguez Buchillon5f7ee682019-09-30 12:50:09 -07001304 def supports_built_in_pd_control(self):
1305 """Return whether the servo type supports pd charging and control."""
Ruben Rodriguez Buchillonf3f63b52020-01-27 17:56:49 -08001306 if 'servo_v4' not in self._servo_type:
Ruben Rodriguez Buchillon5f7ee682019-09-30 12:50:09 -07001307 # Only servo v4 supports this feature.
Ruben Rodriguez Buchillonf3f63b52020-01-27 17:56:49 -08001308 logging.info('%r type does not support pd control.',
1309 self._servo_type)
Ruben Rodriguez Buchillon5f7ee682019-09-30 12:50:09 -07001310 return False
Ruben Rodriguez Buchillonf3f63b52020-01-27 17:56:49 -08001311 # On servo v4, it still needs to be the type-c version.
Ruben Rodriguez Buchillon5f7ee682019-09-30 12:50:09 -07001312 if not self.get('servo_v4_type') == 'type-c':
1313 logging.info('PD controls require a type-c servo v4.')
1314 return False
1315 # Lastly, one cannot really do anything without a plugged in charger.
1316 chg_port_mv = self.get('ppchg5_mv')
1317 if chg_port_mv < V4_CHG_ATTACHED_MIN_VOLTAGE_MV:
1318 logging.warn('It appears that no charger is plugged into servo v4. '
1319 'Charger port voltage: %dmV', chg_port_mv)
1320 return False
1321 logging.info('Charger port voltage: %dmV', chg_port_mv)
1322 return True
1323
Mary Ruthven739b2922019-08-22 11:16:06 -07001324 def set_servo_v4_dts_mode(self, state):
1325 """Set servo v4 dts mode to off or on.
1326
1327 It does nothing if not a servo v4. Disable the ccd watchdog if we're
1328 disabling dts mode. CCD will disconnect. The watchdog only allows CCD
1329 to disconnect for 10 seconds until it kills servod. Disable the
1330 watchdog, so CCD can stay disconnected indefinitely.
1331
1332 @param state: Set servo v4 dts mode 'off' or 'on'.
1333 """
Ruben Rodriguez Buchillonf3f63b52020-01-27 17:56:49 -08001334 if not self._servo_type.startswith('servo_v4'):
Mary Ruthven739b2922019-08-22 11:16:06 -07001335 logging.debug('Not a servo v4, unable to set dts mode %s.', state)
1336 return
1337
1338 # TODO(mruthven): remove watchdog check once the labstation has been
1339 # updated to have support for modifying the watchdog.
Ruben Rodriguez Buchillonf3f63b52020-01-27 17:56:49 -08001340 set_watchdog = (self.has_control('watchdog') and
1341 'ccd' in self._servo_type)
Mary Ruthven739b2922019-08-22 11:16:06 -07001342 enable_watchdog = state == 'on'
1343
1344 if set_watchdog and not enable_watchdog:
1345 self.set_nocheck('watchdog_remove', 'ccd')
1346
1347 self.set_nocheck('servo_v4_dts_mode', state)
1348
1349 if set_watchdog and enable_watchdog:
1350 self.set_nocheck('watchdog_add', 'ccd')
1351
1352
Congbin Guofc3b8962019-03-22 17:38:46 -07001353 @property
1354 def uart_logs_dir(self):
1355 """Return the directory to save UART logs."""
1356 return self._uart.logs_dir if self._uart else ""
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001357
Congbin Guofc3b8962019-03-22 17:38:46 -07001358
1359 @uart_logs_dir.setter
1360 def uart_logs_dir(self, logs_dir):
1361 """Set directory to save UART logs.
1362
1363 @param logs_dir String of directory name."""
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001364 if self._uart:
Congbin Guofc3b8962019-03-22 17:38:46 -07001365 self._uart.logs_dir = logs_dir
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001366
1367
1368 def close(self):
1369 """Close the servo object."""
1370 if self._uart:
1371 self._uart.stop_capture()
Congbin Guofc3b8962019-03-22 17:38:46 -07001372 self._uart.dump()
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001373 self._uart = None