blob: ce9e605ded04aabb4b97c2237e51225ab07c6962 [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
Dana Goyetted5a95542019-12-30 11:14:14 -0800978 be transferred to the remote host. This adds the servod port number, to
979 make sure tests for different DUTs don't trample on each other's files.
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800980
981 @param image_path: a string, name of the firmware image file to be
982 transferred.
983 @return: a string, full path name of the copied file on the remote.
984 """
Mary Ruthven401a6012019-12-13 11:39:44 -0800985 name = os.path.basename(image_path)
Dana Goyetted5a95542019-12-30 11:14:14 -0800986 remote_name = 'dut_%s.%s' % (self._servo_host.servo_port, name)
Mary Ruthven401a6012019-12-13 11:39:44 -0800987 dest_path = os.path.join('/tmp', remote_name)
988 logging.info('Copying %s to %s', name, dest_path)
Fang Deng5d518f42013-08-02 14:04:32 -0700989 self._servo_host.send_file(image_path, dest_path)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800990 return dest_path
991
992
Dan Shifecdaf42015-07-28 10:17:26 -0700993 def system(self, command, timeout=3600):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700994 """Execute the passed in command on the servod host.
995
996 @param command Command to be executed.
Dan Shifecdaf42015-07-28 10:17:26 -0700997 @param timeout Maximum number of seconds of runtime allowed. Default to
998 1 hour.
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700999 """
Vadim Bendebury89ec24e2012-12-17 12:54:18 -08001000 logging.info('Will execute on servo host: %s', command)
Fang Deng5d518f42013-08-02 14:04:32 -07001001 self._servo_host.run(command, timeout=timeout)
Vadim Bendebury89ec24e2012-12-17 12:54:18 -08001002
1003
Dan Shifecdaf42015-07-28 10:17:26 -07001004 def system_output(self, command, timeout=3600,
Vadim Bendebury89ec24e2012-12-17 12:54:18 -08001005 ignore_status=False, args=()):
1006 """Execute the passed in command on the servod host, return stdout.
1007
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -07001008 @param command a string, the command to execute
1009 @param timeout an int, max number of seconds to wait til command
Dan Shifecdaf42015-07-28 10:17:26 -07001010 execution completes. Default to 1 hour.
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -07001011 @param ignore_status a Boolean, if true - ignore command's nonzero exit
Vadim Bendebury89ec24e2012-12-17 12:54:18 -08001012 status, otherwise an exception will be thrown
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -07001013 @param args a tuple of strings, each becoming a separate command line
Vadim Bendebury89ec24e2012-12-17 12:54:18 -08001014 parameter for the command
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -07001015 @return command's stdout as a string.
Vadim Bendebury89ec24e2012-12-17 12:54:18 -08001016 """
Fang Deng5d518f42013-08-02 14:04:32 -07001017 return self._servo_host.run(command, timeout=timeout,
1018 ignore_status=ignore_status,
1019 args=args).stdout.strip()
Vadim Bendeburyb80ba592012-12-07 15:02:34 -08001020
1021
Mary Ruthven38d90af2019-08-16 13:13:31 -07001022 def get_servo_version(self, active=False):
Dan Shia5fef052015-05-18 23:28:47 -07001023 """Get the version of the servo, e.g., servo_v2 or servo_v3.
1024
Mary Ruthven38d90af2019-08-16 13:13:31 -07001025 @param active: Only return the servo type with the active device.
Dan Shia5fef052015-05-18 23:28:47 -07001026 @return: The version of the servo.
1027
1028 """
Mary Ruthven38d90af2019-08-16 13:13:31 -07001029 servo_type = self._server.get_version()
1030 if '_and_' not in servo_type or not active:
1031 return servo_type
1032
1033 # If servo v4 is using ccd and servo micro, modify the servo type to
1034 # reflect the active device.
1035 active_device = self.get('active_v4_device')
1036 if active_device in servo_type:
1037 logging.info('%s is active', active_device)
1038 return 'servo_v4_with_' + active_device
1039
1040 logging.warn("%s is active even though it's not in servo type",
1041 active_device)
1042 return servo_type
Dan Shia5fef052015-05-18 23:28:47 -07001043
1044
Mary Ruthvena3c52b82019-10-09 14:09:08 -07001045 def get_main_servo_device(self):
1046 """Return the main servo device"""
1047 servo_type = self.get_servo_version()
1048 return servo_type.split('_with_')[-1].split('_and_')[0]
1049
1050
1051 def enable_main_servo_device(self):
1052 """Make sure the main device has control of the dut."""
Mary Ruthven1409d9d2019-10-22 20:44:24 -07001053 # Cr50 detects servo using the EC uart. It doesn't work well if the
1054 # board doesn't use EC uart. The lab active_v4_device doesn't handle
1055 # this correctly. Check ec_uart_pty before trying to change the active
1056 # device.
1057 # TODO(crbug.com/1016842): reenable the setting the main device when
1058 # active device works on labstations.
1059 return
Mary Ruthvena3c52b82019-10-09 14:09:08 -07001060 if not self.has_control('active_v4_device'):
1061 return
1062 self.set('active_v4_device', self.get_main_servo_device())
1063
1064
Ruben Rodriguez Buchillon1823bad2019-09-30 17:04:34 -07001065 def main_device_is_ccd(self):
1066 """Whether the main servo device (no prefixes) is a ccd device."""
Mary Ruthven2724ee62019-07-16 11:16:59 -07001067 servo = self._server.get_version()
1068 return 'ccd_cr50' in servo and 'servo_micro' not in servo
1069
1070
Ruben Rodriguez Buchillon1823bad2019-09-30 17:04:34 -07001071 def main_device_is_flex(self):
1072 """Whether the main servo device (no prefixes) is a legacy device."""
1073 return not self.main_device_is_ccd()
1074
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001075 def _initialize_programmer(self, rw_only=False):
1076 """Initialize the firmware programmer.
1077
1078 @param rw_only: True to initialize a programmer which only
1079 programs the RW portions.
1080 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -07001081 if self._programmer:
1082 return
1083 # Initialize firmware programmer
Dan Shia5fef052015-05-18 23:28:47 -07001084 servo_version = self.get_servo_version()
Dan Shi9cb0eec2014-06-03 09:04:50 -07001085 if servo_version.startswith('servo_v2'):
J. Richard Barnette8f19b392014-08-11 14:05:39 -07001086 self._programmer = firmware_programmer.ProgrammerV2(self)
Wai-Hong Tamc36c4d22016-09-09 10:39:45 -07001087 self._programmer_rw = firmware_programmer.ProgrammerV2RwOnly(self)
Kevin Chengdf2e29f2016-09-09 02:31:22 -07001088 # Both servo v3 and v4 use the same programming methods so just leverage
1089 # ProgrammerV3 for servo v4 as well.
1090 elif (servo_version.startswith('servo_v3') or
1091 servo_version.startswith('servo_v4')):
Dan Shia5fef052015-05-18 23:28:47 -07001092 self._programmer = firmware_programmer.ProgrammerV3(self)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001093 self._programmer_rw = firmware_programmer.ProgrammerV3RwOnly(self)
J. Richard Barnette8f19b392014-08-11 14:05:39 -07001094 else:
1095 raise error.TestError(
1096 'No firmware programmer for servo version: %s' %
Congbin Guo42427612019-02-12 10:22:06 -08001097 servo_version)
J. Richard Barnette8f19b392014-08-11 14:05:39 -07001098
1099
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001100 def program_bios(self, image, rw_only=False):
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -08001101 """Program bios on DUT with given image.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -08001102
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -08001103 @param image: a string, file name of the BIOS image to program
1104 on the DUT.
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001105 @param rw_only: True to only program the RW portion of BIOS.
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -08001106
Vadim Bendebury1a7ec632013-02-17 16:22:09 -08001107 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -07001108 self._initialize_programmer()
Ricky Liangc31aab32014-07-03 16:23:29 +08001109 if not self.is_localhost():
1110 image = self._scp_image(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001111 if rw_only:
1112 self._programmer_rw.program_bios(image)
1113 else:
1114 self._programmer.program_bios(image)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -08001115
1116
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001117 def program_ec(self, image, rw_only=False):
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -08001118 """Program ec on DUT with given image.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -08001119
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -08001120 @param image: a string, file name of the EC image to program
1121 on the DUT.
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001122 @param rw_only: True to only program the RW portion of EC.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -08001123
Vadim Bendebury1a7ec632013-02-17 16:22:09 -08001124 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -07001125 self._initialize_programmer()
Ricky Liangc31aab32014-07-03 16:23:29 +08001126 if not self.is_localhost():
1127 image = self._scp_image(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001128 if rw_only:
Congbin Guo42427612019-02-12 10:22:06 -08001129 self._programmer_rw.program_ec(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +08001130 else:
Congbin Guo42427612019-02-12 10:22:06 -08001131 self._programmer.program_ec(image)
1132
1133
1134 def _reprogram(self, tarball_path, firmware_name, image_candidates,
1135 rw_only):
1136 """Helper function to reprogram firmware for EC or BIOS.
1137
1138 @param tarball_path: The path of the downloaded build tarball.
1139 @param: firmware_name: either 'EC' or 'BIOS'.
1140 @param image_candidates: A tuple of the paths of image candidates.
1141 @param rw_only: True to only install firmware to its RW portions. Keep
1142 the RO portions unchanged.
1143
1144 @raise: TestError if cannot extract firmware from the tarball.
1145 """
Mary Ruthven771b2012019-08-19 12:18:49 -07001146 dest_dir = os.path.join(os.path.dirname(tarball_path), firmware_name)
1147 # Create the firmware_name subdirectory if it doesn't exist.
1148 if not os.path.exists(dest_dir):
1149 os.mkdir(dest_dir)
Congbin Guo42427612019-02-12 10:22:06 -08001150 image = _extract_image_from_tarball(tarball_path, dest_dir,
1151 image_candidates)
1152 if not image:
1153 if firmware_name == 'EC':
1154 logging.info('Not a Chrome EC, ignore re-programming it')
1155 return
1156 else:
1157 raise error.TestError('Failed to extract the %s image from '
1158 'tarball' % firmware_name)
1159
Namyoon Woo0e623622019-10-08 16:02:50 -07001160 # Extract subsidiary binaries for EC
1161 if firmware_name == 'EC':
1162 # Find a monitor binary for NPCX_UUT chip type, if any.
1163 mon_candidates = [ w.replace('ec.bin', 'npcx_monitor.bin')
1164 for w in image_candidates ]
1165 _extract_image_from_tarball(tarball_path, dest_dir, mon_candidates)
1166
Congbin Guo42427612019-02-12 10:22:06 -08001167 logging.info('Will re-program %s %snow', firmware_name,
1168 'RW ' if rw_only else '')
1169
1170 if firmware_name == 'EC':
1171 self.program_ec(os.path.join(dest_dir, image), rw_only)
1172 else:
1173 self.program_bios(os.path.join(dest_dir, image), rw_only)
1174
1175
Shelley Chenac61d5a2019-06-24 15:35:46 -07001176 def program_firmware(self, board, model, tarball_path, rw_only=False):
Congbin Guo42427612019-02-12 10:22:06 -08001177 """Program firmware (EC, if applied, and BIOS) of the DUT.
1178
Shelley Chenac61d5a2019-06-24 15:35:46 -07001179 @param board: The DUT board name.
Congbin Guo42427612019-02-12 10:22:06 -08001180 @param model: The DUT model name.
1181 @param tarball_path: The path of the downloaded build tarball.
1182 @param rw_only: True to only install firmware to its RW portions. Keep
1183 the RO portions unchanged.
1184 """
Shelley Chenac61d5a2019-06-24 15:35:46 -07001185 ap_image_candidates = ('image.bin', 'image-%s.bin' % model,
1186 'image-%s.bin' % board)
Justin TerAvest1d0c1bb2019-12-13 11:53:25 -07001187
1188 # Best effort; try to retrieve the EC board from the version as
1189 # reported by the EC.
1190 ec_board = None
1191 try:
1192 ec_board = self.get('ec_board')
1193 except Exception as err:
1194 logging.info('Failed to get ec_board value; ignoring')
1195 pass
1196
1197 ec_image_candidates = ['ec.bin', '%s/ec.bin' % model,
1198 '%s/ec.bin' % board]
1199 if ec_board:
1200 ec_image_candidates.append('%s/ec.bin' % ec_board)
Congbin Guo42427612019-02-12 10:22:06 -08001201
1202 self._reprogram(tarball_path, 'EC', ec_image_candidates, rw_only)
1203 self._reprogram(tarball_path, 'BIOS', ap_image_candidates, rw_only)
1204
1205 self.get_power_state_controller().reset()
1206 time.sleep(Servo.BOOT_DELAY)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001207
Fang Dengafb88142013-05-30 17:44:31 -07001208
1209 def _switch_usbkey_power(self, power_state, detection_delay=False):
1210 """Switch usbkey power.
1211
1212 This function switches usbkey power by setting the value of
1213 'prtctl4_pwren'. If the power is already in the
1214 requested state, this function simply returns.
1215
1216 @param power_state: A string, 'on' or 'off'.
1217 @param detection_delay: A boolean value, if True, sleep
1218 for |USB_DETECTION_DELAY| after switching
1219 the power on.
1220 """
Kevin Chenga22c4a82016-10-07 14:13:25 -07001221 # TODO(kevcheng): Forgive me for this terrible hack. This is just to
1222 # handle beaglebones that haven't yet updated and have the
1223 # safe_switch_usbkey_power RPC. I'll remove this once all beaglebones
1224 # have been updated and also think about a better way to handle
1225 # situations like this.
1226 try:
1227 self._server.safe_switch_usbkey_power(power_state)
1228 except Exception:
1229 self.set('prtctl4_pwren', power_state)
Fang Dengafb88142013-05-30 17:44:31 -07001230 if power_state == 'off':
1231 time.sleep(self.USB_POWEROFF_DELAY)
1232 elif detection_delay:
1233 time.sleep(self.USB_DETECTION_DELAY)
1234
1235
1236 def switch_usbkey(self, usb_state):
1237 """Connect USB flash stick to either host or DUT, or turn USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001238
1239 This function switches the servo multiplexer to provide electrical
Fang Dengafb88142013-05-30 17:44:31 -07001240 connection between the USB port J3 and either host or DUT side. It
1241 can also be used to turn the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001242
Fang Dengafb88142013-05-30 17:44:31 -07001243 Switching to 'dut' or 'host' is accompanied by powercycling
1244 of the USB stick, because it sometimes gets wedged if the mux
1245 is switched while the stick power is on.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001246
Fang Dengafb88142013-05-30 17:44:31 -07001247 @param usb_state: A string, one of 'dut', 'host', or 'off'.
1248 'dut' and 'host' indicate which side the
1249 USB flash device is required to be connected to.
1250 'off' indicates turning the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001251
Fang Dengafb88142013-05-30 17:44:31 -07001252 @raise: error.TestError in case the parameter is not 'dut'
1253 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001254 """
Fang Dengafb88142013-05-30 17:44:31 -07001255 if self.get_usbkey_direction() == usb_state:
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001256 return
1257
Fang Dengafb88142013-05-30 17:44:31 -07001258 if usb_state == 'off':
Dan Shia5fef052015-05-18 23:28:47 -07001259 self._switch_usbkey_power('off')
1260 self._usb_state = usb_state
1261 return
Fang Dengafb88142013-05-30 17:44:31 -07001262 elif usb_state == 'host':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001263 mux_direction = 'servo_sees_usbkey'
Fang Dengafb88142013-05-30 17:44:31 -07001264 elif usb_state == 'dut':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001265 mux_direction = 'dut_sees_usbkey'
1266 else:
Fang Dengafb88142013-05-30 17:44:31 -07001267 raise error.TestError('Unknown USB state request: %s' % usb_state)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001268
Fang Dengafb88142013-05-30 17:44:31 -07001269 self._switch_usbkey_power('off')
Kevin Chenga22c4a82016-10-07 14:13:25 -07001270 # TODO(kevcheng): Forgive me for this terrible hack. This is just to
1271 # handle beaglebones that haven't yet updated and have the
1272 # safe_switch_usbkey RPC. I'll remove this once all beaglebones have
1273 # been updated and also think about a better way to handle situations
1274 # like this.
1275 try:
1276 self._server.safe_switch_usbkey(mux_direction)
1277 except Exception:
1278 self.set('usb_mux_sel1', mux_direction)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001279 time.sleep(self.USB_POWEROFF_DELAY)
Fang Dengafb88142013-05-30 17:44:31 -07001280 self._switch_usbkey_power('on', usb_state == 'host')
1281 self._usb_state = usb_state
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001282
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001283
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001284 def get_usbkey_direction(self):
Fang Dengafb88142013-05-30 17:44:31 -07001285 """Get which side USB is connected to or 'off' if usb power is off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001286
Fang Dengafb88142013-05-30 17:44:31 -07001287 @return: A string, one of 'dut', 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001288 """
Fang Dengafb88142013-05-30 17:44:31 -07001289 if not self._usb_state:
1290 if self.get('prtctl4_pwren') == 'off':
1291 self._usb_state = 'off'
1292 elif self.get('usb_mux_sel1').startswith('dut'):
1293 self._usb_state = 'dut'
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001294 else:
Fang Dengafb88142013-05-30 17:44:31 -07001295 self._usb_state = 'host'
1296 return self._usb_state
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001297
1298
Wai-Hong Tam60377262018-03-01 10:55:39 -08001299 def set_servo_v4_role(self, role):
1300 """Set the power role of servo v4, either 'src' or 'snk'.
1301
1302 It does nothing if not a servo v4.
1303
1304 @param role: Power role for DUT port on servo v4, either 'src' or 'snk'.
1305 """
1306 servo_version = self.get_servo_version()
1307 if servo_version.startswith('servo_v4'):
1308 value = self.get('servo_v4_role')
1309 if value != role:
1310 self.set_nocheck('servo_v4_role', role)
1311 else:
1312 logging.debug('Already in the role: %s.', role)
1313 else:
1314 logging.debug('Not a servo v4, unable to set role to %s.', role)
1315
1316
Ruben Rodriguez Buchillon5f7ee682019-09-30 12:50:09 -07001317 def supports_built_in_pd_control(self):
1318 """Return whether the servo type supports pd charging and control."""
1319 servo_type = self.get('servo_type')
1320 if 'servo_v4' not in servo_type:
1321 # Only servo v4 supports this feature.
1322 logging.info('%r type does not support pd control.', servo_type)
1323 return False
1324 # On servo v4, it still needs ot be the type-c version.
1325 if not self.get('servo_v4_type') == 'type-c':
1326 logging.info('PD controls require a type-c servo v4.')
1327 return False
1328 # Lastly, one cannot really do anything without a plugged in charger.
1329 chg_port_mv = self.get('ppchg5_mv')
1330 if chg_port_mv < V4_CHG_ATTACHED_MIN_VOLTAGE_MV:
1331 logging.warn('It appears that no charger is plugged into servo v4. '
1332 'Charger port voltage: %dmV', chg_port_mv)
1333 return False
1334 logging.info('Charger port voltage: %dmV', chg_port_mv)
1335 return True
1336
Mary Ruthven739b2922019-08-22 11:16:06 -07001337 def set_servo_v4_dts_mode(self, state):
1338 """Set servo v4 dts mode to off or on.
1339
1340 It does nothing if not a servo v4. Disable the ccd watchdog if we're
1341 disabling dts mode. CCD will disconnect. The watchdog only allows CCD
1342 to disconnect for 10 seconds until it kills servod. Disable the
1343 watchdog, so CCD can stay disconnected indefinitely.
1344
1345 @param state: Set servo v4 dts mode 'off' or 'on'.
1346 """
1347 servo_version = self.get_servo_version()
1348 if not servo_version.startswith('servo_v4'):
1349 logging.debug('Not a servo v4, unable to set dts mode %s.', state)
1350 return
1351
1352 # TODO(mruthven): remove watchdog check once the labstation has been
1353 # updated to have support for modifying the watchdog.
1354 set_watchdog = self.has_control('watchdog') and 'ccd' in servo_version
1355 enable_watchdog = state == 'on'
1356
1357 if set_watchdog and not enable_watchdog:
1358 self.set_nocheck('watchdog_remove', 'ccd')
1359
1360 self.set_nocheck('servo_v4_dts_mode', state)
1361
1362 if set_watchdog and enable_watchdog:
1363 self.set_nocheck('watchdog_add', 'ccd')
1364
1365
Congbin Guofc3b8962019-03-22 17:38:46 -07001366 @property
1367 def uart_logs_dir(self):
1368 """Return the directory to save UART logs."""
1369 return self._uart.logs_dir if self._uart else ""
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001370
Congbin Guofc3b8962019-03-22 17:38:46 -07001371
1372 @uart_logs_dir.setter
1373 def uart_logs_dir(self, logs_dir):
1374 """Set directory to save UART logs.
1375
1376 @param logs_dir String of directory name."""
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001377 if self._uart:
Congbin Guofc3b8962019-03-22 17:38:46 -07001378 self._uart.logs_dir = logs_dir
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001379
1380
1381 def close(self):
1382 """Close the servo object."""
1383 if self._uart:
1384 self._uart.stop_capture()
Congbin Guofc3b8962019-03-22 17:38:46 -07001385 self._uart.dump()
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001386 self._uart = None