blob: 5f82cb3915358df92578af57d13ce7f99fe30a7d [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
Mary Ruthven2724ee62019-07-16 11:16:59 -07001062 def running_through_ccd(self):
1063 """Returns True if the setup is using ccd to run."""
1064 servo = self._server.get_version()
1065 return 'ccd_cr50' in servo and 'servo_micro' not in servo
1066
1067
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001068 def _initialize_programmer(self, rw_only=False):
1069 """Initialize the firmware programmer.
1070
1071 @param rw_only: True to initialize a programmer which only
1072 programs the RW portions.
1073 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -07001074 if self._programmer:
1075 return
1076 # Initialize firmware programmer
Dan Shia5fef052015-05-18 23:28:47 -07001077 servo_version = self.get_servo_version()
Dan Shi9cb0eec2014-06-03 09:04:50 -07001078 if servo_version.startswith('servo_v2'):
J. Richard Barnette8f19b392014-08-11 14:05:39 -07001079 self._programmer = firmware_programmer.ProgrammerV2(self)
Wai-Hong Tamc36c4d22016-09-09 10:39:45 -07001080 self._programmer_rw = firmware_programmer.ProgrammerV2RwOnly(self)
Kevin Chengdf2e29f2016-09-09 02:31:22 -07001081 # Both servo v3 and v4 use the same programming methods so just leverage
1082 # ProgrammerV3 for servo v4 as well.
1083 elif (servo_version.startswith('servo_v3') or
1084 servo_version.startswith('servo_v4')):
Dan Shia5fef052015-05-18 23:28:47 -07001085 self._programmer = firmware_programmer.ProgrammerV3(self)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001086 self._programmer_rw = firmware_programmer.ProgrammerV3RwOnly(self)
J. Richard Barnette8f19b392014-08-11 14:05:39 -07001087 else:
1088 raise error.TestError(
1089 'No firmware programmer for servo version: %s' %
Congbin Guo42427612019-02-12 10:22:06 -08001090 servo_version)
J. Richard Barnette8f19b392014-08-11 14:05:39 -07001091
1092
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001093 def program_bios(self, image, rw_only=False):
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -08001094 """Program bios on DUT with given image.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -08001095
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -08001096 @param image: a string, file name of the BIOS image to program
1097 on the DUT.
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001098 @param rw_only: True to only program the RW portion of BIOS.
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -08001099
Vadim Bendebury1a7ec632013-02-17 16:22:09 -08001100 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -07001101 self._initialize_programmer()
Ricky Liangc31aab32014-07-03 16:23:29 +08001102 if not self.is_localhost():
1103 image = self._scp_image(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001104 if rw_only:
1105 self._programmer_rw.program_bios(image)
1106 else:
1107 self._programmer.program_bios(image)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -08001108
1109
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001110 def program_ec(self, image, rw_only=False):
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -08001111 """Program ec on DUT with given image.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -08001112
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -08001113 @param image: a string, file name of the EC image to program
1114 on the DUT.
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001115 @param rw_only: True to only program the RW portion of EC.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -08001116
Vadim Bendebury1a7ec632013-02-17 16:22:09 -08001117 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -07001118 self._initialize_programmer()
Ricky Liangc31aab32014-07-03 16:23:29 +08001119 if not self.is_localhost():
1120 image = self._scp_image(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001121 if rw_only:
Congbin Guo42427612019-02-12 10:22:06 -08001122 self._programmer_rw.program_ec(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001123 else:
Congbin Guo42427612019-02-12 10:22:06 -08001124 self._programmer.program_ec(image)
1125
1126
1127 def _reprogram(self, tarball_path, firmware_name, image_candidates,
1128 rw_only):
1129 """Helper function to reprogram firmware for EC or BIOS.
1130
1131 @param tarball_path: The path of the downloaded build tarball.
1132 @param: firmware_name: either 'EC' or 'BIOS'.
1133 @param image_candidates: A tuple of the paths of image candidates.
1134 @param rw_only: True to only install firmware to its RW portions. Keep
1135 the RO portions unchanged.
1136
1137 @raise: TestError if cannot extract firmware from the tarball.
1138 """
Mary Ruthven771b2012019-08-19 12:18:49 -07001139 dest_dir = os.path.join(os.path.dirname(tarball_path), firmware_name)
1140 # Create the firmware_name subdirectory if it doesn't exist.
1141 if not os.path.exists(dest_dir):
1142 os.mkdir(dest_dir)
Congbin Guo42427612019-02-12 10:22:06 -08001143 image = _extract_image_from_tarball(tarball_path, dest_dir,
1144 image_candidates)
1145 if not image:
1146 if firmware_name == 'EC':
1147 logging.info('Not a Chrome EC, ignore re-programming it')
1148 return
1149 else:
1150 raise error.TestError('Failed to extract the %s image from '
1151 'tarball' % firmware_name)
1152
Namyoon Woo0e623622019-10-08 16:02:50 -07001153 # Extract subsidiary binaries for EC
1154 if firmware_name == 'EC':
1155 # Find a monitor binary for NPCX_UUT chip type, if any.
1156 mon_candidates = [ w.replace('ec.bin', 'npcx_monitor.bin')
1157 for w in image_candidates ]
1158 _extract_image_from_tarball(tarball_path, dest_dir, mon_candidates)
1159
Congbin Guo42427612019-02-12 10:22:06 -08001160 logging.info('Will re-program %s %snow', firmware_name,
1161 'RW ' if rw_only else '')
1162
1163 if firmware_name == 'EC':
1164 self.program_ec(os.path.join(dest_dir, image), rw_only)
1165 else:
1166 self.program_bios(os.path.join(dest_dir, image), rw_only)
1167
1168
Shelley Chenac61d5a2019-06-24 15:35:46 -07001169 def program_firmware(self, board, model, tarball_path, rw_only=False):
Congbin Guo42427612019-02-12 10:22:06 -08001170 """Program firmware (EC, if applied, and BIOS) of the DUT.
1171
Shelley Chenac61d5a2019-06-24 15:35:46 -07001172 @param board: The DUT board name.
Congbin Guo42427612019-02-12 10:22:06 -08001173 @param model: The DUT model name.
1174 @param tarball_path: The path of the downloaded build tarball.
1175 @param rw_only: True to only install firmware to its RW portions. Keep
1176 the RO portions unchanged.
1177 """
Shelley Chenac61d5a2019-06-24 15:35:46 -07001178 ap_image_candidates = ('image.bin', 'image-%s.bin' % model,
1179 'image-%s.bin' % board)
1180 ec_image_candidates = ('ec.bin', '%s/ec.bin' % model,
1181 '%s/ec.bin' % board)
Congbin Guo42427612019-02-12 10:22:06 -08001182
1183 self._reprogram(tarball_path, 'EC', ec_image_candidates, rw_only)
1184 self._reprogram(tarball_path, 'BIOS', ap_image_candidates, rw_only)
1185
1186 self.get_power_state_controller().reset()
1187 time.sleep(Servo.BOOT_DELAY)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001188
Fang Dengafb88142013-05-30 17:44:31 -07001189
1190 def _switch_usbkey_power(self, power_state, detection_delay=False):
1191 """Switch usbkey power.
1192
1193 This function switches usbkey power by setting the value of
1194 'prtctl4_pwren'. If the power is already in the
1195 requested state, this function simply returns.
1196
1197 @param power_state: A string, 'on' or 'off'.
1198 @param detection_delay: A boolean value, if True, sleep
1199 for |USB_DETECTION_DELAY| after switching
1200 the power on.
1201 """
Kevin Chenga22c4a82016-10-07 14:13:25 -07001202 # TODO(kevcheng): Forgive me for this terrible hack. This is just to
1203 # handle beaglebones that haven't yet updated and have the
1204 # safe_switch_usbkey_power RPC. I'll remove this once all beaglebones
1205 # have been updated and also think about a better way to handle
1206 # situations like this.
1207 try:
1208 self._server.safe_switch_usbkey_power(power_state)
1209 except Exception:
1210 self.set('prtctl4_pwren', power_state)
Fang Dengafb88142013-05-30 17:44:31 -07001211 if power_state == 'off':
1212 time.sleep(self.USB_POWEROFF_DELAY)
1213 elif detection_delay:
1214 time.sleep(self.USB_DETECTION_DELAY)
1215
1216
1217 def switch_usbkey(self, usb_state):
1218 """Connect USB flash stick to either host or DUT, or turn USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001219
1220 This function switches the servo multiplexer to provide electrical
Fang Dengafb88142013-05-30 17:44:31 -07001221 connection between the USB port J3 and either host or DUT side. It
1222 can also be used to turn the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001223
Fang Dengafb88142013-05-30 17:44:31 -07001224 Switching to 'dut' or 'host' is accompanied by powercycling
1225 of the USB stick, because it sometimes gets wedged if the mux
1226 is switched while the stick power is on.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001227
Fang Dengafb88142013-05-30 17:44:31 -07001228 @param usb_state: A string, one of 'dut', 'host', or 'off'.
1229 'dut' and 'host' indicate which side the
1230 USB flash device is required to be connected to.
1231 'off' indicates turning the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001232
Fang Dengafb88142013-05-30 17:44:31 -07001233 @raise: error.TestError in case the parameter is not 'dut'
1234 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001235 """
Fang Dengafb88142013-05-30 17:44:31 -07001236 if self.get_usbkey_direction() == usb_state:
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001237 return
1238
Fang Dengafb88142013-05-30 17:44:31 -07001239 if usb_state == 'off':
Dan Shia5fef052015-05-18 23:28:47 -07001240 self._switch_usbkey_power('off')
1241 self._usb_state = usb_state
1242 return
Fang Dengafb88142013-05-30 17:44:31 -07001243 elif usb_state == 'host':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001244 mux_direction = 'servo_sees_usbkey'
Fang Dengafb88142013-05-30 17:44:31 -07001245 elif usb_state == 'dut':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001246 mux_direction = 'dut_sees_usbkey'
1247 else:
Fang Dengafb88142013-05-30 17:44:31 -07001248 raise error.TestError('Unknown USB state request: %s' % usb_state)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001249
Fang Dengafb88142013-05-30 17:44:31 -07001250 self._switch_usbkey_power('off')
Kevin Chenga22c4a82016-10-07 14:13:25 -07001251 # TODO(kevcheng): Forgive me for this terrible hack. This is just to
1252 # handle beaglebones that haven't yet updated and have the
1253 # safe_switch_usbkey RPC. I'll remove this once all beaglebones have
1254 # been updated and also think about a better way to handle situations
1255 # like this.
1256 try:
1257 self._server.safe_switch_usbkey(mux_direction)
1258 except Exception:
1259 self.set('usb_mux_sel1', mux_direction)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001260 time.sleep(self.USB_POWEROFF_DELAY)
Fang Dengafb88142013-05-30 17:44:31 -07001261 self._switch_usbkey_power('on', usb_state == 'host')
1262 self._usb_state = usb_state
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001263
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001264
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001265 def get_usbkey_direction(self):
Fang Dengafb88142013-05-30 17:44:31 -07001266 """Get which side USB is connected to or 'off' if usb power is off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001267
Fang Dengafb88142013-05-30 17:44:31 -07001268 @return: A string, one of 'dut', 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001269 """
Fang Dengafb88142013-05-30 17:44:31 -07001270 if not self._usb_state:
1271 if self.get('prtctl4_pwren') == 'off':
1272 self._usb_state = 'off'
1273 elif self.get('usb_mux_sel1').startswith('dut'):
1274 self._usb_state = 'dut'
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001275 else:
Fang Dengafb88142013-05-30 17:44:31 -07001276 self._usb_state = 'host'
1277 return self._usb_state
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001278
1279
Wai-Hong Tam60377262018-03-01 10:55:39 -08001280 def set_servo_v4_role(self, role):
1281 """Set the power role of servo v4, either 'src' or 'snk'.
1282
1283 It does nothing if not a servo v4.
1284
1285 @param role: Power role for DUT port on servo v4, either 'src' or 'snk'.
1286 """
1287 servo_version = self.get_servo_version()
1288 if servo_version.startswith('servo_v4'):
1289 value = self.get('servo_v4_role')
1290 if value != role:
1291 self.set_nocheck('servo_v4_role', role)
1292 else:
1293 logging.debug('Already in the role: %s.', role)
1294 else:
1295 logging.debug('Not a servo v4, unable to set role to %s.', role)
1296
1297
Ruben Rodriguez Buchillon5f7ee682019-09-30 12:50:09 -07001298 def supports_built_in_pd_control(self):
1299 """Return whether the servo type supports pd charging and control."""
1300 servo_type = self.get('servo_type')
1301 if 'servo_v4' not in servo_type:
1302 # Only servo v4 supports this feature.
1303 logging.info('%r type does not support pd control.', servo_type)
1304 return False
1305 # On servo v4, it still needs ot be the type-c version.
1306 if not self.get('servo_v4_type') == 'type-c':
1307 logging.info('PD controls require a type-c servo v4.')
1308 return False
1309 # Lastly, one cannot really do anything without a plugged in charger.
1310 chg_port_mv = self.get('ppchg5_mv')
1311 if chg_port_mv < V4_CHG_ATTACHED_MIN_VOLTAGE_MV:
1312 logging.warn('It appears that no charger is plugged into servo v4. '
1313 'Charger port voltage: %dmV', chg_port_mv)
1314 return False
1315 logging.info('Charger port voltage: %dmV', chg_port_mv)
1316 return True
1317
Mary Ruthven739b2922019-08-22 11:16:06 -07001318 def set_servo_v4_dts_mode(self, state):
1319 """Set servo v4 dts mode to off or on.
1320
1321 It does nothing if not a servo v4. Disable the ccd watchdog if we're
1322 disabling dts mode. CCD will disconnect. The watchdog only allows CCD
1323 to disconnect for 10 seconds until it kills servod. Disable the
1324 watchdog, so CCD can stay disconnected indefinitely.
1325
1326 @param state: Set servo v4 dts mode 'off' or 'on'.
1327 """
1328 servo_version = self.get_servo_version()
1329 if not servo_version.startswith('servo_v4'):
1330 logging.debug('Not a servo v4, unable to set dts mode %s.', state)
1331 return
1332
1333 # TODO(mruthven): remove watchdog check once the labstation has been
1334 # updated to have support for modifying the watchdog.
1335 set_watchdog = self.has_control('watchdog') and 'ccd' in servo_version
1336 enable_watchdog = state == 'on'
1337
1338 if set_watchdog and not enable_watchdog:
1339 self.set_nocheck('watchdog_remove', 'ccd')
1340
1341 self.set_nocheck('servo_v4_dts_mode', state)
1342
1343 if set_watchdog and enable_watchdog:
1344 self.set_nocheck('watchdog_add', 'ccd')
1345
1346
Congbin Guofc3b8962019-03-22 17:38:46 -07001347 @property
1348 def uart_logs_dir(self):
1349 """Return the directory to save UART logs."""
1350 return self._uart.logs_dir if self._uart else ""
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001351
Congbin Guofc3b8962019-03-22 17:38:46 -07001352
1353 @uart_logs_dir.setter
1354 def uart_logs_dir(self, logs_dir):
1355 """Set directory to save UART logs.
1356
1357 @param logs_dir String of directory name."""
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001358 if self._uart:
Congbin Guofc3b8962019-03-22 17:38:46 -07001359 self._uart.logs_dir = logs_dir
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001360
1361
1362 def close(self):
1363 """Close the servo object."""
1364 if self._uart:
1365 self._uart.stop_capture()
Congbin Guofc3b8962019-03-22 17:38:46 -07001366 self._uart.dump()
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001367 self._uart = None