blob: 1be3fed702d057ad1ca387ff1a99925d21f02917 [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
Mary Ruthven401a6012019-12-13 11:39:44 -080011import random
Congbin Guoa1f9cba2018-07-03 11:36:59 -070012import re
Dana Goyettea2f00ea2019-06-26 16:14:12 -070013import socket
Mary Ruthven401a6012019-12-13 11:39:44 -080014import string
Congbin Guoa1f9cba2018-07-03 11:36:59 -070015import time
16import xmlrpclib
Vadim Bendeburyb80ba592012-12-07 15:02:34 -080017
Simran Basi741b5d42012-05-18 11:27:15 -070018from autotest_lib.client.common_lib import error
Mary Ruthvenecf12712019-06-26 17:36:21 -070019from autotest_lib.client.common_lib import lsbrelease_utils
Congbin Guo42427612019-02-12 10:22:06 -080020from autotest_lib.server import utils as server_utils
Ricky Liangc31aab32014-07-03 16:23:29 +080021from autotest_lib.server.cros.servo import firmware_programmer
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070022
Kevin Chenga22c4a82016-10-07 14:13:25 -070023# Time to wait when probing for a usb device, it takes on avg 17 seconds
24# to do a full probe.
25_USB_PROBE_TIMEOUT = 40
26
J. Richard Barnette41320ee2013-03-11 13:00:13 -070027
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -070028# Regex to match XMLRPC errors due to a servod control not existing.
29NO_CONTROL_RE = re.compile(r'No control named (\w*\.?\w*)')
30
Ruben Rodriguez Buchillon5f7ee682019-09-30 12:50:09 -070031
32# The minimum voltage on the charger port on servo v4 that is expected. This is
33# to query whether a charger is plugged into servo v4 and thus pd control
34# capabilities can be used.
35V4_CHG_ATTACHED_MIN_VOLTAGE_MV = 4400
36
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -070037class ControlUnavailableError(error.TestFail):
38 """Custom error class to indicate a control is unavailable on servod."""
39 pass
40
41
Congbin Guo42427612019-02-12 10:22:06 -080042def _extract_image_from_tarball(tarball, dest_dir, image_candidates):
43 """Try extracting the image_candidates from the tarball.
44
45 @param tarball: The path of the tarball.
46 @param dest_path: The path of the destination.
47 @param image_candidates: A tuple of the paths of image candidates.
48
49 @return: The first path from the image candidates, which succeeds, or None
50 if all the image candidates fail.
51 """
52 for image in image_candidates:
53 status = server_utils.system(
54 ('tar xf %s -C %s %s' % (tarball, dest_dir, image)),
55 timeout=60, ignore_status=True)
56 if status == 0:
57 return image
58 return None
59
60
J. Richard Barnette0c9c5882014-06-11 15:27:05 -070061class _PowerStateController(object):
62
63 """Class to provide board-specific power operations.
64
65 This class is responsible for "power on" and "power off"
66 operations that can operate without making assumptions in
67 advance about board state. It offers an interface that
68 abstracts out the different sequences required for different
69 board types.
70
71 """
72
73 # Constants acceptable to be passed for the `rec_mode` parameter
74 # to power_on().
75 #
76 # REC_ON: Boot the DUT in recovery mode, i.e. boot from USB or
77 # SD card.
78 # REC_OFF: Boot in normal mode, i.e. boot from internal storage.
79
80 REC_ON = 'rec'
81 REC_OFF = 'on'
Shelley Chen65938622018-05-16 07:45:54 -070082 REC_ON_FORCE_MRC = 'rec_force_mrc'
J. Richard Barnette0c9c5882014-06-11 15:27:05 -070083
84 # Delay in seconds needed between asserting and de-asserting
85 # warm reset.
86 _RESET_HOLD_TIME = 0.5
87
88 def __init__(self, servo):
89 """Initialize the power state control.
90
91 @param servo Servo object providing the underlying `set` and `get`
92 methods for the target controls.
93
94 """
95 self._servo = servo
96
97 def reset(self):
98 """Force the DUT to reset.
99
100 The DUT is guaranteed to be on at the end of this call,
101 regardless of its previous state, provided that there is
102 working OS software. This also guarantees that the EC has
103 been restarted.
104
105 """
106 self._servo.set_nocheck('power_state', 'reset')
107
108 def warm_reset(self):
109 """Apply warm reset to the DUT.
110
111 This asserts, then de-asserts the 'warm_reset' signal.
112 Generally, this causes the board to restart.
113
114 """
Ravutappa951ca432019-05-15 09:52:52 -0700115 # TODO: warm_reset support has added to power_state.py. Once it
116 # available to labstation remove fallback method.
117 try:
118 self._servo.set_nocheck('power_state', 'warm_reset')
119 except error.TestFail as err:
120 logging.info("Fallback to warm_reset control method")
121 self._servo.set_get_all(['warm_reset:on',
J. Richard Barnette0c9c5882014-06-11 15:27:05 -0700122 'sleep:%.4f' % self._RESET_HOLD_TIME,
123 'warm_reset:off'])
124
J. Richard Barnette0c9c5882014-06-11 15:27:05 -0700125 def power_off(self):
126 """Force the DUT to power off.
127
128 The DUT is guaranteed to be off at the end of this call,
129 regardless of its previous state, provided that there is
130 working EC and boot firmware. There is no requirement for
131 working OS software.
132
133 """
134 self._servo.set_nocheck('power_state', 'off')
135
136 def power_on(self, rec_mode=REC_OFF):
137 """Force the DUT to power on.
138
139 Prior to calling this function, the DUT must be powered off,
140 e.g. with a call to `power_off()`.
141
142 At power on, recovery mode is set as specified by the
143 corresponding argument. When booting with recovery mode on, it
144 is the caller's responsibility to unplug/plug in a bootable
145 external storage device.
146
147 If the DUT requires a delay after powering on but before
148 processing inputs such as USB stick insertion, the delay is
149 handled by this method; the caller is not responsible for such
150 delays.
151
152 @param rec_mode Setting of recovery mode to be applied at
153 power on. default: REC_OFF aka 'off'
154
155 """
156 self._servo.set_nocheck('power_state', rec_mode)
157
158
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700159class _Uart(object):
Congbin Guo0f00be82019-04-18 17:51:14 -0700160 """Class to capture UART streams of CPU, EC, Cr50, etc."""
161 _UartToCapture = ('cpu', 'ec', 'cr50', 'servo_v4', 'servo_micro', 'usbpd')
162
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700163 def __init__(self, servo):
164 self._servo = servo
165 self._streams = []
Congbin Guo0f00be82019-04-18 17:51:14 -0700166 self.logs_dir = None
Congbin Guofe4d4e82019-04-18 17:51:14 -0700167
Congbin Guo0f00be82019-04-18 17:51:14 -0700168 def _start_stop_capture(self, uart, start):
169 """Helper function to start/stop capturing on specified UART.
170
171 @param uart: The UART name to start/stop capturing.
172 @param start: True to start capturing, otherwise stop.
173
Ruben Rodriguez Buchillonecb525f2019-09-03 14:52:14 -0700174 @returns True if the operation completes successfully.
175 False if the UART capturing is not supported or failed due to
176 an error.
Congbin Guo0f00be82019-04-18 17:51:14 -0700177 """
178 logging.debug('%s capturing %s UART.', 'Start' if start else 'Stop',
179 uart)
Ruben Rodriguez Buchillonb0e2a9f2019-08-12 12:40:44 -0700180 uart_cmd = '%s_uart_capture' % uart
Ruben Rodriguez Buchillonecb525f2019-09-03 14:52:14 -0700181 target_level = 'on' if start else 'off'
182 level = None
Ruben Rodriguez Buchillonb0e2a9f2019-08-12 12:40:44 -0700183 if self._servo.has_control(uart_cmd):
Ruben Rodriguez Buchillon9f485642019-08-21 14:32:22 -0700184 # Do our own implementation of set() here as not_applicable
185 # should also count as a valid control.
Ruben Rodriguez Buchillonecb525f2019-09-03 14:52:14 -0700186 logging.debug('Trying to set %s to %s.', uart_cmd, target_level)
187 try:
188 self._servo.set_nocheck(uart_cmd, target_level)
189 level = self._servo.get(uart_cmd)
190 except error.TestFail as e:
191 # Any sort of test failure here should not stop the test. This
192 # is just to capture more output. Log and move on.
193 logging.warning('Failed to set %s to %s. %s. Ignoring.',
194 uart_cmd, target_level, str(e))
195 if level == target_level:
196 logging.debug('Managed to set %s to %s.', uart_cmd, level)
197 else:
198 logging.debug('Failed to set %s to %s. Got %s.', uart_cmd,
199 target_level, level)
200 return level == target_level
Congbin Guo0f00be82019-04-18 17:51:14 -0700201
202 def start_capture(self):
203 """Start capturing UART streams."""
204 for uart in self._UartToCapture:
205 if self._start_stop_capture(uart, True):
206 self._streams.append(('%s_uart_stream' % uart, '%s_uart.log' %
207 uart))
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700208
Congbin Guofc3b8962019-03-22 17:38:46 -0700209 def dump(self):
210 """Dump UART streams to log files accordingly."""
Congbin Guo0f00be82019-04-18 17:51:14 -0700211 if not self.logs_dir:
Congbin Guofc3b8962019-03-22 17:38:46 -0700212 return
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700213
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700214 for stream, logfile in self._streams:
Congbin Guo0f00be82019-04-18 17:51:14 -0700215 logfile_fullname = os.path.join(self.logs_dir, logfile)
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700216 try:
217 content = self._servo.get(stream)
218 except Exception as err:
219 logging.warn('Failed to get UART log for %s: %s', stream, err)
220 continue
221
Kuang-che Wu12192fc2019-08-31 09:55:00 +0800222 if content == 'not_applicable':
223 logging.warn('%s is not applicable', stream)
224 continue
225
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700226 # The UART stream may contain non-printable characters, and servo
227 # returns it in string representation. We use `ast.leteral_eval`
228 # to revert it back.
229 with open(logfile_fullname, 'a') as fd:
Kuang-che Wu12192fc2019-08-31 09:55:00 +0800230 try:
231 fd.write(ast.literal_eval(content))
232 except ValueError:
233 logging.exception('Invalid value for %s: %r', stream,
234 content)
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700235
236 def stop_capture(self):
237 """Stop capturing UART streams."""
Congbin Guo0f00be82019-04-18 17:51:14 -0700238 for uart in self._UartToCapture:
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700239 try:
Congbin Guo0f00be82019-04-18 17:51:14 -0700240 self._start_stop_capture(uart, False)
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700241 except Exception as err:
242 logging.warn('Failed to stop UART logging for %s: %s', uart,
243 err)
244
245
J. Richard Barnette384056b2012-04-16 11:04:46 -0700246class Servo(object):
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700247
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700248 """Manages control of a Servo board.
249
250 Servo is a board developed by hardware group to aide in the debug and
251 control of various partner devices. Servo's features include the simulation
252 of pressing the power button, closing the lid, and pressing Ctrl-d. This
253 class manages setting up and communicating with a servo demon (servod)
254 process. It provides both high-level functions for common servo tasks and
255 low-level functions for directly setting and reading gpios.
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700256
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700257 """
258
Chrome Bot9a1137d2011-07-19 14:35:00 -0700259 # Power button press delays in seconds.
J. Richard Barnetted2e4cbd2012-06-29 12:18:40 -0700260 #
J. Richard Barnette5383f072012-07-26 17:35:40 -0700261 # The EC specification says that 8.0 seconds should be enough
262 # for the long power press. However, some platforms need a bit
263 # more time. Empirical testing has found these requirements:
264 # Alex: 8.2 seconds
265 # ZGB: 8.5 seconds
266 # The actual value is set to the largest known necessary value.
267 #
268 # TODO(jrbarnette) Being generous is the right thing to do for
269 # existing platforms, but if this code is to be used for
270 # qualification of new hardware, we should be less generous.
Chrome Bot9a1137d2011-07-19 14:35:00 -0700271 SHORT_DELAY = 0.1
J. Richard Barnette5383f072012-07-26 17:35:40 -0700272
Todd Broch31c82502011-08-29 08:14:39 -0700273 # Maximum number of times to re-read power button on release.
Todd Brochcf7c6652012-02-24 13:03:59 -0800274 GET_RETRY_MAX = 10
Chrome Bot9a1137d2011-07-19 14:35:00 -0700275
J. Richard Barnette5383f072012-07-26 17:35:40 -0700276 # Delays to deal with DUT state transitions.
Chrome Bot9a1137d2011-07-19 14:35:00 -0700277 SLEEP_DELAY = 6
278 BOOT_DELAY = 10
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700279
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700280 # Default minimum time interval between 'press' and 'release'
281 # keyboard events.
Vic Yang0aca1c22012-11-19 18:33:56 -0800282 SERVO_KEY_PRESS_DELAY = 0.1
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700283
Tom Wai-Hong Tam93b5c092015-05-14 02:50:43 +0800284 # Time to toggle recovery switch on and off.
285 REC_TOGGLE_DELAY = 0.1
286
Tom Wai-Hong Tamf9ded092015-05-20 05:37:22 +0800287 # Time to toggle development switch on and off.
288 DEV_TOGGLE_DELAY = 0.1
289
Jon Salzc88e5b62011-11-30 14:38:54 +0800290 # Time between an usb disk plugged-in and detected in the system.
291 USB_DETECTION_DELAY = 10
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800292 # Time to keep USB power off before and after USB mux direction is changed
293 USB_POWEROFF_DELAY = 2
Jon Salzc88e5b62011-11-30 14:38:54 +0800294
Simran Basib7850bb2013-07-24 12:33:42 -0700295 # Time to wait before timing out on servo initialization.
296 INIT_TIMEOUT_SECS = 10
Simran Basi59331342013-07-12 12:06:29 -0700297
J. Richard Barnette67ccb872012-04-19 16:34:56 -0700298
Ricky Liang0dd379c2014-04-23 16:29:08 +0800299 def __init__(self, servo_host, servo_serial=None):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700300 """Sets up the servo communication infrastructure.
301
Fang Deng5d518f42013-08-02 14:04:32 -0700302 @param servo_host: A ServoHost object representing
303 the host running servod.
Dana Goyettea2f00ea2019-06-26 16:14:12 -0700304 @type servo_host: autotest_lib.server.hosts.servo_host.ServoHost
Ricky Liang0dd379c2014-04-23 16:29:08 +0800305 @param servo_serial: Serial number of the servo board.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700306 """
Fang Deng5d518f42013-08-02 14:04:32 -0700307 # TODO(fdeng): crbug.com/298379
308 # We should move servo_host object out of servo object
309 # to minimize the dependencies on the rest of Autotest.
310 self._servo_host = servo_host
Ricky Liang0dd379c2014-04-23 16:29:08 +0800311 self._servo_serial = servo_serial
Richard Barnette180efe62016-12-02 23:20:44 +0000312 self._server = servo_host.get_servod_server_proxy()
J. Richard Barnette0c9c5882014-06-11 15:27:05 -0700313 self._power_state = _PowerStateController(self)
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700314 self._uart = _Uart(self)
Fang Dengafb88142013-05-30 17:44:31 -0700315 self._usb_state = None
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700316 self._programmer = None
Dana Goyette4dc0adc2019-05-06 14:51:53 -0700317 self._prev_log_inode = None
318 self._prev_log_size = 0
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800319
Ricky Liang0dd379c2014-04-23 16:29:08 +0800320 @property
321 def servo_serial(self):
322 """Returns the serial number of the servo board."""
323 return self._servo_serial
324
Dana Goyette0b6e6402019-10-04 11:09:24 -0700325 def rotate_servod_logs(self, filename=None, directory=None):
326 """Save the latest servod log into a local directory, then rotate logs.
Dana Goyette4dc0adc2019-05-06 14:51:53 -0700327
Dana Goyette0b6e6402019-10-04 11:09:24 -0700328 The files will be <filename>.DEBUG, <filename>.INFO, <filename>.WARNING,
329 or just <filename>.log if not using split level logging.
330
331 @param filename: local filename prefix (no file extension) to use.
332 If None, rotate log but don't save it.
333 @param directory: local directory to save logs into (if unset, use cwd)
Dana Goyette4dc0adc2019-05-06 14:51:53 -0700334 """
Dana Goyette0b6e6402019-10-04 11:09:24 -0700335 if self.is_localhost():
336 # Local servod usually runs without log-dir, so can't be collected.
337 # TODO(crbug.com/1011516): allow localhost when rotation is enabled
338 return
339
340 log_dir = '/var/log/servod_%s' % self._servo_host.servo_port
341
342 if filename:
Dana Goyettec8613342019-11-13 15:03:28 -0800343 logging.info("Saving servod logs: %s/%s.*", directory or '.',
344 filename)
Dana Goyette0b6e6402019-10-04 11:09:24 -0700345 # TODO(crrev.com/c/1793030): remove no-level case once CL is pushed
346 for level_name in ('', 'DEBUG', 'INFO', 'WARNING'):
347
348 remote_path = os.path.join(log_dir, 'latest')
349 if level_name:
350 remote_path += '.%s' % level_name
351
352 local_path = '%s.%s' % (filename, level_name or 'log')
353 if directory:
354 local_path = os.path.join(directory, local_path)
355
356 try:
357 self._servo_host.get_file(
358 remote_path, local_path, try_rsync=False)
359
360 except error.AutoservRunError as e:
361 result = e.result_obj
362 if result.exit_status != 0:
363 stderr = result.stderr.strip()
364
365 # File not existing is okay, but warn for anything else.
366 if 'no such' not in stderr.lower():
367 logging.warn(
368 "Couldn't retrieve servod log: %s",
369 stderr or '\n%s' % result)
370
371 try:
372 if os.stat(local_path).st_size == 0:
373 os.unlink(local_path)
374 except EnvironmentError:
375 pass
376
377 else:
378 # No filename given, so caller wants to discard the log lines.
379 # Remove the symlinks to prevent old log-dir links from being
380 # picked up multiple times when using servod without log-dir.
381 remote_path = os.path.join(log_dir, 'latest*')
382 self._servo_host.run(
383 "rm %s" % remote_path,
384 stderr_tee=None, ignore_status=True)
385
386 # Servod log rotation renames current log, then creates a new file with
387 # the old name: log.<date> -> log.<date>.1.tbz2 -> log.<date>.2.tbz2
388
389 # Must rotate after copying, or the copy would be the new, empty file.
390 try:
391 self.set_nocheck('rotate_servod_logs', 'yes')
392 except ControlUnavailableError as e:
Dana Goyettec8613342019-11-13 15:03:28 -0800393 # Missing control (possibly old servod)
Dana Goyette0b6e6402019-10-04 11:09:24 -0700394 logging.warn("Couldn't rotate servod logs: %s", str(e))
Dana Goyettec8613342019-11-13 15:03:28 -0800395 except error.TestFail:
396 # Control exists but gave an error; don't let it fail the test.
397 # The error is already logged in set_nocheck().
398 pass
Ricky Liang0dd379c2014-04-23 16:29:08 +0800399
Tom Wai-Hong Tam22ee0e52013-04-03 13:36:39 +0800400 def get_power_state_controller(self):
J. Richard Barnette75136b32013-03-26 13:38:44 -0700401 """Return the power state controller for this Servo.
402
403 The power state controller provides board-independent
404 interfaces for reset, power-on, power-off operations.
405
406 """
407 return self._power_state
408
Fang Deng5d518f42013-08-02 14:04:32 -0700409
Mary Ruthvena3c52b82019-10-09 14:09:08 -0700410 def initialize_dut(self, cold_reset=False, enable_main=True):
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700411 """Initializes a dut for testing purposes.
412
413 This sets various servo signals back to default values
414 appropriate for the target board. By default, if the DUT
415 is already on, it stays on. If the DUT is powered off
416 before initialization, its state afterward is unspecified.
417
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700418 Rationale: Basic initialization of servo sets the lid open,
419 when there is a lid. This operation won't affect powered on
420 units; however, setting the lid open may power on a unit
J. Richard Barnette75136b32013-03-26 13:38:44 -0700421 that's off, depending on the board type and previous state
422 of the device.
423
J. Richard Barnette4b6af0d2014-06-05 09:57:20 -0700424 If `cold_reset` is a true value, the DUT and its EC will be
425 reset, and the DUT rebooted in normal mode.
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700426
427 @param cold_reset If True, cold reset the device after
428 initialization.
Mary Ruthvena3c52b82019-10-09 14:09:08 -0700429 @param enable_main If True, make sure the main servo device has
430 control of the dut.
431
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700432 """
Mary Ruthvena3c52b82019-10-09 14:09:08 -0700433 if enable_main:
434 self.enable_main_servo_device()
435
Dana Goyettea2f00ea2019-06-26 16:14:12 -0700436 try:
437 self._server.hwinit()
438 except socket.error as e:
439 e.filename = '%s:%s' % (self._servo_host.hostname,
440 self._servo_host.servo_port)
441 raise
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700442 self.set('usb_mux_oe1', 'on')
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700443 self._usb_state = None
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700444 self.switch_usbkey('off')
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700445 self._uart.start_capture()
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700446 if cold_reset:
J. Richard Barnette4b6af0d2014-06-05 09:57:20 -0700447 self._power_state.reset()
J. Richard Barnette1777f5d2014-09-03 15:18:19 -0700448 logging.debug('Servo initialized, version is %s',
449 self._server.get_version())
Ruben Rodriguez Buchillonb0e2a9f2019-08-12 12:40:44 -0700450 if self.has_control('init_keyboard'):
451 # This indicates the servod version does not
452 # have explicit keyboard initialization yet.
453 # Ignore this.
Ruben Rodriguez Buchillon05298242019-05-20 11:29:06 -0700454 # TODO(coconutruben): change this back to set() about a month
455 # after crrev.com/c/1586239 has been merged (or whenever that
456 # logic is in the labstation images).
457 self.set_nocheck('init_keyboard','on')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700458
459
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800460 def is_localhost(self):
461 """Is the servod hosted locally?
462
463 Returns:
464 True if local hosted; otherwise, False.
465 """
Fang Deng5d518f42013-08-02 14:04:32 -0700466 return self._servo_host.is_localhost()
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800467
468
Mary Ruthvenecf12712019-06-26 17:36:21 -0700469 def get_os_version(self):
470 """Returns the chromeos release version."""
471 lsb_release_content = self.system_output('cat /etc/lsb-release',
472 ignore_status=True)
473 return lsbrelease_utils.get_chromeos_release_builder_path(
474 lsb_release_content=lsb_release_content)
475
476
Mary Ruthven83bb5952019-06-27 12:34:05 -0700477 def get_servod_version(self):
478 """Returns the servod version."""
479 result = self._servo_host.run('servod --version')
480 # TODO: use system_output once servod --version prints to stdout
481 stdout = result.stdout.strip()
482 return stdout if stdout else result.stderr.strip()
483
484
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700485 def power_long_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700486 """Simulate a long power button press."""
J. Richard Barnettef12bff22012-07-18 14:41:06 -0700487 # After a long power press, the EC may ignore the next power
488 # button press (at least on Alex). To guarantee that this
489 # won't happen, we need to allow the EC one second to
490 # collect itself.
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800491 # long_press is defined as 8.5s in servod
492 self.set_nocheck('power_key', 'long_press')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700493
494
495 def power_normal_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700496 """Simulate a normal power button press."""
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800497 # press is defined as 1.2s in servod
498 self.set_nocheck('power_key', 'press')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700499
500
501 def power_short_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700502 """Simulate a short power button press."""
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800503 # tab is defined as 0.2s in servod
504 self.set_nocheck('power_key', 'tab')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700505
506
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800507 def power_key(self, press_secs='tab'):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700508 """Simulate a power button press.
509
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800510 @param press_secs: int, float, str; time to press key in seconds or
511 known shorthand: 'tab' 'press' 'long_press'
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700512 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800513 self.set_nocheck('power_key', press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700514
515
Ruben Zakarian47c1d4f2019-09-30 14:17:41 -0700516 def pwr_button(self, action='press'):
517 """Simulate a power button press.
518
519 @param action: str; could be press or could be release.
520 """
521 self.set_nocheck('pwr_button', action)
522
523
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700524 def lid_open(self):
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800525 """Simulate opening the lid and raise exception if all attempts fail"""
526 self.set('lid_open', 'yes')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700527
528
529 def lid_close(self):
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800530 """Simulate closing the lid and raise exception if all attempts fail
Craig Harrison48997262011-06-27 14:31:10 -0700531
532 Waits 6 seconds to ensure the device is fully asleep before returning.
533 """
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800534 self.set('lid_open', 'no')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700535 time.sleep(Servo.SLEEP_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700536
Ruben Zakarian47c1d4f2019-09-30 14:17:41 -0700537
538 def vbus_power_get(self):
539 """Get current vbus_power."""
540 return self.get('vbus_power')
541
542
Shelley Chenc26575a2015-09-18 10:56:16 -0700543 def volume_up(self, timeout=300):
Kevin Chenge448a8b2016-09-09 10:18:11 -0700544 """Simulate pushing the volume down button.
545
546 @param timeout: Timeout for setting the volume.
547 """
Shelley Chenc26575a2015-09-18 10:56:16 -0700548 self.set_get_all(['volume_up:yes',
549 'sleep:%.4f' % self.SERVO_KEY_PRESS_DELAY,
550 'volume_up:no'])
551 # we need to wait for commands to take effect before moving on
552 time_left = float(timeout)
553 while time_left > 0.0:
554 value = self.get('volume_up')
555 if value == 'no':
556 return
557 time.sleep(self.SHORT_DELAY)
558 time_left = time_left - self.SHORT_DELAY
559 raise error.TestFail("Failed setting volume_up to no")
560
561 def volume_down(self, timeout=300):
Kevin Chenge448a8b2016-09-09 10:18:11 -0700562 """Simulate pushing the volume down button.
563
564 @param timeout: Timeout for setting the volume.
565 """
Shelley Chenc26575a2015-09-18 10:56:16 -0700566 self.set_get_all(['volume_down:yes',
567 'sleep:%.4f' % self.SERVO_KEY_PRESS_DELAY,
568 'volume_down:no'])
569 # we need to wait for commands to take effect before moving on
570 time_left = float(timeout)
571 while time_left > 0.0:
572 value = self.get('volume_down')
573 if value == 'no':
574 return
575 time.sleep(self.SHORT_DELAY)
576 time_left = time_left - self.SHORT_DELAY
577 raise error.TestFail("Failed setting volume_down to no")
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700578
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800579 def ctrl_d(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800580 """Simulate Ctrl-d simultaneous button presses.
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800581
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800582 @param press_secs: int, float, str; time to press key in seconds or
583 known shorthand: 'tab' 'press' 'long_press'
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800584 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800585 self.set_nocheck('ctrl_d', press_secs)
Gediminas Ramanauskas9da80f22012-11-14 12:59:43 -0800586
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800587
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800588 def ctrl_u(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800589 """Simulate Ctrl-u simultaneous button presses.
590
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800591 @param press_secs: int, float, str; time to press key in seconds or
592 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800593 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800594 self.set_nocheck('ctrl_u', press_secs)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700595
596
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800597 def ctrl_enter(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800598 """Simulate Ctrl-enter simultaneous button presses.
599
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800600 @param press_secs: int, float, str; time to press key in seconds or
601 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800602 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800603 self.set_nocheck('ctrl_enter', press_secs)
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -0700604
605
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800606 def ctrl_key(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800607 """Simulate Enter key button press.
608
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800609 @param press_secs: int, float, str; time to press key in seconds or
610 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800611 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800612 self.set_nocheck('ctrl_key', press_secs)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700613
614
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800615 def enter_key(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800616 """Simulate Enter key button press.
617
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800618 @param press_secs: int, float, str; time to press key in seconds or
619 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800620 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800621 self.set_nocheck('enter_key', press_secs)
Todd Broch9753bd42012-03-21 10:15:08 -0700622
623
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800624 def refresh_key(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800625 """Simulate Refresh key (F3) button press.
626
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800627 @param press_secs: int, float, str; time to press key in seconds or
628 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800629 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800630 self.set_nocheck('refresh_key', press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700631
632
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800633 def ctrl_refresh_key(self, press_secs='tab'):
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800634 """Simulate Ctrl and Refresh (F3) simultaneous press.
635
636 This key combination is an alternative of Space key.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800637
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800638 @param press_secs: int, float, str; time to press key in seconds or
639 known shorthand: 'tab' 'press' 'long_press'
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800640 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800641 self.set_nocheck('ctrl_refresh_key', press_secs)
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800642
643
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800644 def imaginary_key(self, press_secs='tab'):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700645 """Simulate imaginary key button press.
646
647 Maps to a key that doesn't physically exist.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800648
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800649 @param press_secs: int, float, str; time to press key in seconds or
650 known shorthand: 'tab' 'press' 'long_press'
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700651 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800652 self.set_nocheck('imaginary_key', press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700653
654
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800655 def sysrq_x(self, press_secs='tab'):
Vincent Palatine7dc9282016-07-14 11:31:58 +0200656 """Simulate Alt VolumeUp X simulataneous press.
657
658 This key combination is the kernel system request (sysrq) X.
659
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800660 @param press_secs: int, float, str; time to press key in seconds or
661 known shorthand: 'tab' 'press' 'long_press'
Vincent Palatine7dc9282016-07-14 11:31:58 +0200662 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800663 self.set_nocheck('sysrq_x', press_secs)
Vincent Palatine7dc9282016-07-14 11:31:58 +0200664
665
Tom Wai-Hong Tam93b5c092015-05-14 02:50:43 +0800666 def toggle_recovery_switch(self):
667 """Toggle recovery switch on and off."""
668 self.enable_recovery_mode()
669 time.sleep(self.REC_TOGGLE_DELAY)
670 self.disable_recovery_mode()
671
672
Craig Harrison6b36b122011-06-28 17:58:43 -0700673 def enable_recovery_mode(self):
674 """Enable recovery mode on device."""
675 self.set('rec_mode', 'on')
676
677
678 def disable_recovery_mode(self):
679 """Disable recovery mode on device."""
680 self.set('rec_mode', 'off')
681
682
Tom Wai-Hong Tamf9ded092015-05-20 05:37:22 +0800683 def toggle_development_switch(self):
684 """Toggle development switch on and off."""
685 self.enable_development_mode()
686 time.sleep(self.DEV_TOGGLE_DELAY)
687 self.disable_development_mode()
688
689
Craig Harrison6b36b122011-06-28 17:58:43 -0700690 def enable_development_mode(self):
691 """Enable development mode on device."""
692 self.set('dev_mode', 'on')
693
694
695 def disable_development_mode(self):
696 """Disable development mode on device."""
697 self.set('dev_mode', 'off')
698
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700699 def boot_devmode(self):
700 """Boot a dev-mode device that is powered off."""
Tom Wai-Hong Tam23869102012-01-17 15:41:30 +0800701 self.power_short_press()
Craig Harrison48997262011-06-27 14:31:10 -0700702 self.pass_devmode()
703
704
705 def pass_devmode(self):
706 """Pass through boot screens in dev-mode."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700707 time.sleep(Servo.BOOT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700708 self.ctrl_d()
Chrome Bot9a1137d2011-07-19 14:35:00 -0700709 time.sleep(Servo.BOOT_DELAY)
Craig Harrison48997262011-06-27 14:31:10 -0700710
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700711
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800712 def get_board(self):
Wai-Hong Tam0f904002017-09-19 15:52:22 -0700713 """Get the board connected to servod."""
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800714 return self._server.get_board()
715
716
Wai-Hong Tam0f904002017-09-19 15:52:22 -0700717 def get_base_board(self):
718 """Get the board of the base connected to servod."""
Wai-Hong Tam7f6169e2017-09-29 09:23:48 -0700719 try:
720 return self._server.get_base_board()
721 except xmlrpclib.Fault as e:
722 # TODO(waihong): Remove the following compatibility check when
723 # the new versions of hdctools are deployed.
724 if 'not supported' in str(e):
725 logging.warning('The servod is too old that get_base_board '
726 'not supported.')
727 return ''
728 raise
Wai-Hong Tam0f904002017-09-19 15:52:22 -0700729
730
Wai-Hong Tamcc399ee2017-12-08 12:43:28 -0800731 def get_ec_active_copy(self):
732 """Get the active copy of the EC image."""
733 return self.get('ec_active_copy')
734
735
Todd Brochefe72cb2012-07-11 19:58:53 -0700736 def _get_xmlrpclib_exception(self, xmlexc):
737 """Get meaningful exception string from xmlrpc.
738
739 Args:
740 xmlexc: xmlrpclib.Fault object
741
742 xmlrpclib.Fault.faultString has the following format:
743
744 <type 'exception type'>:'actual error message'
745
746 Parse and return the real exception from the servod side instead of the
747 less meaningful one like,
748 <Fault 1: "<type 'exceptions.AttributeError'>:'tca6416' object has no
749 attribute 'hw_driver'">
750
751 Returns:
752 string of underlying exception raised in servod.
753 """
754 return re.sub('^.*>:', '', xmlexc.faultString)
755
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700756 def has_control(self, control):
757 """Query servod server to determine if |control| is a valid control.
758
759 @param control: str, control name to query
760
761 @returns: true if |control| is a known control, false otherwise.
762 """
763 assert control
764 try:
765 # If the control exists, doc() will work.
766 self._server.doc(control)
767 return True
768 except xmlrpclib.Fault as e:
769 if re.search('No control %s' % control,
770 self._get_xmlrpclib_exception(e)):
771 return False
772 raise e
Todd Brochefe72cb2012-07-11 19:58:53 -0700773
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700774 def get(self, gpio_name):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700775 """Get the value of a gpio from Servod.
776
777 @param gpio_name Name of the gpio.
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700778
779 @returns: server response to |gpio_name| request.
780
781 @raise ControlUnavailableError: if |gpio_name| not a known control.
782 @raise error.TestFail: for all other failures doing get().
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700783 """
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700784 assert gpio_name
Todd Brochefe72cb2012-07-11 19:58:53 -0700785 try:
Simran Basi1cbc5f22013-07-17 14:23:58 -0700786 return self._server.get(gpio_name)
Todd Brochefe72cb2012-07-11 19:58:53 -0700787 except xmlrpclib.Fault as e:
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700788 err_str = self._get_xmlrpclib_exception(e)
789 err_msg = "Getting '%s' :: %s" % (gpio_name, err_str)
790 unknown_ctrl = re.findall(NO_CONTROL_RE, err_str)
791 if unknown_ctrl:
792 raise ControlUnavailableError('No control named %r' %
793 unknown_ctrl[0])
794 else:
795 logging.error(err_msg)
796 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700797
798
799 def set(self, gpio_name, gpio_value):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700800 """Set and check the value of a gpio using Servod.
801
802 @param gpio_name Name of the gpio.
803 @param gpio_value New setting for the gpio.
Dana Goyette7ff06c92019-10-11 13:38:03 -0700804 @raise error.TestFail: if the control value fails to change.
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700805 """
Chrome Bot9a1137d2011-07-19 14:35:00 -0700806 self.set_nocheck(gpio_name, gpio_value)
Todd Brochcf7c6652012-02-24 13:03:59 -0800807 retry_count = Servo.GET_RETRY_MAX
Dana Goyette7ff06c92019-10-11 13:38:03 -0700808 actual_value = self.get(gpio_name)
809 while gpio_value != actual_value and retry_count:
Ilja H. Friedel04be2bd2014-05-07 21:29:59 -0700810 logging.warning("%s != %s, retry %d", gpio_name, gpio_value,
Dana Goyette7ff06c92019-10-11 13:38:03 -0700811 retry_count)
Todd Brochcf7c6652012-02-24 13:03:59 -0800812 retry_count -= 1
813 time.sleep(Servo.SHORT_DELAY)
Dana Goyette7ff06c92019-10-11 13:38:03 -0700814 actual_value = self.get(gpio_name)
815
816 if gpio_value != actual_value:
817 raise error.TestFail(
818 'Servo failed to set %s to %s. Got %s.'
819 % (gpio_name, gpio_value, actual_value))
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700820
821
822 def set_nocheck(self, gpio_name, gpio_value):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700823 """Set the value of a gpio using Servod.
824
825 @param gpio_name Name of the gpio.
826 @param gpio_value New setting for the gpio.
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700827
828 @raise ControlUnavailableError: if |gpio_name| not a known control.
829 @raise error.TestFail: for all other failures doing set().
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700830 """
Ruben Rodriguez Buchillon1c0219c2019-06-25 12:30:44 -0700831 # The real danger here is to pass a None value through the xmlrpc.
832 assert gpio_name and gpio_value is not None
Mary Ruthvendfa45342019-06-25 10:10:17 -0700833 logging.debug('Setting %s to %r', gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700834 try:
Simran Basi1cbc5f22013-07-17 14:23:58 -0700835 self._server.set(gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700836 except xmlrpclib.Fault as e:
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700837 err_str = self._get_xmlrpclib_exception(e)
838 err_msg = "Setting '%s' :: %s" % (gpio_name, err_str)
839 unknown_ctrl = re.findall(NO_CONTROL_RE, err_str)
840 if unknown_ctrl:
841 raise ControlUnavailableError('No control named %r' %
842 unknown_ctrl[0])
843 else:
844 logging.error(err_msg)
845 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700846
847
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800848 def set_get_all(self, controls):
849 """Set &| get one or more control values.
850
851 @param controls: list of strings, controls to set &| get.
852
853 @raise: error.TestError in case error occurs setting/getting values.
854 """
855 rv = []
856 try:
Mary Ruthvendfa45342019-06-25 10:10:17 -0700857 logging.debug('Set/get all: %s', str(controls))
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800858 rv = self._server.set_get_all(controls)
859 except xmlrpclib.Fault as e:
860 # TODO(waihong): Remove the following backward compatibility when
861 # the new versions of hdctools are deployed.
862 if 'not supported' in str(e):
863 logging.warning('The servod is too old that set_get_all '
864 'not supported. Use set and get instead.')
865 for control in controls:
866 if ':' in control:
867 (name, value) = control.split(':')
868 if name == 'sleep':
869 time.sleep(float(value))
870 else:
871 self.set_nocheck(name, value)
872 rv.append(True)
873 else:
874 rv.append(self.get(name))
875 else:
876 err_msg = "Problem with '%s' :: %s" % \
877 (controls, self._get_xmlrpclib_exception(e))
878 raise error.TestFail(err_msg)
879 return rv
880
881
Jon Salzc88e5b62011-11-30 14:38:54 +0800882 # TODO(waihong) It may fail if multiple servo's are connected to the same
883 # host. Should look for a better way, like the USB serial name, to identify
884 # the USB device.
Simran Basi741b5d42012-05-18 11:27:15 -0700885 # TODO(sbasi) Remove this code from autoserv once firmware tests have been
886 # updated.
Kevin Chenga22c4a82016-10-07 14:13:25 -0700887 def probe_host_usb_dev(self, timeout=_USB_PROBE_TIMEOUT):
Jon Salzc88e5b62011-11-30 14:38:54 +0800888 """Probe the USB disk device plugged-in the servo from the host side.
889
Kevin Chengeb06fe72016-08-22 15:26:32 -0700890 It uses servod to discover if there is a usb device attached to it.
Jon Salzc88e5b62011-11-30 14:38:54 +0800891
Kevin Chenga22c4a82016-10-07 14:13:25 -0700892 @param timeout The timeout period when probing for the usb host device.
893
894 @return: String of USB disk path (e.g. '/dev/sdb') or None.
Jon Salzc88e5b62011-11-30 14:38:54 +0800895 """
Ruben Rodriguez Buchillon9a4bfc32018-10-16 20:46:25 +0800896 # Set up Servo's usb mux.
897 self.switch_usbkey('host')
Kevin Chenga22c4a82016-10-07 14:13:25 -0700898 return self._server.probe_host_usb_dev(timeout) or None
Jon Salzc88e5b62011-11-30 14:38:54 +0800899
900
Mike Truty49153d82012-08-21 22:27:30 -0500901 def image_to_servo_usb(self, image_path=None,
902 make_image_noninteractive=False):
903 """Install an image to the USB key plugged into the servo.
904
905 This method may copy any image to the servo USB key including a
906 recovery image or a test image. These images are frequently used
907 for test purposes such as restoring a corrupted image or conducting
908 an upgrade of ec/fw/kernel as part of a test of a specific image part.
909
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700910 @param image_path Path on the host to the recovery image.
911 @param make_image_noninteractive Make the recovery image
912 noninteractive, therefore the DUT
913 will reboot automatically after
914 installation.
Mike Truty49153d82012-08-21 22:27:30 -0500915 """
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700916 # We're about to start plugging/unplugging the USB key. We
917 # don't know the state of the DUT, or what it might choose
918 # to do to the device after hotplug. To avoid surprises,
919 # force the DUT to be off.
920 self._server.hwinit()
Ruben Rodriguez Buchillonb0e2a9f2019-08-12 12:40:44 -0700921 if self.has_control('init_keyboard'):
922 # This indicates the servod version does not
923 # have explicit keyboard initialization yet.
924 # Ignore this.
Ruben Rodriguez Buchillonbd3b74f2019-08-08 16:18:27 -0700925 # TODO(coconutruben): change this back to set() about a month
926 # after crrev.com/c/1586239 has been merged (or whenever that
927 # logic is in the labstation images).
928 self.set_nocheck('init_keyboard','on')
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700929 self._power_state.power_off()
Mike Truty49153d82012-08-21 22:27:30 -0500930
Mike Truty49153d82012-08-21 22:27:30 -0500931 if image_path:
Garry Wangd2656812019-08-07 17:29:16 -0700932 # Set up Servo's usb mux.
933 self.switch_usbkey('host')
Tom Wai-Hong Tam42f136d2012-10-26 11:11:23 +0800934 logging.info('Searching for usb device and copying image to it. '
935 'Please wait a few minutes...')
Mike Truty49153d82012-08-21 22:27:30 -0500936 if not self._server.download_image_to_usb(image_path):
937 logging.error('Failed to transfer requested image to USB. '
938 'Please take a look at Servo Logs.')
939 raise error.AutotestError('Download image to usb failed.')
940 if make_image_noninteractive:
941 logging.info('Making image noninteractive')
942 if not self._server.make_image_noninteractive():
943 logging.error('Failed to make image noninteractive. '
944 'Please take a look at Servo Logs.')
945
Kalin Stoyanovb3c11f32018-05-11 09:02:00 -0700946 def boot_in_recovery_mode(self):
947 """Boot host DUT in recovery mode."""
948 self._power_state.power_on(rec_mode=self._power_state.REC_ON)
949 self.switch_usbkey('dut')
950
Mike Truty49153d82012-08-21 22:27:30 -0500951
Simran Basi741b5d42012-05-18 11:27:15 -0700952 def install_recovery_image(self, image_path=None,
J. Richard Barnetteb6de7e32013-02-14 13:28:04 -0800953 make_image_noninteractive=False):
Dan Shic67f1332016-04-06 12:38:06 -0700954 """Install the recovery image specified by the path onto the DUT.
Jon Salzc88e5b62011-11-30 14:38:54 +0800955
956 This method uses google recovery mode to install a recovery image
957 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 +0800958 board specified by the usb_dev. If no image path is specified
Jon Salzc88e5b62011-11-30 14:38:54 +0800959 we use the recovery image already on the usb image.
960
Dan Shic67f1332016-04-06 12:38:06 -0700961 @param image_path: Path on the host to the recovery image.
962 @param make_image_noninteractive: Make the recovery image
963 noninteractive, therefore the DUT will reboot automatically
964 after installation.
Jon Salzc88e5b62011-11-30 14:38:54 +0800965 """
Mike Truty49153d82012-08-21 22:27:30 -0500966 self.image_to_servo_usb(image_path, make_image_noninteractive)
Garry Wangd2656812019-08-07 17:29:16 -0700967 # Give the DUT some time to power_off if we skip
968 # download image to usb. (crbug.com/982993)
969 if not image_path:
970 time.sleep(10)
Kalin Stoyanovb3c11f32018-05-11 09:02:00 -0700971 self.boot_in_recovery_mode()
Jon Salzc88e5b62011-11-30 14:38:54 +0800972
973
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800974 def _scp_image(self, image_path):
975 """Copy image to the servo host.
976
977 When programming a firmware image on the DUT, the image must be
978 located on the host to which the servo device is connected. Sometimes
979 servo is controlled by a remote host, in this case the image needs to
Mary Ruthven401a6012019-12-13 11:39:44 -0800980 be transferred to the remote host. This adds a random extension to
981 prevent multiple tests from copying a image to the same location on
982 the remote host.
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800983
984 @param image_path: a string, name of the firmware image file to be
985 transferred.
986 @return: a string, full path name of the copied file on the remote.
987 """
Mary Ruthven401a6012019-12-13 11:39:44 -0800988 name = os.path.basename(image_path)
989 ext = ''.join([random.choice(string.ascii_letters) for i in xrange(10)])
990 remote_name = name + '.' + ext
991 dest_path = os.path.join('/tmp', remote_name)
992 logging.info('Copying %s to %s', name, dest_path)
Fang Deng5d518f42013-08-02 14:04:32 -0700993 self._servo_host.send_file(image_path, dest_path)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800994 return dest_path
995
996
Dan Shifecdaf42015-07-28 10:17:26 -0700997 def system(self, command, timeout=3600):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700998 """Execute the passed in command on the servod host.
999
1000 @param command Command to be executed.
Dan Shifecdaf42015-07-28 10:17:26 -07001001 @param timeout Maximum number of seconds of runtime allowed. Default to
1002 1 hour.
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -07001003 """
Vadim Bendebury89ec24e2012-12-17 12:54:18 -08001004 logging.info('Will execute on servo host: %s', command)
Fang Deng5d518f42013-08-02 14:04:32 -07001005 self._servo_host.run(command, timeout=timeout)
Vadim Bendebury89ec24e2012-12-17 12:54:18 -08001006
1007
Dan Shifecdaf42015-07-28 10:17:26 -07001008 def system_output(self, command, timeout=3600,
Vadim Bendebury89ec24e2012-12-17 12:54:18 -08001009 ignore_status=False, args=()):
1010 """Execute the passed in command on the servod host, return stdout.
1011
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -07001012 @param command a string, the command to execute
1013 @param timeout an int, max number of seconds to wait til command
Dan Shifecdaf42015-07-28 10:17:26 -07001014 execution completes. Default to 1 hour.
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -07001015 @param ignore_status a Boolean, if true - ignore command's nonzero exit
Vadim Bendebury89ec24e2012-12-17 12:54:18 -08001016 status, otherwise an exception will be thrown
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -07001017 @param args a tuple of strings, each becoming a separate command line
Vadim Bendebury89ec24e2012-12-17 12:54:18 -08001018 parameter for the command
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -07001019 @return command's stdout as a string.
Vadim Bendebury89ec24e2012-12-17 12:54:18 -08001020 """
Fang Deng5d518f42013-08-02 14:04:32 -07001021 return self._servo_host.run(command, timeout=timeout,
1022 ignore_status=ignore_status,
1023 args=args).stdout.strip()
Vadim Bendeburyb80ba592012-12-07 15:02:34 -08001024
1025
Mary Ruthven38d90af2019-08-16 13:13:31 -07001026 def get_servo_version(self, active=False):
Dan Shia5fef052015-05-18 23:28:47 -07001027 """Get the version of the servo, e.g., servo_v2 or servo_v3.
1028
Mary Ruthven38d90af2019-08-16 13:13:31 -07001029 @param active: Only return the servo type with the active device.
Dan Shia5fef052015-05-18 23:28:47 -07001030 @return: The version of the servo.
1031
1032 """
Mary Ruthven38d90af2019-08-16 13:13:31 -07001033 servo_type = self._server.get_version()
1034 if '_and_' not in servo_type or not active:
1035 return servo_type
1036
1037 # If servo v4 is using ccd and servo micro, modify the servo type to
1038 # reflect the active device.
1039 active_device = self.get('active_v4_device')
1040 if active_device in servo_type:
1041 logging.info('%s is active', active_device)
1042 return 'servo_v4_with_' + active_device
1043
1044 logging.warn("%s is active even though it's not in servo type",
1045 active_device)
1046 return servo_type
Dan Shia5fef052015-05-18 23:28:47 -07001047
1048
Mary Ruthvena3c52b82019-10-09 14:09:08 -07001049 def get_main_servo_device(self):
1050 """Return the main servo device"""
1051 servo_type = self.get_servo_version()
1052 return servo_type.split('_with_')[-1].split('_and_')[0]
1053
1054
1055 def enable_main_servo_device(self):
1056 """Make sure the main device has control of the dut."""
Mary Ruthven1409d9d2019-10-22 20:44:24 -07001057 # Cr50 detects servo using the EC uart. It doesn't work well if the
1058 # board doesn't use EC uart. The lab active_v4_device doesn't handle
1059 # this correctly. Check ec_uart_pty before trying to change the active
1060 # device.
1061 # TODO(crbug.com/1016842): reenable the setting the main device when
1062 # active device works on labstations.
1063 return
Mary Ruthvena3c52b82019-10-09 14:09:08 -07001064 if not self.has_control('active_v4_device'):
1065 return
1066 self.set('active_v4_device', self.get_main_servo_device())
1067
1068
Ruben Rodriguez Buchillon1823bad2019-09-30 17:04:34 -07001069 def main_device_is_ccd(self):
1070 """Whether the main servo device (no prefixes) is a ccd device."""
Mary Ruthven2724ee62019-07-16 11:16:59 -07001071 servo = self._server.get_version()
1072 return 'ccd_cr50' in servo and 'servo_micro' not in servo
1073
1074
Ruben Rodriguez Buchillon1823bad2019-09-30 17:04:34 -07001075 def main_device_is_flex(self):
1076 """Whether the main servo device (no prefixes) is a legacy device."""
1077 return not self.main_device_is_ccd()
1078
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001079 def _initialize_programmer(self, rw_only=False):
1080 """Initialize the firmware programmer.
1081
1082 @param rw_only: True to initialize a programmer which only
1083 programs the RW portions.
1084 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -07001085 if self._programmer:
1086 return
1087 # Initialize firmware programmer
Dan Shia5fef052015-05-18 23:28:47 -07001088 servo_version = self.get_servo_version()
Dan Shi9cb0eec2014-06-03 09:04:50 -07001089 if servo_version.startswith('servo_v2'):
J. Richard Barnette8f19b392014-08-11 14:05:39 -07001090 self._programmer = firmware_programmer.ProgrammerV2(self)
Wai-Hong Tamc36c4d22016-09-09 10:39:45 -07001091 self._programmer_rw = firmware_programmer.ProgrammerV2RwOnly(self)
Kevin Chengdf2e29f2016-09-09 02:31:22 -07001092 # Both servo v3 and v4 use the same programming methods so just leverage
1093 # ProgrammerV3 for servo v4 as well.
1094 elif (servo_version.startswith('servo_v3') or
1095 servo_version.startswith('servo_v4')):
Dan Shia5fef052015-05-18 23:28:47 -07001096 self._programmer = firmware_programmer.ProgrammerV3(self)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001097 self._programmer_rw = firmware_programmer.ProgrammerV3RwOnly(self)
J. Richard Barnette8f19b392014-08-11 14:05:39 -07001098 else:
1099 raise error.TestError(
1100 'No firmware programmer for servo version: %s' %
Congbin Guo42427612019-02-12 10:22:06 -08001101 servo_version)
J. Richard Barnette8f19b392014-08-11 14:05:39 -07001102
1103
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001104 def program_bios(self, image, rw_only=False):
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -08001105 """Program bios on DUT with given image.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -08001106
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -08001107 @param image: a string, file name of the BIOS image to program
1108 on the DUT.
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001109 @param rw_only: True to only program the RW portion of BIOS.
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -08001110
Vadim Bendebury1a7ec632013-02-17 16:22:09 -08001111 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -07001112 self._initialize_programmer()
Ricky Liangc31aab32014-07-03 16:23:29 +08001113 if not self.is_localhost():
1114 image = self._scp_image(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001115 if rw_only:
1116 self._programmer_rw.program_bios(image)
1117 else:
1118 self._programmer.program_bios(image)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -08001119
1120
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001121 def program_ec(self, image, rw_only=False):
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -08001122 """Program ec on DUT with given image.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -08001123
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -08001124 @param image: a string, file name of the EC image to program
1125 on the DUT.
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001126 @param rw_only: True to only program the RW portion of EC.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -08001127
Vadim Bendebury1a7ec632013-02-17 16:22:09 -08001128 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -07001129 self._initialize_programmer()
Ricky Liangc31aab32014-07-03 16:23:29 +08001130 if not self.is_localhost():
1131 image = self._scp_image(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001132 if rw_only:
Congbin Guo42427612019-02-12 10:22:06 -08001133 self._programmer_rw.program_ec(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001134 else:
Congbin Guo42427612019-02-12 10:22:06 -08001135 self._programmer.program_ec(image)
1136
1137
1138 def _reprogram(self, tarball_path, firmware_name, image_candidates,
1139 rw_only):
1140 """Helper function to reprogram firmware for EC or BIOS.
1141
1142 @param tarball_path: The path of the downloaded build tarball.
1143 @param: firmware_name: either 'EC' or 'BIOS'.
1144 @param image_candidates: A tuple of the paths of image candidates.
1145 @param rw_only: True to only install firmware to its RW portions. Keep
1146 the RO portions unchanged.
1147
1148 @raise: TestError if cannot extract firmware from the tarball.
1149 """
Mary Ruthven771b2012019-08-19 12:18:49 -07001150 dest_dir = os.path.join(os.path.dirname(tarball_path), firmware_name)
1151 # Create the firmware_name subdirectory if it doesn't exist.
1152 if not os.path.exists(dest_dir):
1153 os.mkdir(dest_dir)
Congbin Guo42427612019-02-12 10:22:06 -08001154 image = _extract_image_from_tarball(tarball_path, dest_dir,
1155 image_candidates)
1156 if not image:
1157 if firmware_name == 'EC':
1158 logging.info('Not a Chrome EC, ignore re-programming it')
1159 return
1160 else:
1161 raise error.TestError('Failed to extract the %s image from '
1162 'tarball' % firmware_name)
1163
Namyoon Woo0e623622019-10-08 16:02:50 -07001164 # Extract subsidiary binaries for EC
1165 if firmware_name == 'EC':
1166 # Find a monitor binary for NPCX_UUT chip type, if any.
1167 mon_candidates = [ w.replace('ec.bin', 'npcx_monitor.bin')
1168 for w in image_candidates ]
1169 _extract_image_from_tarball(tarball_path, dest_dir, mon_candidates)
1170
Congbin Guo42427612019-02-12 10:22:06 -08001171 logging.info('Will re-program %s %snow', firmware_name,
1172 'RW ' if rw_only else '')
1173
1174 if firmware_name == 'EC':
1175 self.program_ec(os.path.join(dest_dir, image), rw_only)
1176 else:
1177 self.program_bios(os.path.join(dest_dir, image), rw_only)
1178
1179
Shelley Chenac61d5a2019-06-24 15:35:46 -07001180 def program_firmware(self, board, model, tarball_path, rw_only=False):
Congbin Guo42427612019-02-12 10:22:06 -08001181 """Program firmware (EC, if applied, and BIOS) of the DUT.
1182
Shelley Chenac61d5a2019-06-24 15:35:46 -07001183 @param board: The DUT board name.
Congbin Guo42427612019-02-12 10:22:06 -08001184 @param model: The DUT model name.
1185 @param tarball_path: The path of the downloaded build tarball.
1186 @param rw_only: True to only install firmware to its RW portions. Keep
1187 the RO portions unchanged.
1188 """
Shelley Chenac61d5a2019-06-24 15:35:46 -07001189 ap_image_candidates = ('image.bin', 'image-%s.bin' % model,
1190 'image-%s.bin' % board)
1191 ec_image_candidates = ('ec.bin', '%s/ec.bin' % model,
1192 '%s/ec.bin' % board)
Congbin Guo42427612019-02-12 10:22:06 -08001193
1194 self._reprogram(tarball_path, 'EC', ec_image_candidates, rw_only)
1195 self._reprogram(tarball_path, 'BIOS', ap_image_candidates, rw_only)
1196
1197 self.get_power_state_controller().reset()
1198 time.sleep(Servo.BOOT_DELAY)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001199
Fang Dengafb88142013-05-30 17:44:31 -07001200
1201 def _switch_usbkey_power(self, power_state, detection_delay=False):
1202 """Switch usbkey power.
1203
1204 This function switches usbkey power by setting the value of
1205 'prtctl4_pwren'. If the power is already in the
1206 requested state, this function simply returns.
1207
1208 @param power_state: A string, 'on' or 'off'.
1209 @param detection_delay: A boolean value, if True, sleep
1210 for |USB_DETECTION_DELAY| after switching
1211 the power on.
1212 """
Kevin Chenga22c4a82016-10-07 14:13:25 -07001213 # TODO(kevcheng): Forgive me for this terrible hack. This is just to
1214 # handle beaglebones that haven't yet updated and have the
1215 # safe_switch_usbkey_power RPC. I'll remove this once all beaglebones
1216 # have been updated and also think about a better way to handle
1217 # situations like this.
1218 try:
1219 self._server.safe_switch_usbkey_power(power_state)
1220 except Exception:
1221 self.set('prtctl4_pwren', power_state)
Fang Dengafb88142013-05-30 17:44:31 -07001222 if power_state == 'off':
1223 time.sleep(self.USB_POWEROFF_DELAY)
1224 elif detection_delay:
1225 time.sleep(self.USB_DETECTION_DELAY)
1226
1227
1228 def switch_usbkey(self, usb_state):
1229 """Connect USB flash stick to either host or DUT, or turn USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001230
1231 This function switches the servo multiplexer to provide electrical
Fang Dengafb88142013-05-30 17:44:31 -07001232 connection between the USB port J3 and either host or DUT side. It
1233 can also be used to turn the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001234
Fang Dengafb88142013-05-30 17:44:31 -07001235 Switching to 'dut' or 'host' is accompanied by powercycling
1236 of the USB stick, because it sometimes gets wedged if the mux
1237 is switched while the stick power is on.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001238
Fang Dengafb88142013-05-30 17:44:31 -07001239 @param usb_state: A string, one of 'dut', 'host', or 'off'.
1240 'dut' and 'host' indicate which side the
1241 USB flash device is required to be connected to.
1242 'off' indicates turning the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001243
Fang Dengafb88142013-05-30 17:44:31 -07001244 @raise: error.TestError in case the parameter is not 'dut'
1245 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001246 """
Fang Dengafb88142013-05-30 17:44:31 -07001247 if self.get_usbkey_direction() == usb_state:
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001248 return
1249
Fang Dengafb88142013-05-30 17:44:31 -07001250 if usb_state == 'off':
Dan Shia5fef052015-05-18 23:28:47 -07001251 self._switch_usbkey_power('off')
1252 self._usb_state = usb_state
1253 return
Fang Dengafb88142013-05-30 17:44:31 -07001254 elif usb_state == 'host':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001255 mux_direction = 'servo_sees_usbkey'
Fang Dengafb88142013-05-30 17:44:31 -07001256 elif usb_state == 'dut':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001257 mux_direction = 'dut_sees_usbkey'
1258 else:
Fang Dengafb88142013-05-30 17:44:31 -07001259 raise error.TestError('Unknown USB state request: %s' % usb_state)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001260
Fang Dengafb88142013-05-30 17:44:31 -07001261 self._switch_usbkey_power('off')
Kevin Chenga22c4a82016-10-07 14:13:25 -07001262 # TODO(kevcheng): Forgive me for this terrible hack. This is just to
1263 # handle beaglebones that haven't yet updated and have the
1264 # safe_switch_usbkey RPC. I'll remove this once all beaglebones have
1265 # been updated and also think about a better way to handle situations
1266 # like this.
1267 try:
1268 self._server.safe_switch_usbkey(mux_direction)
1269 except Exception:
1270 self.set('usb_mux_sel1', mux_direction)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001271 time.sleep(self.USB_POWEROFF_DELAY)
Fang Dengafb88142013-05-30 17:44:31 -07001272 self._switch_usbkey_power('on', usb_state == 'host')
1273 self._usb_state = usb_state
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001274
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001275
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001276 def get_usbkey_direction(self):
Fang Dengafb88142013-05-30 17:44:31 -07001277 """Get which side USB is connected to or 'off' if usb power is off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001278
Fang Dengafb88142013-05-30 17:44:31 -07001279 @return: A string, one of 'dut', 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001280 """
Fang Dengafb88142013-05-30 17:44:31 -07001281 if not self._usb_state:
1282 if self.get('prtctl4_pwren') == 'off':
1283 self._usb_state = 'off'
1284 elif self.get('usb_mux_sel1').startswith('dut'):
1285 self._usb_state = 'dut'
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001286 else:
Fang Dengafb88142013-05-30 17:44:31 -07001287 self._usb_state = 'host'
1288 return self._usb_state
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001289
1290
Wai-Hong Tam60377262018-03-01 10:55:39 -08001291 def set_servo_v4_role(self, role):
1292 """Set the power role of servo v4, either 'src' or 'snk'.
1293
1294 It does nothing if not a servo v4.
1295
1296 @param role: Power role for DUT port on servo v4, either 'src' or 'snk'.
1297 """
1298 servo_version = self.get_servo_version()
1299 if servo_version.startswith('servo_v4'):
1300 value = self.get('servo_v4_role')
1301 if value != role:
1302 self.set_nocheck('servo_v4_role', role)
1303 else:
1304 logging.debug('Already in the role: %s.', role)
1305 else:
1306 logging.debug('Not a servo v4, unable to set role to %s.', role)
1307
1308
Ruben Rodriguez Buchillon5f7ee682019-09-30 12:50:09 -07001309 def supports_built_in_pd_control(self):
1310 """Return whether the servo type supports pd charging and control."""
1311 servo_type = self.get('servo_type')
1312 if 'servo_v4' not in servo_type:
1313 # Only servo v4 supports this feature.
1314 logging.info('%r type does not support pd control.', servo_type)
1315 return False
1316 # On servo v4, it still needs ot be the type-c version.
1317 if not self.get('servo_v4_type') == 'type-c':
1318 logging.info('PD controls require a type-c servo v4.')
1319 return False
1320 # Lastly, one cannot really do anything without a plugged in charger.
1321 chg_port_mv = self.get('ppchg5_mv')
1322 if chg_port_mv < V4_CHG_ATTACHED_MIN_VOLTAGE_MV:
1323 logging.warn('It appears that no charger is plugged into servo v4. '
1324 'Charger port voltage: %dmV', chg_port_mv)
1325 return False
1326 logging.info('Charger port voltage: %dmV', chg_port_mv)
1327 return True
1328
Mary Ruthven739b2922019-08-22 11:16:06 -07001329 def set_servo_v4_dts_mode(self, state):
1330 """Set servo v4 dts mode to off or on.
1331
1332 It does nothing if not a servo v4. Disable the ccd watchdog if we're
1333 disabling dts mode. CCD will disconnect. The watchdog only allows CCD
1334 to disconnect for 10 seconds until it kills servod. Disable the
1335 watchdog, so CCD can stay disconnected indefinitely.
1336
1337 @param state: Set servo v4 dts mode 'off' or 'on'.
1338 """
1339 servo_version = self.get_servo_version()
1340 if not servo_version.startswith('servo_v4'):
1341 logging.debug('Not a servo v4, unable to set dts mode %s.', state)
1342 return
1343
1344 # TODO(mruthven): remove watchdog check once the labstation has been
1345 # updated to have support for modifying the watchdog.
1346 set_watchdog = self.has_control('watchdog') and 'ccd' in servo_version
1347 enable_watchdog = state == 'on'
1348
1349 if set_watchdog and not enable_watchdog:
1350 self.set_nocheck('watchdog_remove', 'ccd')
1351
1352 self.set_nocheck('servo_v4_dts_mode', state)
1353
1354 if set_watchdog and enable_watchdog:
1355 self.set_nocheck('watchdog_add', 'ccd')
1356
1357
Congbin Guofc3b8962019-03-22 17:38:46 -07001358 @property
1359 def uart_logs_dir(self):
1360 """Return the directory to save UART logs."""
1361 return self._uart.logs_dir if self._uart else ""
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001362
Congbin Guofc3b8962019-03-22 17:38:46 -07001363
1364 @uart_logs_dir.setter
1365 def uart_logs_dir(self, logs_dir):
1366 """Set directory to save UART logs.
1367
1368 @param logs_dir String of directory name."""
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001369 if self._uart:
Congbin Guofc3b8962019-03-22 17:38:46 -07001370 self._uart.logs_dir = logs_dir
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001371
1372
1373 def close(self):
1374 """Close the servo object."""
1375 if self._uart:
1376 self._uart.stop_capture()
Congbin Guofc3b8962019-03-22 17:38:46 -07001377 self._uart.dump()
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001378 self._uart = None