blob: 97c7d497421a5a4e396d7ed1d60d305a49702f11 [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()
J. Richard Barnette0c9c5882014-06-11 15:27:05 -0700317 self._power_state = _PowerStateController(self)
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700318 self._uart = _Uart(self)
Fang Dengafb88142013-05-30 17:44:31 -0700319 self._usb_state = None
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700320 self._programmer = None
Dana Goyette4dc0adc2019-05-06 14:51:53 -0700321 self._prev_log_inode = None
322 self._prev_log_size = 0
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800323
Ricky Liang0dd379c2014-04-23 16:29:08 +0800324 @property
325 def servo_serial(self):
326 """Returns the serial number of the servo board."""
327 return self._servo_serial
328
Dana Goyette0b6e6402019-10-04 11:09:24 -0700329 def rotate_servod_logs(self, filename=None, directory=None):
330 """Save the latest servod log into a local directory, then rotate logs.
Dana Goyette4dc0adc2019-05-06 14:51:53 -0700331
Dana Goyette0b6e6402019-10-04 11:09:24 -0700332 The files will be <filename>.DEBUG, <filename>.INFO, <filename>.WARNING,
333 or just <filename>.log if not using split level logging.
334
335 @param filename: local filename prefix (no file extension) to use.
336 If None, rotate log but don't save it.
337 @param directory: local directory to save logs into (if unset, use cwd)
Dana Goyette4dc0adc2019-05-06 14:51:53 -0700338 """
Dana Goyette0b6e6402019-10-04 11:09:24 -0700339 if self.is_localhost():
340 # Local servod usually runs without log-dir, so can't be collected.
341 # TODO(crbug.com/1011516): allow localhost when rotation is enabled
342 return
343
344 log_dir = '/var/log/servod_%s' % self._servo_host.servo_port
345
346 if filename:
Dana Goyettec8613342019-11-13 15:03:28 -0800347 logging.info("Saving servod logs: %s/%s.*", directory or '.',
348 filename)
Dana Goyette0b6e6402019-10-04 11:09:24 -0700349 # TODO(crrev.com/c/1793030): remove no-level case once CL is pushed
350 for level_name in ('', 'DEBUG', 'INFO', 'WARNING'):
351
352 remote_path = os.path.join(log_dir, 'latest')
353 if level_name:
354 remote_path += '.%s' % level_name
355
356 local_path = '%s.%s' % (filename, level_name or 'log')
357 if directory:
358 local_path = os.path.join(directory, local_path)
359
360 try:
361 self._servo_host.get_file(
362 remote_path, local_path, try_rsync=False)
363
364 except error.AutoservRunError as e:
365 result = e.result_obj
366 if result.exit_status != 0:
367 stderr = result.stderr.strip()
368
369 # File not existing is okay, but warn for anything else.
370 if 'no such' not in stderr.lower():
371 logging.warn(
372 "Couldn't retrieve servod log: %s",
373 stderr or '\n%s' % result)
374
375 try:
376 if os.stat(local_path).st_size == 0:
377 os.unlink(local_path)
378 except EnvironmentError:
379 pass
380
381 else:
382 # No filename given, so caller wants to discard the log lines.
383 # Remove the symlinks to prevent old log-dir links from being
384 # picked up multiple times when using servod without log-dir.
385 remote_path = os.path.join(log_dir, 'latest*')
386 self._servo_host.run(
387 "rm %s" % remote_path,
388 stderr_tee=None, ignore_status=True)
389
390 # Servod log rotation renames current log, then creates a new file with
391 # the old name: log.<date> -> log.<date>.1.tbz2 -> log.<date>.2.tbz2
392
393 # Must rotate after copying, or the copy would be the new, empty file.
394 try:
395 self.set_nocheck('rotate_servod_logs', 'yes')
396 except ControlUnavailableError as e:
Dana Goyettec8613342019-11-13 15:03:28 -0800397 # Missing control (possibly old servod)
Dana Goyette0b6e6402019-10-04 11:09:24 -0700398 logging.warn("Couldn't rotate servod logs: %s", str(e))
Dana Goyettec8613342019-11-13 15:03:28 -0800399 except error.TestFail:
400 # Control exists but gave an error; don't let it fail the test.
401 # The error is already logged in set_nocheck().
402 pass
Ricky Liang0dd379c2014-04-23 16:29:08 +0800403
Tom Wai-Hong Tam22ee0e52013-04-03 13:36:39 +0800404 def get_power_state_controller(self):
J. Richard Barnette75136b32013-03-26 13:38:44 -0700405 """Return the power state controller for this Servo.
406
407 The power state controller provides board-independent
408 interfaces for reset, power-on, power-off operations.
409
410 """
411 return self._power_state
412
Fang Deng5d518f42013-08-02 14:04:32 -0700413
Mary Ruthvena3c52b82019-10-09 14:09:08 -0700414 def initialize_dut(self, cold_reset=False, enable_main=True):
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700415 """Initializes a dut for testing purposes.
416
417 This sets various servo signals back to default values
418 appropriate for the target board. By default, if the DUT
419 is already on, it stays on. If the DUT is powered off
420 before initialization, its state afterward is unspecified.
421
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700422 Rationale: Basic initialization of servo sets the lid open,
423 when there is a lid. This operation won't affect powered on
424 units; however, setting the lid open may power on a unit
J. Richard Barnette75136b32013-03-26 13:38:44 -0700425 that's off, depending on the board type and previous state
426 of the device.
427
J. Richard Barnette4b6af0d2014-06-05 09:57:20 -0700428 If `cold_reset` is a true value, the DUT and its EC will be
429 reset, and the DUT rebooted in normal mode.
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700430
431 @param cold_reset If True, cold reset the device after
432 initialization.
Mary Ruthvena3c52b82019-10-09 14:09:08 -0700433 @param enable_main If True, make sure the main servo device has
434 control of the dut.
435
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700436 """
Mary Ruthvena3c52b82019-10-09 14:09:08 -0700437 if enable_main:
438 self.enable_main_servo_device()
439
Dana Goyettea2f00ea2019-06-26 16:14:12 -0700440 try:
441 self._server.hwinit()
442 except socket.error as e:
443 e.filename = '%s:%s' % (self._servo_host.hostname,
444 self._servo_host.servo_port)
445 raise
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700446 self.set('usb_mux_oe1', 'on')
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700447 self._usb_state = None
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700448 self.switch_usbkey('off')
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700449 self._uart.start_capture()
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700450 if cold_reset:
J. Richard Barnette4b6af0d2014-06-05 09:57:20 -0700451 self._power_state.reset()
J. Richard Barnette1777f5d2014-09-03 15:18:19 -0700452 logging.debug('Servo initialized, version is %s',
453 self._server.get_version())
Ruben Rodriguez Buchillonb0e2a9f2019-08-12 12:40:44 -0700454 if self.has_control('init_keyboard'):
455 # This indicates the servod version does not
456 # have explicit keyboard initialization yet.
457 # Ignore this.
Ruben Rodriguez Buchillon05298242019-05-20 11:29:06 -0700458 # TODO(coconutruben): change this back to set() about a month
459 # after crrev.com/c/1586239 has been merged (or whenever that
460 # logic is in the labstation images).
461 self.set_nocheck('init_keyboard','on')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700462
463
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800464 def is_localhost(self):
465 """Is the servod hosted locally?
466
467 Returns:
468 True if local hosted; otherwise, False.
469 """
Fang Deng5d518f42013-08-02 14:04:32 -0700470 return self._servo_host.is_localhost()
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800471
472
Mary Ruthvenecf12712019-06-26 17:36:21 -0700473 def get_os_version(self):
474 """Returns the chromeos release version."""
475 lsb_release_content = self.system_output('cat /etc/lsb-release',
476 ignore_status=True)
477 return lsbrelease_utils.get_chromeos_release_builder_path(
478 lsb_release_content=lsb_release_content)
479
480
Mary Ruthven83bb5952019-06-27 12:34:05 -0700481 def get_servod_version(self):
482 """Returns the servod version."""
483 result = self._servo_host.run('servod --version')
484 # TODO: use system_output once servod --version prints to stdout
485 stdout = result.stdout.strip()
486 return stdout if stdout else result.stderr.strip()
487
488
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700489 def power_long_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700490 """Simulate a long power button press."""
J. Richard Barnettef12bff22012-07-18 14:41:06 -0700491 # After a long power press, the EC may ignore the next power
492 # button press (at least on Alex). To guarantee that this
493 # won't happen, we need to allow the EC one second to
494 # collect itself.
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800495 # long_press is defined as 8.5s in servod
496 self.set_nocheck('power_key', 'long_press')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700497
498
499 def power_normal_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700500 """Simulate a normal power button press."""
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800501 # press is defined as 1.2s in servod
502 self.set_nocheck('power_key', 'press')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700503
504
505 def power_short_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700506 """Simulate a short power button press."""
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800507 # tab is defined as 0.2s in servod
508 self.set_nocheck('power_key', 'tab')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700509
510
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800511 def power_key(self, press_secs='tab'):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700512 """Simulate a power button press.
513
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800514 @param press_secs: int, float, str; time to press key in seconds or
515 known shorthand: 'tab' 'press' 'long_press'
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700516 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800517 self.set_nocheck('power_key', press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700518
519
Ruben Zakarian47c1d4f2019-09-30 14:17:41 -0700520 def pwr_button(self, action='press'):
521 """Simulate a power button press.
522
523 @param action: str; could be press or could be release.
524 """
525 self.set_nocheck('pwr_button', action)
526
527
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700528 def lid_open(self):
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800529 """Simulate opening the lid and raise exception if all attempts fail"""
530 self.set('lid_open', 'yes')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700531
532
533 def lid_close(self):
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800534 """Simulate closing the lid and raise exception if all attempts fail
Craig Harrison48997262011-06-27 14:31:10 -0700535
536 Waits 6 seconds to ensure the device is fully asleep before returning.
537 """
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800538 self.set('lid_open', 'no')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700539 time.sleep(Servo.SLEEP_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700540
Ruben Zakarian47c1d4f2019-09-30 14:17:41 -0700541
542 def vbus_power_get(self):
543 """Get current vbus_power."""
544 return self.get('vbus_power')
545
546
Shelley Chenc26575a2015-09-18 10:56:16 -0700547 def volume_up(self, timeout=300):
Kevin Chenge448a8b2016-09-09 10:18:11 -0700548 """Simulate pushing the volume down button.
549
550 @param timeout: Timeout for setting the volume.
551 """
Shelley Chenc26575a2015-09-18 10:56:16 -0700552 self.set_get_all(['volume_up:yes',
553 'sleep:%.4f' % self.SERVO_KEY_PRESS_DELAY,
554 'volume_up:no'])
555 # we need to wait for commands to take effect before moving on
556 time_left = float(timeout)
557 while time_left > 0.0:
558 value = self.get('volume_up')
559 if value == 'no':
560 return
561 time.sleep(self.SHORT_DELAY)
562 time_left = time_left - self.SHORT_DELAY
563 raise error.TestFail("Failed setting volume_up to no")
564
565 def volume_down(self, timeout=300):
Kevin Chenge448a8b2016-09-09 10:18:11 -0700566 """Simulate pushing the volume down button.
567
568 @param timeout: Timeout for setting the volume.
569 """
Shelley Chenc26575a2015-09-18 10:56:16 -0700570 self.set_get_all(['volume_down:yes',
571 'sleep:%.4f' % self.SERVO_KEY_PRESS_DELAY,
572 'volume_down:no'])
573 # we need to wait for commands to take effect before moving on
574 time_left = float(timeout)
575 while time_left > 0.0:
576 value = self.get('volume_down')
577 if value == 'no':
578 return
579 time.sleep(self.SHORT_DELAY)
580 time_left = time_left - self.SHORT_DELAY
581 raise error.TestFail("Failed setting volume_down to no")
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700582
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800583 def ctrl_d(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800584 """Simulate Ctrl-d simultaneous button presses.
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800585
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800586 @param press_secs: int, float, str; time to press key in seconds or
587 known shorthand: 'tab' 'press' 'long_press'
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800588 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800589 self.set_nocheck('ctrl_d', press_secs)
Gediminas Ramanauskas9da80f22012-11-14 12:59:43 -0800590
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800591
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800592 def ctrl_u(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800593 """Simulate Ctrl-u simultaneous button presses.
594
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800595 @param press_secs: int, float, str; time to press key in seconds or
596 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800597 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800598 self.set_nocheck('ctrl_u', press_secs)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700599
600
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800601 def ctrl_enter(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800602 """Simulate Ctrl-enter simultaneous button presses.
603
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800604 @param press_secs: int, float, str; time to press key in seconds or
605 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800606 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800607 self.set_nocheck('ctrl_enter', press_secs)
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -0700608
609
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800610 def ctrl_key(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800611 """Simulate Enter key button press.
612
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800613 @param press_secs: int, float, str; time to press key in seconds or
614 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800615 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800616 self.set_nocheck('ctrl_key', press_secs)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700617
618
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800619 def enter_key(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800620 """Simulate Enter key button press.
621
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800622 @param press_secs: int, float, str; time to press key in seconds or
623 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800624 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800625 self.set_nocheck('enter_key', press_secs)
Todd Broch9753bd42012-03-21 10:15:08 -0700626
627
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800628 def refresh_key(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800629 """Simulate Refresh key (F3) button press.
630
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800631 @param press_secs: int, float, str; time to press key in seconds or
632 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800633 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800634 self.set_nocheck('refresh_key', press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700635
636
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800637 def ctrl_refresh_key(self, press_secs='tab'):
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800638 """Simulate Ctrl and Refresh (F3) simultaneous press.
639
640 This key combination is an alternative of Space key.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800641
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800642 @param press_secs: int, float, str; time to press key in seconds or
643 known shorthand: 'tab' 'press' 'long_press'
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800644 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800645 self.set_nocheck('ctrl_refresh_key', press_secs)
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800646
647
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800648 def imaginary_key(self, press_secs='tab'):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700649 """Simulate imaginary key button press.
650
651 Maps to a key that doesn't physically exist.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800652
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800653 @param press_secs: int, float, str; time to press key in seconds or
654 known shorthand: 'tab' 'press' 'long_press'
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700655 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800656 self.set_nocheck('imaginary_key', press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700657
658
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800659 def sysrq_x(self, press_secs='tab'):
Vincent Palatine7dc9282016-07-14 11:31:58 +0200660 """Simulate Alt VolumeUp X simulataneous press.
661
662 This key combination is the kernel system request (sysrq) X.
663
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800664 @param press_secs: int, float, str; time to press key in seconds or
665 known shorthand: 'tab' 'press' 'long_press'
Vincent Palatine7dc9282016-07-14 11:31:58 +0200666 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800667 self.set_nocheck('sysrq_x', press_secs)
Vincent Palatine7dc9282016-07-14 11:31:58 +0200668
669
Tom Wai-Hong Tam93b5c092015-05-14 02:50:43 +0800670 def toggle_recovery_switch(self):
671 """Toggle recovery switch on and off."""
672 self.enable_recovery_mode()
673 time.sleep(self.REC_TOGGLE_DELAY)
674 self.disable_recovery_mode()
675
676
Craig Harrison6b36b122011-06-28 17:58:43 -0700677 def enable_recovery_mode(self):
678 """Enable recovery mode on device."""
679 self.set('rec_mode', 'on')
680
681
682 def disable_recovery_mode(self):
683 """Disable recovery mode on device."""
684 self.set('rec_mode', 'off')
685
686
Tom Wai-Hong Tamf9ded092015-05-20 05:37:22 +0800687 def toggle_development_switch(self):
688 """Toggle development switch on and off."""
689 self.enable_development_mode()
690 time.sleep(self.DEV_TOGGLE_DELAY)
691 self.disable_development_mode()
692
693
Craig Harrison6b36b122011-06-28 17:58:43 -0700694 def enable_development_mode(self):
695 """Enable development mode on device."""
696 self.set('dev_mode', 'on')
697
698
699 def disable_development_mode(self):
700 """Disable development mode on device."""
701 self.set('dev_mode', 'off')
702
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700703 def boot_devmode(self):
704 """Boot a dev-mode device that is powered off."""
Tom Wai-Hong Tam23869102012-01-17 15:41:30 +0800705 self.power_short_press()
Craig Harrison48997262011-06-27 14:31:10 -0700706 self.pass_devmode()
707
708
709 def pass_devmode(self):
710 """Pass through boot screens in dev-mode."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700711 time.sleep(Servo.BOOT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700712 self.ctrl_d()
Chrome Bot9a1137d2011-07-19 14:35:00 -0700713 time.sleep(Servo.BOOT_DELAY)
Craig Harrison48997262011-06-27 14:31:10 -0700714
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700715
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800716 def get_board(self):
Wai-Hong Tam0f904002017-09-19 15:52:22 -0700717 """Get the board connected to servod."""
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800718 return self._server.get_board()
719
720
Wai-Hong Tam0f904002017-09-19 15:52:22 -0700721 def get_base_board(self):
722 """Get the board of the base connected to servod."""
Wai-Hong Tam7f6169e2017-09-29 09:23:48 -0700723 try:
724 return self._server.get_base_board()
725 except xmlrpclib.Fault as e:
726 # TODO(waihong): Remove the following compatibility check when
727 # the new versions of hdctools are deployed.
728 if 'not supported' in str(e):
729 logging.warning('The servod is too old that get_base_board '
730 'not supported.')
731 return ''
732 raise
Wai-Hong Tam0f904002017-09-19 15:52:22 -0700733
734
Wai-Hong Tamcc399ee2017-12-08 12:43:28 -0800735 def get_ec_active_copy(self):
736 """Get the active copy of the EC image."""
737 return self.get('ec_active_copy')
738
739
Todd Brochefe72cb2012-07-11 19:58:53 -0700740 def _get_xmlrpclib_exception(self, xmlexc):
741 """Get meaningful exception string from xmlrpc.
742
743 Args:
744 xmlexc: xmlrpclib.Fault object
745
746 xmlrpclib.Fault.faultString has the following format:
747
748 <type 'exception type'>:'actual error message'
749
750 Parse and return the real exception from the servod side instead of the
751 less meaningful one like,
752 <Fault 1: "<type 'exceptions.AttributeError'>:'tca6416' object has no
753 attribute 'hw_driver'">
754
755 Returns:
756 string of underlying exception raised in servod.
757 """
758 return re.sub('^.*>:', '', xmlexc.faultString)
759
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700760 def has_control(self, control):
761 """Query servod server to determine if |control| is a valid control.
762
763 @param control: str, control name to query
764
765 @returns: true if |control| is a known control, false otherwise.
766 """
767 assert control
768 try:
769 # If the control exists, doc() will work.
770 self._server.doc(control)
771 return True
772 except xmlrpclib.Fault as e:
773 if re.search('No control %s' % control,
774 self._get_xmlrpclib_exception(e)):
775 return False
776 raise e
Todd Brochefe72cb2012-07-11 19:58:53 -0700777
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700778 def get(self, gpio_name):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700779 """Get the value of a gpio from Servod.
780
781 @param gpio_name Name of the gpio.
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700782
783 @returns: server response to |gpio_name| request.
784
785 @raise ControlUnavailableError: if |gpio_name| not a known control.
786 @raise error.TestFail: for all other failures doing get().
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700787 """
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700788 assert gpio_name
Todd Brochefe72cb2012-07-11 19:58:53 -0700789 try:
Simran Basi1cbc5f22013-07-17 14:23:58 -0700790 return self._server.get(gpio_name)
Todd Brochefe72cb2012-07-11 19:58:53 -0700791 except xmlrpclib.Fault as e:
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700792 err_str = self._get_xmlrpclib_exception(e)
793 err_msg = "Getting '%s' :: %s" % (gpio_name, err_str)
794 unknown_ctrl = re.findall(NO_CONTROL_RE, err_str)
795 if unknown_ctrl:
796 raise ControlUnavailableError('No control named %r' %
797 unknown_ctrl[0])
798 else:
799 logging.error(err_msg)
800 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700801
802
803 def set(self, gpio_name, gpio_value):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700804 """Set and check the value of a gpio using Servod.
805
806 @param gpio_name Name of the gpio.
807 @param gpio_value New setting for the gpio.
Dana Goyette7ff06c92019-10-11 13:38:03 -0700808 @raise error.TestFail: if the control value fails to change.
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700809 """
Chrome Bot9a1137d2011-07-19 14:35:00 -0700810 self.set_nocheck(gpio_name, gpio_value)
Todd Brochcf7c6652012-02-24 13:03:59 -0800811 retry_count = Servo.GET_RETRY_MAX
Dana Goyette7ff06c92019-10-11 13:38:03 -0700812 actual_value = self.get(gpio_name)
813 while gpio_value != actual_value and retry_count:
Ilja H. Friedel04be2bd2014-05-07 21:29:59 -0700814 logging.warning("%s != %s, retry %d", gpio_name, gpio_value,
Dana Goyette7ff06c92019-10-11 13:38:03 -0700815 retry_count)
Todd Brochcf7c6652012-02-24 13:03:59 -0800816 retry_count -= 1
817 time.sleep(Servo.SHORT_DELAY)
Dana Goyette7ff06c92019-10-11 13:38:03 -0700818 actual_value = self.get(gpio_name)
819
820 if gpio_value != actual_value:
821 raise error.TestFail(
822 'Servo failed to set %s to %s. Got %s.'
823 % (gpio_name, gpio_value, actual_value))
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700824
825
826 def set_nocheck(self, gpio_name, gpio_value):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700827 """Set the value of a gpio using Servod.
828
829 @param gpio_name Name of the gpio.
830 @param gpio_value New setting for the gpio.
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700831
832 @raise ControlUnavailableError: if |gpio_name| not a known control.
833 @raise error.TestFail: for all other failures doing set().
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700834 """
Ruben Rodriguez Buchillon1c0219c2019-06-25 12:30:44 -0700835 # The real danger here is to pass a None value through the xmlrpc.
836 assert gpio_name and gpio_value is not None
Mary Ruthvendfa45342019-06-25 10:10:17 -0700837 logging.debug('Setting %s to %r', gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700838 try:
Simran Basi1cbc5f22013-07-17 14:23:58 -0700839 self._server.set(gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700840 except xmlrpclib.Fault as e:
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700841 err_str = self._get_xmlrpclib_exception(e)
842 err_msg = "Setting '%s' :: %s" % (gpio_name, err_str)
843 unknown_ctrl = re.findall(NO_CONTROL_RE, err_str)
844 if unknown_ctrl:
845 raise ControlUnavailableError('No control named %r' %
846 unknown_ctrl[0])
847 else:
848 logging.error(err_msg)
849 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700850
851
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800852 def set_get_all(self, controls):
853 """Set &| get one or more control values.
854
855 @param controls: list of strings, controls to set &| get.
856
857 @raise: error.TestError in case error occurs setting/getting values.
858 """
859 rv = []
860 try:
Mary Ruthvendfa45342019-06-25 10:10:17 -0700861 logging.debug('Set/get all: %s', str(controls))
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800862 rv = self._server.set_get_all(controls)
863 except xmlrpclib.Fault as e:
864 # TODO(waihong): Remove the following backward compatibility when
865 # the new versions of hdctools are deployed.
866 if 'not supported' in str(e):
867 logging.warning('The servod is too old that set_get_all '
868 'not supported. Use set and get instead.')
869 for control in controls:
870 if ':' in control:
871 (name, value) = control.split(':')
872 if name == 'sleep':
873 time.sleep(float(value))
874 else:
875 self.set_nocheck(name, value)
876 rv.append(True)
877 else:
878 rv.append(self.get(name))
879 else:
880 err_msg = "Problem with '%s' :: %s" % \
881 (controls, self._get_xmlrpclib_exception(e))
882 raise error.TestFail(err_msg)
883 return rv
884
885
Jon Salzc88e5b62011-11-30 14:38:54 +0800886 # TODO(waihong) It may fail if multiple servo's are connected to the same
887 # host. Should look for a better way, like the USB serial name, to identify
888 # the USB device.
Simran Basi741b5d42012-05-18 11:27:15 -0700889 # TODO(sbasi) Remove this code from autoserv once firmware tests have been
890 # updated.
Kevin Chenga22c4a82016-10-07 14:13:25 -0700891 def probe_host_usb_dev(self, timeout=_USB_PROBE_TIMEOUT):
Jon Salzc88e5b62011-11-30 14:38:54 +0800892 """Probe the USB disk device plugged-in the servo from the host side.
893
Kevin Chengeb06fe72016-08-22 15:26:32 -0700894 It uses servod to discover if there is a usb device attached to it.
Jon Salzc88e5b62011-11-30 14:38:54 +0800895
Kevin Chenga22c4a82016-10-07 14:13:25 -0700896 @param timeout The timeout period when probing for the usb host device.
897
898 @return: String of USB disk path (e.g. '/dev/sdb') or None.
Jon Salzc88e5b62011-11-30 14:38:54 +0800899 """
Ruben Rodriguez Buchillon9a4bfc32018-10-16 20:46:25 +0800900 # Set up Servo's usb mux.
901 self.switch_usbkey('host')
Kevin Chenga22c4a82016-10-07 14:13:25 -0700902 return self._server.probe_host_usb_dev(timeout) or None
Jon Salzc88e5b62011-11-30 14:38:54 +0800903
904
Mike Truty49153d82012-08-21 22:27:30 -0500905 def image_to_servo_usb(self, image_path=None,
906 make_image_noninteractive=False):
907 """Install an image to the USB key plugged into the servo.
908
909 This method may copy any image to the servo USB key including a
910 recovery image or a test image. These images are frequently used
911 for test purposes such as restoring a corrupted image or conducting
912 an upgrade of ec/fw/kernel as part of a test of a specific image part.
913
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700914 @param image_path Path on the host to the recovery image.
915 @param make_image_noninteractive Make the recovery image
916 noninteractive, therefore the DUT
917 will reboot automatically after
918 installation.
Mike Truty49153d82012-08-21 22:27:30 -0500919 """
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700920 # We're about to start plugging/unplugging the USB key. We
921 # don't know the state of the DUT, or what it might choose
922 # to do to the device after hotplug. To avoid surprises,
923 # force the DUT to be off.
924 self._server.hwinit()
Ruben Rodriguez Buchillonb0e2a9f2019-08-12 12:40:44 -0700925 if self.has_control('init_keyboard'):
926 # This indicates the servod version does not
927 # have explicit keyboard initialization yet.
928 # Ignore this.
Ruben Rodriguez Buchillonbd3b74f2019-08-08 16:18:27 -0700929 # TODO(coconutruben): change this back to set() about a month
930 # after crrev.com/c/1586239 has been merged (or whenever that
931 # logic is in the labstation images).
932 self.set_nocheck('init_keyboard','on')
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700933 self._power_state.power_off()
Mike Truty49153d82012-08-21 22:27:30 -0500934
Mike Truty49153d82012-08-21 22:27:30 -0500935 if image_path:
Garry Wangd2656812019-08-07 17:29:16 -0700936 # Set up Servo's usb mux.
937 self.switch_usbkey('host')
Tom Wai-Hong Tam42f136d2012-10-26 11:11:23 +0800938 logging.info('Searching for usb device and copying image to it. '
939 'Please wait a few minutes...')
Mike Truty49153d82012-08-21 22:27:30 -0500940 if not self._server.download_image_to_usb(image_path):
941 logging.error('Failed to transfer requested image to USB. '
942 'Please take a look at Servo Logs.')
943 raise error.AutotestError('Download image to usb failed.')
944 if make_image_noninteractive:
945 logging.info('Making image noninteractive')
946 if not self._server.make_image_noninteractive():
947 logging.error('Failed to make image noninteractive. '
948 'Please take a look at Servo Logs.')
949
Kalin Stoyanovb3c11f32018-05-11 09:02:00 -0700950 def boot_in_recovery_mode(self):
951 """Boot host DUT in recovery mode."""
952 self._power_state.power_on(rec_mode=self._power_state.REC_ON)
953 self.switch_usbkey('dut')
954
Mike Truty49153d82012-08-21 22:27:30 -0500955
Simran Basi741b5d42012-05-18 11:27:15 -0700956 def install_recovery_image(self, image_path=None,
J. Richard Barnetteb6de7e32013-02-14 13:28:04 -0800957 make_image_noninteractive=False):
Dan Shic67f1332016-04-06 12:38:06 -0700958 """Install the recovery image specified by the path onto the DUT.
Jon Salzc88e5b62011-11-30 14:38:54 +0800959
960 This method uses google recovery mode to install a recovery image
961 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 +0800962 board specified by the usb_dev. If no image path is specified
Jon Salzc88e5b62011-11-30 14:38:54 +0800963 we use the recovery image already on the usb image.
964
Dan Shic67f1332016-04-06 12:38:06 -0700965 @param image_path: Path on the host to the recovery image.
966 @param make_image_noninteractive: Make the recovery image
967 noninteractive, therefore the DUT will reboot automatically
968 after installation.
Jon Salzc88e5b62011-11-30 14:38:54 +0800969 """
Mike Truty49153d82012-08-21 22:27:30 -0500970 self.image_to_servo_usb(image_path, make_image_noninteractive)
Garry Wangd2656812019-08-07 17:29:16 -0700971 # Give the DUT some time to power_off if we skip
972 # download image to usb. (crbug.com/982993)
973 if not image_path:
974 time.sleep(10)
Kalin Stoyanovb3c11f32018-05-11 09:02:00 -0700975 self.boot_in_recovery_mode()
Jon Salzc88e5b62011-11-30 14:38:54 +0800976
977
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800978 def _scp_image(self, image_path):
979 """Copy image to the servo host.
980
981 When programming a firmware image on the DUT, the image must be
982 located on the host to which the servo device is connected. Sometimes
983 servo is controlled by a remote host, in this case the image needs to
Dana Goyetted5a95542019-12-30 11:14:14 -0800984 be transferred to the remote host. This adds the servod port number, to
985 make sure tests for different DUTs don't trample on each other's files.
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800986
987 @param image_path: a string, name of the firmware image file to be
988 transferred.
989 @return: a string, full path name of the copied file on the remote.
990 """
Mary Ruthven401a6012019-12-13 11:39:44 -0800991 name = os.path.basename(image_path)
Dana Goyetted5a95542019-12-30 11:14:14 -0800992 remote_name = 'dut_%s.%s' % (self._servo_host.servo_port, name)
Mary Ruthven401a6012019-12-13 11:39:44 -0800993 dest_path = os.path.join('/tmp', remote_name)
994 logging.info('Copying %s to %s', name, dest_path)
Fang Deng5d518f42013-08-02 14:04:32 -0700995 self._servo_host.send_file(image_path, dest_path)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800996 return dest_path
997
998
Dan Shifecdaf42015-07-28 10:17:26 -0700999 def system(self, command, timeout=3600):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -07001000 """Execute the passed in command on the servod host.
1001
1002 @param command Command to be executed.
Dan Shifecdaf42015-07-28 10:17:26 -07001003 @param timeout Maximum number of seconds of runtime allowed. Default to
1004 1 hour.
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -07001005 """
Vadim Bendebury89ec24e2012-12-17 12:54:18 -08001006 logging.info('Will execute on servo host: %s', command)
Fang Deng5d518f42013-08-02 14:04:32 -07001007 self._servo_host.run(command, timeout=timeout)
Vadim Bendebury89ec24e2012-12-17 12:54:18 -08001008
1009
Dan Shifecdaf42015-07-28 10:17:26 -07001010 def system_output(self, command, timeout=3600,
Vadim Bendebury89ec24e2012-12-17 12:54:18 -08001011 ignore_status=False, args=()):
1012 """Execute the passed in command on the servod host, return stdout.
1013
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -07001014 @param command a string, the command to execute
1015 @param timeout an int, max number of seconds to wait til command
Dan Shifecdaf42015-07-28 10:17:26 -07001016 execution completes. Default to 1 hour.
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -07001017 @param ignore_status a Boolean, if true - ignore command's nonzero exit
Vadim Bendebury89ec24e2012-12-17 12:54:18 -08001018 status, otherwise an exception will be thrown
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -07001019 @param args a tuple of strings, each becoming a separate command line
Vadim Bendebury89ec24e2012-12-17 12:54:18 -08001020 parameter for the command
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -07001021 @return command's stdout as a string.
Vadim Bendebury89ec24e2012-12-17 12:54:18 -08001022 """
Fang Deng5d518f42013-08-02 14:04:32 -07001023 return self._servo_host.run(command, timeout=timeout,
1024 ignore_status=ignore_status,
1025 args=args).stdout.strip()
Vadim Bendeburyb80ba592012-12-07 15:02:34 -08001026
1027
Mary Ruthven38d90af2019-08-16 13:13:31 -07001028 def get_servo_version(self, active=False):
Dan Shia5fef052015-05-18 23:28:47 -07001029 """Get the version of the servo, e.g., servo_v2 or servo_v3.
1030
Mary Ruthven38d90af2019-08-16 13:13:31 -07001031 @param active: Only return the servo type with the active device.
Dan Shia5fef052015-05-18 23:28:47 -07001032 @return: The version of the servo.
1033
1034 """
Mary Ruthven38d90af2019-08-16 13:13:31 -07001035 servo_type = self._server.get_version()
1036 if '_and_' not in servo_type or not active:
1037 return servo_type
1038
1039 # If servo v4 is using ccd and servo micro, modify the servo type to
1040 # reflect the active device.
1041 active_device = self.get('active_v4_device')
1042 if active_device in servo_type:
1043 logging.info('%s is active', active_device)
1044 return 'servo_v4_with_' + active_device
1045
1046 logging.warn("%s is active even though it's not in servo type",
1047 active_device)
1048 return servo_type
Dan Shia5fef052015-05-18 23:28:47 -07001049
1050
Mary Ruthvena3c52b82019-10-09 14:09:08 -07001051 def get_main_servo_device(self):
1052 """Return the main servo device"""
1053 servo_type = self.get_servo_version()
1054 return servo_type.split('_with_')[-1].split('_and_')[0]
1055
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
Dan Shia5fef052015-05-18 23:28:47 -07001090 servo_version = self.get_servo_version()
Dan Shi9cb0eec2014-06-03 09:04:50 -07001091 if servo_version.startswith('servo_v2'):
J. Richard Barnette8f19b392014-08-11 14:05:39 -07001092 self._programmer = firmware_programmer.ProgrammerV2(self)
Wai-Hong Tamc36c4d22016-09-09 10:39:45 -07001093 self._programmer_rw = firmware_programmer.ProgrammerV2RwOnly(self)
Kevin Chengdf2e29f2016-09-09 02:31:22 -07001094 # Both servo v3 and v4 use the same programming methods so just leverage
1095 # ProgrammerV3 for servo v4 as well.
1096 elif (servo_version.startswith('servo_v3') or
1097 servo_version.startswith('servo_v4')):
Dan Shia5fef052015-05-18 23:28:47 -07001098 self._programmer = firmware_programmer.ProgrammerV3(self)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001099 self._programmer_rw = firmware_programmer.ProgrammerV3RwOnly(self)
J. Richard Barnette8f19b392014-08-11 14:05:39 -07001100 else:
1101 raise error.TestError(
1102 'No firmware programmer for servo version: %s' %
Congbin Guo42427612019-02-12 10:22:06 -08001103 servo_version)
J. Richard Barnette8f19b392014-08-11 14:05:39 -07001104
1105
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001106 def program_bios(self, image, rw_only=False):
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -08001107 """Program bios on DUT with given image.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -08001108
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -08001109 @param image: a string, file name of the BIOS image to program
1110 on the DUT.
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001111 @param rw_only: True to only program the RW portion of BIOS.
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -08001112
Vadim Bendebury1a7ec632013-02-17 16:22:09 -08001113 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -07001114 self._initialize_programmer()
Ricky Liangc31aab32014-07-03 16:23:29 +08001115 if not self.is_localhost():
1116 image = self._scp_image(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001117 if rw_only:
1118 self._programmer_rw.program_bios(image)
1119 else:
1120 self._programmer.program_bios(image)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -08001121
1122
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001123 def program_ec(self, image, rw_only=False):
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -08001124 """Program ec on DUT with given image.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -08001125
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -08001126 @param image: a string, file name of the EC image to program
1127 on the DUT.
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001128 @param rw_only: True to only program the RW portion of EC.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -08001129
Vadim Bendebury1a7ec632013-02-17 16:22:09 -08001130 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -07001131 self._initialize_programmer()
Ricky Liangc31aab32014-07-03 16:23:29 +08001132 if not self.is_localhost():
1133 image = self._scp_image(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001134 if rw_only:
Congbin Guo42427612019-02-12 10:22:06 -08001135 self._programmer_rw.program_ec(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001136 else:
Congbin Guo42427612019-02-12 10:22:06 -08001137 self._programmer.program_ec(image)
1138
1139
Brent Peterson1cb623a2020-01-09 13:14:28 -08001140 def extract_ec_image(self, board, model, tarball_path):
1141 """Helper function to extract EC image from downloaded tarball.
Congbin Guo42427612019-02-12 10:22:06 -08001142
Shelley Chenac61d5a2019-06-24 15:35:46 -07001143 @param board: The DUT board name.
Congbin Guo42427612019-02-12 10:22:06 -08001144 @param model: The DUT model name.
1145 @param tarball_path: The path of the downloaded build tarball.
Justin TerAvest1d0c1bb2019-12-13 11:53:25 -07001146
Brent Peterson1cb623a2020-01-09 13:14:28 -08001147 @return: Path to extracted EC image.
1148 """
Justin TerAvest1d0c1bb2019-12-13 11:53:25 -07001149 # Best effort; try to retrieve the EC board from the version as
1150 # reported by the EC.
1151 ec_board = None
1152 try:
1153 ec_board = self.get('ec_board')
1154 except Exception as err:
1155 logging.info('Failed to get ec_board value; ignoring')
1156 pass
1157
Brent Peterson1cb623a2020-01-09 13:14:28 -08001158 # Array of candidates for EC image
1159 ec_image_candidates = ['ec.bin',
1160 '%s/ec.bin' % model,
Justin TerAvest1d0c1bb2019-12-13 11:53:25 -07001161 '%s/ec.bin' % board]
1162 if ec_board:
1163 ec_image_candidates.append('%s/ec.bin' % ec_board)
Congbin Guo42427612019-02-12 10:22:06 -08001164
Brent Peterson1cb623a2020-01-09 13:14:28 -08001165 # Extract EC image from tarball
1166 dest_dir = os.path.join(os.path.dirname(tarball_path), 'EC')
1167 ec_image = _extract_image_from_tarball(tarball_path, dest_dir,
1168 ec_image_candidates)
Congbin Guo42427612019-02-12 10:22:06 -08001169
Brent Peterson1cb623a2020-01-09 13:14:28 -08001170 # Return path to EC image
1171 return os.path.join(dest_dir, ec_image)
1172
1173
1174 def extract_bios_image(self, board, model, tarball_path):
1175 """Helper function to extract BIOS image from downloaded tarball.
1176
1177 @param board: The DUT board name.
1178 @param model: The DUT model name.
1179 @param tarball_path: The path of the downloaded build tarball.
1180
1181 @return: Path to extracted BIOS image.
1182 """
1183
1184 # Array of candidates for BIOS image
1185 bios_image_candidates = ['image.bin',
1186 'image-%s.bin' % model,
1187 'image-%s.bin' % board]
1188
1189 # Extract BIOS image from tarball
1190 dest_dir = os.path.join(os.path.dirname(tarball_path), 'BIOS')
1191 bios_image = _extract_image_from_tarball(tarball_path, dest_dir,
1192 bios_image_candidates)
1193
1194 # Return path to BIOS image
1195 return os.path.join(dest_dir, bios_image)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001196
Fang Dengafb88142013-05-30 17:44:31 -07001197
1198 def _switch_usbkey_power(self, power_state, detection_delay=False):
1199 """Switch usbkey power.
1200
1201 This function switches usbkey power by setting the value of
1202 'prtctl4_pwren'. If the power is already in the
1203 requested state, this function simply returns.
1204
1205 @param power_state: A string, 'on' or 'off'.
1206 @param detection_delay: A boolean value, if True, sleep
1207 for |USB_DETECTION_DELAY| after switching
1208 the power on.
1209 """
Kevin Chenga22c4a82016-10-07 14:13:25 -07001210 # TODO(kevcheng): Forgive me for this terrible hack. This is just to
1211 # handle beaglebones that haven't yet updated and have the
1212 # safe_switch_usbkey_power RPC. I'll remove this once all beaglebones
1213 # have been updated and also think about a better way to handle
1214 # situations like this.
1215 try:
1216 self._server.safe_switch_usbkey_power(power_state)
1217 except Exception:
1218 self.set('prtctl4_pwren', power_state)
Fang Dengafb88142013-05-30 17:44:31 -07001219 if power_state == 'off':
1220 time.sleep(self.USB_POWEROFF_DELAY)
1221 elif detection_delay:
1222 time.sleep(self.USB_DETECTION_DELAY)
1223
1224
1225 def switch_usbkey(self, usb_state):
1226 """Connect USB flash stick to either host or DUT, or turn USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001227
1228 This function switches the servo multiplexer to provide electrical
Fang Dengafb88142013-05-30 17:44:31 -07001229 connection between the USB port J3 and either host or DUT side. It
1230 can also be used to turn the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001231
Fang Dengafb88142013-05-30 17:44:31 -07001232 Switching to 'dut' or 'host' is accompanied by powercycling
1233 of the USB stick, because it sometimes gets wedged if the mux
1234 is switched while the stick power is on.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001235
Fang Dengafb88142013-05-30 17:44:31 -07001236 @param usb_state: A string, one of 'dut', 'host', or 'off'.
1237 'dut' and 'host' indicate which side the
1238 USB flash device is required to be connected to.
1239 'off' indicates turning the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001240
Fang Dengafb88142013-05-30 17:44:31 -07001241 @raise: error.TestError in case the parameter is not 'dut'
1242 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001243 """
Fang Dengafb88142013-05-30 17:44:31 -07001244 if self.get_usbkey_direction() == usb_state:
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001245 return
1246
Fang Dengafb88142013-05-30 17:44:31 -07001247 if usb_state == 'off':
Dan Shia5fef052015-05-18 23:28:47 -07001248 self._switch_usbkey_power('off')
1249 self._usb_state = usb_state
1250 return
Fang Dengafb88142013-05-30 17:44:31 -07001251 elif usb_state == 'host':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001252 mux_direction = 'servo_sees_usbkey'
Fang Dengafb88142013-05-30 17:44:31 -07001253 elif usb_state == 'dut':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001254 mux_direction = 'dut_sees_usbkey'
1255 else:
Fang Dengafb88142013-05-30 17:44:31 -07001256 raise error.TestError('Unknown USB state request: %s' % usb_state)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001257
Fang Dengafb88142013-05-30 17:44:31 -07001258 self._switch_usbkey_power('off')
Kevin Chenga22c4a82016-10-07 14:13:25 -07001259 # TODO(kevcheng): Forgive me for this terrible hack. This is just to
1260 # handle beaglebones that haven't yet updated and have the
1261 # safe_switch_usbkey RPC. I'll remove this once all beaglebones have
1262 # been updated and also think about a better way to handle situations
1263 # like this.
1264 try:
1265 self._server.safe_switch_usbkey(mux_direction)
1266 except Exception:
1267 self.set('usb_mux_sel1', mux_direction)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001268 time.sleep(self.USB_POWEROFF_DELAY)
Fang Dengafb88142013-05-30 17:44:31 -07001269 self._switch_usbkey_power('on', usb_state == 'host')
1270 self._usb_state = usb_state
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001271
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001272
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001273 def get_usbkey_direction(self):
Fang Dengafb88142013-05-30 17:44:31 -07001274 """Get which side USB is connected to or 'off' if usb power is off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001275
Fang Dengafb88142013-05-30 17:44:31 -07001276 @return: A string, one of 'dut', 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001277 """
Fang Dengafb88142013-05-30 17:44:31 -07001278 if not self._usb_state:
1279 if self.get('prtctl4_pwren') == 'off':
1280 self._usb_state = 'off'
1281 elif self.get('usb_mux_sel1').startswith('dut'):
1282 self._usb_state = 'dut'
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001283 else:
Fang Dengafb88142013-05-30 17:44:31 -07001284 self._usb_state = 'host'
1285 return self._usb_state
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001286
1287
Wai-Hong Tam60377262018-03-01 10:55:39 -08001288 def set_servo_v4_role(self, role):
1289 """Set the power role of servo v4, either 'src' or 'snk'.
1290
1291 It does nothing if not a servo v4.
1292
1293 @param role: Power role for DUT port on servo v4, either 'src' or 'snk'.
1294 """
1295 servo_version = self.get_servo_version()
1296 if servo_version.startswith('servo_v4'):
1297 value = self.get('servo_v4_role')
1298 if value != role:
1299 self.set_nocheck('servo_v4_role', role)
1300 else:
1301 logging.debug('Already in the role: %s.', role)
1302 else:
1303 logging.debug('Not a servo v4, unable to set role to %s.', role)
1304
1305
Ruben Rodriguez Buchillon5f7ee682019-09-30 12:50:09 -07001306 def supports_built_in_pd_control(self):
1307 """Return whether the servo type supports pd charging and control."""
1308 servo_type = self.get('servo_type')
1309 if 'servo_v4' not in servo_type:
1310 # Only servo v4 supports this feature.
1311 logging.info('%r type does not support pd control.', servo_type)
1312 return False
1313 # On servo v4, it still needs ot be the type-c version.
1314 if not self.get('servo_v4_type') == 'type-c':
1315 logging.info('PD controls require a type-c servo v4.')
1316 return False
1317 # Lastly, one cannot really do anything without a plugged in charger.
1318 chg_port_mv = self.get('ppchg5_mv')
1319 if chg_port_mv < V4_CHG_ATTACHED_MIN_VOLTAGE_MV:
1320 logging.warn('It appears that no charger is plugged into servo v4. '
1321 'Charger port voltage: %dmV', chg_port_mv)
1322 return False
1323 logging.info('Charger port voltage: %dmV', chg_port_mv)
1324 return True
1325
Mary Ruthven739b2922019-08-22 11:16:06 -07001326 def set_servo_v4_dts_mode(self, state):
1327 """Set servo v4 dts mode to off or on.
1328
1329 It does nothing if not a servo v4. Disable the ccd watchdog if we're
1330 disabling dts mode. CCD will disconnect. The watchdog only allows CCD
1331 to disconnect for 10 seconds until it kills servod. Disable the
1332 watchdog, so CCD can stay disconnected indefinitely.
1333
1334 @param state: Set servo v4 dts mode 'off' or 'on'.
1335 """
1336 servo_version = self.get_servo_version()
1337 if not servo_version.startswith('servo_v4'):
1338 logging.debug('Not a servo v4, unable to set dts mode %s.', state)
1339 return
1340
1341 # TODO(mruthven): remove watchdog check once the labstation has been
1342 # updated to have support for modifying the watchdog.
1343 set_watchdog = self.has_control('watchdog') and 'ccd' in servo_version
1344 enable_watchdog = state == 'on'
1345
1346 if set_watchdog and not enable_watchdog:
1347 self.set_nocheck('watchdog_remove', 'ccd')
1348
1349 self.set_nocheck('servo_v4_dts_mode', state)
1350
1351 if set_watchdog and enable_watchdog:
1352 self.set_nocheck('watchdog_add', 'ccd')
1353
1354
Congbin Guofc3b8962019-03-22 17:38:46 -07001355 @property
1356 def uart_logs_dir(self):
1357 """Return the directory to save UART logs."""
1358 return self._uart.logs_dir if self._uart else ""
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001359
Congbin Guofc3b8962019-03-22 17:38:46 -07001360
1361 @uart_logs_dir.setter
1362 def uart_logs_dir(self, logs_dir):
1363 """Set directory to save UART logs.
1364
1365 @param logs_dir String of directory name."""
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001366 if self._uart:
Congbin Guofc3b8962019-03-22 17:38:46 -07001367 self._uart.logs_dir = logs_dir
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001368
1369
1370 def close(self):
1371 """Close the servo object."""
1372 if self._uart:
1373 self._uart.stop_capture()
Congbin Guofc3b8962019-03-22 17:38:46 -07001374 self._uart.dump()
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001375 self._uart = None