blob: a2da497b6066930d57299e59cee5e328d83a691c [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 """
50 for image in image_candidates:
51 status = server_utils.system(
52 ('tar xf %s -C %s %s' % (tarball, dest_dir, image)),
53 timeout=60, ignore_status=True)
54 if status == 0:
55 return image
56 return None
57
58
J. Richard Barnette0c9c5882014-06-11 15:27:05 -070059class _PowerStateController(object):
60
61 """Class to provide board-specific power operations.
62
63 This class is responsible for "power on" and "power off"
64 operations that can operate without making assumptions in
65 advance about board state. It offers an interface that
66 abstracts out the different sequences required for different
67 board types.
68
69 """
70
71 # Constants acceptable to be passed for the `rec_mode` parameter
72 # to power_on().
73 #
74 # REC_ON: Boot the DUT in recovery mode, i.e. boot from USB or
75 # SD card.
76 # REC_OFF: Boot in normal mode, i.e. boot from internal storage.
77
78 REC_ON = 'rec'
79 REC_OFF = 'on'
Shelley Chen65938622018-05-16 07:45:54 -070080 REC_ON_FORCE_MRC = 'rec_force_mrc'
J. Richard Barnette0c9c5882014-06-11 15:27:05 -070081
82 # Delay in seconds needed between asserting and de-asserting
83 # warm reset.
84 _RESET_HOLD_TIME = 0.5
85
86 def __init__(self, servo):
87 """Initialize the power state control.
88
89 @param servo Servo object providing the underlying `set` and `get`
90 methods for the target controls.
91
92 """
93 self._servo = servo
94
95 def reset(self):
96 """Force the DUT to reset.
97
98 The DUT is guaranteed to be on at the end of this call,
99 regardless of its previous state, provided that there is
100 working OS software. This also guarantees that the EC has
101 been restarted.
102
103 """
104 self._servo.set_nocheck('power_state', 'reset')
105
106 def warm_reset(self):
107 """Apply warm reset to the DUT.
108
109 This asserts, then de-asserts the 'warm_reset' signal.
110 Generally, this causes the board to restart.
111
112 """
Ravutappa951ca432019-05-15 09:52:52 -0700113 # TODO: warm_reset support has added to power_state.py. Once it
114 # available to labstation remove fallback method.
115 try:
116 self._servo.set_nocheck('power_state', 'warm_reset')
117 except error.TestFail as err:
118 logging.info("Fallback to warm_reset control method")
119 self._servo.set_get_all(['warm_reset:on',
J. Richard Barnette0c9c5882014-06-11 15:27:05 -0700120 'sleep:%.4f' % self._RESET_HOLD_TIME,
121 'warm_reset:off'])
122
J. Richard Barnette0c9c5882014-06-11 15:27:05 -0700123 def power_off(self):
124 """Force the DUT to power off.
125
126 The DUT is guaranteed to be off at the end of this call,
127 regardless of its previous state, provided that there is
128 working EC and boot firmware. There is no requirement for
129 working OS software.
130
131 """
132 self._servo.set_nocheck('power_state', 'off')
133
134 def power_on(self, rec_mode=REC_OFF):
135 """Force the DUT to power on.
136
137 Prior to calling this function, the DUT must be powered off,
138 e.g. with a call to `power_off()`.
139
140 At power on, recovery mode is set as specified by the
141 corresponding argument. When booting with recovery mode on, it
142 is the caller's responsibility to unplug/plug in a bootable
143 external storage device.
144
145 If the DUT requires a delay after powering on but before
146 processing inputs such as USB stick insertion, the delay is
147 handled by this method; the caller is not responsible for such
148 delays.
149
150 @param rec_mode Setting of recovery mode to be applied at
151 power on. default: REC_OFF aka 'off'
152
153 """
154 self._servo.set_nocheck('power_state', rec_mode)
155
156
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700157class _Uart(object):
Congbin Guo0f00be82019-04-18 17:51:14 -0700158 """Class to capture UART streams of CPU, EC, Cr50, etc."""
159 _UartToCapture = ('cpu', 'ec', 'cr50', 'servo_v4', 'servo_micro', 'usbpd')
160
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700161 def __init__(self, servo):
162 self._servo = servo
163 self._streams = []
Congbin Guo0f00be82019-04-18 17:51:14 -0700164 self.logs_dir = None
Congbin Guofe4d4e82019-04-18 17:51:14 -0700165
Congbin Guo0f00be82019-04-18 17:51:14 -0700166 def _start_stop_capture(self, uart, start):
167 """Helper function to start/stop capturing on specified UART.
168
169 @param uart: The UART name to start/stop capturing.
170 @param start: True to start capturing, otherwise stop.
171
Ruben Rodriguez Buchillonecb525f2019-09-03 14:52:14 -0700172 @returns True if the operation completes successfully.
173 False if the UART capturing is not supported or failed due to
174 an error.
Congbin Guo0f00be82019-04-18 17:51:14 -0700175 """
176 logging.debug('%s capturing %s UART.', 'Start' if start else 'Stop',
177 uart)
Ruben Rodriguez Buchillonb0e2a9f2019-08-12 12:40:44 -0700178 uart_cmd = '%s_uart_capture' % uart
Ruben Rodriguez Buchillonecb525f2019-09-03 14:52:14 -0700179 target_level = 'on' if start else 'off'
180 level = None
Ruben Rodriguez Buchillonb0e2a9f2019-08-12 12:40:44 -0700181 if self._servo.has_control(uart_cmd):
Ruben Rodriguez Buchillon9f485642019-08-21 14:32:22 -0700182 # Do our own implementation of set() here as not_applicable
183 # should also count as a valid control.
Ruben Rodriguez Buchillonecb525f2019-09-03 14:52:14 -0700184 logging.debug('Trying to set %s to %s.', uart_cmd, target_level)
185 try:
186 self._servo.set_nocheck(uart_cmd, target_level)
187 level = self._servo.get(uart_cmd)
188 except error.TestFail as e:
189 # Any sort of test failure here should not stop the test. This
190 # is just to capture more output. Log and move on.
191 logging.warning('Failed to set %s to %s. %s. Ignoring.',
192 uart_cmd, target_level, str(e))
193 if level == target_level:
194 logging.debug('Managed to set %s to %s.', uart_cmd, level)
195 else:
196 logging.debug('Failed to set %s to %s. Got %s.', uart_cmd,
197 target_level, level)
198 return level == target_level
Congbin Guo0f00be82019-04-18 17:51:14 -0700199
200 def start_capture(self):
201 """Start capturing UART streams."""
202 for uart in self._UartToCapture:
203 if self._start_stop_capture(uart, True):
204 self._streams.append(('%s_uart_stream' % uart, '%s_uart.log' %
205 uart))
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700206
Congbin Guofc3b8962019-03-22 17:38:46 -0700207 def dump(self):
208 """Dump UART streams to log files accordingly."""
Congbin Guo0f00be82019-04-18 17:51:14 -0700209 if not self.logs_dir:
Congbin Guofc3b8962019-03-22 17:38:46 -0700210 return
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700211
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700212 for stream, logfile in self._streams:
Congbin Guo0f00be82019-04-18 17:51:14 -0700213 logfile_fullname = os.path.join(self.logs_dir, logfile)
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700214 try:
215 content = self._servo.get(stream)
216 except Exception as err:
217 logging.warn('Failed to get UART log for %s: %s', stream, err)
218 continue
219
Kuang-che Wu12192fc2019-08-31 09:55:00 +0800220 if content == 'not_applicable':
221 logging.warn('%s is not applicable', stream)
222 continue
223
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700224 # The UART stream may contain non-printable characters, and servo
225 # returns it in string representation. We use `ast.leteral_eval`
226 # to revert it back.
227 with open(logfile_fullname, 'a') as fd:
Kuang-che Wu12192fc2019-08-31 09:55:00 +0800228 try:
229 fd.write(ast.literal_eval(content))
230 except ValueError:
231 logging.exception('Invalid value for %s: %r', stream,
232 content)
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700233
234 def stop_capture(self):
235 """Stop capturing UART streams."""
Congbin Guo0f00be82019-04-18 17:51:14 -0700236 for uart in self._UartToCapture:
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700237 try:
Congbin Guo0f00be82019-04-18 17:51:14 -0700238 self._start_stop_capture(uart, False)
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700239 except Exception as err:
240 logging.warn('Failed to stop UART logging for %s: %s', uart,
241 err)
242
243
J. Richard Barnette384056b2012-04-16 11:04:46 -0700244class Servo(object):
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700245
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700246 """Manages control of a Servo board.
247
248 Servo is a board developed by hardware group to aide in the debug and
249 control of various partner devices. Servo's features include the simulation
250 of pressing the power button, closing the lid, and pressing Ctrl-d. This
251 class manages setting up and communicating with a servo demon (servod)
252 process. It provides both high-level functions for common servo tasks and
253 low-level functions for directly setting and reading gpios.
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700254
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700255 """
256
Chrome Bot9a1137d2011-07-19 14:35:00 -0700257 # Power button press delays in seconds.
J. Richard Barnetted2e4cbd2012-06-29 12:18:40 -0700258 #
J. Richard Barnette5383f072012-07-26 17:35:40 -0700259 # The EC specification says that 8.0 seconds should be enough
260 # for the long power press. However, some platforms need a bit
261 # more time. Empirical testing has found these requirements:
262 # Alex: 8.2 seconds
263 # ZGB: 8.5 seconds
264 # The actual value is set to the largest known necessary value.
265 #
266 # TODO(jrbarnette) Being generous is the right thing to do for
267 # existing platforms, but if this code is to be used for
268 # qualification of new hardware, we should be less generous.
Chrome Bot9a1137d2011-07-19 14:35:00 -0700269 SHORT_DELAY = 0.1
J. Richard Barnette5383f072012-07-26 17:35:40 -0700270
Todd Broch31c82502011-08-29 08:14:39 -0700271 # Maximum number of times to re-read power button on release.
Todd Brochcf7c6652012-02-24 13:03:59 -0800272 GET_RETRY_MAX = 10
Chrome Bot9a1137d2011-07-19 14:35:00 -0700273
J. Richard Barnette5383f072012-07-26 17:35:40 -0700274 # Delays to deal with DUT state transitions.
Chrome Bot9a1137d2011-07-19 14:35:00 -0700275 SLEEP_DELAY = 6
276 BOOT_DELAY = 10
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700277
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700278 # Default minimum time interval between 'press' and 'release'
279 # keyboard events.
Vic Yang0aca1c22012-11-19 18:33:56 -0800280 SERVO_KEY_PRESS_DELAY = 0.1
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700281
Tom Wai-Hong Tam93b5c092015-05-14 02:50:43 +0800282 # Time to toggle recovery switch on and off.
283 REC_TOGGLE_DELAY = 0.1
284
Tom Wai-Hong Tamf9ded092015-05-20 05:37:22 +0800285 # Time to toggle development switch on and off.
286 DEV_TOGGLE_DELAY = 0.1
287
Jon Salzc88e5b62011-11-30 14:38:54 +0800288 # Time between an usb disk plugged-in and detected in the system.
289 USB_DETECTION_DELAY = 10
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800290 # Time to keep USB power off before and after USB mux direction is changed
291 USB_POWEROFF_DELAY = 2
Jon Salzc88e5b62011-11-30 14:38:54 +0800292
Simran Basib7850bb2013-07-24 12:33:42 -0700293 # Time to wait before timing out on servo initialization.
294 INIT_TIMEOUT_SECS = 10
Simran Basi59331342013-07-12 12:06:29 -0700295
J. Richard Barnette67ccb872012-04-19 16:34:56 -0700296
Ricky Liang0dd379c2014-04-23 16:29:08 +0800297 def __init__(self, servo_host, servo_serial=None):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700298 """Sets up the servo communication infrastructure.
299
Fang Deng5d518f42013-08-02 14:04:32 -0700300 @param servo_host: A ServoHost object representing
301 the host running servod.
Dana Goyettea2f00ea2019-06-26 16:14:12 -0700302 @type servo_host: autotest_lib.server.hosts.servo_host.ServoHost
Ricky Liang0dd379c2014-04-23 16:29:08 +0800303 @param servo_serial: Serial number of the servo board.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700304 """
Fang Deng5d518f42013-08-02 14:04:32 -0700305 # TODO(fdeng): crbug.com/298379
306 # We should move servo_host object out of servo object
307 # to minimize the dependencies on the rest of Autotest.
308 self._servo_host = servo_host
Ricky Liang0dd379c2014-04-23 16:29:08 +0800309 self._servo_serial = servo_serial
Richard Barnette180efe62016-12-02 23:20:44 +0000310 self._server = servo_host.get_servod_server_proxy()
J. Richard Barnette0c9c5882014-06-11 15:27:05 -0700311 self._power_state = _PowerStateController(self)
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700312 self._uart = _Uart(self)
Fang Dengafb88142013-05-30 17:44:31 -0700313 self._usb_state = None
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700314 self._programmer = None
Dana Goyette4dc0adc2019-05-06 14:51:53 -0700315 self._prev_log_inode = None
316 self._prev_log_size = 0
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800317
Ricky Liang0dd379c2014-04-23 16:29:08 +0800318 @property
319 def servo_serial(self):
320 """Returns the serial number of the servo board."""
321 return self._servo_serial
322
Dana Goyette0b6e6402019-10-04 11:09:24 -0700323 def rotate_servod_logs(self, filename=None, directory=None):
324 """Save the latest servod log into a local directory, then rotate logs.
Dana Goyette4dc0adc2019-05-06 14:51:53 -0700325
Dana Goyette0b6e6402019-10-04 11:09:24 -0700326 The files will be <filename>.DEBUG, <filename>.INFO, <filename>.WARNING,
327 or just <filename>.log if not using split level logging.
328
329 @param filename: local filename prefix (no file extension) to use.
330 If None, rotate log but don't save it.
331 @param directory: local directory to save logs into (if unset, use cwd)
Dana Goyette4dc0adc2019-05-06 14:51:53 -0700332 """
Dana Goyette0b6e6402019-10-04 11:09:24 -0700333 if self.is_localhost():
334 # Local servod usually runs without log-dir, so can't be collected.
335 # TODO(crbug.com/1011516): allow localhost when rotation is enabled
336 return
337
338 log_dir = '/var/log/servod_%s' % self._servo_host.servo_port
339
340 if filename:
Dana Goyettec8613342019-11-13 15:03:28 -0800341 logging.info("Saving servod logs: %s/%s.*", directory or '.',
342 filename)
Dana Goyette0b6e6402019-10-04 11:09:24 -0700343 # TODO(crrev.com/c/1793030): remove no-level case once CL is pushed
344 for level_name in ('', 'DEBUG', 'INFO', 'WARNING'):
345
346 remote_path = os.path.join(log_dir, 'latest')
347 if level_name:
348 remote_path += '.%s' % level_name
349
350 local_path = '%s.%s' % (filename, level_name or 'log')
351 if directory:
352 local_path = os.path.join(directory, local_path)
353
354 try:
355 self._servo_host.get_file(
356 remote_path, local_path, try_rsync=False)
357
358 except error.AutoservRunError as e:
359 result = e.result_obj
360 if result.exit_status != 0:
361 stderr = result.stderr.strip()
362
363 # File not existing is okay, but warn for anything else.
364 if 'no such' not in stderr.lower():
365 logging.warn(
366 "Couldn't retrieve servod log: %s",
367 stderr or '\n%s' % result)
368
369 try:
370 if os.stat(local_path).st_size == 0:
371 os.unlink(local_path)
372 except EnvironmentError:
373 pass
374
375 else:
376 # No filename given, so caller wants to discard the log lines.
377 # Remove the symlinks to prevent old log-dir links from being
378 # picked up multiple times when using servod without log-dir.
379 remote_path = os.path.join(log_dir, 'latest*')
380 self._servo_host.run(
381 "rm %s" % remote_path,
382 stderr_tee=None, ignore_status=True)
383
384 # Servod log rotation renames current log, then creates a new file with
385 # the old name: log.<date> -> log.<date>.1.tbz2 -> log.<date>.2.tbz2
386
387 # Must rotate after copying, or the copy would be the new, empty file.
388 try:
389 self.set_nocheck('rotate_servod_logs', 'yes')
390 except ControlUnavailableError as e:
Dana Goyettec8613342019-11-13 15:03:28 -0800391 # Missing control (possibly old servod)
Dana Goyette0b6e6402019-10-04 11:09:24 -0700392 logging.warn("Couldn't rotate servod logs: %s", str(e))
Dana Goyettec8613342019-11-13 15:03:28 -0800393 except error.TestFail:
394 # Control exists but gave an error; don't let it fail the test.
395 # The error is already logged in set_nocheck().
396 pass
Ricky Liang0dd379c2014-04-23 16:29:08 +0800397
Tom Wai-Hong Tam22ee0e52013-04-03 13:36:39 +0800398 def get_power_state_controller(self):
J. Richard Barnette75136b32013-03-26 13:38:44 -0700399 """Return the power state controller for this Servo.
400
401 The power state controller provides board-independent
402 interfaces for reset, power-on, power-off operations.
403
404 """
405 return self._power_state
406
Fang Deng5d518f42013-08-02 14:04:32 -0700407
Mary Ruthvena3c52b82019-10-09 14:09:08 -0700408 def initialize_dut(self, cold_reset=False, enable_main=True):
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700409 """Initializes a dut for testing purposes.
410
411 This sets various servo signals back to default values
412 appropriate for the target board. By default, if the DUT
413 is already on, it stays on. If the DUT is powered off
414 before initialization, its state afterward is unspecified.
415
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700416 Rationale: Basic initialization of servo sets the lid open,
417 when there is a lid. This operation won't affect powered on
418 units; however, setting the lid open may power on a unit
J. Richard Barnette75136b32013-03-26 13:38:44 -0700419 that's off, depending on the board type and previous state
420 of the device.
421
J. Richard Barnette4b6af0d2014-06-05 09:57:20 -0700422 If `cold_reset` is a true value, the DUT and its EC will be
423 reset, and the DUT rebooted in normal mode.
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700424
425 @param cold_reset If True, cold reset the device after
426 initialization.
Mary Ruthvena3c52b82019-10-09 14:09:08 -0700427 @param enable_main If True, make sure the main servo device has
428 control of the dut.
429
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700430 """
Mary Ruthvena3c52b82019-10-09 14:09:08 -0700431 if enable_main:
432 self.enable_main_servo_device()
433
Dana Goyettea2f00ea2019-06-26 16:14:12 -0700434 try:
435 self._server.hwinit()
436 except socket.error as e:
437 e.filename = '%s:%s' % (self._servo_host.hostname,
438 self._servo_host.servo_port)
439 raise
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700440 self.set('usb_mux_oe1', 'on')
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700441 self._usb_state = None
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700442 self.switch_usbkey('off')
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700443 self._uart.start_capture()
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700444 if cold_reset:
J. Richard Barnette4b6af0d2014-06-05 09:57:20 -0700445 self._power_state.reset()
J. Richard Barnette1777f5d2014-09-03 15:18:19 -0700446 logging.debug('Servo initialized, version is %s',
447 self._server.get_version())
Ruben Rodriguez Buchillonb0e2a9f2019-08-12 12:40:44 -0700448 if self.has_control('init_keyboard'):
449 # This indicates the servod version does not
450 # have explicit keyboard initialization yet.
451 # Ignore this.
Ruben Rodriguez Buchillon05298242019-05-20 11:29:06 -0700452 # TODO(coconutruben): change this back to set() about a month
453 # after crrev.com/c/1586239 has been merged (or whenever that
454 # logic is in the labstation images).
455 self.set_nocheck('init_keyboard','on')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700456
457
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800458 def is_localhost(self):
459 """Is the servod hosted locally?
460
461 Returns:
462 True if local hosted; otherwise, False.
463 """
Fang Deng5d518f42013-08-02 14:04:32 -0700464 return self._servo_host.is_localhost()
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800465
466
Mary Ruthvenecf12712019-06-26 17:36:21 -0700467 def get_os_version(self):
468 """Returns the chromeos release version."""
469 lsb_release_content = self.system_output('cat /etc/lsb-release',
470 ignore_status=True)
471 return lsbrelease_utils.get_chromeos_release_builder_path(
472 lsb_release_content=lsb_release_content)
473
474
Mary Ruthven83bb5952019-06-27 12:34:05 -0700475 def get_servod_version(self):
476 """Returns the servod version."""
477 result = self._servo_host.run('servod --version')
478 # TODO: use system_output once servod --version prints to stdout
479 stdout = result.stdout.strip()
480 return stdout if stdout else result.stderr.strip()
481
482
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700483 def power_long_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700484 """Simulate a long power button press."""
J. Richard Barnettef12bff22012-07-18 14:41:06 -0700485 # After a long power press, the EC may ignore the next power
486 # button press (at least on Alex). To guarantee that this
487 # won't happen, we need to allow the EC one second to
488 # collect itself.
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800489 # long_press is defined as 8.5s in servod
490 self.set_nocheck('power_key', 'long_press')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700491
492
493 def power_normal_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700494 """Simulate a normal power button press."""
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800495 # press is defined as 1.2s in servod
496 self.set_nocheck('power_key', 'press')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700497
498
499 def power_short_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700500 """Simulate a short power button press."""
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800501 # tab is defined as 0.2s in servod
502 self.set_nocheck('power_key', 'tab')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700503
504
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800505 def power_key(self, press_secs='tab'):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700506 """Simulate a power button press.
507
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800508 @param press_secs: int, float, str; time to press key in seconds or
509 known shorthand: 'tab' 'press' 'long_press'
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700510 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800511 self.set_nocheck('power_key', press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700512
513
Ruben Zakarian47c1d4f2019-09-30 14:17:41 -0700514 def pwr_button(self, action='press'):
515 """Simulate a power button press.
516
517 @param action: str; could be press or could be release.
518 """
519 self.set_nocheck('pwr_button', action)
520
521
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700522 def lid_open(self):
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800523 """Simulate opening the lid and raise exception if all attempts fail"""
524 self.set('lid_open', 'yes')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700525
526
527 def lid_close(self):
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800528 """Simulate closing the lid and raise exception if all attempts fail
Craig Harrison48997262011-06-27 14:31:10 -0700529
530 Waits 6 seconds to ensure the device is fully asleep before returning.
531 """
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800532 self.set('lid_open', 'no')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700533 time.sleep(Servo.SLEEP_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700534
Ruben Zakarian47c1d4f2019-09-30 14:17:41 -0700535
536 def vbus_power_get(self):
537 """Get current vbus_power."""
538 return self.get('vbus_power')
539
540
Shelley Chenc26575a2015-09-18 10:56:16 -0700541 def volume_up(self, timeout=300):
Kevin Chenge448a8b2016-09-09 10:18:11 -0700542 """Simulate pushing the volume down button.
543
544 @param timeout: Timeout for setting the volume.
545 """
Shelley Chenc26575a2015-09-18 10:56:16 -0700546 self.set_get_all(['volume_up:yes',
547 'sleep:%.4f' % self.SERVO_KEY_PRESS_DELAY,
548 'volume_up:no'])
549 # we need to wait for commands to take effect before moving on
550 time_left = float(timeout)
551 while time_left > 0.0:
552 value = self.get('volume_up')
553 if value == 'no':
554 return
555 time.sleep(self.SHORT_DELAY)
556 time_left = time_left - self.SHORT_DELAY
557 raise error.TestFail("Failed setting volume_up to no")
558
559 def volume_down(self, timeout=300):
Kevin Chenge448a8b2016-09-09 10:18:11 -0700560 """Simulate pushing the volume down button.
561
562 @param timeout: Timeout for setting the volume.
563 """
Shelley Chenc26575a2015-09-18 10:56:16 -0700564 self.set_get_all(['volume_down:yes',
565 'sleep:%.4f' % self.SERVO_KEY_PRESS_DELAY,
566 'volume_down:no'])
567 # we need to wait for commands to take effect before moving on
568 time_left = float(timeout)
569 while time_left > 0.0:
570 value = self.get('volume_down')
571 if value == 'no':
572 return
573 time.sleep(self.SHORT_DELAY)
574 time_left = time_left - self.SHORT_DELAY
575 raise error.TestFail("Failed setting volume_down to no")
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700576
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800577 def ctrl_d(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800578 """Simulate Ctrl-d simultaneous button presses.
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800579
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800580 @param press_secs: int, float, str; time to press key in seconds or
581 known shorthand: 'tab' 'press' 'long_press'
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800582 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800583 self.set_nocheck('ctrl_d', press_secs)
Gediminas Ramanauskas9da80f22012-11-14 12:59:43 -0800584
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800585
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800586 def ctrl_u(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800587 """Simulate Ctrl-u simultaneous button presses.
588
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800589 @param press_secs: int, float, str; time to press key in seconds or
590 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800591 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800592 self.set_nocheck('ctrl_u', press_secs)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700593
594
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800595 def ctrl_enter(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800596 """Simulate Ctrl-enter simultaneous button presses.
597
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800598 @param press_secs: int, float, str; time to press key in seconds or
599 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800600 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800601 self.set_nocheck('ctrl_enter', press_secs)
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -0700602
603
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800604 def ctrl_key(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800605 """Simulate Enter key button press.
606
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800607 @param press_secs: int, float, str; time to press key in seconds or
608 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800609 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800610 self.set_nocheck('ctrl_key', press_secs)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700611
612
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800613 def enter_key(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800614 """Simulate Enter key button press.
615
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800616 @param press_secs: int, float, str; time to press key in seconds or
617 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800618 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800619 self.set_nocheck('enter_key', press_secs)
Todd Broch9753bd42012-03-21 10:15:08 -0700620
621
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800622 def refresh_key(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800623 """Simulate Refresh key (F3) button press.
624
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800625 @param press_secs: int, float, str; time to press key in seconds or
626 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800627 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800628 self.set_nocheck('refresh_key', press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700629
630
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800631 def ctrl_refresh_key(self, press_secs='tab'):
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800632 """Simulate Ctrl and Refresh (F3) simultaneous press.
633
634 This key combination is an alternative of Space key.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800635
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800636 @param press_secs: int, float, str; time to press key in seconds or
637 known shorthand: 'tab' 'press' 'long_press'
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800638 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800639 self.set_nocheck('ctrl_refresh_key', press_secs)
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800640
641
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800642 def imaginary_key(self, press_secs='tab'):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700643 """Simulate imaginary key button press.
644
645 Maps to a key that doesn't physically exist.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800646
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800647 @param press_secs: int, float, str; time to press key in seconds or
648 known shorthand: 'tab' 'press' 'long_press'
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700649 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800650 self.set_nocheck('imaginary_key', press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700651
652
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800653 def sysrq_x(self, press_secs='tab'):
Vincent Palatine7dc9282016-07-14 11:31:58 +0200654 """Simulate Alt VolumeUp X simulataneous press.
655
656 This key combination is the kernel system request (sysrq) X.
657
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800658 @param press_secs: int, float, str; time to press key in seconds or
659 known shorthand: 'tab' 'press' 'long_press'
Vincent Palatine7dc9282016-07-14 11:31:58 +0200660 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800661 self.set_nocheck('sysrq_x', press_secs)
Vincent Palatine7dc9282016-07-14 11:31:58 +0200662
663
Tom Wai-Hong Tam93b5c092015-05-14 02:50:43 +0800664 def toggle_recovery_switch(self):
665 """Toggle recovery switch on and off."""
666 self.enable_recovery_mode()
667 time.sleep(self.REC_TOGGLE_DELAY)
668 self.disable_recovery_mode()
669
670
Craig Harrison6b36b122011-06-28 17:58:43 -0700671 def enable_recovery_mode(self):
672 """Enable recovery mode on device."""
673 self.set('rec_mode', 'on')
674
675
676 def disable_recovery_mode(self):
677 """Disable recovery mode on device."""
678 self.set('rec_mode', 'off')
679
680
Tom Wai-Hong Tamf9ded092015-05-20 05:37:22 +0800681 def toggle_development_switch(self):
682 """Toggle development switch on and off."""
683 self.enable_development_mode()
684 time.sleep(self.DEV_TOGGLE_DELAY)
685 self.disable_development_mode()
686
687
Craig Harrison6b36b122011-06-28 17:58:43 -0700688 def enable_development_mode(self):
689 """Enable development mode on device."""
690 self.set('dev_mode', 'on')
691
692
693 def disable_development_mode(self):
694 """Disable development mode on device."""
695 self.set('dev_mode', 'off')
696
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700697 def boot_devmode(self):
698 """Boot a dev-mode device that is powered off."""
Tom Wai-Hong Tam23869102012-01-17 15:41:30 +0800699 self.power_short_press()
Craig Harrison48997262011-06-27 14:31:10 -0700700 self.pass_devmode()
701
702
703 def pass_devmode(self):
704 """Pass through boot screens in dev-mode."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700705 time.sleep(Servo.BOOT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700706 self.ctrl_d()
Chrome Bot9a1137d2011-07-19 14:35:00 -0700707 time.sleep(Servo.BOOT_DELAY)
Craig Harrison48997262011-06-27 14:31:10 -0700708
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700709
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800710 def get_board(self):
Wai-Hong Tam0f904002017-09-19 15:52:22 -0700711 """Get the board connected to servod."""
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800712 return self._server.get_board()
713
714
Wai-Hong Tam0f904002017-09-19 15:52:22 -0700715 def get_base_board(self):
716 """Get the board of the base connected to servod."""
Wai-Hong Tam7f6169e2017-09-29 09:23:48 -0700717 try:
718 return self._server.get_base_board()
719 except xmlrpclib.Fault as e:
720 # TODO(waihong): Remove the following compatibility check when
721 # the new versions of hdctools are deployed.
722 if 'not supported' in str(e):
723 logging.warning('The servod is too old that get_base_board '
724 'not supported.')
725 return ''
726 raise
Wai-Hong Tam0f904002017-09-19 15:52:22 -0700727
728
Wai-Hong Tamcc399ee2017-12-08 12:43:28 -0800729 def get_ec_active_copy(self):
730 """Get the active copy of the EC image."""
731 return self.get('ec_active_copy')
732
733
Todd Brochefe72cb2012-07-11 19:58:53 -0700734 def _get_xmlrpclib_exception(self, xmlexc):
735 """Get meaningful exception string from xmlrpc.
736
737 Args:
738 xmlexc: xmlrpclib.Fault object
739
740 xmlrpclib.Fault.faultString has the following format:
741
742 <type 'exception type'>:'actual error message'
743
744 Parse and return the real exception from the servod side instead of the
745 less meaningful one like,
746 <Fault 1: "<type 'exceptions.AttributeError'>:'tca6416' object has no
747 attribute 'hw_driver'">
748
749 Returns:
750 string of underlying exception raised in servod.
751 """
752 return re.sub('^.*>:', '', xmlexc.faultString)
753
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700754 def has_control(self, control):
755 """Query servod server to determine if |control| is a valid control.
756
757 @param control: str, control name to query
758
759 @returns: true if |control| is a known control, false otherwise.
760 """
761 assert control
762 try:
763 # If the control exists, doc() will work.
764 self._server.doc(control)
765 return True
766 except xmlrpclib.Fault as e:
767 if re.search('No control %s' % control,
768 self._get_xmlrpclib_exception(e)):
769 return False
770 raise e
Todd Brochefe72cb2012-07-11 19:58:53 -0700771
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700772 def get(self, gpio_name):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700773 """Get the value of a gpio from Servod.
774
775 @param gpio_name Name of the gpio.
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700776
777 @returns: server response to |gpio_name| request.
778
779 @raise ControlUnavailableError: if |gpio_name| not a known control.
780 @raise error.TestFail: for all other failures doing get().
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700781 """
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700782 assert gpio_name
Todd Brochefe72cb2012-07-11 19:58:53 -0700783 try:
Simran Basi1cbc5f22013-07-17 14:23:58 -0700784 return self._server.get(gpio_name)
Todd Brochefe72cb2012-07-11 19:58:53 -0700785 except xmlrpclib.Fault as e:
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700786 err_str = self._get_xmlrpclib_exception(e)
787 err_msg = "Getting '%s' :: %s" % (gpio_name, err_str)
788 unknown_ctrl = re.findall(NO_CONTROL_RE, err_str)
789 if unknown_ctrl:
790 raise ControlUnavailableError('No control named %r' %
791 unknown_ctrl[0])
792 else:
793 logging.error(err_msg)
794 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700795
796
797 def set(self, gpio_name, gpio_value):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700798 """Set and check the value of a gpio using Servod.
799
800 @param gpio_name Name of the gpio.
801 @param gpio_value New setting for the gpio.
Dana Goyette7ff06c92019-10-11 13:38:03 -0700802 @raise error.TestFail: if the control value fails to change.
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700803 """
Chrome Bot9a1137d2011-07-19 14:35:00 -0700804 self.set_nocheck(gpio_name, gpio_value)
Todd Brochcf7c6652012-02-24 13:03:59 -0800805 retry_count = Servo.GET_RETRY_MAX
Dana Goyette7ff06c92019-10-11 13:38:03 -0700806 actual_value = self.get(gpio_name)
807 while gpio_value != actual_value and retry_count:
Ilja H. Friedel04be2bd2014-05-07 21:29:59 -0700808 logging.warning("%s != %s, retry %d", gpio_name, gpio_value,
Dana Goyette7ff06c92019-10-11 13:38:03 -0700809 retry_count)
Todd Brochcf7c6652012-02-24 13:03:59 -0800810 retry_count -= 1
811 time.sleep(Servo.SHORT_DELAY)
Dana Goyette7ff06c92019-10-11 13:38:03 -0700812 actual_value = self.get(gpio_name)
813
814 if gpio_value != actual_value:
815 raise error.TestFail(
816 'Servo failed to set %s to %s. Got %s.'
817 % (gpio_name, gpio_value, actual_value))
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700818
819
820 def set_nocheck(self, gpio_name, gpio_value):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700821 """Set the value of a gpio using Servod.
822
823 @param gpio_name Name of the gpio.
824 @param gpio_value New setting for the gpio.
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700825
826 @raise ControlUnavailableError: if |gpio_name| not a known control.
827 @raise error.TestFail: for all other failures doing set().
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700828 """
Ruben Rodriguez Buchillon1c0219c2019-06-25 12:30:44 -0700829 # The real danger here is to pass a None value through the xmlrpc.
830 assert gpio_name and gpio_value is not None
Mary Ruthvendfa45342019-06-25 10:10:17 -0700831 logging.debug('Setting %s to %r', gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700832 try:
Simran Basi1cbc5f22013-07-17 14:23:58 -0700833 self._server.set(gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700834 except xmlrpclib.Fault as e:
Ruben Rodriguez Buchillon2cba8e42019-08-12 11:31:53 -0700835 err_str = self._get_xmlrpclib_exception(e)
836 err_msg = "Setting '%s' :: %s" % (gpio_name, err_str)
837 unknown_ctrl = re.findall(NO_CONTROL_RE, err_str)
838 if unknown_ctrl:
839 raise ControlUnavailableError('No control named %r' %
840 unknown_ctrl[0])
841 else:
842 logging.error(err_msg)
843 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700844
845
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800846 def set_get_all(self, controls):
847 """Set &| get one or more control values.
848
849 @param controls: list of strings, controls to set &| get.
850
851 @raise: error.TestError in case error occurs setting/getting values.
852 """
853 rv = []
854 try:
Mary Ruthvendfa45342019-06-25 10:10:17 -0700855 logging.debug('Set/get all: %s', str(controls))
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800856 rv = self._server.set_get_all(controls)
857 except xmlrpclib.Fault as e:
858 # TODO(waihong): Remove the following backward compatibility when
859 # the new versions of hdctools are deployed.
860 if 'not supported' in str(e):
861 logging.warning('The servod is too old that set_get_all '
862 'not supported. Use set and get instead.')
863 for control in controls:
864 if ':' in control:
865 (name, value) = control.split(':')
866 if name == 'sleep':
867 time.sleep(float(value))
868 else:
869 self.set_nocheck(name, value)
870 rv.append(True)
871 else:
872 rv.append(self.get(name))
873 else:
874 err_msg = "Problem with '%s' :: %s" % \
875 (controls, self._get_xmlrpclib_exception(e))
876 raise error.TestFail(err_msg)
877 return rv
878
879
Jon Salzc88e5b62011-11-30 14:38:54 +0800880 # TODO(waihong) It may fail if multiple servo's are connected to the same
881 # host. Should look for a better way, like the USB serial name, to identify
882 # the USB device.
Simran Basi741b5d42012-05-18 11:27:15 -0700883 # TODO(sbasi) Remove this code from autoserv once firmware tests have been
884 # updated.
Kevin Chenga22c4a82016-10-07 14:13:25 -0700885 def probe_host_usb_dev(self, timeout=_USB_PROBE_TIMEOUT):
Jon Salzc88e5b62011-11-30 14:38:54 +0800886 """Probe the USB disk device plugged-in the servo from the host side.
887
Kevin Chengeb06fe72016-08-22 15:26:32 -0700888 It uses servod to discover if there is a usb device attached to it.
Jon Salzc88e5b62011-11-30 14:38:54 +0800889
Kevin Chenga22c4a82016-10-07 14:13:25 -0700890 @param timeout The timeout period when probing for the usb host device.
891
892 @return: String of USB disk path (e.g. '/dev/sdb') or None.
Jon Salzc88e5b62011-11-30 14:38:54 +0800893 """
Ruben Rodriguez Buchillon9a4bfc32018-10-16 20:46:25 +0800894 # Set up Servo's usb mux.
895 self.switch_usbkey('host')
Kevin Chenga22c4a82016-10-07 14:13:25 -0700896 return self._server.probe_host_usb_dev(timeout) or None
Jon Salzc88e5b62011-11-30 14:38:54 +0800897
898
Mike Truty49153d82012-08-21 22:27:30 -0500899 def image_to_servo_usb(self, image_path=None,
900 make_image_noninteractive=False):
901 """Install an image to the USB key plugged into the servo.
902
903 This method may copy any image to the servo USB key including a
904 recovery image or a test image. These images are frequently used
905 for test purposes such as restoring a corrupted image or conducting
906 an upgrade of ec/fw/kernel as part of a test of a specific image part.
907
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700908 @param image_path Path on the host to the recovery image.
909 @param make_image_noninteractive Make the recovery image
910 noninteractive, therefore the DUT
911 will reboot automatically after
912 installation.
Mike Truty49153d82012-08-21 22:27:30 -0500913 """
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700914 # We're about to start plugging/unplugging the USB key. We
915 # don't know the state of the DUT, or what it might choose
916 # to do to the device after hotplug. To avoid surprises,
917 # force the DUT to be off.
918 self._server.hwinit()
Ruben Rodriguez Buchillonb0e2a9f2019-08-12 12:40:44 -0700919 if self.has_control('init_keyboard'):
920 # This indicates the servod version does not
921 # have explicit keyboard initialization yet.
922 # Ignore this.
Ruben Rodriguez Buchillonbd3b74f2019-08-08 16:18:27 -0700923 # TODO(coconutruben): change this back to set() about a month
924 # after crrev.com/c/1586239 has been merged (or whenever that
925 # logic is in the labstation images).
926 self.set_nocheck('init_keyboard','on')
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700927 self._power_state.power_off()
Mike Truty49153d82012-08-21 22:27:30 -0500928
Mike Truty49153d82012-08-21 22:27:30 -0500929 if image_path:
Garry Wangd2656812019-08-07 17:29:16 -0700930 # Set up Servo's usb mux.
931 self.switch_usbkey('host')
Tom Wai-Hong Tam42f136d2012-10-26 11:11:23 +0800932 logging.info('Searching for usb device and copying image to it. '
933 'Please wait a few minutes...')
Mike Truty49153d82012-08-21 22:27:30 -0500934 if not self._server.download_image_to_usb(image_path):
935 logging.error('Failed to transfer requested image to USB. '
936 'Please take a look at Servo Logs.')
937 raise error.AutotestError('Download image to usb failed.')
938 if make_image_noninteractive:
939 logging.info('Making image noninteractive')
940 if not self._server.make_image_noninteractive():
941 logging.error('Failed to make image noninteractive. '
942 'Please take a look at Servo Logs.')
943
Kalin Stoyanovb3c11f32018-05-11 09:02:00 -0700944 def boot_in_recovery_mode(self):
945 """Boot host DUT in recovery mode."""
946 self._power_state.power_on(rec_mode=self._power_state.REC_ON)
947 self.switch_usbkey('dut')
948
Mike Truty49153d82012-08-21 22:27:30 -0500949
Simran Basi741b5d42012-05-18 11:27:15 -0700950 def install_recovery_image(self, image_path=None,
J. Richard Barnetteb6de7e32013-02-14 13:28:04 -0800951 make_image_noninteractive=False):
Dan Shic67f1332016-04-06 12:38:06 -0700952 """Install the recovery image specified by the path onto the DUT.
Jon Salzc88e5b62011-11-30 14:38:54 +0800953
954 This method uses google recovery mode to install a recovery image
955 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 +0800956 board specified by the usb_dev. If no image path is specified
Jon Salzc88e5b62011-11-30 14:38:54 +0800957 we use the recovery image already on the usb image.
958
Dan Shic67f1332016-04-06 12:38:06 -0700959 @param image_path: Path on the host to the recovery image.
960 @param make_image_noninteractive: Make the recovery image
961 noninteractive, therefore the DUT will reboot automatically
962 after installation.
Jon Salzc88e5b62011-11-30 14:38:54 +0800963 """
Mike Truty49153d82012-08-21 22:27:30 -0500964 self.image_to_servo_usb(image_path, make_image_noninteractive)
Garry Wangd2656812019-08-07 17:29:16 -0700965 # Give the DUT some time to power_off if we skip
966 # download image to usb. (crbug.com/982993)
967 if not image_path:
968 time.sleep(10)
Kalin Stoyanovb3c11f32018-05-11 09:02:00 -0700969 self.boot_in_recovery_mode()
Jon Salzc88e5b62011-11-30 14:38:54 +0800970
971
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800972 def _scp_image(self, image_path):
973 """Copy image to the servo host.
974
975 When programming a firmware image on the DUT, the image must be
976 located on the host to which the servo device is connected. Sometimes
977 servo is controlled by a remote host, in this case the image needs to
978 be transferred to the remote host.
979
980 @param image_path: a string, name of the firmware image file to be
981 transferred.
982 @return: a string, full path name of the copied file on the remote.
983 """
984
985 dest_path = os.path.join('/tmp', os.path.basename(image_path))
Fang Deng5d518f42013-08-02 14:04:32 -0700986 self._servo_host.send_file(image_path, dest_path)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800987 return dest_path
988
989
Dan Shifecdaf42015-07-28 10:17:26 -0700990 def system(self, command, timeout=3600):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700991 """Execute the passed in command on the servod host.
992
993 @param command Command to be executed.
Dan Shifecdaf42015-07-28 10:17:26 -0700994 @param timeout Maximum number of seconds of runtime allowed. Default to
995 1 hour.
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700996 """
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800997 logging.info('Will execute on servo host: %s', command)
Fang Deng5d518f42013-08-02 14:04:32 -0700998 self._servo_host.run(command, timeout=timeout)
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800999
1000
Dan Shifecdaf42015-07-28 10:17:26 -07001001 def system_output(self, command, timeout=3600,
Vadim Bendebury89ec24e2012-12-17 12:54:18 -08001002 ignore_status=False, args=()):
1003 """Execute the passed in command on the servod host, return stdout.
1004
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -07001005 @param command a string, the command to execute
1006 @param timeout an int, max number of seconds to wait til command
Dan Shifecdaf42015-07-28 10:17:26 -07001007 execution completes. Default to 1 hour.
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -07001008 @param ignore_status a Boolean, if true - ignore command's nonzero exit
Vadim Bendebury89ec24e2012-12-17 12:54:18 -08001009 status, otherwise an exception will be thrown
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -07001010 @param args a tuple of strings, each becoming a separate command line
Vadim Bendebury89ec24e2012-12-17 12:54:18 -08001011 parameter for the command
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -07001012 @return command's stdout as a string.
Vadim Bendebury89ec24e2012-12-17 12:54:18 -08001013 """
Fang Deng5d518f42013-08-02 14:04:32 -07001014 return self._servo_host.run(command, timeout=timeout,
1015 ignore_status=ignore_status,
1016 args=args).stdout.strip()
Vadim Bendeburyb80ba592012-12-07 15:02:34 -08001017
1018
Mary Ruthven38d90af2019-08-16 13:13:31 -07001019 def get_servo_version(self, active=False):
Dan Shia5fef052015-05-18 23:28:47 -07001020 """Get the version of the servo, e.g., servo_v2 or servo_v3.
1021
Mary Ruthven38d90af2019-08-16 13:13:31 -07001022 @param active: Only return the servo type with the active device.
Dan Shia5fef052015-05-18 23:28:47 -07001023 @return: The version of the servo.
1024
1025 """
Mary Ruthven38d90af2019-08-16 13:13:31 -07001026 servo_type = self._server.get_version()
1027 if '_and_' not in servo_type or not active:
1028 return servo_type
1029
1030 # If servo v4 is using ccd and servo micro, modify the servo type to
1031 # reflect the active device.
1032 active_device = self.get('active_v4_device')
1033 if active_device in servo_type:
1034 logging.info('%s is active', active_device)
1035 return 'servo_v4_with_' + active_device
1036
1037 logging.warn("%s is active even though it's not in servo type",
1038 active_device)
1039 return servo_type
Dan Shia5fef052015-05-18 23:28:47 -07001040
1041
Mary Ruthvena3c52b82019-10-09 14:09:08 -07001042 def get_main_servo_device(self):
1043 """Return the main servo device"""
1044 servo_type = self.get_servo_version()
1045 return servo_type.split('_with_')[-1].split('_and_')[0]
1046
1047
1048 def enable_main_servo_device(self):
1049 """Make sure the main device has control of the dut."""
Mary Ruthven1409d9d2019-10-22 20:44:24 -07001050 # Cr50 detects servo using the EC uart. It doesn't work well if the
1051 # board doesn't use EC uart. The lab active_v4_device doesn't handle
1052 # this correctly. Check ec_uart_pty before trying to change the active
1053 # device.
1054 # TODO(crbug.com/1016842): reenable the setting the main device when
1055 # active device works on labstations.
1056 return
Mary Ruthvena3c52b82019-10-09 14:09:08 -07001057 if not self.has_control('active_v4_device'):
1058 return
1059 self.set('active_v4_device', self.get_main_servo_device())
1060
1061
Ruben Rodriguez Buchillon1823bad2019-09-30 17:04:34 -07001062 def main_device_is_ccd(self):
1063 """Whether the main servo device (no prefixes) is a ccd device."""
Mary Ruthven2724ee62019-07-16 11:16:59 -07001064 servo = self._server.get_version()
1065 return 'ccd_cr50' in servo and 'servo_micro' not in servo
1066
1067
Ruben Rodriguez Buchillon1823bad2019-09-30 17:04:34 -07001068 def main_device_is_flex(self):
1069 """Whether the main servo device (no prefixes) is a legacy device."""
1070 return not self.main_device_is_ccd()
1071
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001072 def _initialize_programmer(self, rw_only=False):
1073 """Initialize the firmware programmer.
1074
1075 @param rw_only: True to initialize a programmer which only
1076 programs the RW portions.
1077 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -07001078 if self._programmer:
1079 return
1080 # Initialize firmware programmer
Dan Shia5fef052015-05-18 23:28:47 -07001081 servo_version = self.get_servo_version()
Dan Shi9cb0eec2014-06-03 09:04:50 -07001082 if servo_version.startswith('servo_v2'):
J. Richard Barnette8f19b392014-08-11 14:05:39 -07001083 self._programmer = firmware_programmer.ProgrammerV2(self)
Wai-Hong Tamc36c4d22016-09-09 10:39:45 -07001084 self._programmer_rw = firmware_programmer.ProgrammerV2RwOnly(self)
Kevin Chengdf2e29f2016-09-09 02:31:22 -07001085 # Both servo v3 and v4 use the same programming methods so just leverage
1086 # ProgrammerV3 for servo v4 as well.
1087 elif (servo_version.startswith('servo_v3') or
1088 servo_version.startswith('servo_v4')):
Dan Shia5fef052015-05-18 23:28:47 -07001089 self._programmer = firmware_programmer.ProgrammerV3(self)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001090 self._programmer_rw = firmware_programmer.ProgrammerV3RwOnly(self)
J. Richard Barnette8f19b392014-08-11 14:05:39 -07001091 else:
1092 raise error.TestError(
1093 'No firmware programmer for servo version: %s' %
Congbin Guo42427612019-02-12 10:22:06 -08001094 servo_version)
J. Richard Barnette8f19b392014-08-11 14:05:39 -07001095
1096
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001097 def program_bios(self, image, rw_only=False):
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -08001098 """Program bios on DUT with given image.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -08001099
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -08001100 @param image: a string, file name of the BIOS image to program
1101 on the DUT.
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001102 @param rw_only: True to only program the RW portion of BIOS.
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -08001103
Vadim Bendebury1a7ec632013-02-17 16:22:09 -08001104 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -07001105 self._initialize_programmer()
Ricky Liangc31aab32014-07-03 16:23:29 +08001106 if not self.is_localhost():
1107 image = self._scp_image(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001108 if rw_only:
1109 self._programmer_rw.program_bios(image)
1110 else:
1111 self._programmer.program_bios(image)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -08001112
1113
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001114 def program_ec(self, image, rw_only=False):
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -08001115 """Program ec on DUT with given image.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -08001116
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -08001117 @param image: a string, file name of the EC image to program
1118 on the DUT.
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001119 @param rw_only: True to only program the RW portion of EC.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -08001120
Vadim Bendebury1a7ec632013-02-17 16:22:09 -08001121 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -07001122 self._initialize_programmer()
Ricky Liangc31aab32014-07-03 16:23:29 +08001123 if not self.is_localhost():
1124 image = self._scp_image(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001125 if rw_only:
Congbin Guo42427612019-02-12 10:22:06 -08001126 self._programmer_rw.program_ec(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001127 else:
Congbin Guo42427612019-02-12 10:22:06 -08001128 self._programmer.program_ec(image)
1129
1130
1131 def _reprogram(self, tarball_path, firmware_name, image_candidates,
1132 rw_only):
1133 """Helper function to reprogram firmware for EC or BIOS.
1134
1135 @param tarball_path: The path of the downloaded build tarball.
1136 @param: firmware_name: either 'EC' or 'BIOS'.
1137 @param image_candidates: A tuple of the paths of image candidates.
1138 @param rw_only: True to only install firmware to its RW portions. Keep
1139 the RO portions unchanged.
1140
1141 @raise: TestError if cannot extract firmware from the tarball.
1142 """
Mary Ruthven771b2012019-08-19 12:18:49 -07001143 dest_dir = os.path.join(os.path.dirname(tarball_path), firmware_name)
1144 # Create the firmware_name subdirectory if it doesn't exist.
1145 if not os.path.exists(dest_dir):
1146 os.mkdir(dest_dir)
Congbin Guo42427612019-02-12 10:22:06 -08001147 image = _extract_image_from_tarball(tarball_path, dest_dir,
1148 image_candidates)
1149 if not image:
1150 if firmware_name == 'EC':
1151 logging.info('Not a Chrome EC, ignore re-programming it')
1152 return
1153 else:
1154 raise error.TestError('Failed to extract the %s image from '
1155 'tarball' % firmware_name)
1156
Namyoon Woo0e623622019-10-08 16:02:50 -07001157 # Extract subsidiary binaries for EC
1158 if firmware_name == 'EC':
1159 # Find a monitor binary for NPCX_UUT chip type, if any.
1160 mon_candidates = [ w.replace('ec.bin', 'npcx_monitor.bin')
1161 for w in image_candidates ]
1162 _extract_image_from_tarball(tarball_path, dest_dir, mon_candidates)
1163
Congbin Guo42427612019-02-12 10:22:06 -08001164 logging.info('Will re-program %s %snow', firmware_name,
1165 'RW ' if rw_only else '')
1166
1167 if firmware_name == 'EC':
1168 self.program_ec(os.path.join(dest_dir, image), rw_only)
1169 else:
1170 self.program_bios(os.path.join(dest_dir, image), rw_only)
1171
1172
Shelley Chenac61d5a2019-06-24 15:35:46 -07001173 def program_firmware(self, board, model, tarball_path, rw_only=False):
Congbin Guo42427612019-02-12 10:22:06 -08001174 """Program firmware (EC, if applied, and BIOS) of the DUT.
1175
Shelley Chenac61d5a2019-06-24 15:35:46 -07001176 @param board: The DUT board name.
Congbin Guo42427612019-02-12 10:22:06 -08001177 @param model: The DUT model name.
1178 @param tarball_path: The path of the downloaded build tarball.
1179 @param rw_only: True to only install firmware to its RW portions. Keep
1180 the RO portions unchanged.
1181 """
Shelley Chenac61d5a2019-06-24 15:35:46 -07001182 ap_image_candidates = ('image.bin', 'image-%s.bin' % model,
1183 'image-%s.bin' % board)
1184 ec_image_candidates = ('ec.bin', '%s/ec.bin' % model,
1185 '%s/ec.bin' % board)
Congbin Guo42427612019-02-12 10:22:06 -08001186
1187 self._reprogram(tarball_path, 'EC', ec_image_candidates, rw_only)
1188 self._reprogram(tarball_path, 'BIOS', ap_image_candidates, rw_only)
1189
1190 self.get_power_state_controller().reset()
1191 time.sleep(Servo.BOOT_DELAY)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001192
Fang Dengafb88142013-05-30 17:44:31 -07001193
1194 def _switch_usbkey_power(self, power_state, detection_delay=False):
1195 """Switch usbkey power.
1196
1197 This function switches usbkey power by setting the value of
1198 'prtctl4_pwren'. If the power is already in the
1199 requested state, this function simply returns.
1200
1201 @param power_state: A string, 'on' or 'off'.
1202 @param detection_delay: A boolean value, if True, sleep
1203 for |USB_DETECTION_DELAY| after switching
1204 the power on.
1205 """
Kevin Chenga22c4a82016-10-07 14:13:25 -07001206 # TODO(kevcheng): Forgive me for this terrible hack. This is just to
1207 # handle beaglebones that haven't yet updated and have the
1208 # safe_switch_usbkey_power RPC. I'll remove this once all beaglebones
1209 # have been updated and also think about a better way to handle
1210 # situations like this.
1211 try:
1212 self._server.safe_switch_usbkey_power(power_state)
1213 except Exception:
1214 self.set('prtctl4_pwren', power_state)
Fang Dengafb88142013-05-30 17:44:31 -07001215 if power_state == 'off':
1216 time.sleep(self.USB_POWEROFF_DELAY)
1217 elif detection_delay:
1218 time.sleep(self.USB_DETECTION_DELAY)
1219
1220
1221 def switch_usbkey(self, usb_state):
1222 """Connect USB flash stick to either host or DUT, or turn USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001223
1224 This function switches the servo multiplexer to provide electrical
Fang Dengafb88142013-05-30 17:44:31 -07001225 connection between the USB port J3 and either host or DUT side. It
1226 can also be used to turn the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001227
Fang Dengafb88142013-05-30 17:44:31 -07001228 Switching to 'dut' or 'host' is accompanied by powercycling
1229 of the USB stick, because it sometimes gets wedged if the mux
1230 is switched while the stick power is on.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001231
Fang Dengafb88142013-05-30 17:44:31 -07001232 @param usb_state: A string, one of 'dut', 'host', or 'off'.
1233 'dut' and 'host' indicate which side the
1234 USB flash device is required to be connected to.
1235 'off' indicates turning the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001236
Fang Dengafb88142013-05-30 17:44:31 -07001237 @raise: error.TestError in case the parameter is not 'dut'
1238 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001239 """
Fang Dengafb88142013-05-30 17:44:31 -07001240 if self.get_usbkey_direction() == usb_state:
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001241 return
1242
Fang Dengafb88142013-05-30 17:44:31 -07001243 if usb_state == 'off':
Dan Shia5fef052015-05-18 23:28:47 -07001244 self._switch_usbkey_power('off')
1245 self._usb_state = usb_state
1246 return
Fang Dengafb88142013-05-30 17:44:31 -07001247 elif usb_state == 'host':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001248 mux_direction = 'servo_sees_usbkey'
Fang Dengafb88142013-05-30 17:44:31 -07001249 elif usb_state == 'dut':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001250 mux_direction = 'dut_sees_usbkey'
1251 else:
Fang Dengafb88142013-05-30 17:44:31 -07001252 raise error.TestError('Unknown USB state request: %s' % usb_state)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001253
Fang Dengafb88142013-05-30 17:44:31 -07001254 self._switch_usbkey_power('off')
Kevin Chenga22c4a82016-10-07 14:13:25 -07001255 # TODO(kevcheng): Forgive me for this terrible hack. This is just to
1256 # handle beaglebones that haven't yet updated and have the
1257 # safe_switch_usbkey RPC. I'll remove this once all beaglebones have
1258 # been updated and also think about a better way to handle situations
1259 # like this.
1260 try:
1261 self._server.safe_switch_usbkey(mux_direction)
1262 except Exception:
1263 self.set('usb_mux_sel1', mux_direction)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001264 time.sleep(self.USB_POWEROFF_DELAY)
Fang Dengafb88142013-05-30 17:44:31 -07001265 self._switch_usbkey_power('on', usb_state == 'host')
1266 self._usb_state = usb_state
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001267
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001268
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001269 def get_usbkey_direction(self):
Fang Dengafb88142013-05-30 17:44:31 -07001270 """Get which side USB is connected to or 'off' if usb power is off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001271
Fang Dengafb88142013-05-30 17:44:31 -07001272 @return: A string, one of 'dut', 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001273 """
Fang Dengafb88142013-05-30 17:44:31 -07001274 if not self._usb_state:
1275 if self.get('prtctl4_pwren') == 'off':
1276 self._usb_state = 'off'
1277 elif self.get('usb_mux_sel1').startswith('dut'):
1278 self._usb_state = 'dut'
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001279 else:
Fang Dengafb88142013-05-30 17:44:31 -07001280 self._usb_state = 'host'
1281 return self._usb_state
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001282
1283
Wai-Hong Tam60377262018-03-01 10:55:39 -08001284 def set_servo_v4_role(self, role):
1285 """Set the power role of servo v4, either 'src' or 'snk'.
1286
1287 It does nothing if not a servo v4.
1288
1289 @param role: Power role for DUT port on servo v4, either 'src' or 'snk'.
1290 """
1291 servo_version = self.get_servo_version()
1292 if servo_version.startswith('servo_v4'):
1293 value = self.get('servo_v4_role')
1294 if value != role:
1295 self.set_nocheck('servo_v4_role', role)
1296 else:
1297 logging.debug('Already in the role: %s.', role)
1298 else:
1299 logging.debug('Not a servo v4, unable to set role to %s.', role)
1300
1301
Ruben Rodriguez Buchillon5f7ee682019-09-30 12:50:09 -07001302 def supports_built_in_pd_control(self):
1303 """Return whether the servo type supports pd charging and control."""
1304 servo_type = self.get('servo_type')
1305 if 'servo_v4' not in servo_type:
1306 # Only servo v4 supports this feature.
1307 logging.info('%r type does not support pd control.', servo_type)
1308 return False
1309 # On servo v4, it still needs ot be the type-c version.
1310 if not self.get('servo_v4_type') == 'type-c':
1311 logging.info('PD controls require a type-c servo v4.')
1312 return False
1313 # Lastly, one cannot really do anything without a plugged in charger.
1314 chg_port_mv = self.get('ppchg5_mv')
1315 if chg_port_mv < V4_CHG_ATTACHED_MIN_VOLTAGE_MV:
1316 logging.warn('It appears that no charger is plugged into servo v4. '
1317 'Charger port voltage: %dmV', chg_port_mv)
1318 return False
1319 logging.info('Charger port voltage: %dmV', chg_port_mv)
1320 return True
1321
Mary Ruthven739b2922019-08-22 11:16:06 -07001322 def set_servo_v4_dts_mode(self, state):
1323 """Set servo v4 dts mode to off or on.
1324
1325 It does nothing if not a servo v4. Disable the ccd watchdog if we're
1326 disabling dts mode. CCD will disconnect. The watchdog only allows CCD
1327 to disconnect for 10 seconds until it kills servod. Disable the
1328 watchdog, so CCD can stay disconnected indefinitely.
1329
1330 @param state: Set servo v4 dts mode 'off' or 'on'.
1331 """
1332 servo_version = self.get_servo_version()
1333 if not servo_version.startswith('servo_v4'):
1334 logging.debug('Not a servo v4, unable to set dts mode %s.', state)
1335 return
1336
1337 # TODO(mruthven): remove watchdog check once the labstation has been
1338 # updated to have support for modifying the watchdog.
1339 set_watchdog = self.has_control('watchdog') and 'ccd' in servo_version
1340 enable_watchdog = state == 'on'
1341
1342 if set_watchdog and not enable_watchdog:
1343 self.set_nocheck('watchdog_remove', 'ccd')
1344
1345 self.set_nocheck('servo_v4_dts_mode', state)
1346
1347 if set_watchdog and enable_watchdog:
1348 self.set_nocheck('watchdog_add', 'ccd')
1349
1350
Congbin Guofc3b8962019-03-22 17:38:46 -07001351 @property
1352 def uart_logs_dir(self):
1353 """Return the directory to save UART logs."""
1354 return self._uart.logs_dir if self._uart else ""
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001355
Congbin Guofc3b8962019-03-22 17:38:46 -07001356
1357 @uart_logs_dir.setter
1358 def uart_logs_dir(self, logs_dir):
1359 """Set directory to save UART logs.
1360
1361 @param logs_dir String of directory name."""
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001362 if self._uart:
Congbin Guofc3b8962019-03-22 17:38:46 -07001363 self._uart.logs_dir = logs_dir
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001364
1365
1366 def close(self):
1367 """Close the servo object."""
1368 if self._uart:
1369 self._uart.stop_capture()
Congbin Guofc3b8962019-03-22 17:38:46 -07001370 self._uart.dump()
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001371 self._uart = None