blob: 929aec8a398b5edddebf35fba8fa21ae57f64810 [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
Congbin Guo42427612019-02-12 10:22:06 -080016from autotest_lib.server import utils as server_utils
Ricky Liangc31aab32014-07-03 16:23:29 +080017from autotest_lib.server.cros.servo import firmware_programmer
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070018
Kevin Chenga22c4a82016-10-07 14:13:25 -070019# Time to wait when probing for a usb device, it takes on avg 17 seconds
20# to do a full probe.
21_USB_PROBE_TIMEOUT = 40
22
J. Richard Barnette41320ee2013-03-11 13:00:13 -070023
Congbin Guo42427612019-02-12 10:22:06 -080024def _extract_image_from_tarball(tarball, dest_dir, image_candidates):
25 """Try extracting the image_candidates from the tarball.
26
27 @param tarball: The path of the tarball.
28 @param dest_path: The path of the destination.
29 @param image_candidates: A tuple of the paths of image candidates.
30
31 @return: The first path from the image candidates, which succeeds, or None
32 if all the image candidates fail.
33 """
34 for image in image_candidates:
35 status = server_utils.system(
36 ('tar xf %s -C %s %s' % (tarball, dest_dir, image)),
37 timeout=60, ignore_status=True)
38 if status == 0:
39 return image
40 return None
41
42
J. Richard Barnette0c9c5882014-06-11 15:27:05 -070043class _PowerStateController(object):
44
45 """Class to provide board-specific power operations.
46
47 This class is responsible for "power on" and "power off"
48 operations that can operate without making assumptions in
49 advance about board state. It offers an interface that
50 abstracts out the different sequences required for different
51 board types.
52
53 """
54
55 # Constants acceptable to be passed for the `rec_mode` parameter
56 # to power_on().
57 #
58 # REC_ON: Boot the DUT in recovery mode, i.e. boot from USB or
59 # SD card.
60 # REC_OFF: Boot in normal mode, i.e. boot from internal storage.
61
62 REC_ON = 'rec'
63 REC_OFF = 'on'
Shelley Chen65938622018-05-16 07:45:54 -070064 REC_ON_FORCE_MRC = 'rec_force_mrc'
J. Richard Barnette0c9c5882014-06-11 15:27:05 -070065
66 # Delay in seconds needed between asserting and de-asserting
67 # warm reset.
68 _RESET_HOLD_TIME = 0.5
69
70 def __init__(self, servo):
71 """Initialize the power state control.
72
73 @param servo Servo object providing the underlying `set` and `get`
74 methods for the target controls.
75
76 """
77 self._servo = servo
78
79 def reset(self):
80 """Force the DUT to reset.
81
82 The DUT is guaranteed to be on at the end of this call,
83 regardless of its previous state, provided that there is
84 working OS software. This also guarantees that the EC has
85 been restarted.
86
87 """
88 self._servo.set_nocheck('power_state', 'reset')
89
90 def warm_reset(self):
91 """Apply warm reset to the DUT.
92
93 This asserts, then de-asserts the 'warm_reset' signal.
94 Generally, this causes the board to restart.
95
96 """
97 self._servo.set_get_all(['warm_reset:on',
98 'sleep:%.4f' % self._RESET_HOLD_TIME,
99 'warm_reset:off'])
100
J. Richard Barnette0c9c5882014-06-11 15:27:05 -0700101 def power_off(self):
102 """Force the DUT to power off.
103
104 The DUT is guaranteed to be off at the end of this call,
105 regardless of its previous state, provided that there is
106 working EC and boot firmware. There is no requirement for
107 working OS software.
108
109 """
110 self._servo.set_nocheck('power_state', 'off')
111
112 def power_on(self, rec_mode=REC_OFF):
113 """Force the DUT to power on.
114
115 Prior to calling this function, the DUT must be powered off,
116 e.g. with a call to `power_off()`.
117
118 At power on, recovery mode is set as specified by the
119 corresponding argument. When booting with recovery mode on, it
120 is the caller's responsibility to unplug/plug in a bootable
121 external storage device.
122
123 If the DUT requires a delay after powering on but before
124 processing inputs such as USB stick insertion, the delay is
125 handled by this method; the caller is not responsible for such
126 delays.
127
128 @param rec_mode Setting of recovery mode to be applied at
129 power on. default: REC_OFF aka 'off'
130
131 """
132 self._servo.set_nocheck('power_state', rec_mode)
133
134
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700135class _Uart(object):
Congbin Guofe4d4e82019-04-18 17:51:14 -0700136 """Class to capture UART streams of CPU, EC, Cr50, etc."""
137 _UartToCapture = ('cpu', 'ec', 'cr50', 'servo_v4', 'servo_micro')
138
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700139 def __init__(self, servo):
140 self._servo = servo
141 self._streams = []
Congbin Guofe4d4e82019-04-18 17:51:14 -0700142 self.logs_dir = None
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700143
Congbin Guofe4d4e82019-04-18 17:51:14 -0700144 def _start_stop_capture(self, uart, start):
145 """Helper function to start/stop capturing on specified UART.
146
147 @param uart: The UART name to start/stop capturing.
148 @param start: True to start capturing, otherwise stop.
149
150 @returns True if the operation completes successfully. False if the UART
151 capturing is not supported.
152 @raises error.TestFail when operation is supported but failed.
153 """
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700154 try:
Congbin Guofe4d4e82019-04-18 17:51:14 -0700155 logging.debug('%s capturing %s UART.',
156 'Start' if start else 'Stop', uart)
157 self._servo.set('%s_uart_capture' % uart,
158 'on' if start else 'off')
159 return True
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700160 except error.TestFail as err:
161 if 'No control named' in str(err):
Congbin Guofe4d4e82019-04-18 17:51:14 -0700162 logging.debug('The servod is too old that %s_uart_capture '
163 'not supported.', uart)
164 return False
165 raise err
166
167 def start_capture(self):
168 """Start capturing UART streams."""
169 for uart in self._UartToCapture:
170 if self._start_stop_capture(uart, True):
171 self._streams.append(('%s_uart_stream' % uart, '%s_uart.log' %
172 uart))
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700173
Congbin Guofc3b8962019-03-22 17:38:46 -0700174 def dump(self):
175 """Dump UART streams to log files accordingly."""
Congbin Guofe4d4e82019-04-18 17:51:14 -0700176 if not self.logs_dir:
Congbin Guofc3b8962019-03-22 17:38:46 -0700177 return
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700178
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700179 for stream, logfile in self._streams:
Congbin Guofe4d4e82019-04-18 17:51:14 -0700180 logfile_fullname = os.path.join(self.logs_dir, logfile)
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700181 try:
182 content = self._servo.get(stream)
183 except Exception as err:
184 logging.warn('Failed to get UART log for %s: %s', stream, err)
185 continue
186
187 # The UART stream may contain non-printable characters, and servo
188 # returns it in string representation. We use `ast.leteral_eval`
189 # to revert it back.
190 with open(logfile_fullname, 'a') as fd:
191 fd.write(ast.literal_eval(content))
192
193 def stop_capture(self):
194 """Stop capturing UART streams."""
Congbin Guofe4d4e82019-04-18 17:51:14 -0700195 for uart in self._UartToCapture:
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700196 try:
Congbin Guofe4d4e82019-04-18 17:51:14 -0700197 self._start_stop_capture(uart, False)
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700198 except Exception as err:
199 logging.warn('Failed to stop UART logging for %s: %s', uart,
200 err)
201
202
J. Richard Barnette384056b2012-04-16 11:04:46 -0700203class Servo(object):
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700204
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700205 """Manages control of a Servo board.
206
207 Servo is a board developed by hardware group to aide in the debug and
208 control of various partner devices. Servo's features include the simulation
209 of pressing the power button, closing the lid, and pressing Ctrl-d. This
210 class manages setting up and communicating with a servo demon (servod)
211 process. It provides both high-level functions for common servo tasks and
212 low-level functions for directly setting and reading gpios.
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700213
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700214 """
215
Chrome Bot9a1137d2011-07-19 14:35:00 -0700216 # Power button press delays in seconds.
J. Richard Barnetted2e4cbd2012-06-29 12:18:40 -0700217 #
J. Richard Barnette5383f072012-07-26 17:35:40 -0700218 # The EC specification says that 8.0 seconds should be enough
219 # for the long power press. However, some platforms need a bit
220 # more time. Empirical testing has found these requirements:
221 # Alex: 8.2 seconds
222 # ZGB: 8.5 seconds
223 # The actual value is set to the largest known necessary value.
224 #
225 # TODO(jrbarnette) Being generous is the right thing to do for
226 # existing platforms, but if this code is to be used for
227 # qualification of new hardware, we should be less generous.
Chrome Bot9a1137d2011-07-19 14:35:00 -0700228 SHORT_DELAY = 0.1
J. Richard Barnette5383f072012-07-26 17:35:40 -0700229
Todd Broch31c82502011-08-29 08:14:39 -0700230 # Maximum number of times to re-read power button on release.
Todd Brochcf7c6652012-02-24 13:03:59 -0800231 GET_RETRY_MAX = 10
Chrome Bot9a1137d2011-07-19 14:35:00 -0700232
J. Richard Barnette5383f072012-07-26 17:35:40 -0700233 # Delays to deal with DUT state transitions.
Chrome Bot9a1137d2011-07-19 14:35:00 -0700234 SLEEP_DELAY = 6
235 BOOT_DELAY = 10
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700236
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700237 # Default minimum time interval between 'press' and 'release'
238 # keyboard events.
Vic Yang0aca1c22012-11-19 18:33:56 -0800239 SERVO_KEY_PRESS_DELAY = 0.1
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700240
Tom Wai-Hong Tam93b5c092015-05-14 02:50:43 +0800241 # Time to toggle recovery switch on and off.
242 REC_TOGGLE_DELAY = 0.1
243
Tom Wai-Hong Tamf9ded092015-05-20 05:37:22 +0800244 # Time to toggle development switch on and off.
245 DEV_TOGGLE_DELAY = 0.1
246
Jon Salzc88e5b62011-11-30 14:38:54 +0800247 # Time between an usb disk plugged-in and detected in the system.
248 USB_DETECTION_DELAY = 10
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800249 # Time to keep USB power off before and after USB mux direction is changed
250 USB_POWEROFF_DELAY = 2
Jon Salzc88e5b62011-11-30 14:38:54 +0800251
Simran Basib7850bb2013-07-24 12:33:42 -0700252 # Time to wait before timing out on servo initialization.
253 INIT_TIMEOUT_SECS = 10
Simran Basi59331342013-07-12 12:06:29 -0700254
J. Richard Barnette67ccb872012-04-19 16:34:56 -0700255
Ricky Liang0dd379c2014-04-23 16:29:08 +0800256 def __init__(self, servo_host, servo_serial=None):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700257 """Sets up the servo communication infrastructure.
258
Fang Deng5d518f42013-08-02 14:04:32 -0700259 @param servo_host: A ServoHost object representing
260 the host running servod.
Ricky Liang0dd379c2014-04-23 16:29:08 +0800261 @param servo_serial: Serial number of the servo board.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700262 """
Fang Deng5d518f42013-08-02 14:04:32 -0700263 # TODO(fdeng): crbug.com/298379
264 # We should move servo_host object out of servo object
265 # to minimize the dependencies on the rest of Autotest.
266 self._servo_host = servo_host
Ricky Liang0dd379c2014-04-23 16:29:08 +0800267 self._servo_serial = servo_serial
Richard Barnette180efe62016-12-02 23:20:44 +0000268 self._server = servo_host.get_servod_server_proxy()
J. Richard Barnette0c9c5882014-06-11 15:27:05 -0700269 self._power_state = _PowerStateController(self)
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700270 self._uart = _Uart(self)
Fang Dengafb88142013-05-30 17:44:31 -0700271 self._usb_state = None
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700272 self._programmer = None
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800273
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800274
Ricky Liang0dd379c2014-04-23 16:29:08 +0800275 @property
276 def servo_serial(self):
277 """Returns the serial number of the servo board."""
278 return self._servo_serial
279
280
Tom Wai-Hong Tam22ee0e52013-04-03 13:36:39 +0800281 def get_power_state_controller(self):
J. Richard Barnette75136b32013-03-26 13:38:44 -0700282 """Return the power state controller for this Servo.
283
284 The power state controller provides board-independent
285 interfaces for reset, power-on, power-off operations.
286
287 """
288 return self._power_state
289
Fang Deng5d518f42013-08-02 14:04:32 -0700290
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700291 def initialize_dut(self, cold_reset=False):
292 """Initializes a dut for testing purposes.
293
294 This sets various servo signals back to default values
295 appropriate for the target board. By default, if the DUT
296 is already on, it stays on. If the DUT is powered off
297 before initialization, its state afterward is unspecified.
298
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700299 Rationale: Basic initialization of servo sets the lid open,
300 when there is a lid. This operation won't affect powered on
301 units; however, setting the lid open may power on a unit
J. Richard Barnette75136b32013-03-26 13:38:44 -0700302 that's off, depending on the board type and previous state
303 of the device.
304
J. Richard Barnette4b6af0d2014-06-05 09:57:20 -0700305 If `cold_reset` is a true value, the DUT and its EC will be
306 reset, and the DUT rebooted in normal mode.
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700307
308 @param cold_reset If True, cold reset the device after
309 initialization.
310 """
311 self._server.hwinit()
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700312 self.set('usb_mux_oe1', 'on')
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700313 self._usb_state = None
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700314 self.switch_usbkey('off')
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700315 self._uart.start_capture()
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700316 if cold_reset:
J. Richard Barnette4b6af0d2014-06-05 09:57:20 -0700317 self._power_state.reset()
J. Richard Barnette1777f5d2014-09-03 15:18:19 -0700318 logging.debug('Servo initialized, version is %s',
319 self._server.get_version())
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700320
321
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800322 def is_localhost(self):
323 """Is the servod hosted locally?
324
325 Returns:
326 True if local hosted; otherwise, False.
327 """
Fang Deng5d518f42013-08-02 14:04:32 -0700328 return self._servo_host.is_localhost()
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800329
330
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700331 def power_long_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700332 """Simulate a long power button press."""
J. Richard Barnettef12bff22012-07-18 14:41:06 -0700333 # After a long power press, the EC may ignore the next power
334 # button press (at least on Alex). To guarantee that this
335 # won't happen, we need to allow the EC one second to
336 # collect itself.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800337 self._server.power_long_press()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700338
339
340 def power_normal_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700341 """Simulate a normal power button press."""
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800342 self._server.power_normal_press()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700343
344
345 def power_short_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700346 """Simulate a short power button press."""
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800347 self._server.power_short_press()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700348
349
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800350 def power_key(self, press_secs=''):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700351 """Simulate a power button press.
352
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800353 @param press_secs : Str. Time to press key.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700354 """
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800355 self._server.power_key(press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700356
357
358 def lid_open(self):
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800359 """Simulate opening the lid and raise exception if all attempts fail"""
360 self.set('lid_open', 'yes')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700361
362
363 def lid_close(self):
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800364 """Simulate closing the lid and raise exception if all attempts fail
Craig Harrison48997262011-06-27 14:31:10 -0700365
366 Waits 6 seconds to ensure the device is fully asleep before returning.
367 """
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800368 self.set('lid_open', 'no')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700369 time.sleep(Servo.SLEEP_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700370
Shelley Chenc26575a2015-09-18 10:56:16 -0700371 def volume_up(self, timeout=300):
Kevin Chenge448a8b2016-09-09 10:18:11 -0700372 """Simulate pushing the volume down button.
373
374 @param timeout: Timeout for setting the volume.
375 """
Shelley Chenc26575a2015-09-18 10:56:16 -0700376 self.set_get_all(['volume_up:yes',
377 'sleep:%.4f' % self.SERVO_KEY_PRESS_DELAY,
378 'volume_up:no'])
379 # we need to wait for commands to take effect before moving on
380 time_left = float(timeout)
381 while time_left > 0.0:
382 value = self.get('volume_up')
383 if value == 'no':
384 return
385 time.sleep(self.SHORT_DELAY)
386 time_left = time_left - self.SHORT_DELAY
387 raise error.TestFail("Failed setting volume_up to no")
388
389 def volume_down(self, timeout=300):
Kevin Chenge448a8b2016-09-09 10:18:11 -0700390 """Simulate pushing the volume down button.
391
392 @param timeout: Timeout for setting the volume.
393 """
Shelley Chenc26575a2015-09-18 10:56:16 -0700394 self.set_get_all(['volume_down:yes',
395 'sleep:%.4f' % self.SERVO_KEY_PRESS_DELAY,
396 'volume_down:no'])
397 # we need to wait for commands to take effect before moving on
398 time_left = float(timeout)
399 while time_left > 0.0:
400 value = self.get('volume_down')
401 if value == 'no':
402 return
403 time.sleep(self.SHORT_DELAY)
404 time_left = time_left - self.SHORT_DELAY
405 raise error.TestFail("Failed setting volume_down to no")
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700406
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800407 def ctrl_d(self, press_secs=''):
408 """Simulate Ctrl-d simultaneous button presses.
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800409
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800410 @param press_secs : Str. Time to press key.
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800411 """
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800412 self._server.ctrl_d(press_secs)
Gediminas Ramanauskas9da80f22012-11-14 12:59:43 -0800413
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800414
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800415 def ctrl_u(self):
416 """Simulate Ctrl-u simultaneous button presses.
417
418 @param press_secs : Str. Time to press key.
419 """
420 self._server.ctrl_u()
Todd Broch9dfc3a82011-11-01 08:09:28 -0700421
422
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800423 def ctrl_enter(self, press_secs=''):
424 """Simulate Ctrl-enter simultaneous button presses.
425
426 @param press_secs : Str. Time to press key.
427 """
428 self._server.ctrl_enter(press_secs)
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -0700429
430
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800431 def d_key(self, press_secs=''):
432 """Simulate Enter key button press.
433
434 @param press_secs : Str. Time to press key.
435 """
436 self._server.d_key(press_secs)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700437
438
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800439 def ctrl_key(self, press_secs=''):
440 """Simulate Enter key button press.
441
442 @param press_secs : Str. Time to press key.
443 """
444 self._server.ctrl_key(press_secs)
Todd Broch9753bd42012-03-21 10:15:08 -0700445
446
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800447 def enter_key(self, press_secs=''):
448 """Simulate Enter key button press.
449
450 @param press_secs : Str. Time to press key.
451 """
452 self._server.enter_key(press_secs)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700453
454
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800455 def refresh_key(self, press_secs=''):
456 """Simulate Refresh key (F3) button press.
457
458 @param press_secs : Str. Time to press key.
459 """
460 self._server.refresh_key(press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700461
462
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800463 def ctrl_refresh_key(self, press_secs=''):
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800464 """Simulate Ctrl and Refresh (F3) simultaneous press.
465
466 This key combination is an alternative of Space key.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800467
468 @param press_secs : Str. Time to press key.
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800469 """
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800470 self._server.ctrl_refresh_key(press_secs)
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800471
472
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800473 def imaginary_key(self, press_secs=''):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700474 """Simulate imaginary key button press.
475
476 Maps to a key that doesn't physically exist.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800477
478 @param press_secs : Str. Time to press key.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700479 """
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800480 self._server.imaginary_key(press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700481
482
Vincent Palatine7dc9282016-07-14 11:31:58 +0200483 def sysrq_x(self, press_secs=''):
484 """Simulate Alt VolumeUp X simulataneous press.
485
486 This key combination is the kernel system request (sysrq) X.
487
488 @param press_secs : Str. Time to press key.
489 """
490 self._server.sysrq_x(press_secs)
491
492
Tom Wai-Hong Tam93b5c092015-05-14 02:50:43 +0800493 def toggle_recovery_switch(self):
494 """Toggle recovery switch on and off."""
495 self.enable_recovery_mode()
496 time.sleep(self.REC_TOGGLE_DELAY)
497 self.disable_recovery_mode()
498
499
Craig Harrison6b36b122011-06-28 17:58:43 -0700500 def enable_recovery_mode(self):
501 """Enable recovery mode on device."""
502 self.set('rec_mode', 'on')
503
504
505 def disable_recovery_mode(self):
506 """Disable recovery mode on device."""
507 self.set('rec_mode', 'off')
508
509
Tom Wai-Hong Tamf9ded092015-05-20 05:37:22 +0800510 def toggle_development_switch(self):
511 """Toggle development switch on and off."""
512 self.enable_development_mode()
513 time.sleep(self.DEV_TOGGLE_DELAY)
514 self.disable_development_mode()
515
516
Craig Harrison6b36b122011-06-28 17:58:43 -0700517 def enable_development_mode(self):
518 """Enable development mode on device."""
519 self.set('dev_mode', 'on')
520
521
522 def disable_development_mode(self):
523 """Disable development mode on device."""
524 self.set('dev_mode', 'off')
525
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700526 def boot_devmode(self):
527 """Boot a dev-mode device that is powered off."""
Tom Wai-Hong Tam23869102012-01-17 15:41:30 +0800528 self.power_short_press()
Craig Harrison48997262011-06-27 14:31:10 -0700529 self.pass_devmode()
530
531
532 def pass_devmode(self):
533 """Pass through boot screens in dev-mode."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700534 time.sleep(Servo.BOOT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700535 self.ctrl_d()
Chrome Bot9a1137d2011-07-19 14:35:00 -0700536 time.sleep(Servo.BOOT_DELAY)
Craig Harrison48997262011-06-27 14:31:10 -0700537
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700538
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800539 def get_board(self):
Wai-Hong Tam0f904002017-09-19 15:52:22 -0700540 """Get the board connected to servod."""
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800541 return self._server.get_board()
542
543
Wai-Hong Tam0f904002017-09-19 15:52:22 -0700544 def get_base_board(self):
545 """Get the board of the base connected to servod."""
Wai-Hong Tam7f6169e2017-09-29 09:23:48 -0700546 try:
547 return self._server.get_base_board()
548 except xmlrpclib.Fault as e:
549 # TODO(waihong): Remove the following compatibility check when
550 # the new versions of hdctools are deployed.
551 if 'not supported' in str(e):
552 logging.warning('The servod is too old that get_base_board '
553 'not supported.')
554 return ''
555 raise
Wai-Hong Tam0f904002017-09-19 15:52:22 -0700556
557
Wai-Hong Tamcc399ee2017-12-08 12:43:28 -0800558 def get_ec_active_copy(self):
559 """Get the active copy of the EC image."""
560 return self.get('ec_active_copy')
561
562
Todd Brochefe72cb2012-07-11 19:58:53 -0700563 def _get_xmlrpclib_exception(self, xmlexc):
564 """Get meaningful exception string from xmlrpc.
565
566 Args:
567 xmlexc: xmlrpclib.Fault object
568
569 xmlrpclib.Fault.faultString has the following format:
570
571 <type 'exception type'>:'actual error message'
572
573 Parse and return the real exception from the servod side instead of the
574 less meaningful one like,
575 <Fault 1: "<type 'exceptions.AttributeError'>:'tca6416' object has no
576 attribute 'hw_driver'">
577
578 Returns:
579 string of underlying exception raised in servod.
580 """
581 return re.sub('^.*>:', '', xmlexc.faultString)
582
583
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700584 def get(self, gpio_name):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700585 """Get the value of a gpio from Servod.
586
587 @param gpio_name Name of the gpio.
588 """
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700589 assert gpio_name
Todd Brochefe72cb2012-07-11 19:58:53 -0700590 try:
Simran Basi1cbc5f22013-07-17 14:23:58 -0700591 return self._server.get(gpio_name)
Todd Brochefe72cb2012-07-11 19:58:53 -0700592 except xmlrpclib.Fault as e:
593 err_msg = "Getting '%s' :: %s" % \
594 (gpio_name, self._get_xmlrpclib_exception(e))
595 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700596
597
598 def set(self, gpio_name, gpio_value):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700599 """Set and check the value of a gpio using Servod.
600
601 @param gpio_name Name of the gpio.
602 @param gpio_value New setting for the gpio.
603 """
Chrome Bot9a1137d2011-07-19 14:35:00 -0700604 self.set_nocheck(gpio_name, gpio_value)
Todd Brochcf7c6652012-02-24 13:03:59 -0800605 retry_count = Servo.GET_RETRY_MAX
606 while gpio_value != self.get(gpio_name) and retry_count:
Ilja H. Friedel04be2bd2014-05-07 21:29:59 -0700607 logging.warning("%s != %s, retry %d", gpio_name, gpio_value,
Todd Brochcf7c6652012-02-24 13:03:59 -0800608 retry_count)
609 retry_count -= 1
610 time.sleep(Servo.SHORT_DELAY)
611 if not retry_count:
612 assert gpio_value == self.get(gpio_name), \
613 'Servo failed to set %s to %s' % (gpio_name, gpio_value)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700614
615
616 def set_nocheck(self, gpio_name, gpio_value):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700617 """Set the value of a gpio using Servod.
618
619 @param gpio_name Name of the gpio.
620 @param gpio_value New setting for the gpio.
621 """
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700622 assert gpio_name and gpio_value
Mary Ruthven2f72e2a2018-05-01 17:12:58 -0700623 logging.info('Setting %s to %r', gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700624 try:
Simran Basi1cbc5f22013-07-17 14:23:58 -0700625 self._server.set(gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700626 except xmlrpclib.Fault as e:
Mary Ruthven73d3a352018-05-10 15:35:20 -0700627 err_msg = "Setting '%s' to %r :: %s" % \
Todd Brochefe72cb2012-07-11 19:58:53 -0700628 (gpio_name, gpio_value, self._get_xmlrpclib_exception(e))
629 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700630
631
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800632 def set_get_all(self, controls):
633 """Set &| get one or more control values.
634
635 @param controls: list of strings, controls to set &| get.
636
637 @raise: error.TestError in case error occurs setting/getting values.
638 """
639 rv = []
640 try:
Vic Yangcad9acb2013-05-21 14:02:05 +0800641 logging.info('Set/get all: %s', str(controls))
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800642 rv = self._server.set_get_all(controls)
643 except xmlrpclib.Fault as e:
644 # TODO(waihong): Remove the following backward compatibility when
645 # the new versions of hdctools are deployed.
646 if 'not supported' in str(e):
647 logging.warning('The servod is too old that set_get_all '
648 'not supported. Use set and get instead.')
649 for control in controls:
650 if ':' in control:
651 (name, value) = control.split(':')
652 if name == 'sleep':
653 time.sleep(float(value))
654 else:
655 self.set_nocheck(name, value)
656 rv.append(True)
657 else:
658 rv.append(self.get(name))
659 else:
660 err_msg = "Problem with '%s' :: %s" % \
661 (controls, self._get_xmlrpclib_exception(e))
662 raise error.TestFail(err_msg)
663 return rv
664
665
Jon Salzc88e5b62011-11-30 14:38:54 +0800666 # TODO(waihong) It may fail if multiple servo's are connected to the same
667 # host. Should look for a better way, like the USB serial name, to identify
668 # the USB device.
Simran Basi741b5d42012-05-18 11:27:15 -0700669 # TODO(sbasi) Remove this code from autoserv once firmware tests have been
670 # updated.
Kevin Chenga22c4a82016-10-07 14:13:25 -0700671 def probe_host_usb_dev(self, timeout=_USB_PROBE_TIMEOUT):
Jon Salzc88e5b62011-11-30 14:38:54 +0800672 """Probe the USB disk device plugged-in the servo from the host side.
673
Kevin Chengeb06fe72016-08-22 15:26:32 -0700674 It uses servod to discover if there is a usb device attached to it.
Jon Salzc88e5b62011-11-30 14:38:54 +0800675
Kevin Chenga22c4a82016-10-07 14:13:25 -0700676 @param timeout The timeout period when probing for the usb host device.
677
678 @return: String of USB disk path (e.g. '/dev/sdb') or None.
Jon Salzc88e5b62011-11-30 14:38:54 +0800679 """
Ruben Rodriguez Buchillon9a4bfc32018-10-16 20:46:25 +0800680 # Set up Servo's usb mux.
681 self.switch_usbkey('host')
Kevin Chenga22c4a82016-10-07 14:13:25 -0700682 return self._server.probe_host_usb_dev(timeout) or None
Jon Salzc88e5b62011-11-30 14:38:54 +0800683
684
Mike Truty49153d82012-08-21 22:27:30 -0500685 def image_to_servo_usb(self, image_path=None,
686 make_image_noninteractive=False):
687 """Install an image to the USB key plugged into the servo.
688
689 This method may copy any image to the servo USB key including a
690 recovery image or a test image. These images are frequently used
691 for test purposes such as restoring a corrupted image or conducting
692 an upgrade of ec/fw/kernel as part of a test of a specific image part.
693
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700694 @param image_path Path on the host to the recovery image.
695 @param make_image_noninteractive Make the recovery image
696 noninteractive, therefore the DUT
697 will reboot automatically after
698 installation.
Mike Truty49153d82012-08-21 22:27:30 -0500699 """
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700700 # We're about to start plugging/unplugging the USB key. We
701 # don't know the state of the DUT, or what it might choose
702 # to do to the device after hotplug. To avoid surprises,
703 # force the DUT to be off.
704 self._server.hwinit()
705 self._power_state.power_off()
Mike Truty49153d82012-08-21 22:27:30 -0500706
707 # Set up Servo's usb mux.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800708 self.switch_usbkey('host')
Mike Truty49153d82012-08-21 22:27:30 -0500709 if image_path:
Tom Wai-Hong Tam42f136d2012-10-26 11:11:23 +0800710 logging.info('Searching for usb device and copying image to it. '
711 'Please wait a few minutes...')
Mike Truty49153d82012-08-21 22:27:30 -0500712 if not self._server.download_image_to_usb(image_path):
713 logging.error('Failed to transfer requested image to USB. '
714 'Please take a look at Servo Logs.')
715 raise error.AutotestError('Download image to usb failed.')
716 if make_image_noninteractive:
717 logging.info('Making image noninteractive')
718 if not self._server.make_image_noninteractive():
719 logging.error('Failed to make image noninteractive. '
720 'Please take a look at Servo Logs.')
721
Kalin Stoyanovb3c11f32018-05-11 09:02:00 -0700722 def boot_in_recovery_mode(self):
723 """Boot host DUT in recovery mode."""
724 self._power_state.power_on(rec_mode=self._power_state.REC_ON)
725 self.switch_usbkey('dut')
726
Mike Truty49153d82012-08-21 22:27:30 -0500727
Simran Basi741b5d42012-05-18 11:27:15 -0700728 def install_recovery_image(self, image_path=None,
J. Richard Barnetteb6de7e32013-02-14 13:28:04 -0800729 make_image_noninteractive=False):
Dan Shic67f1332016-04-06 12:38:06 -0700730 """Install the recovery image specified by the path onto the DUT.
Jon Salzc88e5b62011-11-30 14:38:54 +0800731
732 This method uses google recovery mode to install a recovery image
733 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 +0800734 board specified by the usb_dev. If no image path is specified
Jon Salzc88e5b62011-11-30 14:38:54 +0800735 we use the recovery image already on the usb image.
736
Dan Shic67f1332016-04-06 12:38:06 -0700737 @param image_path: Path on the host to the recovery image.
738 @param make_image_noninteractive: Make the recovery image
739 noninteractive, therefore the DUT will reboot automatically
740 after installation.
Jon Salzc88e5b62011-11-30 14:38:54 +0800741 """
Mike Truty49153d82012-08-21 22:27:30 -0500742 self.image_to_servo_usb(image_path, make_image_noninteractive)
Kalin Stoyanovb3c11f32018-05-11 09:02:00 -0700743 self.boot_in_recovery_mode()
Jon Salzc88e5b62011-11-30 14:38:54 +0800744
745
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800746 def _scp_image(self, image_path):
747 """Copy image to the servo host.
748
749 When programming a firmware image on the DUT, the image must be
750 located on the host to which the servo device is connected. Sometimes
751 servo is controlled by a remote host, in this case the image needs to
752 be transferred to the remote host.
753
754 @param image_path: a string, name of the firmware image file to be
755 transferred.
756 @return: a string, full path name of the copied file on the remote.
757 """
758
759 dest_path = os.path.join('/tmp', os.path.basename(image_path))
Fang Deng5d518f42013-08-02 14:04:32 -0700760 self._servo_host.send_file(image_path, dest_path)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800761 return dest_path
762
763
Dan Shifecdaf42015-07-28 10:17:26 -0700764 def system(self, command, timeout=3600):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700765 """Execute the passed in command on the servod host.
766
767 @param command Command to be executed.
Dan Shifecdaf42015-07-28 10:17:26 -0700768 @param timeout Maximum number of seconds of runtime allowed. Default to
769 1 hour.
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700770 """
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800771 logging.info('Will execute on servo host: %s', command)
Fang Deng5d518f42013-08-02 14:04:32 -0700772 self._servo_host.run(command, timeout=timeout)
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800773
774
Dan Shifecdaf42015-07-28 10:17:26 -0700775 def system_output(self, command, timeout=3600,
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800776 ignore_status=False, args=()):
777 """Execute the passed in command on the servod host, return stdout.
778
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700779 @param command a string, the command to execute
780 @param timeout an int, max number of seconds to wait til command
Dan Shifecdaf42015-07-28 10:17:26 -0700781 execution completes. Default to 1 hour.
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700782 @param ignore_status a Boolean, if true - ignore command's nonzero exit
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800783 status, otherwise an exception will be thrown
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700784 @param args a tuple of strings, each becoming a separate command line
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800785 parameter for the command
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700786 @return command's stdout as a string.
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800787 """
Fang Deng5d518f42013-08-02 14:04:32 -0700788 return self._servo_host.run(command, timeout=timeout,
789 ignore_status=ignore_status,
790 args=args).stdout.strip()
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800791
792
Dan Shia5fef052015-05-18 23:28:47 -0700793 def get_servo_version(self):
794 """Get the version of the servo, e.g., servo_v2 or servo_v3.
795
796 @return: The version of the servo.
797
798 """
799 return self._server.get_version()
800
801
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800802 def _initialize_programmer(self, rw_only=False):
803 """Initialize the firmware programmer.
804
805 @param rw_only: True to initialize a programmer which only
806 programs the RW portions.
807 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700808 if self._programmer:
809 return
810 # Initialize firmware programmer
Dan Shia5fef052015-05-18 23:28:47 -0700811 servo_version = self.get_servo_version()
Dan Shi9cb0eec2014-06-03 09:04:50 -0700812 if servo_version.startswith('servo_v2'):
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700813 self._programmer = firmware_programmer.ProgrammerV2(self)
Wai-Hong Tamc36c4d22016-09-09 10:39:45 -0700814 self._programmer_rw = firmware_programmer.ProgrammerV2RwOnly(self)
Kevin Chengdf2e29f2016-09-09 02:31:22 -0700815 # Both servo v3 and v4 use the same programming methods so just leverage
816 # ProgrammerV3 for servo v4 as well.
817 elif (servo_version.startswith('servo_v3') or
818 servo_version.startswith('servo_v4')):
Dan Shia5fef052015-05-18 23:28:47 -0700819 self._programmer = firmware_programmer.ProgrammerV3(self)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800820 self._programmer_rw = firmware_programmer.ProgrammerV3RwOnly(self)
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700821 else:
822 raise error.TestError(
823 'No firmware programmer for servo version: %s' %
Congbin Guo42427612019-02-12 10:22:06 -0800824 servo_version)
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700825
826
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800827 def program_bios(self, image, rw_only=False):
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800828 """Program bios on DUT with given image.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800829
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800830 @param image: a string, file name of the BIOS image to program
831 on the DUT.
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800832 @param rw_only: True to only program the RW portion of BIOS.
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800833
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800834 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700835 self._initialize_programmer()
Ricky Liangc31aab32014-07-03 16:23:29 +0800836 if not self.is_localhost():
837 image = self._scp_image(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800838 if rw_only:
839 self._programmer_rw.program_bios(image)
840 else:
841 self._programmer.program_bios(image)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800842
843
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800844 def program_ec(self, image, rw_only=False):
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800845 """Program ec on DUT with given image.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800846
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800847 @param image: a string, file name of the EC image to program
848 on the DUT.
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800849 @param rw_only: True to only program the RW portion of EC.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800850
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800851 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700852 self._initialize_programmer()
Ricky Liangc31aab32014-07-03 16:23:29 +0800853 if not self.is_localhost():
854 image = self._scp_image(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800855 if rw_only:
Congbin Guo42427612019-02-12 10:22:06 -0800856 self._programmer_rw.program_ec(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800857 else:
Congbin Guo42427612019-02-12 10:22:06 -0800858 self._programmer.program_ec(image)
859
860
861 def _reprogram(self, tarball_path, firmware_name, image_candidates,
862 rw_only):
863 """Helper function to reprogram firmware for EC or BIOS.
864
865 @param tarball_path: The path of the downloaded build tarball.
866 @param: firmware_name: either 'EC' or 'BIOS'.
867 @param image_candidates: A tuple of the paths of image candidates.
868 @param rw_only: True to only install firmware to its RW portions. Keep
869 the RO portions unchanged.
870
871 @raise: TestError if cannot extract firmware from the tarball.
872 """
873 dest_dir = os.path.dirname(tarball_path)
874 image = _extract_image_from_tarball(tarball_path, dest_dir,
875 image_candidates)
876 if not image:
877 if firmware_name == 'EC':
878 logging.info('Not a Chrome EC, ignore re-programming it')
879 return
880 else:
881 raise error.TestError('Failed to extract the %s image from '
882 'tarball' % firmware_name)
883
884 logging.info('Will re-program %s %snow', firmware_name,
885 'RW ' if rw_only else '')
886
887 if firmware_name == 'EC':
888 self.program_ec(os.path.join(dest_dir, image), rw_only)
889 else:
890 self.program_bios(os.path.join(dest_dir, image), rw_only)
891
892
893 def program_firmware(self, model, tarball_path, rw_only=False):
894 """Program firmware (EC, if applied, and BIOS) of the DUT.
895
896 @param model: The DUT model name.
897 @param tarball_path: The path of the downloaded build tarball.
898 @param rw_only: True to only install firmware to its RW portions. Keep
899 the RO portions unchanged.
900 """
901 ap_image_candidates = ('image.bin', 'image-%s.bin' % model)
902 ec_image_candidates = ('ec.bin', '%s/ec.bin' % model)
903
904 self._reprogram(tarball_path, 'EC', ec_image_candidates, rw_only)
905 self._reprogram(tarball_path, 'BIOS', ap_image_candidates, rw_only)
906
907 self.get_power_state_controller().reset()
908 time.sleep(Servo.BOOT_DELAY)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800909
Fang Dengafb88142013-05-30 17:44:31 -0700910
911 def _switch_usbkey_power(self, power_state, detection_delay=False):
912 """Switch usbkey power.
913
914 This function switches usbkey power by setting the value of
915 'prtctl4_pwren'. If the power is already in the
916 requested state, this function simply returns.
917
918 @param power_state: A string, 'on' or 'off'.
919 @param detection_delay: A boolean value, if True, sleep
920 for |USB_DETECTION_DELAY| after switching
921 the power on.
922 """
Kevin Chenga22c4a82016-10-07 14:13:25 -0700923 # TODO(kevcheng): Forgive me for this terrible hack. This is just to
924 # handle beaglebones that haven't yet updated and have the
925 # safe_switch_usbkey_power RPC. I'll remove this once all beaglebones
926 # have been updated and also think about a better way to handle
927 # situations like this.
928 try:
929 self._server.safe_switch_usbkey_power(power_state)
930 except Exception:
931 self.set('prtctl4_pwren', power_state)
Fang Dengafb88142013-05-30 17:44:31 -0700932 if power_state == 'off':
933 time.sleep(self.USB_POWEROFF_DELAY)
934 elif detection_delay:
935 time.sleep(self.USB_DETECTION_DELAY)
936
937
938 def switch_usbkey(self, usb_state):
939 """Connect USB flash stick to either host or DUT, or turn USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800940
941 This function switches the servo multiplexer to provide electrical
Fang Dengafb88142013-05-30 17:44:31 -0700942 connection between the USB port J3 and either host or DUT side. It
943 can also be used to turn the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800944
Fang Dengafb88142013-05-30 17:44:31 -0700945 Switching to 'dut' or 'host' is accompanied by powercycling
946 of the USB stick, because it sometimes gets wedged if the mux
947 is switched while the stick power is on.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800948
Fang Dengafb88142013-05-30 17:44:31 -0700949 @param usb_state: A string, one of 'dut', 'host', or 'off'.
950 'dut' and 'host' indicate which side the
951 USB flash device is required to be connected to.
952 'off' indicates turning the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800953
Fang Dengafb88142013-05-30 17:44:31 -0700954 @raise: error.TestError in case the parameter is not 'dut'
955 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800956 """
Fang Dengafb88142013-05-30 17:44:31 -0700957 if self.get_usbkey_direction() == usb_state:
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800958 return
959
Fang Dengafb88142013-05-30 17:44:31 -0700960 if usb_state == 'off':
Dan Shia5fef052015-05-18 23:28:47 -0700961 self._switch_usbkey_power('off')
962 self._usb_state = usb_state
963 return
Fang Dengafb88142013-05-30 17:44:31 -0700964 elif usb_state == 'host':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800965 mux_direction = 'servo_sees_usbkey'
Fang Dengafb88142013-05-30 17:44:31 -0700966 elif usb_state == 'dut':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800967 mux_direction = 'dut_sees_usbkey'
968 else:
Fang Dengafb88142013-05-30 17:44:31 -0700969 raise error.TestError('Unknown USB state request: %s' % usb_state)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800970
Fang Dengafb88142013-05-30 17:44:31 -0700971 self._switch_usbkey_power('off')
Kevin Chenga22c4a82016-10-07 14:13:25 -0700972 # TODO(kevcheng): Forgive me for this terrible hack. This is just to
973 # handle beaglebones that haven't yet updated and have the
974 # safe_switch_usbkey RPC. I'll remove this once all beaglebones have
975 # been updated and also think about a better way to handle situations
976 # like this.
977 try:
978 self._server.safe_switch_usbkey(mux_direction)
979 except Exception:
980 self.set('usb_mux_sel1', mux_direction)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800981 time.sleep(self.USB_POWEROFF_DELAY)
Fang Dengafb88142013-05-30 17:44:31 -0700982 self._switch_usbkey_power('on', usb_state == 'host')
983 self._usb_state = usb_state
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800984
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800985
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800986 def get_usbkey_direction(self):
Fang Dengafb88142013-05-30 17:44:31 -0700987 """Get which side USB is connected to or 'off' if usb power is off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800988
Fang Dengafb88142013-05-30 17:44:31 -0700989 @return: A string, one of 'dut', 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800990 """
Fang Dengafb88142013-05-30 17:44:31 -0700991 if not self._usb_state:
992 if self.get('prtctl4_pwren') == 'off':
993 self._usb_state = 'off'
994 elif self.get('usb_mux_sel1').startswith('dut'):
995 self._usb_state = 'dut'
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800996 else:
Fang Dengafb88142013-05-30 17:44:31 -0700997 self._usb_state = 'host'
998 return self._usb_state
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700999
1000
Wai-Hong Tam60377262018-03-01 10:55:39 -08001001 def set_servo_v4_role(self, role):
1002 """Set the power role of servo v4, either 'src' or 'snk'.
1003
1004 It does nothing if not a servo v4.
1005
1006 @param role: Power role for DUT port on servo v4, either 'src' or 'snk'.
1007 """
1008 servo_version = self.get_servo_version()
1009 if servo_version.startswith('servo_v4'):
1010 value = self.get('servo_v4_role')
1011 if value != role:
1012 self.set_nocheck('servo_v4_role', role)
1013 else:
1014 logging.debug('Already in the role: %s.', role)
1015 else:
1016 logging.debug('Not a servo v4, unable to set role to %s.', role)
1017
1018
Congbin Guofc3b8962019-03-22 17:38:46 -07001019 @property
1020 def uart_logs_dir(self):
1021 """Return the directory to save UART logs."""
1022 return self._uart.logs_dir if self._uart else ""
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001023
Congbin Guofc3b8962019-03-22 17:38:46 -07001024
1025 @uart_logs_dir.setter
1026 def uart_logs_dir(self, logs_dir):
1027 """Set directory to save UART logs.
1028
1029 @param logs_dir String of directory name."""
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001030 if self._uart:
Congbin Guofc3b8962019-03-22 17:38:46 -07001031 self._uart.logs_dir = logs_dir
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001032
1033
1034 def close(self):
1035 """Close the servo object."""
1036 if self._uart:
1037 self._uart.stop_capture()
Congbin Guofc3b8962019-03-22 17:38:46 -07001038 self._uart.dump()
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001039 self._uart = None