blob: 45df8bef5beba5c3ea22b4edb21e7704fda56434 [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
12import time
13import xmlrpclib
Vadim Bendeburyb80ba592012-12-07 15:02:34 -080014
Simran Basi741b5d42012-05-18 11:27:15 -070015from autotest_lib.client.common_lib import error
Mary Ruthvenecf12712019-06-26 17:36:21 -070016from autotest_lib.client.common_lib import lsbrelease_utils
Congbin Guo42427612019-02-12 10:22:06 -080017from autotest_lib.server import utils as server_utils
Ricky Liangc31aab32014-07-03 16:23:29 +080018from autotest_lib.server.cros.servo import firmware_programmer
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070019
Kevin Chenga22c4a82016-10-07 14:13:25 -070020# Time to wait when probing for a usb device, it takes on avg 17 seconds
21# to do a full probe.
22_USB_PROBE_TIMEOUT = 40
23
J. Richard Barnette41320ee2013-03-11 13:00:13 -070024
Congbin Guo42427612019-02-12 10:22:06 -080025def _extract_image_from_tarball(tarball, dest_dir, image_candidates):
26 """Try extracting the image_candidates from the tarball.
27
28 @param tarball: The path of the tarball.
29 @param dest_path: The path of the destination.
30 @param image_candidates: A tuple of the paths of image candidates.
31
32 @return: The first path from the image candidates, which succeeds, or None
33 if all the image candidates fail.
34 """
35 for image in image_candidates:
36 status = server_utils.system(
37 ('tar xf %s -C %s %s' % (tarball, dest_dir, image)),
38 timeout=60, ignore_status=True)
39 if status == 0:
40 return image
41 return None
42
43
J. Richard Barnette0c9c5882014-06-11 15:27:05 -070044class _PowerStateController(object):
45
46 """Class to provide board-specific power operations.
47
48 This class is responsible for "power on" and "power off"
49 operations that can operate without making assumptions in
50 advance about board state. It offers an interface that
51 abstracts out the different sequences required for different
52 board types.
53
54 """
55
56 # Constants acceptable to be passed for the `rec_mode` parameter
57 # to power_on().
58 #
59 # REC_ON: Boot the DUT in recovery mode, i.e. boot from USB or
60 # SD card.
61 # REC_OFF: Boot in normal mode, i.e. boot from internal storage.
62
63 REC_ON = 'rec'
64 REC_OFF = 'on'
Shelley Chen65938622018-05-16 07:45:54 -070065 REC_ON_FORCE_MRC = 'rec_force_mrc'
J. Richard Barnette0c9c5882014-06-11 15:27:05 -070066
67 # Delay in seconds needed between asserting and de-asserting
68 # warm reset.
69 _RESET_HOLD_TIME = 0.5
70
71 def __init__(self, servo):
72 """Initialize the power state control.
73
74 @param servo Servo object providing the underlying `set` and `get`
75 methods for the target controls.
76
77 """
78 self._servo = servo
79
80 def reset(self):
81 """Force the DUT to reset.
82
83 The DUT is guaranteed to be on at the end of this call,
84 regardless of its previous state, provided that there is
85 working OS software. This also guarantees that the EC has
86 been restarted.
87
88 """
89 self._servo.set_nocheck('power_state', 'reset')
90
91 def warm_reset(self):
92 """Apply warm reset to the DUT.
93
94 This asserts, then de-asserts the 'warm_reset' signal.
95 Generally, this causes the board to restart.
96
97 """
98 self._servo.set_get_all(['warm_reset:on',
99 'sleep:%.4f' % self._RESET_HOLD_TIME,
100 'warm_reset:off'])
101
J. Richard Barnette0c9c5882014-06-11 15:27:05 -0700102 def power_off(self):
103 """Force the DUT to power off.
104
105 The DUT is guaranteed to be off at the end of this call,
106 regardless of its previous state, provided that there is
107 working EC and boot firmware. There is no requirement for
108 working OS software.
109
110 """
111 self._servo.set_nocheck('power_state', 'off')
112
113 def power_on(self, rec_mode=REC_OFF):
114 """Force the DUT to power on.
115
116 Prior to calling this function, the DUT must be powered off,
117 e.g. with a call to `power_off()`.
118
119 At power on, recovery mode is set as specified by the
120 corresponding argument. When booting with recovery mode on, it
121 is the caller's responsibility to unplug/plug in a bootable
122 external storage device.
123
124 If the DUT requires a delay after powering on but before
125 processing inputs such as USB stick insertion, the delay is
126 handled by this method; the caller is not responsible for such
127 delays.
128
129 @param rec_mode Setting of recovery mode to be applied at
130 power on. default: REC_OFF aka 'off'
131
132 """
133 self._servo.set_nocheck('power_state', rec_mode)
134
135
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700136class _Uart(object):
Congbin Guo0f00be82019-04-18 17:51:14 -0700137 """Class to capture UART streams of CPU, EC, Cr50, etc."""
138 _UartToCapture = ('cpu', 'ec', 'cr50', 'servo_v4', 'servo_micro', 'usbpd')
139
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700140 def __init__(self, servo):
141 self._servo = servo
142 self._streams = []
Congbin Guo0f00be82019-04-18 17:51:14 -0700143 self.logs_dir = None
Congbin Guofe4d4e82019-04-18 17:51:14 -0700144
Congbin Guo0f00be82019-04-18 17:51:14 -0700145 def _start_stop_capture(self, uart, start):
146 """Helper function to start/stop capturing on specified UART.
147
148 @param uart: The UART name to start/stop capturing.
149 @param start: True to start capturing, otherwise stop.
150
151 @returns True if the operation completes successfully. False if the UART
152 capturing is not supported or failed due to an error.
153 """
154 logging.debug('%s capturing %s UART.', 'Start' if start else 'Stop',
155 uart)
Congbin Guo39dc6f22019-04-23 23:01:21 +0000156 try:
Congbin Guo0f00be82019-04-18 17:51:14 -0700157 self._servo.set('%s_uart_capture' % uart,
158 'on' if start else 'off')
Congbin Guo39dc6f22019-04-23 23:01:21 +0000159 except error.TestFail as err:
160 if 'No control named' in str(err):
Congbin Guo0f00be82019-04-18 17:51:14 -0700161 logging.debug("The servod doesn't support %s_uart_capture.",
162 uart)
163 else:
164 logging.debug("Can't caputre UART for %s: %s", uart, err)
165 return False
166
167 return True
168
169 def start_capture(self):
170 """Start capturing UART streams."""
171 for uart in self._UartToCapture:
172 if self._start_stop_capture(uart, True):
173 self._streams.append(('%s_uart_stream' % uart, '%s_uart.log' %
174 uart))
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700175
Congbin Guofc3b8962019-03-22 17:38:46 -0700176 def dump(self):
177 """Dump UART streams to log files accordingly."""
Congbin Guo0f00be82019-04-18 17:51:14 -0700178 if not self.logs_dir:
Congbin Guofc3b8962019-03-22 17:38:46 -0700179 return
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700180
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700181 for stream, logfile in self._streams:
Congbin Guo0f00be82019-04-18 17:51:14 -0700182 logfile_fullname = os.path.join(self.logs_dir, logfile)
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700183 try:
184 content = self._servo.get(stream)
185 except Exception as err:
186 logging.warn('Failed to get UART log for %s: %s', stream, err)
187 continue
188
189 # The UART stream may contain non-printable characters, and servo
190 # returns it in string representation. We use `ast.leteral_eval`
191 # to revert it back.
192 with open(logfile_fullname, 'a') as fd:
193 fd.write(ast.literal_eval(content))
194
195 def stop_capture(self):
196 """Stop capturing UART streams."""
Congbin Guo0f00be82019-04-18 17:51:14 -0700197 for uart in self._UartToCapture:
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700198 try:
Congbin Guo0f00be82019-04-18 17:51:14 -0700199 self._start_stop_capture(uart, False)
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700200 except Exception as err:
201 logging.warn('Failed to stop UART logging for %s: %s', uart,
202 err)
203
204
J. Richard Barnette384056b2012-04-16 11:04:46 -0700205class Servo(object):
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700206
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700207 """Manages control of a Servo board.
208
209 Servo is a board developed by hardware group to aide in the debug and
210 control of various partner devices. Servo's features include the simulation
211 of pressing the power button, closing the lid, and pressing Ctrl-d. This
212 class manages setting up and communicating with a servo demon (servod)
213 process. It provides both high-level functions for common servo tasks and
214 low-level functions for directly setting and reading gpios.
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700215
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700216 """
217
Chrome Bot9a1137d2011-07-19 14:35:00 -0700218 # Power button press delays in seconds.
J. Richard Barnetted2e4cbd2012-06-29 12:18:40 -0700219 #
J. Richard Barnette5383f072012-07-26 17:35:40 -0700220 # The EC specification says that 8.0 seconds should be enough
221 # for the long power press. However, some platforms need a bit
222 # more time. Empirical testing has found these requirements:
223 # Alex: 8.2 seconds
224 # ZGB: 8.5 seconds
225 # The actual value is set to the largest known necessary value.
226 #
227 # TODO(jrbarnette) Being generous is the right thing to do for
228 # existing platforms, but if this code is to be used for
229 # qualification of new hardware, we should be less generous.
Chrome Bot9a1137d2011-07-19 14:35:00 -0700230 SHORT_DELAY = 0.1
J. Richard Barnette5383f072012-07-26 17:35:40 -0700231
Todd Broch31c82502011-08-29 08:14:39 -0700232 # Maximum number of times to re-read power button on release.
Todd Brochcf7c6652012-02-24 13:03:59 -0800233 GET_RETRY_MAX = 10
Chrome Bot9a1137d2011-07-19 14:35:00 -0700234
J. Richard Barnette5383f072012-07-26 17:35:40 -0700235 # Delays to deal with DUT state transitions.
Chrome Bot9a1137d2011-07-19 14:35:00 -0700236 SLEEP_DELAY = 6
237 BOOT_DELAY = 10
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700238
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700239 # Default minimum time interval between 'press' and 'release'
240 # keyboard events.
Vic Yang0aca1c22012-11-19 18:33:56 -0800241 SERVO_KEY_PRESS_DELAY = 0.1
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700242
Tom Wai-Hong Tam93b5c092015-05-14 02:50:43 +0800243 # Time to toggle recovery switch on and off.
244 REC_TOGGLE_DELAY = 0.1
245
Tom Wai-Hong Tamf9ded092015-05-20 05:37:22 +0800246 # Time to toggle development switch on and off.
247 DEV_TOGGLE_DELAY = 0.1
248
Jon Salzc88e5b62011-11-30 14:38:54 +0800249 # Time between an usb disk plugged-in and detected in the system.
250 USB_DETECTION_DELAY = 10
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800251 # Time to keep USB power off before and after USB mux direction is changed
252 USB_POWEROFF_DELAY = 2
Jon Salzc88e5b62011-11-30 14:38:54 +0800253
Simran Basib7850bb2013-07-24 12:33:42 -0700254 # Time to wait before timing out on servo initialization.
255 INIT_TIMEOUT_SECS = 10
Simran Basi59331342013-07-12 12:06:29 -0700256
J. Richard Barnette67ccb872012-04-19 16:34:56 -0700257
Ricky Liang0dd379c2014-04-23 16:29:08 +0800258 def __init__(self, servo_host, servo_serial=None):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700259 """Sets up the servo communication infrastructure.
260
Fang Deng5d518f42013-08-02 14:04:32 -0700261 @param servo_host: A ServoHost object representing
262 the host running servod.
Ricky Liang0dd379c2014-04-23 16:29:08 +0800263 @param servo_serial: Serial number of the servo board.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700264 """
Fang Deng5d518f42013-08-02 14:04:32 -0700265 # TODO(fdeng): crbug.com/298379
266 # We should move servo_host object out of servo object
267 # to minimize the dependencies on the rest of Autotest.
268 self._servo_host = servo_host
Ricky Liang0dd379c2014-04-23 16:29:08 +0800269 self._servo_serial = servo_serial
Richard Barnette180efe62016-12-02 23:20:44 +0000270 self._server = servo_host.get_servod_server_proxy()
J. Richard Barnette0c9c5882014-06-11 15:27:05 -0700271 self._power_state = _PowerStateController(self)
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700272 self._uart = _Uart(self)
Fang Dengafb88142013-05-30 17:44:31 -0700273 self._usb_state = None
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700274 self._programmer = None
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800275
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800276
Ricky Liang0dd379c2014-04-23 16:29:08 +0800277 @property
278 def servo_serial(self):
279 """Returns the serial number of the servo board."""
280 return self._servo_serial
281
282
Tom Wai-Hong Tam22ee0e52013-04-03 13:36:39 +0800283 def get_power_state_controller(self):
J. Richard Barnette75136b32013-03-26 13:38:44 -0700284 """Return the power state controller for this Servo.
285
286 The power state controller provides board-independent
287 interfaces for reset, power-on, power-off operations.
288
289 """
290 return self._power_state
291
Fang Deng5d518f42013-08-02 14:04:32 -0700292
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700293 def initialize_dut(self, cold_reset=False):
294 """Initializes a dut for testing purposes.
295
296 This sets various servo signals back to default values
297 appropriate for the target board. By default, if the DUT
298 is already on, it stays on. If the DUT is powered off
299 before initialization, its state afterward is unspecified.
300
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700301 Rationale: Basic initialization of servo sets the lid open,
302 when there is a lid. This operation won't affect powered on
303 units; however, setting the lid open may power on a unit
J. Richard Barnette75136b32013-03-26 13:38:44 -0700304 that's off, depending on the board type and previous state
305 of the device.
306
J. Richard Barnette4b6af0d2014-06-05 09:57:20 -0700307 If `cold_reset` is a true value, the DUT and its EC will be
308 reset, and the DUT rebooted in normal mode.
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700309
310 @param cold_reset If True, cold reset the device after
311 initialization.
312 """
313 self._server.hwinit()
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700314 self.set('usb_mux_oe1', 'on')
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700315 self._usb_state = None
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700316 self.switch_usbkey('off')
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700317 self._uart.start_capture()
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700318 if cold_reset:
J. Richard Barnette4b6af0d2014-06-05 09:57:20 -0700319 self._power_state.reset()
J. Richard Barnette1777f5d2014-09-03 15:18:19 -0700320 logging.debug('Servo initialized, version is %s',
321 self._server.get_version())
Ruben Rodriguez Buchillon7121b092019-04-29 12:39:34 -0700322 try:
Ruben Rodriguez Buchillon05298242019-05-20 11:29:06 -0700323 # TODO(coconutruben): change this back to set() about a month
324 # after crrev.com/c/1586239 has been merged (or whenever that
325 # logic is in the labstation images).
326 self.set_nocheck('init_keyboard','on')
Ruben Rodriguez Buchillon7121b092019-04-29 12:39:34 -0700327 except error.TestFail as err:
328 if 'No control named' in str(err):
329 # This indicates the servod version does not
330 # have explicit keyboard initialization yet.
331 # Ignore this.
332 pass
333 else:
334 raise err
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700335
336
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800337 def is_localhost(self):
338 """Is the servod hosted locally?
339
340 Returns:
341 True if local hosted; otherwise, False.
342 """
Fang Deng5d518f42013-08-02 14:04:32 -0700343 return self._servo_host.is_localhost()
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800344
345
Mary Ruthvenecf12712019-06-26 17:36:21 -0700346 def get_os_version(self):
347 """Returns the chromeos release version."""
348 lsb_release_content = self.system_output('cat /etc/lsb-release',
349 ignore_status=True)
350 return lsbrelease_utils.get_chromeos_release_builder_path(
351 lsb_release_content=lsb_release_content)
352
353
Mary Ruthven83bb5952019-06-27 12:34:05 -0700354 def get_servod_version(self):
355 """Returns the servod version."""
356 result = self._servo_host.run('servod --version')
357 # TODO: use system_output once servod --version prints to stdout
358 stdout = result.stdout.strip()
359 return stdout if stdout else result.stderr.strip()
360
361
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700362 def power_long_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700363 """Simulate a long power button press."""
J. Richard Barnettef12bff22012-07-18 14:41:06 -0700364 # After a long power press, the EC may ignore the next power
365 # button press (at least on Alex). To guarantee that this
366 # won't happen, we need to allow the EC one second to
367 # collect itself.
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800368 # long_press is defined as 8.5s in servod
369 self.set_nocheck('power_key', 'long_press')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700370
371
372 def power_normal_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700373 """Simulate a normal power button press."""
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800374 # press is defined as 1.2s in servod
375 self.set_nocheck('power_key', 'press')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700376
377
378 def power_short_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700379 """Simulate a short power button press."""
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800380 # tab is defined as 0.2s in servod
381 self.set_nocheck('power_key', 'tab')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700382
383
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800384 def power_key(self, press_secs='tab'):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700385 """Simulate a power button press.
386
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800387 @param press_secs: int, float, str; time to press key in seconds or
388 known shorthand: 'tab' 'press' 'long_press'
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700389 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800390 self.set_nocheck('power_key', press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700391
392
393 def lid_open(self):
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800394 """Simulate opening the lid and raise exception if all attempts fail"""
395 self.set('lid_open', 'yes')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700396
397
398 def lid_close(self):
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800399 """Simulate closing the lid and raise exception if all attempts fail
Craig Harrison48997262011-06-27 14:31:10 -0700400
401 Waits 6 seconds to ensure the device is fully asleep before returning.
402 """
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800403 self.set('lid_open', 'no')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700404 time.sleep(Servo.SLEEP_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700405
Shelley Chenc26575a2015-09-18 10:56:16 -0700406 def volume_up(self, timeout=300):
Kevin Chenge448a8b2016-09-09 10:18:11 -0700407 """Simulate pushing the volume down button.
408
409 @param timeout: Timeout for setting the volume.
410 """
Shelley Chenc26575a2015-09-18 10:56:16 -0700411 self.set_get_all(['volume_up:yes',
412 'sleep:%.4f' % self.SERVO_KEY_PRESS_DELAY,
413 'volume_up:no'])
414 # we need to wait for commands to take effect before moving on
415 time_left = float(timeout)
416 while time_left > 0.0:
417 value = self.get('volume_up')
418 if value == 'no':
419 return
420 time.sleep(self.SHORT_DELAY)
421 time_left = time_left - self.SHORT_DELAY
422 raise error.TestFail("Failed setting volume_up to no")
423
424 def volume_down(self, timeout=300):
Kevin Chenge448a8b2016-09-09 10:18:11 -0700425 """Simulate pushing the volume down button.
426
427 @param timeout: Timeout for setting the volume.
428 """
Shelley Chenc26575a2015-09-18 10:56:16 -0700429 self.set_get_all(['volume_down:yes',
430 'sleep:%.4f' % self.SERVO_KEY_PRESS_DELAY,
431 'volume_down:no'])
432 # we need to wait for commands to take effect before moving on
433 time_left = float(timeout)
434 while time_left > 0.0:
435 value = self.get('volume_down')
436 if value == 'no':
437 return
438 time.sleep(self.SHORT_DELAY)
439 time_left = time_left - self.SHORT_DELAY
440 raise error.TestFail("Failed setting volume_down to no")
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700441
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800442 def ctrl_d(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800443 """Simulate Ctrl-d simultaneous button presses.
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800444
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800445 @param press_secs: int, float, str; time to press key in seconds or
446 known shorthand: 'tab' 'press' 'long_press'
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800447 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800448 self.set_nocheck('ctrl_d', press_secs)
Gediminas Ramanauskas9da80f22012-11-14 12:59:43 -0800449
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800450
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800451 def ctrl_u(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800452 """Simulate Ctrl-u simultaneous button presses.
453
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800454 @param press_secs: int, float, str; time to press key in seconds or
455 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800456 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800457 self.set_nocheck('ctrl_u', press_secs)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700458
459
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800460 def ctrl_enter(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800461 """Simulate Ctrl-enter simultaneous button presses.
462
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800463 @param press_secs: int, float, str; time to press key in seconds or
464 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800465 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800466 self.set_nocheck('ctrl_enter', press_secs)
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -0700467
468
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800469 def ctrl_key(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800470 """Simulate Enter key button press.
471
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800472 @param press_secs: int, float, str; time to press key in seconds or
473 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800474 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800475 self.set_nocheck('ctrl_key', press_secs)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700476
477
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800478 def enter_key(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800479 """Simulate Enter key button press.
480
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800481 @param press_secs: int, float, str; time to press key in seconds or
482 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800483 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800484 self.set_nocheck('enter_key', press_secs)
Todd Broch9753bd42012-03-21 10:15:08 -0700485
486
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800487 def refresh_key(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800488 """Simulate Refresh key (F3) button press.
489
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800490 @param press_secs: int, float, str; time to press key in seconds or
491 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800492 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800493 self.set_nocheck('refresh_key', press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700494
495
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800496 def ctrl_refresh_key(self, press_secs='tab'):
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800497 """Simulate Ctrl and Refresh (F3) simultaneous press.
498
499 This key combination is an alternative of Space key.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800500
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800501 @param press_secs: int, float, str; time to press key in seconds or
502 known shorthand: 'tab' 'press' 'long_press'
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800503 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800504 self.set_nocheck('ctrl_refresh_key', press_secs)
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800505
506
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800507 def imaginary_key(self, press_secs='tab'):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700508 """Simulate imaginary key button press.
509
510 Maps to a key that doesn't physically exist.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800511
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800512 @param press_secs: int, float, str; time to press key in seconds or
513 known shorthand: 'tab' 'press' 'long_press'
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700514 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800515 self.set_nocheck('imaginary_key', press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700516
517
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800518 def sysrq_x(self, press_secs='tab'):
Vincent Palatine7dc9282016-07-14 11:31:58 +0200519 """Simulate Alt VolumeUp X simulataneous press.
520
521 This key combination is the kernel system request (sysrq) X.
522
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800523 @param press_secs: int, float, str; time to press key in seconds or
524 known shorthand: 'tab' 'press' 'long_press'
Vincent Palatine7dc9282016-07-14 11:31:58 +0200525 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800526 self.set_nocheck('sysrq_x', press_secs)
Vincent Palatine7dc9282016-07-14 11:31:58 +0200527
528
Tom Wai-Hong Tam93b5c092015-05-14 02:50:43 +0800529 def toggle_recovery_switch(self):
530 """Toggle recovery switch on and off."""
531 self.enable_recovery_mode()
532 time.sleep(self.REC_TOGGLE_DELAY)
533 self.disable_recovery_mode()
534
535
Craig Harrison6b36b122011-06-28 17:58:43 -0700536 def enable_recovery_mode(self):
537 """Enable recovery mode on device."""
538 self.set('rec_mode', 'on')
539
540
541 def disable_recovery_mode(self):
542 """Disable recovery mode on device."""
543 self.set('rec_mode', 'off')
544
545
Tom Wai-Hong Tamf9ded092015-05-20 05:37:22 +0800546 def toggle_development_switch(self):
547 """Toggle development switch on and off."""
548 self.enable_development_mode()
549 time.sleep(self.DEV_TOGGLE_DELAY)
550 self.disable_development_mode()
551
552
Craig Harrison6b36b122011-06-28 17:58:43 -0700553 def enable_development_mode(self):
554 """Enable development mode on device."""
555 self.set('dev_mode', 'on')
556
557
558 def disable_development_mode(self):
559 """Disable development mode on device."""
560 self.set('dev_mode', 'off')
561
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700562 def boot_devmode(self):
563 """Boot a dev-mode device that is powered off."""
Tom Wai-Hong Tam23869102012-01-17 15:41:30 +0800564 self.power_short_press()
Craig Harrison48997262011-06-27 14:31:10 -0700565 self.pass_devmode()
566
567
568 def pass_devmode(self):
569 """Pass through boot screens in dev-mode."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700570 time.sleep(Servo.BOOT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700571 self.ctrl_d()
Chrome Bot9a1137d2011-07-19 14:35:00 -0700572 time.sleep(Servo.BOOT_DELAY)
Craig Harrison48997262011-06-27 14:31:10 -0700573
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700574
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800575 def get_board(self):
Wai-Hong Tam0f904002017-09-19 15:52:22 -0700576 """Get the board connected to servod."""
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800577 return self._server.get_board()
578
579
Wai-Hong Tam0f904002017-09-19 15:52:22 -0700580 def get_base_board(self):
581 """Get the board of the base connected to servod."""
Wai-Hong Tam7f6169e2017-09-29 09:23:48 -0700582 try:
583 return self._server.get_base_board()
584 except xmlrpclib.Fault as e:
585 # TODO(waihong): Remove the following compatibility check when
586 # the new versions of hdctools are deployed.
587 if 'not supported' in str(e):
588 logging.warning('The servod is too old that get_base_board '
589 'not supported.')
590 return ''
591 raise
Wai-Hong Tam0f904002017-09-19 15:52:22 -0700592
593
Wai-Hong Tamcc399ee2017-12-08 12:43:28 -0800594 def get_ec_active_copy(self):
595 """Get the active copy of the EC image."""
596 return self.get('ec_active_copy')
597
598
Todd Brochefe72cb2012-07-11 19:58:53 -0700599 def _get_xmlrpclib_exception(self, xmlexc):
600 """Get meaningful exception string from xmlrpc.
601
602 Args:
603 xmlexc: xmlrpclib.Fault object
604
605 xmlrpclib.Fault.faultString has the following format:
606
607 <type 'exception type'>:'actual error message'
608
609 Parse and return the real exception from the servod side instead of the
610 less meaningful one like,
611 <Fault 1: "<type 'exceptions.AttributeError'>:'tca6416' object has no
612 attribute 'hw_driver'">
613
614 Returns:
615 string of underlying exception raised in servod.
616 """
617 return re.sub('^.*>:', '', xmlexc.faultString)
618
619
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700620 def get(self, gpio_name):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700621 """Get the value of a gpio from Servod.
622
623 @param gpio_name Name of the gpio.
624 """
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700625 assert gpio_name
Todd Brochefe72cb2012-07-11 19:58:53 -0700626 try:
Simran Basi1cbc5f22013-07-17 14:23:58 -0700627 return self._server.get(gpio_name)
Todd Brochefe72cb2012-07-11 19:58:53 -0700628 except xmlrpclib.Fault as e:
629 err_msg = "Getting '%s' :: %s" % \
630 (gpio_name, self._get_xmlrpclib_exception(e))
631 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700632
633
634 def set(self, gpio_name, gpio_value):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700635 """Set and check the value of a gpio using Servod.
636
637 @param gpio_name Name of the gpio.
638 @param gpio_value New setting for the gpio.
639 """
Chrome Bot9a1137d2011-07-19 14:35:00 -0700640 self.set_nocheck(gpio_name, gpio_value)
Todd Brochcf7c6652012-02-24 13:03:59 -0800641 retry_count = Servo.GET_RETRY_MAX
642 while gpio_value != self.get(gpio_name) and retry_count:
Ilja H. Friedel04be2bd2014-05-07 21:29:59 -0700643 logging.warning("%s != %s, retry %d", gpio_name, gpio_value,
Todd Brochcf7c6652012-02-24 13:03:59 -0800644 retry_count)
645 retry_count -= 1
646 time.sleep(Servo.SHORT_DELAY)
647 if not retry_count:
648 assert gpio_value == self.get(gpio_name), \
649 'Servo failed to set %s to %s' % (gpio_name, gpio_value)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700650
651
652 def set_nocheck(self, gpio_name, gpio_value):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700653 """Set the value of a gpio using Servod.
654
655 @param gpio_name Name of the gpio.
656 @param gpio_value New setting for the gpio.
657 """
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700658 assert gpio_name and gpio_value
Mary Ruthvendfa45342019-06-25 10:10:17 -0700659 logging.debug('Setting %s to %r', gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700660 try:
Simran Basi1cbc5f22013-07-17 14:23:58 -0700661 self._server.set(gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700662 except xmlrpclib.Fault as e:
Mary Ruthven73d3a352018-05-10 15:35:20 -0700663 err_msg = "Setting '%s' to %r :: %s" % \
Todd Brochefe72cb2012-07-11 19:58:53 -0700664 (gpio_name, gpio_value, self._get_xmlrpclib_exception(e))
665 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700666
667
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800668 def set_get_all(self, controls):
669 """Set &| get one or more control values.
670
671 @param controls: list of strings, controls to set &| get.
672
673 @raise: error.TestError in case error occurs setting/getting values.
674 """
675 rv = []
676 try:
Mary Ruthvendfa45342019-06-25 10:10:17 -0700677 logging.debug('Set/get all: %s', str(controls))
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800678 rv = self._server.set_get_all(controls)
679 except xmlrpclib.Fault as e:
680 # TODO(waihong): Remove the following backward compatibility when
681 # the new versions of hdctools are deployed.
682 if 'not supported' in str(e):
683 logging.warning('The servod is too old that set_get_all '
684 'not supported. Use set and get instead.')
685 for control in controls:
686 if ':' in control:
687 (name, value) = control.split(':')
688 if name == 'sleep':
689 time.sleep(float(value))
690 else:
691 self.set_nocheck(name, value)
692 rv.append(True)
693 else:
694 rv.append(self.get(name))
695 else:
696 err_msg = "Problem with '%s' :: %s" % \
697 (controls, self._get_xmlrpclib_exception(e))
698 raise error.TestFail(err_msg)
699 return rv
700
701
Jon Salzc88e5b62011-11-30 14:38:54 +0800702 # TODO(waihong) It may fail if multiple servo's are connected to the same
703 # host. Should look for a better way, like the USB serial name, to identify
704 # the USB device.
Simran Basi741b5d42012-05-18 11:27:15 -0700705 # TODO(sbasi) Remove this code from autoserv once firmware tests have been
706 # updated.
Kevin Chenga22c4a82016-10-07 14:13:25 -0700707 def probe_host_usb_dev(self, timeout=_USB_PROBE_TIMEOUT):
Jon Salzc88e5b62011-11-30 14:38:54 +0800708 """Probe the USB disk device plugged-in the servo from the host side.
709
Kevin Chengeb06fe72016-08-22 15:26:32 -0700710 It uses servod to discover if there is a usb device attached to it.
Jon Salzc88e5b62011-11-30 14:38:54 +0800711
Kevin Chenga22c4a82016-10-07 14:13:25 -0700712 @param timeout The timeout period when probing for the usb host device.
713
714 @return: String of USB disk path (e.g. '/dev/sdb') or None.
Jon Salzc88e5b62011-11-30 14:38:54 +0800715 """
Ruben Rodriguez Buchillon9a4bfc32018-10-16 20:46:25 +0800716 # Set up Servo's usb mux.
717 self.switch_usbkey('host')
Kevin Chenga22c4a82016-10-07 14:13:25 -0700718 return self._server.probe_host_usb_dev(timeout) or None
Jon Salzc88e5b62011-11-30 14:38:54 +0800719
720
Mike Truty49153d82012-08-21 22:27:30 -0500721 def image_to_servo_usb(self, image_path=None,
722 make_image_noninteractive=False):
723 """Install an image to the USB key plugged into the servo.
724
725 This method may copy any image to the servo USB key including a
726 recovery image or a test image. These images are frequently used
727 for test purposes such as restoring a corrupted image or conducting
728 an upgrade of ec/fw/kernel as part of a test of a specific image part.
729
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700730 @param image_path Path on the host to the recovery image.
731 @param make_image_noninteractive Make the recovery image
732 noninteractive, therefore the DUT
733 will reboot automatically after
734 installation.
Mike Truty49153d82012-08-21 22:27:30 -0500735 """
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700736 # We're about to start plugging/unplugging the USB key. We
737 # don't know the state of the DUT, or what it might choose
738 # to do to the device after hotplug. To avoid surprises,
739 # force the DUT to be off.
740 self._server.hwinit()
741 self._power_state.power_off()
Mike Truty49153d82012-08-21 22:27:30 -0500742
743 # Set up Servo's usb mux.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800744 self.switch_usbkey('host')
Mike Truty49153d82012-08-21 22:27:30 -0500745 if image_path:
Tom Wai-Hong Tam42f136d2012-10-26 11:11:23 +0800746 logging.info('Searching for usb device and copying image to it. '
747 'Please wait a few minutes...')
Mike Truty49153d82012-08-21 22:27:30 -0500748 if not self._server.download_image_to_usb(image_path):
749 logging.error('Failed to transfer requested image to USB. '
750 'Please take a look at Servo Logs.')
751 raise error.AutotestError('Download image to usb failed.')
752 if make_image_noninteractive:
753 logging.info('Making image noninteractive')
754 if not self._server.make_image_noninteractive():
755 logging.error('Failed to make image noninteractive. '
756 'Please take a look at Servo Logs.')
757
Kalin Stoyanovb3c11f32018-05-11 09:02:00 -0700758 def boot_in_recovery_mode(self):
759 """Boot host DUT in recovery mode."""
760 self._power_state.power_on(rec_mode=self._power_state.REC_ON)
761 self.switch_usbkey('dut')
762
Mike Truty49153d82012-08-21 22:27:30 -0500763
Simran Basi741b5d42012-05-18 11:27:15 -0700764 def install_recovery_image(self, image_path=None,
J. Richard Barnetteb6de7e32013-02-14 13:28:04 -0800765 make_image_noninteractive=False):
Dan Shic67f1332016-04-06 12:38:06 -0700766 """Install the recovery image specified by the path onto the DUT.
Jon Salzc88e5b62011-11-30 14:38:54 +0800767
768 This method uses google recovery mode to install a recovery image
769 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 +0800770 board specified by the usb_dev. If no image path is specified
Jon Salzc88e5b62011-11-30 14:38:54 +0800771 we use the recovery image already on the usb image.
772
Dan Shic67f1332016-04-06 12:38:06 -0700773 @param image_path: Path on the host to the recovery image.
774 @param make_image_noninteractive: Make the recovery image
775 noninteractive, therefore the DUT will reboot automatically
776 after installation.
Jon Salzc88e5b62011-11-30 14:38:54 +0800777 """
Mike Truty49153d82012-08-21 22:27:30 -0500778 self.image_to_servo_usb(image_path, make_image_noninteractive)
Kalin Stoyanovb3c11f32018-05-11 09:02:00 -0700779 self.boot_in_recovery_mode()
Jon Salzc88e5b62011-11-30 14:38:54 +0800780
781
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800782 def _scp_image(self, image_path):
783 """Copy image to the servo host.
784
785 When programming a firmware image on the DUT, the image must be
786 located on the host to which the servo device is connected. Sometimes
787 servo is controlled by a remote host, in this case the image needs to
788 be transferred to the remote host.
789
790 @param image_path: a string, name of the firmware image file to be
791 transferred.
792 @return: a string, full path name of the copied file on the remote.
793 """
794
795 dest_path = os.path.join('/tmp', os.path.basename(image_path))
Fang Deng5d518f42013-08-02 14:04:32 -0700796 self._servo_host.send_file(image_path, dest_path)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800797 return dest_path
798
799
Dan Shifecdaf42015-07-28 10:17:26 -0700800 def system(self, command, timeout=3600):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700801 """Execute the passed in command on the servod host.
802
803 @param command Command to be executed.
Dan Shifecdaf42015-07-28 10:17:26 -0700804 @param timeout Maximum number of seconds of runtime allowed. Default to
805 1 hour.
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700806 """
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800807 logging.info('Will execute on servo host: %s', command)
Fang Deng5d518f42013-08-02 14:04:32 -0700808 self._servo_host.run(command, timeout=timeout)
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800809
810
Dan Shifecdaf42015-07-28 10:17:26 -0700811 def system_output(self, command, timeout=3600,
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800812 ignore_status=False, args=()):
813 """Execute the passed in command on the servod host, return stdout.
814
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700815 @param command a string, the command to execute
816 @param timeout an int, max number of seconds to wait til command
Dan Shifecdaf42015-07-28 10:17:26 -0700817 execution completes. Default to 1 hour.
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700818 @param ignore_status a Boolean, if true - ignore command's nonzero exit
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800819 status, otherwise an exception will be thrown
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700820 @param args a tuple of strings, each becoming a separate command line
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800821 parameter for the command
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700822 @return command's stdout as a string.
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800823 """
Fang Deng5d518f42013-08-02 14:04:32 -0700824 return self._servo_host.run(command, timeout=timeout,
825 ignore_status=ignore_status,
826 args=args).stdout.strip()
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800827
828
Dan Shia5fef052015-05-18 23:28:47 -0700829 def get_servo_version(self):
830 """Get the version of the servo, e.g., servo_v2 or servo_v3.
831
832 @return: The version of the servo.
833
834 """
835 return self._server.get_version()
836
837
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800838 def _initialize_programmer(self, rw_only=False):
839 """Initialize the firmware programmer.
840
841 @param rw_only: True to initialize a programmer which only
842 programs the RW portions.
843 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700844 if self._programmer:
845 return
846 # Initialize firmware programmer
Dan Shia5fef052015-05-18 23:28:47 -0700847 servo_version = self.get_servo_version()
Dan Shi9cb0eec2014-06-03 09:04:50 -0700848 if servo_version.startswith('servo_v2'):
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700849 self._programmer = firmware_programmer.ProgrammerV2(self)
Wai-Hong Tamc36c4d22016-09-09 10:39:45 -0700850 self._programmer_rw = firmware_programmer.ProgrammerV2RwOnly(self)
Kevin Chengdf2e29f2016-09-09 02:31:22 -0700851 # Both servo v3 and v4 use the same programming methods so just leverage
852 # ProgrammerV3 for servo v4 as well.
853 elif (servo_version.startswith('servo_v3') or
854 servo_version.startswith('servo_v4')):
Dan Shia5fef052015-05-18 23:28:47 -0700855 self._programmer = firmware_programmer.ProgrammerV3(self)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800856 self._programmer_rw = firmware_programmer.ProgrammerV3RwOnly(self)
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700857 else:
858 raise error.TestError(
859 'No firmware programmer for servo version: %s' %
Congbin Guo42427612019-02-12 10:22:06 -0800860 servo_version)
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700861
862
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800863 def program_bios(self, image, rw_only=False):
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800864 """Program bios on DUT with given image.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800865
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800866 @param image: a string, file name of the BIOS image to program
867 on the DUT.
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800868 @param rw_only: True to only program the RW portion of BIOS.
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800869
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800870 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700871 self._initialize_programmer()
Ricky Liangc31aab32014-07-03 16:23:29 +0800872 if not self.is_localhost():
873 image = self._scp_image(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800874 if rw_only:
875 self._programmer_rw.program_bios(image)
876 else:
877 self._programmer.program_bios(image)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800878
879
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800880 def program_ec(self, image, rw_only=False):
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800881 """Program ec on DUT with given image.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800882
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800883 @param image: a string, file name of the EC image to program
884 on the DUT.
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800885 @param rw_only: True to only program the RW portion of EC.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800886
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800887 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700888 self._initialize_programmer()
Ricky Liangc31aab32014-07-03 16:23:29 +0800889 if not self.is_localhost():
890 image = self._scp_image(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800891 if rw_only:
Congbin Guo42427612019-02-12 10:22:06 -0800892 self._programmer_rw.program_ec(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800893 else:
Congbin Guo42427612019-02-12 10:22:06 -0800894 self._programmer.program_ec(image)
895
896
897 def _reprogram(self, tarball_path, firmware_name, image_candidates,
898 rw_only):
899 """Helper function to reprogram firmware for EC or BIOS.
900
901 @param tarball_path: The path of the downloaded build tarball.
902 @param: firmware_name: either 'EC' or 'BIOS'.
903 @param image_candidates: A tuple of the paths of image candidates.
904 @param rw_only: True to only install firmware to its RW portions. Keep
905 the RO portions unchanged.
906
907 @raise: TestError if cannot extract firmware from the tarball.
908 """
909 dest_dir = os.path.dirname(tarball_path)
910 image = _extract_image_from_tarball(tarball_path, dest_dir,
911 image_candidates)
912 if not image:
913 if firmware_name == 'EC':
914 logging.info('Not a Chrome EC, ignore re-programming it')
915 return
916 else:
917 raise error.TestError('Failed to extract the %s image from '
918 'tarball' % firmware_name)
919
920 logging.info('Will re-program %s %snow', firmware_name,
921 'RW ' if rw_only else '')
922
923 if firmware_name == 'EC':
924 self.program_ec(os.path.join(dest_dir, image), rw_only)
925 else:
926 self.program_bios(os.path.join(dest_dir, image), rw_only)
927
928
Shelley Chenac61d5a2019-06-24 15:35:46 -0700929 def program_firmware(self, board, model, tarball_path, rw_only=False):
Congbin Guo42427612019-02-12 10:22:06 -0800930 """Program firmware (EC, if applied, and BIOS) of the DUT.
931
Shelley Chenac61d5a2019-06-24 15:35:46 -0700932 @param board: The DUT board name.
Congbin Guo42427612019-02-12 10:22:06 -0800933 @param model: The DUT model name.
934 @param tarball_path: The path of the downloaded build tarball.
935 @param rw_only: True to only install firmware to its RW portions. Keep
936 the RO portions unchanged.
937 """
Shelley Chenac61d5a2019-06-24 15:35:46 -0700938 ap_image_candidates = ('image.bin', 'image-%s.bin' % model,
939 'image-%s.bin' % board)
940 ec_image_candidates = ('ec.bin', '%s/ec.bin' % model,
941 '%s/ec.bin' % board)
Congbin Guo42427612019-02-12 10:22:06 -0800942
943 self._reprogram(tarball_path, 'EC', ec_image_candidates, rw_only)
944 self._reprogram(tarball_path, 'BIOS', ap_image_candidates, rw_only)
945
946 self.get_power_state_controller().reset()
947 time.sleep(Servo.BOOT_DELAY)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800948
Fang Dengafb88142013-05-30 17:44:31 -0700949
950 def _switch_usbkey_power(self, power_state, detection_delay=False):
951 """Switch usbkey power.
952
953 This function switches usbkey power by setting the value of
954 'prtctl4_pwren'. If the power is already in the
955 requested state, this function simply returns.
956
957 @param power_state: A string, 'on' or 'off'.
958 @param detection_delay: A boolean value, if True, sleep
959 for |USB_DETECTION_DELAY| after switching
960 the power on.
961 """
Kevin Chenga22c4a82016-10-07 14:13:25 -0700962 # TODO(kevcheng): Forgive me for this terrible hack. This is just to
963 # handle beaglebones that haven't yet updated and have the
964 # safe_switch_usbkey_power RPC. I'll remove this once all beaglebones
965 # have been updated and also think about a better way to handle
966 # situations like this.
967 try:
968 self._server.safe_switch_usbkey_power(power_state)
969 except Exception:
970 self.set('prtctl4_pwren', power_state)
Fang Dengafb88142013-05-30 17:44:31 -0700971 if power_state == 'off':
972 time.sleep(self.USB_POWEROFF_DELAY)
973 elif detection_delay:
974 time.sleep(self.USB_DETECTION_DELAY)
975
976
977 def switch_usbkey(self, usb_state):
978 """Connect USB flash stick to either host or DUT, or turn USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800979
980 This function switches the servo multiplexer to provide electrical
Fang Dengafb88142013-05-30 17:44:31 -0700981 connection between the USB port J3 and either host or DUT side. It
982 can also be used to turn the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800983
Fang Dengafb88142013-05-30 17:44:31 -0700984 Switching to 'dut' or 'host' is accompanied by powercycling
985 of the USB stick, because it sometimes gets wedged if the mux
986 is switched while the stick power is on.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800987
Fang Dengafb88142013-05-30 17:44:31 -0700988 @param usb_state: A string, one of 'dut', 'host', or 'off'.
989 'dut' and 'host' indicate which side the
990 USB flash device is required to be connected to.
991 'off' indicates turning the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800992
Fang Dengafb88142013-05-30 17:44:31 -0700993 @raise: error.TestError in case the parameter is not 'dut'
994 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800995 """
Fang Dengafb88142013-05-30 17:44:31 -0700996 if self.get_usbkey_direction() == usb_state:
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800997 return
998
Fang Dengafb88142013-05-30 17:44:31 -0700999 if usb_state == 'off':
Dan Shia5fef052015-05-18 23:28:47 -07001000 self._switch_usbkey_power('off')
1001 self._usb_state = usb_state
1002 return
Fang Dengafb88142013-05-30 17:44:31 -07001003 elif usb_state == 'host':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001004 mux_direction = 'servo_sees_usbkey'
Fang Dengafb88142013-05-30 17:44:31 -07001005 elif usb_state == 'dut':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001006 mux_direction = 'dut_sees_usbkey'
1007 else:
Fang Dengafb88142013-05-30 17:44:31 -07001008 raise error.TestError('Unknown USB state request: %s' % usb_state)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001009
Fang Dengafb88142013-05-30 17:44:31 -07001010 self._switch_usbkey_power('off')
Kevin Chenga22c4a82016-10-07 14:13:25 -07001011 # TODO(kevcheng): Forgive me for this terrible hack. This is just to
1012 # handle beaglebones that haven't yet updated and have the
1013 # safe_switch_usbkey RPC. I'll remove this once all beaglebones have
1014 # been updated and also think about a better way to handle situations
1015 # like this.
1016 try:
1017 self._server.safe_switch_usbkey(mux_direction)
1018 except Exception:
1019 self.set('usb_mux_sel1', mux_direction)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001020 time.sleep(self.USB_POWEROFF_DELAY)
Fang Dengafb88142013-05-30 17:44:31 -07001021 self._switch_usbkey_power('on', usb_state == 'host')
1022 self._usb_state = usb_state
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001023
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001024
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001025 def get_usbkey_direction(self):
Fang Dengafb88142013-05-30 17:44:31 -07001026 """Get which side USB is connected to or 'off' if usb power is off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001027
Fang Dengafb88142013-05-30 17:44:31 -07001028 @return: A string, one of 'dut', 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001029 """
Fang Dengafb88142013-05-30 17:44:31 -07001030 if not self._usb_state:
1031 if self.get('prtctl4_pwren') == 'off':
1032 self._usb_state = 'off'
1033 elif self.get('usb_mux_sel1').startswith('dut'):
1034 self._usb_state = 'dut'
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001035 else:
Fang Dengafb88142013-05-30 17:44:31 -07001036 self._usb_state = 'host'
1037 return self._usb_state
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001038
1039
Wai-Hong Tam60377262018-03-01 10:55:39 -08001040 def set_servo_v4_role(self, role):
1041 """Set the power role of servo v4, either 'src' or 'snk'.
1042
1043 It does nothing if not a servo v4.
1044
1045 @param role: Power role for DUT port on servo v4, either 'src' or 'snk'.
1046 """
1047 servo_version = self.get_servo_version()
1048 if servo_version.startswith('servo_v4'):
1049 value = self.get('servo_v4_role')
1050 if value != role:
1051 self.set_nocheck('servo_v4_role', role)
1052 else:
1053 logging.debug('Already in the role: %s.', role)
1054 else:
1055 logging.debug('Not a servo v4, unable to set role to %s.', role)
1056
1057
Congbin Guofc3b8962019-03-22 17:38:46 -07001058 @property
1059 def uart_logs_dir(self):
1060 """Return the directory to save UART logs."""
1061 return self._uart.logs_dir if self._uart else ""
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001062
Congbin Guofc3b8962019-03-22 17:38:46 -07001063
1064 @uart_logs_dir.setter
1065 def uart_logs_dir(self, logs_dir):
1066 """Set directory to save UART logs.
1067
1068 @param logs_dir String of directory name."""
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001069 if self._uart:
Congbin Guofc3b8962019-03-22 17:38:46 -07001070 self._uart.logs_dir = logs_dir
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001071
1072
1073 def close(self):
1074 """Close the servo object."""
1075 if self._uart:
1076 self._uart.stop_capture()
Congbin Guofc3b8962019-03-22 17:38:46 -07001077 self._uart.dump()
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001078 self._uart = None