blob: da4b2c033ecba48e392a9875e5f01c64af145c6d [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
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700354 def power_long_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700355 """Simulate a long power button press."""
J. Richard Barnettef12bff22012-07-18 14:41:06 -0700356 # After a long power press, the EC may ignore the next power
357 # button press (at least on Alex). To guarantee that this
358 # won't happen, we need to allow the EC one second to
359 # collect itself.
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800360 # long_press is defined as 8.5s in servod
361 self.set_nocheck('power_key', 'long_press')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700362
363
364 def power_normal_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700365 """Simulate a normal power button press."""
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800366 # press is defined as 1.2s in servod
367 self.set_nocheck('power_key', 'press')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700368
369
370 def power_short_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700371 """Simulate a short power button press."""
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800372 # tab is defined as 0.2s in servod
373 self.set_nocheck('power_key', 'tab')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700374
375
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800376 def power_key(self, press_secs='tab'):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700377 """Simulate a power button press.
378
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800379 @param press_secs: int, float, str; time to press key in seconds or
380 known shorthand: 'tab' 'press' 'long_press'
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700381 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800382 self.set_nocheck('power_key', press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700383
384
385 def lid_open(self):
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800386 """Simulate opening the lid and raise exception if all attempts fail"""
387 self.set('lid_open', 'yes')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700388
389
390 def lid_close(self):
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800391 """Simulate closing the lid and raise exception if all attempts fail
Craig Harrison48997262011-06-27 14:31:10 -0700392
393 Waits 6 seconds to ensure the device is fully asleep before returning.
394 """
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800395 self.set('lid_open', 'no')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700396 time.sleep(Servo.SLEEP_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700397
Shelley Chenc26575a2015-09-18 10:56:16 -0700398 def volume_up(self, timeout=300):
Kevin Chenge448a8b2016-09-09 10:18:11 -0700399 """Simulate pushing the volume down button.
400
401 @param timeout: Timeout for setting the volume.
402 """
Shelley Chenc26575a2015-09-18 10:56:16 -0700403 self.set_get_all(['volume_up:yes',
404 'sleep:%.4f' % self.SERVO_KEY_PRESS_DELAY,
405 'volume_up:no'])
406 # we need to wait for commands to take effect before moving on
407 time_left = float(timeout)
408 while time_left > 0.0:
409 value = self.get('volume_up')
410 if value == 'no':
411 return
412 time.sleep(self.SHORT_DELAY)
413 time_left = time_left - self.SHORT_DELAY
414 raise error.TestFail("Failed setting volume_up to no")
415
416 def volume_down(self, timeout=300):
Kevin Chenge448a8b2016-09-09 10:18:11 -0700417 """Simulate pushing the volume down button.
418
419 @param timeout: Timeout for setting the volume.
420 """
Shelley Chenc26575a2015-09-18 10:56:16 -0700421 self.set_get_all(['volume_down:yes',
422 'sleep:%.4f' % self.SERVO_KEY_PRESS_DELAY,
423 'volume_down:no'])
424 # we need to wait for commands to take effect before moving on
425 time_left = float(timeout)
426 while time_left > 0.0:
427 value = self.get('volume_down')
428 if value == 'no':
429 return
430 time.sleep(self.SHORT_DELAY)
431 time_left = time_left - self.SHORT_DELAY
432 raise error.TestFail("Failed setting volume_down to no")
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700433
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800434 def ctrl_d(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800435 """Simulate Ctrl-d simultaneous button presses.
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800436
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800437 @param press_secs: int, float, str; time to press key in seconds or
438 known shorthand: 'tab' 'press' 'long_press'
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800439 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800440 self.set_nocheck('ctrl_d', press_secs)
Gediminas Ramanauskas9da80f22012-11-14 12:59:43 -0800441
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800442
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800443 def ctrl_u(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800444 """Simulate Ctrl-u simultaneous button presses.
445
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800446 @param press_secs: int, float, str; time to press key in seconds or
447 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800448 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800449 self.set_nocheck('ctrl_u', press_secs)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700450
451
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800452 def ctrl_enter(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800453 """Simulate Ctrl-enter simultaneous button presses.
454
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800455 @param press_secs: int, float, str; time to press key in seconds or
456 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800457 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800458 self.set_nocheck('ctrl_enter', press_secs)
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -0700459
460
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800461 def ctrl_key(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800462 """Simulate Enter key button press.
463
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800464 @param press_secs: int, float, str; time to press key in seconds or
465 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800466 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800467 self.set_nocheck('ctrl_key', press_secs)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700468
469
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800470 def enter_key(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800471 """Simulate Enter key button press.
472
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800473 @param press_secs: int, float, str; time to press key in seconds or
474 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800475 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800476 self.set_nocheck('enter_key', press_secs)
Todd Broch9753bd42012-03-21 10:15:08 -0700477
478
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800479 def refresh_key(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800480 """Simulate Refresh key (F3) button press.
481
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800482 @param press_secs: int, float, str; time to press key in seconds or
483 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800484 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800485 self.set_nocheck('refresh_key', press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700486
487
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800488 def ctrl_refresh_key(self, press_secs='tab'):
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800489 """Simulate Ctrl and Refresh (F3) simultaneous press.
490
491 This key combination is an alternative of Space key.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800492
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800493 @param press_secs: int, float, str; time to press key in seconds or
494 known shorthand: 'tab' 'press' 'long_press'
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800495 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800496 self.set_nocheck('ctrl_refresh_key', press_secs)
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800497
498
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800499 def imaginary_key(self, press_secs='tab'):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700500 """Simulate imaginary key button press.
501
502 Maps to a key that doesn't physically exist.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800503
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800504 @param press_secs: int, float, str; time to press key in seconds or
505 known shorthand: 'tab' 'press' 'long_press'
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700506 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800507 self.set_nocheck('imaginary_key', press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700508
509
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800510 def sysrq_x(self, press_secs='tab'):
Vincent Palatine7dc9282016-07-14 11:31:58 +0200511 """Simulate Alt VolumeUp X simulataneous press.
512
513 This key combination is the kernel system request (sysrq) X.
514
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800515 @param press_secs: int, float, str; time to press key in seconds or
516 known shorthand: 'tab' 'press' 'long_press'
Vincent Palatine7dc9282016-07-14 11:31:58 +0200517 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800518 self.set_nocheck('sysrq_x', press_secs)
Vincent Palatine7dc9282016-07-14 11:31:58 +0200519
520
Tom Wai-Hong Tam93b5c092015-05-14 02:50:43 +0800521 def toggle_recovery_switch(self):
522 """Toggle recovery switch on and off."""
523 self.enable_recovery_mode()
524 time.sleep(self.REC_TOGGLE_DELAY)
525 self.disable_recovery_mode()
526
527
Craig Harrison6b36b122011-06-28 17:58:43 -0700528 def enable_recovery_mode(self):
529 """Enable recovery mode on device."""
530 self.set('rec_mode', 'on')
531
532
533 def disable_recovery_mode(self):
534 """Disable recovery mode on device."""
535 self.set('rec_mode', 'off')
536
537
Tom Wai-Hong Tamf9ded092015-05-20 05:37:22 +0800538 def toggle_development_switch(self):
539 """Toggle development switch on and off."""
540 self.enable_development_mode()
541 time.sleep(self.DEV_TOGGLE_DELAY)
542 self.disable_development_mode()
543
544
Craig Harrison6b36b122011-06-28 17:58:43 -0700545 def enable_development_mode(self):
546 """Enable development mode on device."""
547 self.set('dev_mode', 'on')
548
549
550 def disable_development_mode(self):
551 """Disable development mode on device."""
552 self.set('dev_mode', 'off')
553
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700554 def boot_devmode(self):
555 """Boot a dev-mode device that is powered off."""
Tom Wai-Hong Tam23869102012-01-17 15:41:30 +0800556 self.power_short_press()
Craig Harrison48997262011-06-27 14:31:10 -0700557 self.pass_devmode()
558
559
560 def pass_devmode(self):
561 """Pass through boot screens in dev-mode."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700562 time.sleep(Servo.BOOT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700563 self.ctrl_d()
Chrome Bot9a1137d2011-07-19 14:35:00 -0700564 time.sleep(Servo.BOOT_DELAY)
Craig Harrison48997262011-06-27 14:31:10 -0700565
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700566
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800567 def get_board(self):
Wai-Hong Tam0f904002017-09-19 15:52:22 -0700568 """Get the board connected to servod."""
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800569 return self._server.get_board()
570
571
Wai-Hong Tam0f904002017-09-19 15:52:22 -0700572 def get_base_board(self):
573 """Get the board of the base connected to servod."""
Wai-Hong Tam7f6169e2017-09-29 09:23:48 -0700574 try:
575 return self._server.get_base_board()
576 except xmlrpclib.Fault as e:
577 # TODO(waihong): Remove the following compatibility check when
578 # the new versions of hdctools are deployed.
579 if 'not supported' in str(e):
580 logging.warning('The servod is too old that get_base_board '
581 'not supported.')
582 return ''
583 raise
Wai-Hong Tam0f904002017-09-19 15:52:22 -0700584
585
Wai-Hong Tamcc399ee2017-12-08 12:43:28 -0800586 def get_ec_active_copy(self):
587 """Get the active copy of the EC image."""
588 return self.get('ec_active_copy')
589
590
Todd Brochefe72cb2012-07-11 19:58:53 -0700591 def _get_xmlrpclib_exception(self, xmlexc):
592 """Get meaningful exception string from xmlrpc.
593
594 Args:
595 xmlexc: xmlrpclib.Fault object
596
597 xmlrpclib.Fault.faultString has the following format:
598
599 <type 'exception type'>:'actual error message'
600
601 Parse and return the real exception from the servod side instead of the
602 less meaningful one like,
603 <Fault 1: "<type 'exceptions.AttributeError'>:'tca6416' object has no
604 attribute 'hw_driver'">
605
606 Returns:
607 string of underlying exception raised in servod.
608 """
609 return re.sub('^.*>:', '', xmlexc.faultString)
610
611
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700612 def get(self, gpio_name):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700613 """Get the value of a gpio from Servod.
614
615 @param gpio_name Name of the gpio.
616 """
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700617 assert gpio_name
Todd Brochefe72cb2012-07-11 19:58:53 -0700618 try:
Simran Basi1cbc5f22013-07-17 14:23:58 -0700619 return self._server.get(gpio_name)
Todd Brochefe72cb2012-07-11 19:58:53 -0700620 except xmlrpclib.Fault as e:
621 err_msg = "Getting '%s' :: %s" % \
622 (gpio_name, self._get_xmlrpclib_exception(e))
623 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700624
625
626 def set(self, gpio_name, gpio_value):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700627 """Set and check the value of a gpio using Servod.
628
629 @param gpio_name Name of the gpio.
630 @param gpio_value New setting for the gpio.
631 """
Chrome Bot9a1137d2011-07-19 14:35:00 -0700632 self.set_nocheck(gpio_name, gpio_value)
Todd Brochcf7c6652012-02-24 13:03:59 -0800633 retry_count = Servo.GET_RETRY_MAX
634 while gpio_value != self.get(gpio_name) and retry_count:
Ilja H. Friedel04be2bd2014-05-07 21:29:59 -0700635 logging.warning("%s != %s, retry %d", gpio_name, gpio_value,
Todd Brochcf7c6652012-02-24 13:03:59 -0800636 retry_count)
637 retry_count -= 1
638 time.sleep(Servo.SHORT_DELAY)
639 if not retry_count:
640 assert gpio_value == self.get(gpio_name), \
641 'Servo failed to set %s to %s' % (gpio_name, gpio_value)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700642
643
644 def set_nocheck(self, gpio_name, gpio_value):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700645 """Set the value of a gpio using Servod.
646
647 @param gpio_name Name of the gpio.
648 @param gpio_value New setting for the gpio.
649 """
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700650 assert gpio_name and gpio_value
Mary Ruthvendfa45342019-06-25 10:10:17 -0700651 logging.debug('Setting %s to %r', gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700652 try:
Simran Basi1cbc5f22013-07-17 14:23:58 -0700653 self._server.set(gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700654 except xmlrpclib.Fault as e:
Mary Ruthven73d3a352018-05-10 15:35:20 -0700655 err_msg = "Setting '%s' to %r :: %s" % \
Todd Brochefe72cb2012-07-11 19:58:53 -0700656 (gpio_name, gpio_value, self._get_xmlrpclib_exception(e))
657 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700658
659
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800660 def set_get_all(self, controls):
661 """Set &| get one or more control values.
662
663 @param controls: list of strings, controls to set &| get.
664
665 @raise: error.TestError in case error occurs setting/getting values.
666 """
667 rv = []
668 try:
Mary Ruthvendfa45342019-06-25 10:10:17 -0700669 logging.debug('Set/get all: %s', str(controls))
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800670 rv = self._server.set_get_all(controls)
671 except xmlrpclib.Fault as e:
672 # TODO(waihong): Remove the following backward compatibility when
673 # the new versions of hdctools are deployed.
674 if 'not supported' in str(e):
675 logging.warning('The servod is too old that set_get_all '
676 'not supported. Use set and get instead.')
677 for control in controls:
678 if ':' in control:
679 (name, value) = control.split(':')
680 if name == 'sleep':
681 time.sleep(float(value))
682 else:
683 self.set_nocheck(name, value)
684 rv.append(True)
685 else:
686 rv.append(self.get(name))
687 else:
688 err_msg = "Problem with '%s' :: %s" % \
689 (controls, self._get_xmlrpclib_exception(e))
690 raise error.TestFail(err_msg)
691 return rv
692
693
Jon Salzc88e5b62011-11-30 14:38:54 +0800694 # TODO(waihong) It may fail if multiple servo's are connected to the same
695 # host. Should look for a better way, like the USB serial name, to identify
696 # the USB device.
Simran Basi741b5d42012-05-18 11:27:15 -0700697 # TODO(sbasi) Remove this code from autoserv once firmware tests have been
698 # updated.
Kevin Chenga22c4a82016-10-07 14:13:25 -0700699 def probe_host_usb_dev(self, timeout=_USB_PROBE_TIMEOUT):
Jon Salzc88e5b62011-11-30 14:38:54 +0800700 """Probe the USB disk device plugged-in the servo from the host side.
701
Kevin Chengeb06fe72016-08-22 15:26:32 -0700702 It uses servod to discover if there is a usb device attached to it.
Jon Salzc88e5b62011-11-30 14:38:54 +0800703
Kevin Chenga22c4a82016-10-07 14:13:25 -0700704 @param timeout The timeout period when probing for the usb host device.
705
706 @return: String of USB disk path (e.g. '/dev/sdb') or None.
Jon Salzc88e5b62011-11-30 14:38:54 +0800707 """
Ruben Rodriguez Buchillon9a4bfc32018-10-16 20:46:25 +0800708 # Set up Servo's usb mux.
709 self.switch_usbkey('host')
Kevin Chenga22c4a82016-10-07 14:13:25 -0700710 return self._server.probe_host_usb_dev(timeout) or None
Jon Salzc88e5b62011-11-30 14:38:54 +0800711
712
Mike Truty49153d82012-08-21 22:27:30 -0500713 def image_to_servo_usb(self, image_path=None,
714 make_image_noninteractive=False):
715 """Install an image to the USB key plugged into the servo.
716
717 This method may copy any image to the servo USB key including a
718 recovery image or a test image. These images are frequently used
719 for test purposes such as restoring a corrupted image or conducting
720 an upgrade of ec/fw/kernel as part of a test of a specific image part.
721
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700722 @param image_path Path on the host to the recovery image.
723 @param make_image_noninteractive Make the recovery image
724 noninteractive, therefore the DUT
725 will reboot automatically after
726 installation.
Mike Truty49153d82012-08-21 22:27:30 -0500727 """
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700728 # We're about to start plugging/unplugging the USB key. We
729 # don't know the state of the DUT, or what it might choose
730 # to do to the device after hotplug. To avoid surprises,
731 # force the DUT to be off.
732 self._server.hwinit()
733 self._power_state.power_off()
Mike Truty49153d82012-08-21 22:27:30 -0500734
735 # Set up Servo's usb mux.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800736 self.switch_usbkey('host')
Mike Truty49153d82012-08-21 22:27:30 -0500737 if image_path:
Tom Wai-Hong Tam42f136d2012-10-26 11:11:23 +0800738 logging.info('Searching for usb device and copying image to it. '
739 'Please wait a few minutes...')
Mike Truty49153d82012-08-21 22:27:30 -0500740 if not self._server.download_image_to_usb(image_path):
741 logging.error('Failed to transfer requested image to USB. '
742 'Please take a look at Servo Logs.')
743 raise error.AutotestError('Download image to usb failed.')
744 if make_image_noninteractive:
745 logging.info('Making image noninteractive')
746 if not self._server.make_image_noninteractive():
747 logging.error('Failed to make image noninteractive. '
748 'Please take a look at Servo Logs.')
749
Kalin Stoyanovb3c11f32018-05-11 09:02:00 -0700750 def boot_in_recovery_mode(self):
751 """Boot host DUT in recovery mode."""
752 self._power_state.power_on(rec_mode=self._power_state.REC_ON)
753 self.switch_usbkey('dut')
754
Mike Truty49153d82012-08-21 22:27:30 -0500755
Simran Basi741b5d42012-05-18 11:27:15 -0700756 def install_recovery_image(self, image_path=None,
J. Richard Barnetteb6de7e32013-02-14 13:28:04 -0800757 make_image_noninteractive=False):
Dan Shic67f1332016-04-06 12:38:06 -0700758 """Install the recovery image specified by the path onto the DUT.
Jon Salzc88e5b62011-11-30 14:38:54 +0800759
760 This method uses google recovery mode to install a recovery image
761 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 +0800762 board specified by the usb_dev. If no image path is specified
Jon Salzc88e5b62011-11-30 14:38:54 +0800763 we use the recovery image already on the usb image.
764
Dan Shic67f1332016-04-06 12:38:06 -0700765 @param image_path: Path on the host to the recovery image.
766 @param make_image_noninteractive: Make the recovery image
767 noninteractive, therefore the DUT will reboot automatically
768 after installation.
Jon Salzc88e5b62011-11-30 14:38:54 +0800769 """
Mike Truty49153d82012-08-21 22:27:30 -0500770 self.image_to_servo_usb(image_path, make_image_noninteractive)
Kalin Stoyanovb3c11f32018-05-11 09:02:00 -0700771 self.boot_in_recovery_mode()
Jon Salzc88e5b62011-11-30 14:38:54 +0800772
773
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800774 def _scp_image(self, image_path):
775 """Copy image to the servo host.
776
777 When programming a firmware image on the DUT, the image must be
778 located on the host to which the servo device is connected. Sometimes
779 servo is controlled by a remote host, in this case the image needs to
780 be transferred to the remote host.
781
782 @param image_path: a string, name of the firmware image file to be
783 transferred.
784 @return: a string, full path name of the copied file on the remote.
785 """
786
787 dest_path = os.path.join('/tmp', os.path.basename(image_path))
Fang Deng5d518f42013-08-02 14:04:32 -0700788 self._servo_host.send_file(image_path, dest_path)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800789 return dest_path
790
791
Dan Shifecdaf42015-07-28 10:17:26 -0700792 def system(self, command, timeout=3600):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700793 """Execute the passed in command on the servod host.
794
795 @param command Command to be executed.
Dan Shifecdaf42015-07-28 10:17:26 -0700796 @param timeout Maximum number of seconds of runtime allowed. Default to
797 1 hour.
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700798 """
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800799 logging.info('Will execute on servo host: %s', command)
Fang Deng5d518f42013-08-02 14:04:32 -0700800 self._servo_host.run(command, timeout=timeout)
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800801
802
Dan Shifecdaf42015-07-28 10:17:26 -0700803 def system_output(self, command, timeout=3600,
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800804 ignore_status=False, args=()):
805 """Execute the passed in command on the servod host, return stdout.
806
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700807 @param command a string, the command to execute
808 @param timeout an int, max number of seconds to wait til command
Dan Shifecdaf42015-07-28 10:17:26 -0700809 execution completes. Default to 1 hour.
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700810 @param ignore_status a Boolean, if true - ignore command's nonzero exit
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800811 status, otherwise an exception will be thrown
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700812 @param args a tuple of strings, each becoming a separate command line
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800813 parameter for the command
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700814 @return command's stdout as a string.
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800815 """
Fang Deng5d518f42013-08-02 14:04:32 -0700816 return self._servo_host.run(command, timeout=timeout,
817 ignore_status=ignore_status,
818 args=args).stdout.strip()
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800819
820
Dan Shia5fef052015-05-18 23:28:47 -0700821 def get_servo_version(self):
822 """Get the version of the servo, e.g., servo_v2 or servo_v3.
823
824 @return: The version of the servo.
825
826 """
827 return self._server.get_version()
828
829
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800830 def _initialize_programmer(self, rw_only=False):
831 """Initialize the firmware programmer.
832
833 @param rw_only: True to initialize a programmer which only
834 programs the RW portions.
835 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700836 if self._programmer:
837 return
838 # Initialize firmware programmer
Dan Shia5fef052015-05-18 23:28:47 -0700839 servo_version = self.get_servo_version()
Dan Shi9cb0eec2014-06-03 09:04:50 -0700840 if servo_version.startswith('servo_v2'):
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700841 self._programmer = firmware_programmer.ProgrammerV2(self)
Wai-Hong Tamc36c4d22016-09-09 10:39:45 -0700842 self._programmer_rw = firmware_programmer.ProgrammerV2RwOnly(self)
Kevin Chengdf2e29f2016-09-09 02:31:22 -0700843 # Both servo v3 and v4 use the same programming methods so just leverage
844 # ProgrammerV3 for servo v4 as well.
845 elif (servo_version.startswith('servo_v3') or
846 servo_version.startswith('servo_v4')):
Dan Shia5fef052015-05-18 23:28:47 -0700847 self._programmer = firmware_programmer.ProgrammerV3(self)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800848 self._programmer_rw = firmware_programmer.ProgrammerV3RwOnly(self)
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700849 else:
850 raise error.TestError(
851 'No firmware programmer for servo version: %s' %
Congbin Guo42427612019-02-12 10:22:06 -0800852 servo_version)
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700853
854
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800855 def program_bios(self, image, rw_only=False):
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800856 """Program bios on DUT with given image.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800857
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800858 @param image: a string, file name of the BIOS image to program
859 on the DUT.
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800860 @param rw_only: True to only program the RW portion of BIOS.
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800861
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800862 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700863 self._initialize_programmer()
Ricky Liangc31aab32014-07-03 16:23:29 +0800864 if not self.is_localhost():
865 image = self._scp_image(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800866 if rw_only:
867 self._programmer_rw.program_bios(image)
868 else:
869 self._programmer.program_bios(image)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800870
871
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800872 def program_ec(self, image, rw_only=False):
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800873 """Program ec on DUT with given image.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800874
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800875 @param image: a string, file name of the EC image to program
876 on the DUT.
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800877 @param rw_only: True to only program the RW portion of EC.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800878
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800879 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700880 self._initialize_programmer()
Ricky Liangc31aab32014-07-03 16:23:29 +0800881 if not self.is_localhost():
882 image = self._scp_image(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800883 if rw_only:
Congbin Guo42427612019-02-12 10:22:06 -0800884 self._programmer_rw.program_ec(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800885 else:
Congbin Guo42427612019-02-12 10:22:06 -0800886 self._programmer.program_ec(image)
887
888
889 def _reprogram(self, tarball_path, firmware_name, image_candidates,
890 rw_only):
891 """Helper function to reprogram firmware for EC or BIOS.
892
893 @param tarball_path: The path of the downloaded build tarball.
894 @param: firmware_name: either 'EC' or 'BIOS'.
895 @param image_candidates: A tuple of the paths of image candidates.
896 @param rw_only: True to only install firmware to its RW portions. Keep
897 the RO portions unchanged.
898
899 @raise: TestError if cannot extract firmware from the tarball.
900 """
901 dest_dir = os.path.dirname(tarball_path)
902 image = _extract_image_from_tarball(tarball_path, dest_dir,
903 image_candidates)
904 if not image:
905 if firmware_name == 'EC':
906 logging.info('Not a Chrome EC, ignore re-programming it')
907 return
908 else:
909 raise error.TestError('Failed to extract the %s image from '
910 'tarball' % firmware_name)
911
912 logging.info('Will re-program %s %snow', firmware_name,
913 'RW ' if rw_only else '')
914
915 if firmware_name == 'EC':
916 self.program_ec(os.path.join(dest_dir, image), rw_only)
917 else:
918 self.program_bios(os.path.join(dest_dir, image), rw_only)
919
920
Shelley Chenac61d5a2019-06-24 15:35:46 -0700921 def program_firmware(self, board, model, tarball_path, rw_only=False):
Congbin Guo42427612019-02-12 10:22:06 -0800922 """Program firmware (EC, if applied, and BIOS) of the DUT.
923
Shelley Chenac61d5a2019-06-24 15:35:46 -0700924 @param board: The DUT board name.
Congbin Guo42427612019-02-12 10:22:06 -0800925 @param model: The DUT model name.
926 @param tarball_path: The path of the downloaded build tarball.
927 @param rw_only: True to only install firmware to its RW portions. Keep
928 the RO portions unchanged.
929 """
Shelley Chenac61d5a2019-06-24 15:35:46 -0700930 ap_image_candidates = ('image.bin', 'image-%s.bin' % model,
931 'image-%s.bin' % board)
932 ec_image_candidates = ('ec.bin', '%s/ec.bin' % model,
933 '%s/ec.bin' % board)
Congbin Guo42427612019-02-12 10:22:06 -0800934
935 self._reprogram(tarball_path, 'EC', ec_image_candidates, rw_only)
936 self._reprogram(tarball_path, 'BIOS', ap_image_candidates, rw_only)
937
938 self.get_power_state_controller().reset()
939 time.sleep(Servo.BOOT_DELAY)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800940
Fang Dengafb88142013-05-30 17:44:31 -0700941
942 def _switch_usbkey_power(self, power_state, detection_delay=False):
943 """Switch usbkey power.
944
945 This function switches usbkey power by setting the value of
946 'prtctl4_pwren'. If the power is already in the
947 requested state, this function simply returns.
948
949 @param power_state: A string, 'on' or 'off'.
950 @param detection_delay: A boolean value, if True, sleep
951 for |USB_DETECTION_DELAY| after switching
952 the power on.
953 """
Kevin Chenga22c4a82016-10-07 14:13:25 -0700954 # TODO(kevcheng): Forgive me for this terrible hack. This is just to
955 # handle beaglebones that haven't yet updated and have the
956 # safe_switch_usbkey_power RPC. I'll remove this once all beaglebones
957 # have been updated and also think about a better way to handle
958 # situations like this.
959 try:
960 self._server.safe_switch_usbkey_power(power_state)
961 except Exception:
962 self.set('prtctl4_pwren', power_state)
Fang Dengafb88142013-05-30 17:44:31 -0700963 if power_state == 'off':
964 time.sleep(self.USB_POWEROFF_DELAY)
965 elif detection_delay:
966 time.sleep(self.USB_DETECTION_DELAY)
967
968
969 def switch_usbkey(self, usb_state):
970 """Connect USB flash stick to either host or DUT, or turn USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800971
972 This function switches the servo multiplexer to provide electrical
Fang Dengafb88142013-05-30 17:44:31 -0700973 connection between the USB port J3 and either host or DUT side. It
974 can also be used to turn the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800975
Fang Dengafb88142013-05-30 17:44:31 -0700976 Switching to 'dut' or 'host' is accompanied by powercycling
977 of the USB stick, because it sometimes gets wedged if the mux
978 is switched while the stick power is on.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800979
Fang Dengafb88142013-05-30 17:44:31 -0700980 @param usb_state: A string, one of 'dut', 'host', or 'off'.
981 'dut' and 'host' indicate which side the
982 USB flash device is required to be connected to.
983 'off' indicates turning the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800984
Fang Dengafb88142013-05-30 17:44:31 -0700985 @raise: error.TestError in case the parameter is not 'dut'
986 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800987 """
Fang Dengafb88142013-05-30 17:44:31 -0700988 if self.get_usbkey_direction() == usb_state:
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800989 return
990
Fang Dengafb88142013-05-30 17:44:31 -0700991 if usb_state == 'off':
Dan Shia5fef052015-05-18 23:28:47 -0700992 self._switch_usbkey_power('off')
993 self._usb_state = usb_state
994 return
Fang Dengafb88142013-05-30 17:44:31 -0700995 elif usb_state == 'host':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800996 mux_direction = 'servo_sees_usbkey'
Fang Dengafb88142013-05-30 17:44:31 -0700997 elif usb_state == 'dut':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800998 mux_direction = 'dut_sees_usbkey'
999 else:
Fang Dengafb88142013-05-30 17:44:31 -07001000 raise error.TestError('Unknown USB state request: %s' % usb_state)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001001
Fang Dengafb88142013-05-30 17:44:31 -07001002 self._switch_usbkey_power('off')
Kevin Chenga22c4a82016-10-07 14:13:25 -07001003 # TODO(kevcheng): Forgive me for this terrible hack. This is just to
1004 # handle beaglebones that haven't yet updated and have the
1005 # safe_switch_usbkey RPC. I'll remove this once all beaglebones have
1006 # been updated and also think about a better way to handle situations
1007 # like this.
1008 try:
1009 self._server.safe_switch_usbkey(mux_direction)
1010 except Exception:
1011 self.set('usb_mux_sel1', mux_direction)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001012 time.sleep(self.USB_POWEROFF_DELAY)
Fang Dengafb88142013-05-30 17:44:31 -07001013 self._switch_usbkey_power('on', usb_state == 'host')
1014 self._usb_state = usb_state
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001015
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001016
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001017 def get_usbkey_direction(self):
Fang Dengafb88142013-05-30 17:44:31 -07001018 """Get which side USB is connected to or 'off' if usb power is off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001019
Fang Dengafb88142013-05-30 17:44:31 -07001020 @return: A string, one of 'dut', 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001021 """
Fang Dengafb88142013-05-30 17:44:31 -07001022 if not self._usb_state:
1023 if self.get('prtctl4_pwren') == 'off':
1024 self._usb_state = 'off'
1025 elif self.get('usb_mux_sel1').startswith('dut'):
1026 self._usb_state = 'dut'
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001027 else:
Fang Dengafb88142013-05-30 17:44:31 -07001028 self._usb_state = 'host'
1029 return self._usb_state
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001030
1031
Wai-Hong Tam60377262018-03-01 10:55:39 -08001032 def set_servo_v4_role(self, role):
1033 """Set the power role of servo v4, either 'src' or 'snk'.
1034
1035 It does nothing if not a servo v4.
1036
1037 @param role: Power role for DUT port on servo v4, either 'src' or 'snk'.
1038 """
1039 servo_version = self.get_servo_version()
1040 if servo_version.startswith('servo_v4'):
1041 value = self.get('servo_v4_role')
1042 if value != role:
1043 self.set_nocheck('servo_v4_role', role)
1044 else:
1045 logging.debug('Already in the role: %s.', role)
1046 else:
1047 logging.debug('Not a servo v4, unable to set role to %s.', role)
1048
1049
Congbin Guofc3b8962019-03-22 17:38:46 -07001050 @property
1051 def uart_logs_dir(self):
1052 """Return the directory to save UART logs."""
1053 return self._uart.logs_dir if self._uart else ""
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001054
Congbin Guofc3b8962019-03-22 17:38:46 -07001055
1056 @uart_logs_dir.setter
1057 def uart_logs_dir(self, logs_dir):
1058 """Set directory to save UART logs.
1059
1060 @param logs_dir String of directory name."""
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001061 if self._uart:
Congbin Guofc3b8962019-03-22 17:38:46 -07001062 self._uart.logs_dir = logs_dir
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001063
1064
1065 def close(self):
1066 """Close the servo object."""
1067 if self._uart:
1068 self._uart.stop_capture()
Congbin Guofc3b8962019-03-22 17:38:46 -07001069 self._uart.dump()
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001070 self._uart = None