blob: da9f48406df62b5d281a53d03f41abc00dcb69b1 [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):
136 """Class to capture CPU/EC UART streams."""
137 def __init__(self, servo):
138 self._servo = servo
139 self._streams = []
140
141 def start_capture(self):
142 """Start capturing Uart streams."""
143 logging.debug('Start capturing CPU/EC UART.')
144 self._servo.set('cpu_uart_capture', 'on')
145 self._streams.append(('cpu_uart_stream', 'cpu_uart.log'))
146 try:
147 self._servo.set('ec_uart_capture', 'on')
148 self._streams.append(('ec_uart_stream', 'ec_uart.log'))
149 except error.TestFail as err:
150 if 'No control named' in str(err):
151 logging.debug('The servod is too old that ec_uart_capture not '
152 'supported.')
153
154 def dump(self, output_dir):
155 """Dump UART streams to log files accordingly.
156
157 @param output_dir: A string of output directory name.
158 """
159 for stream, logfile in self._streams:
160 logfile_fullname = os.path.join(output_dir, logfile)
161 try:
162 content = self._servo.get(stream)
163 except Exception as err:
164 logging.warn('Failed to get UART log for %s: %s', stream, err)
165 continue
166
167 # The UART stream may contain non-printable characters, and servo
168 # returns it in string representation. We use `ast.leteral_eval`
169 # to revert it back.
170 with open(logfile_fullname, 'a') as fd:
171 fd.write(ast.literal_eval(content))
172
173 def stop_capture(self):
174 """Stop capturing UART streams."""
175 logging.debug('Stop capturing CPU/EC UART.')
176 for uart in ('cpu_uart_capture', 'ec_uart_capture'):
177 try:
178 self._servo.set(uart, 'off')
179 except error.TestFail as err:
180 if 'No control named' in str(err):
181 logging.debug('The servod is too old that %s not '
182 'supported.', uart)
183 except Exception as err:
184 logging.warn('Failed to stop UART logging for %s: %s', uart,
185 err)
186
187
J. Richard Barnette384056b2012-04-16 11:04:46 -0700188class Servo(object):
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700189
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700190 """Manages control of a Servo board.
191
192 Servo is a board developed by hardware group to aide in the debug and
193 control of various partner devices. Servo's features include the simulation
194 of pressing the power button, closing the lid, and pressing Ctrl-d. This
195 class manages setting up and communicating with a servo demon (servod)
196 process. It provides both high-level functions for common servo tasks and
197 low-level functions for directly setting and reading gpios.
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700198
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700199 """
200
Chrome Bot9a1137d2011-07-19 14:35:00 -0700201 # Power button press delays in seconds.
J. Richard Barnetted2e4cbd2012-06-29 12:18:40 -0700202 #
J. Richard Barnette5383f072012-07-26 17:35:40 -0700203 # The EC specification says that 8.0 seconds should be enough
204 # for the long power press. However, some platforms need a bit
205 # more time. Empirical testing has found these requirements:
206 # Alex: 8.2 seconds
207 # ZGB: 8.5 seconds
208 # The actual value is set to the largest known necessary value.
209 #
210 # TODO(jrbarnette) Being generous is the right thing to do for
211 # existing platforms, but if this code is to be used for
212 # qualification of new hardware, we should be less generous.
Chrome Bot9a1137d2011-07-19 14:35:00 -0700213 SHORT_DELAY = 0.1
J. Richard Barnette5383f072012-07-26 17:35:40 -0700214
Todd Broch31c82502011-08-29 08:14:39 -0700215 # Maximum number of times to re-read power button on release.
Todd Brochcf7c6652012-02-24 13:03:59 -0800216 GET_RETRY_MAX = 10
Chrome Bot9a1137d2011-07-19 14:35:00 -0700217
J. Richard Barnette5383f072012-07-26 17:35:40 -0700218 # Delays to deal with DUT state transitions.
Chrome Bot9a1137d2011-07-19 14:35:00 -0700219 SLEEP_DELAY = 6
220 BOOT_DELAY = 10
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700221
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700222 # Default minimum time interval between 'press' and 'release'
223 # keyboard events.
Vic Yang0aca1c22012-11-19 18:33:56 -0800224 SERVO_KEY_PRESS_DELAY = 0.1
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700225
Tom Wai-Hong Tam93b5c092015-05-14 02:50:43 +0800226 # Time to toggle recovery switch on and off.
227 REC_TOGGLE_DELAY = 0.1
228
Tom Wai-Hong Tamf9ded092015-05-20 05:37:22 +0800229 # Time to toggle development switch on and off.
230 DEV_TOGGLE_DELAY = 0.1
231
Jon Salzc88e5b62011-11-30 14:38:54 +0800232 # Time between an usb disk plugged-in and detected in the system.
233 USB_DETECTION_DELAY = 10
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800234 # Time to keep USB power off before and after USB mux direction is changed
235 USB_POWEROFF_DELAY = 2
Jon Salzc88e5b62011-11-30 14:38:54 +0800236
Simran Basib7850bb2013-07-24 12:33:42 -0700237 # Time to wait before timing out on servo initialization.
238 INIT_TIMEOUT_SECS = 10
Simran Basi59331342013-07-12 12:06:29 -0700239
J. Richard Barnette67ccb872012-04-19 16:34:56 -0700240
Ricky Liang0dd379c2014-04-23 16:29:08 +0800241 def __init__(self, servo_host, servo_serial=None):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700242 """Sets up the servo communication infrastructure.
243
Fang Deng5d518f42013-08-02 14:04:32 -0700244 @param servo_host: A ServoHost object representing
245 the host running servod.
Ricky Liang0dd379c2014-04-23 16:29:08 +0800246 @param servo_serial: Serial number of the servo board.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700247 """
Fang Deng5d518f42013-08-02 14:04:32 -0700248 # TODO(fdeng): crbug.com/298379
249 # We should move servo_host object out of servo object
250 # to minimize the dependencies on the rest of Autotest.
251 self._servo_host = servo_host
Ricky Liang0dd379c2014-04-23 16:29:08 +0800252 self._servo_serial = servo_serial
Richard Barnette180efe62016-12-02 23:20:44 +0000253 self._server = servo_host.get_servod_server_proxy()
J. Richard Barnette0c9c5882014-06-11 15:27:05 -0700254 self._power_state = _PowerStateController(self)
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700255 self._uart = _Uart(self)
Fang Dengafb88142013-05-30 17:44:31 -0700256 self._usb_state = None
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700257 self._programmer = None
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800258
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800259
Ricky Liang0dd379c2014-04-23 16:29:08 +0800260 @property
261 def servo_serial(self):
262 """Returns the serial number of the servo board."""
263 return self._servo_serial
264
265
Tom Wai-Hong Tam22ee0e52013-04-03 13:36:39 +0800266 def get_power_state_controller(self):
J. Richard Barnette75136b32013-03-26 13:38:44 -0700267 """Return the power state controller for this Servo.
268
269 The power state controller provides board-independent
270 interfaces for reset, power-on, power-off operations.
271
272 """
273 return self._power_state
274
Fang Deng5d518f42013-08-02 14:04:32 -0700275
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700276 def initialize_dut(self, cold_reset=False):
277 """Initializes a dut for testing purposes.
278
279 This sets various servo signals back to default values
280 appropriate for the target board. By default, if the DUT
281 is already on, it stays on. If the DUT is powered off
282 before initialization, its state afterward is unspecified.
283
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700284 Rationale: Basic initialization of servo sets the lid open,
285 when there is a lid. This operation won't affect powered on
286 units; however, setting the lid open may power on a unit
J. Richard Barnette75136b32013-03-26 13:38:44 -0700287 that's off, depending on the board type and previous state
288 of the device.
289
J. Richard Barnette4b6af0d2014-06-05 09:57:20 -0700290 If `cold_reset` is a true value, the DUT and its EC will be
291 reset, and the DUT rebooted in normal mode.
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700292
293 @param cold_reset If True, cold reset the device after
294 initialization.
295 """
296 self._server.hwinit()
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700297 self.set('usb_mux_oe1', 'on')
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700298 self._usb_state = None
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700299 self.switch_usbkey('off')
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700300 self._uart.start_capture()
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700301 if cold_reset:
J. Richard Barnette4b6af0d2014-06-05 09:57:20 -0700302 self._power_state.reset()
J. Richard Barnette1777f5d2014-09-03 15:18:19 -0700303 logging.debug('Servo initialized, version is %s',
304 self._server.get_version())
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700305
306
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800307 def is_localhost(self):
308 """Is the servod hosted locally?
309
310 Returns:
311 True if local hosted; otherwise, False.
312 """
Fang Deng5d518f42013-08-02 14:04:32 -0700313 return self._servo_host.is_localhost()
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800314
315
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700316 def power_long_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700317 """Simulate a long power button press."""
J. Richard Barnettef12bff22012-07-18 14:41:06 -0700318 # After a long power press, the EC may ignore the next power
319 # button press (at least on Alex). To guarantee that this
320 # won't happen, we need to allow the EC one second to
321 # collect itself.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800322 self._server.power_long_press()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700323
324
325 def power_normal_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700326 """Simulate a normal power button press."""
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800327 self._server.power_normal_press()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700328
329
330 def power_short_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700331 """Simulate a short power button press."""
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800332 self._server.power_short_press()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700333
334
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800335 def power_key(self, press_secs=''):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700336 """Simulate a power button press.
337
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800338 @param press_secs : Str. Time to press key.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700339 """
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800340 self._server.power_key(press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700341
342
343 def lid_open(self):
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800344 """Simulate opening the lid and raise exception if all attempts fail"""
345 self.set('lid_open', 'yes')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700346
347
348 def lid_close(self):
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800349 """Simulate closing the lid and raise exception if all attempts fail
Craig Harrison48997262011-06-27 14:31:10 -0700350
351 Waits 6 seconds to ensure the device is fully asleep before returning.
352 """
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800353 self.set('lid_open', 'no')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700354 time.sleep(Servo.SLEEP_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700355
Shelley Chenc26575a2015-09-18 10:56:16 -0700356 def volume_up(self, timeout=300):
Kevin Chenge448a8b2016-09-09 10:18:11 -0700357 """Simulate pushing the volume down button.
358
359 @param timeout: Timeout for setting the volume.
360 """
Shelley Chenc26575a2015-09-18 10:56:16 -0700361 self.set_get_all(['volume_up:yes',
362 'sleep:%.4f' % self.SERVO_KEY_PRESS_DELAY,
363 'volume_up:no'])
364 # we need to wait for commands to take effect before moving on
365 time_left = float(timeout)
366 while time_left > 0.0:
367 value = self.get('volume_up')
368 if value == 'no':
369 return
370 time.sleep(self.SHORT_DELAY)
371 time_left = time_left - self.SHORT_DELAY
372 raise error.TestFail("Failed setting volume_up to no")
373
374 def volume_down(self, timeout=300):
Kevin Chenge448a8b2016-09-09 10:18:11 -0700375 """Simulate pushing the volume down button.
376
377 @param timeout: Timeout for setting the volume.
378 """
Shelley Chenc26575a2015-09-18 10:56:16 -0700379 self.set_get_all(['volume_down:yes',
380 'sleep:%.4f' % self.SERVO_KEY_PRESS_DELAY,
381 'volume_down:no'])
382 # we need to wait for commands to take effect before moving on
383 time_left = float(timeout)
384 while time_left > 0.0:
385 value = self.get('volume_down')
386 if value == 'no':
387 return
388 time.sleep(self.SHORT_DELAY)
389 time_left = time_left - self.SHORT_DELAY
390 raise error.TestFail("Failed setting volume_down to no")
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700391
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800392 def ctrl_d(self, press_secs=''):
393 """Simulate Ctrl-d simultaneous button presses.
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800394
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800395 @param press_secs : Str. Time to press key.
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800396 """
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800397 self._server.ctrl_d(press_secs)
Gediminas Ramanauskas9da80f22012-11-14 12:59:43 -0800398
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800399
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800400 def ctrl_u(self):
401 """Simulate Ctrl-u simultaneous button presses.
402
403 @param press_secs : Str. Time to press key.
404 """
405 self._server.ctrl_u()
Todd Broch9dfc3a82011-11-01 08:09:28 -0700406
407
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800408 def ctrl_enter(self, press_secs=''):
409 """Simulate Ctrl-enter simultaneous button presses.
410
411 @param press_secs : Str. Time to press key.
412 """
413 self._server.ctrl_enter(press_secs)
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -0700414
415
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800416 def d_key(self, press_secs=''):
417 """Simulate Enter key button press.
418
419 @param press_secs : Str. Time to press key.
420 """
421 self._server.d_key(press_secs)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700422
423
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800424 def ctrl_key(self, press_secs=''):
425 """Simulate Enter key button press.
426
427 @param press_secs : Str. Time to press key.
428 """
429 self._server.ctrl_key(press_secs)
Todd Broch9753bd42012-03-21 10:15:08 -0700430
431
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800432 def enter_key(self, press_secs=''):
433 """Simulate Enter key button press.
434
435 @param press_secs : Str. Time to press key.
436 """
437 self._server.enter_key(press_secs)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700438
439
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800440 def refresh_key(self, press_secs=''):
441 """Simulate Refresh key (F3) button press.
442
443 @param press_secs : Str. Time to press key.
444 """
445 self._server.refresh_key(press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700446
447
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800448 def ctrl_refresh_key(self, press_secs=''):
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800449 """Simulate Ctrl and Refresh (F3) simultaneous press.
450
451 This key combination is an alternative of Space key.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800452
453 @param press_secs : Str. Time to press key.
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800454 """
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800455 self._server.ctrl_refresh_key(press_secs)
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800456
457
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800458 def imaginary_key(self, press_secs=''):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700459 """Simulate imaginary key button press.
460
461 Maps to a key that doesn't physically exist.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800462
463 @param press_secs : Str. Time to press key.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700464 """
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800465 self._server.imaginary_key(press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700466
467
Vincent Palatine7dc9282016-07-14 11:31:58 +0200468 def sysrq_x(self, press_secs=''):
469 """Simulate Alt VolumeUp X simulataneous press.
470
471 This key combination is the kernel system request (sysrq) X.
472
473 @param press_secs : Str. Time to press key.
474 """
475 self._server.sysrq_x(press_secs)
476
477
Tom Wai-Hong Tam93b5c092015-05-14 02:50:43 +0800478 def toggle_recovery_switch(self):
479 """Toggle recovery switch on and off."""
480 self.enable_recovery_mode()
481 time.sleep(self.REC_TOGGLE_DELAY)
482 self.disable_recovery_mode()
483
484
Craig Harrison6b36b122011-06-28 17:58:43 -0700485 def enable_recovery_mode(self):
486 """Enable recovery mode on device."""
487 self.set('rec_mode', 'on')
488
489
490 def disable_recovery_mode(self):
491 """Disable recovery mode on device."""
492 self.set('rec_mode', 'off')
493
494
Tom Wai-Hong Tamf9ded092015-05-20 05:37:22 +0800495 def toggle_development_switch(self):
496 """Toggle development switch on and off."""
497 self.enable_development_mode()
498 time.sleep(self.DEV_TOGGLE_DELAY)
499 self.disable_development_mode()
500
501
Craig Harrison6b36b122011-06-28 17:58:43 -0700502 def enable_development_mode(self):
503 """Enable development mode on device."""
504 self.set('dev_mode', 'on')
505
506
507 def disable_development_mode(self):
508 """Disable development mode on device."""
509 self.set('dev_mode', 'off')
510
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700511 def boot_devmode(self):
512 """Boot a dev-mode device that is powered off."""
Tom Wai-Hong Tam23869102012-01-17 15:41:30 +0800513 self.power_short_press()
Craig Harrison48997262011-06-27 14:31:10 -0700514 self.pass_devmode()
515
516
517 def pass_devmode(self):
518 """Pass through boot screens in dev-mode."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700519 time.sleep(Servo.BOOT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700520 self.ctrl_d()
Chrome Bot9a1137d2011-07-19 14:35:00 -0700521 time.sleep(Servo.BOOT_DELAY)
Craig Harrison48997262011-06-27 14:31:10 -0700522
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700523
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800524 def get_board(self):
Wai-Hong Tam0f904002017-09-19 15:52:22 -0700525 """Get the board connected to servod."""
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800526 return self._server.get_board()
527
528
Wai-Hong Tam0f904002017-09-19 15:52:22 -0700529 def get_base_board(self):
530 """Get the board of the base connected to servod."""
Wai-Hong Tam7f6169e2017-09-29 09:23:48 -0700531 try:
532 return self._server.get_base_board()
533 except xmlrpclib.Fault as e:
534 # TODO(waihong): Remove the following compatibility check when
535 # the new versions of hdctools are deployed.
536 if 'not supported' in str(e):
537 logging.warning('The servod is too old that get_base_board '
538 'not supported.')
539 return ''
540 raise
Wai-Hong Tam0f904002017-09-19 15:52:22 -0700541
542
Wai-Hong Tamcc399ee2017-12-08 12:43:28 -0800543 def get_ec_active_copy(self):
544 """Get the active copy of the EC image."""
545 return self.get('ec_active_copy')
546
547
Todd Brochefe72cb2012-07-11 19:58:53 -0700548 def _get_xmlrpclib_exception(self, xmlexc):
549 """Get meaningful exception string from xmlrpc.
550
551 Args:
552 xmlexc: xmlrpclib.Fault object
553
554 xmlrpclib.Fault.faultString has the following format:
555
556 <type 'exception type'>:'actual error message'
557
558 Parse and return the real exception from the servod side instead of the
559 less meaningful one like,
560 <Fault 1: "<type 'exceptions.AttributeError'>:'tca6416' object has no
561 attribute 'hw_driver'">
562
563 Returns:
564 string of underlying exception raised in servod.
565 """
566 return re.sub('^.*>:', '', xmlexc.faultString)
567
568
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700569 def get(self, gpio_name):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700570 """Get the value of a gpio from Servod.
571
572 @param gpio_name Name of the gpio.
573 """
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700574 assert gpio_name
Todd Brochefe72cb2012-07-11 19:58:53 -0700575 try:
Simran Basi1cbc5f22013-07-17 14:23:58 -0700576 return self._server.get(gpio_name)
Todd Brochefe72cb2012-07-11 19:58:53 -0700577 except xmlrpclib.Fault as e:
578 err_msg = "Getting '%s' :: %s" % \
579 (gpio_name, self._get_xmlrpclib_exception(e))
580 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700581
582
583 def set(self, gpio_name, gpio_value):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700584 """Set and check the value of a gpio using Servod.
585
586 @param gpio_name Name of the gpio.
587 @param gpio_value New setting for the gpio.
588 """
Chrome Bot9a1137d2011-07-19 14:35:00 -0700589 self.set_nocheck(gpio_name, gpio_value)
Todd Brochcf7c6652012-02-24 13:03:59 -0800590 retry_count = Servo.GET_RETRY_MAX
591 while gpio_value != self.get(gpio_name) and retry_count:
Ilja H. Friedel04be2bd2014-05-07 21:29:59 -0700592 logging.warning("%s != %s, retry %d", gpio_name, gpio_value,
Todd Brochcf7c6652012-02-24 13:03:59 -0800593 retry_count)
594 retry_count -= 1
595 time.sleep(Servo.SHORT_DELAY)
596 if not retry_count:
597 assert gpio_value == self.get(gpio_name), \
598 'Servo failed to set %s to %s' % (gpio_name, gpio_value)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700599
600
601 def set_nocheck(self, gpio_name, gpio_value):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700602 """Set the value of a gpio using Servod.
603
604 @param gpio_name Name of the gpio.
605 @param gpio_value New setting for the gpio.
606 """
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700607 assert gpio_name and gpio_value
Mary Ruthven2f72e2a2018-05-01 17:12:58 -0700608 logging.info('Setting %s to %r', gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700609 try:
Simran Basi1cbc5f22013-07-17 14:23:58 -0700610 self._server.set(gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700611 except xmlrpclib.Fault as e:
Mary Ruthven73d3a352018-05-10 15:35:20 -0700612 err_msg = "Setting '%s' to %r :: %s" % \
Todd Brochefe72cb2012-07-11 19:58:53 -0700613 (gpio_name, gpio_value, self._get_xmlrpclib_exception(e))
614 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700615
616
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800617 def set_get_all(self, controls):
618 """Set &| get one or more control values.
619
620 @param controls: list of strings, controls to set &| get.
621
622 @raise: error.TestError in case error occurs setting/getting values.
623 """
624 rv = []
625 try:
Vic Yangcad9acb2013-05-21 14:02:05 +0800626 logging.info('Set/get all: %s', str(controls))
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800627 rv = self._server.set_get_all(controls)
628 except xmlrpclib.Fault as e:
629 # TODO(waihong): Remove the following backward compatibility when
630 # the new versions of hdctools are deployed.
631 if 'not supported' in str(e):
632 logging.warning('The servod is too old that set_get_all '
633 'not supported. Use set and get instead.')
634 for control in controls:
635 if ':' in control:
636 (name, value) = control.split(':')
637 if name == 'sleep':
638 time.sleep(float(value))
639 else:
640 self.set_nocheck(name, value)
641 rv.append(True)
642 else:
643 rv.append(self.get(name))
644 else:
645 err_msg = "Problem with '%s' :: %s" % \
646 (controls, self._get_xmlrpclib_exception(e))
647 raise error.TestFail(err_msg)
648 return rv
649
650
Jon Salzc88e5b62011-11-30 14:38:54 +0800651 # TODO(waihong) It may fail if multiple servo's are connected to the same
652 # host. Should look for a better way, like the USB serial name, to identify
653 # the USB device.
Simran Basi741b5d42012-05-18 11:27:15 -0700654 # TODO(sbasi) Remove this code from autoserv once firmware tests have been
655 # updated.
Kevin Chenga22c4a82016-10-07 14:13:25 -0700656 def probe_host_usb_dev(self, timeout=_USB_PROBE_TIMEOUT):
Jon Salzc88e5b62011-11-30 14:38:54 +0800657 """Probe the USB disk device plugged-in the servo from the host side.
658
Kevin Chengeb06fe72016-08-22 15:26:32 -0700659 It uses servod to discover if there is a usb device attached to it.
Jon Salzc88e5b62011-11-30 14:38:54 +0800660
Kevin Chenga22c4a82016-10-07 14:13:25 -0700661 @param timeout The timeout period when probing for the usb host device.
662
663 @return: String of USB disk path (e.g. '/dev/sdb') or None.
Jon Salzc88e5b62011-11-30 14:38:54 +0800664 """
Ruben Rodriguez Buchillon9a4bfc32018-10-16 20:46:25 +0800665 # Set up Servo's usb mux.
666 self.switch_usbkey('host')
Kevin Chenga22c4a82016-10-07 14:13:25 -0700667 return self._server.probe_host_usb_dev(timeout) or None
Jon Salzc88e5b62011-11-30 14:38:54 +0800668
669
Mike Truty49153d82012-08-21 22:27:30 -0500670 def image_to_servo_usb(self, image_path=None,
671 make_image_noninteractive=False):
672 """Install an image to the USB key plugged into the servo.
673
674 This method may copy any image to the servo USB key including a
675 recovery image or a test image. These images are frequently used
676 for test purposes such as restoring a corrupted image or conducting
677 an upgrade of ec/fw/kernel as part of a test of a specific image part.
678
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700679 @param image_path Path on the host to the recovery image.
680 @param make_image_noninteractive Make the recovery image
681 noninteractive, therefore the DUT
682 will reboot automatically after
683 installation.
Mike Truty49153d82012-08-21 22:27:30 -0500684 """
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700685 # We're about to start plugging/unplugging the USB key. We
686 # don't know the state of the DUT, or what it might choose
687 # to do to the device after hotplug. To avoid surprises,
688 # force the DUT to be off.
689 self._server.hwinit()
690 self._power_state.power_off()
Mike Truty49153d82012-08-21 22:27:30 -0500691
692 # Set up Servo's usb mux.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800693 self.switch_usbkey('host')
Mike Truty49153d82012-08-21 22:27:30 -0500694 if image_path:
Tom Wai-Hong Tam42f136d2012-10-26 11:11:23 +0800695 logging.info('Searching for usb device and copying image to it. '
696 'Please wait a few minutes...')
Mike Truty49153d82012-08-21 22:27:30 -0500697 if not self._server.download_image_to_usb(image_path):
698 logging.error('Failed to transfer requested image to USB. '
699 'Please take a look at Servo Logs.')
700 raise error.AutotestError('Download image to usb failed.')
701 if make_image_noninteractive:
702 logging.info('Making image noninteractive')
703 if not self._server.make_image_noninteractive():
704 logging.error('Failed to make image noninteractive. '
705 'Please take a look at Servo Logs.')
706
Kalin Stoyanovb3c11f32018-05-11 09:02:00 -0700707 def boot_in_recovery_mode(self):
708 """Boot host DUT in recovery mode."""
709 self._power_state.power_on(rec_mode=self._power_state.REC_ON)
710 self.switch_usbkey('dut')
711
Mike Truty49153d82012-08-21 22:27:30 -0500712
Simran Basi741b5d42012-05-18 11:27:15 -0700713 def install_recovery_image(self, image_path=None,
J. Richard Barnetteb6de7e32013-02-14 13:28:04 -0800714 make_image_noninteractive=False):
Dan Shic67f1332016-04-06 12:38:06 -0700715 """Install the recovery image specified by the path onto the DUT.
Jon Salzc88e5b62011-11-30 14:38:54 +0800716
717 This method uses google recovery mode to install a recovery image
718 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 +0800719 board specified by the usb_dev. If no image path is specified
Jon Salzc88e5b62011-11-30 14:38:54 +0800720 we use the recovery image already on the usb image.
721
Dan Shic67f1332016-04-06 12:38:06 -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 will reboot automatically
725 after installation.
Jon Salzc88e5b62011-11-30 14:38:54 +0800726 """
Mike Truty49153d82012-08-21 22:27:30 -0500727 self.image_to_servo_usb(image_path, make_image_noninteractive)
Kalin Stoyanovb3c11f32018-05-11 09:02:00 -0700728 self.boot_in_recovery_mode()
Jon Salzc88e5b62011-11-30 14:38:54 +0800729
730
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800731 def _scp_image(self, image_path):
732 """Copy image to the servo host.
733
734 When programming a firmware image on the DUT, the image must be
735 located on the host to which the servo device is connected. Sometimes
736 servo is controlled by a remote host, in this case the image needs to
737 be transferred to the remote host.
738
739 @param image_path: a string, name of the firmware image file to be
740 transferred.
741 @return: a string, full path name of the copied file on the remote.
742 """
743
744 dest_path = os.path.join('/tmp', os.path.basename(image_path))
Fang Deng5d518f42013-08-02 14:04:32 -0700745 self._servo_host.send_file(image_path, dest_path)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800746 return dest_path
747
748
Dan Shifecdaf42015-07-28 10:17:26 -0700749 def system(self, command, timeout=3600):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700750 """Execute the passed in command on the servod host.
751
752 @param command Command to be executed.
Dan Shifecdaf42015-07-28 10:17:26 -0700753 @param timeout Maximum number of seconds of runtime allowed. Default to
754 1 hour.
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700755 """
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800756 logging.info('Will execute on servo host: %s', command)
Fang Deng5d518f42013-08-02 14:04:32 -0700757 self._servo_host.run(command, timeout=timeout)
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800758
759
Dan Shifecdaf42015-07-28 10:17:26 -0700760 def system_output(self, command, timeout=3600,
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800761 ignore_status=False, args=()):
762 """Execute the passed in command on the servod host, return stdout.
763
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700764 @param command a string, the command to execute
765 @param timeout an int, max number of seconds to wait til command
Dan Shifecdaf42015-07-28 10:17:26 -0700766 execution completes. Default to 1 hour.
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700767 @param ignore_status a Boolean, if true - ignore command's nonzero exit
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800768 status, otherwise an exception will be thrown
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700769 @param args a tuple of strings, each becoming a separate command line
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800770 parameter for the command
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700771 @return command's stdout as a string.
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800772 """
Fang Deng5d518f42013-08-02 14:04:32 -0700773 return self._servo_host.run(command, timeout=timeout,
774 ignore_status=ignore_status,
775 args=args).stdout.strip()
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800776
777
Dan Shia5fef052015-05-18 23:28:47 -0700778 def get_servo_version(self):
779 """Get the version of the servo, e.g., servo_v2 or servo_v3.
780
781 @return: The version of the servo.
782
783 """
784 return self._server.get_version()
785
786
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800787 def _initialize_programmer(self, rw_only=False):
788 """Initialize the firmware programmer.
789
790 @param rw_only: True to initialize a programmer which only
791 programs the RW portions.
792 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700793 if self._programmer:
794 return
795 # Initialize firmware programmer
Dan Shia5fef052015-05-18 23:28:47 -0700796 servo_version = self.get_servo_version()
Dan Shi9cb0eec2014-06-03 09:04:50 -0700797 if servo_version.startswith('servo_v2'):
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700798 self._programmer = firmware_programmer.ProgrammerV2(self)
Wai-Hong Tamc36c4d22016-09-09 10:39:45 -0700799 self._programmer_rw = firmware_programmer.ProgrammerV2RwOnly(self)
Kevin Chengdf2e29f2016-09-09 02:31:22 -0700800 # Both servo v3 and v4 use the same programming methods so just leverage
801 # ProgrammerV3 for servo v4 as well.
802 elif (servo_version.startswith('servo_v3') or
803 servo_version.startswith('servo_v4')):
Dan Shia5fef052015-05-18 23:28:47 -0700804 self._programmer = firmware_programmer.ProgrammerV3(self)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800805 self._programmer_rw = firmware_programmer.ProgrammerV3RwOnly(self)
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700806 else:
807 raise error.TestError(
808 'No firmware programmer for servo version: %s' %
Congbin Guo42427612019-02-12 10:22:06 -0800809 servo_version)
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700810
811
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800812 def program_bios(self, image, rw_only=False):
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800813 """Program bios on DUT with given image.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800814
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800815 @param image: a string, file name of the BIOS image to program
816 on the DUT.
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800817 @param rw_only: True to only program the RW portion of BIOS.
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800818
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800819 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700820 self._initialize_programmer()
Ricky Liangc31aab32014-07-03 16:23:29 +0800821 if not self.is_localhost():
822 image = self._scp_image(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800823 if rw_only:
824 self._programmer_rw.program_bios(image)
825 else:
826 self._programmer.program_bios(image)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800827
828
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800829 def program_ec(self, image, rw_only=False):
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800830 """Program ec on DUT with given image.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800831
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800832 @param image: a string, file name of the EC image to program
833 on the DUT.
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800834 @param rw_only: True to only program the RW portion of EC.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800835
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800836 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700837 self._initialize_programmer()
Ricky Liangc31aab32014-07-03 16:23:29 +0800838 if not self.is_localhost():
839 image = self._scp_image(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800840 if rw_only:
Congbin Guo42427612019-02-12 10:22:06 -0800841 self._programmer_rw.program_ec(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800842 else:
Congbin Guo42427612019-02-12 10:22:06 -0800843 self._programmer.program_ec(image)
844
845
846 def _reprogram(self, tarball_path, firmware_name, image_candidates,
847 rw_only):
848 """Helper function to reprogram firmware for EC or BIOS.
849
850 @param tarball_path: The path of the downloaded build tarball.
851 @param: firmware_name: either 'EC' or 'BIOS'.
852 @param image_candidates: A tuple of the paths of image candidates.
853 @param rw_only: True to only install firmware to its RW portions. Keep
854 the RO portions unchanged.
855
856 @raise: TestError if cannot extract firmware from the tarball.
857 """
858 dest_dir = os.path.dirname(tarball_path)
859 image = _extract_image_from_tarball(tarball_path, dest_dir,
860 image_candidates)
861 if not image:
862 if firmware_name == 'EC':
863 logging.info('Not a Chrome EC, ignore re-programming it')
864 return
865 else:
866 raise error.TestError('Failed to extract the %s image from '
867 'tarball' % firmware_name)
868
869 logging.info('Will re-program %s %snow', firmware_name,
870 'RW ' if rw_only else '')
871
872 if firmware_name == 'EC':
873 self.program_ec(os.path.join(dest_dir, image), rw_only)
874 else:
875 self.program_bios(os.path.join(dest_dir, image), rw_only)
876
877
878 def program_firmware(self, model, tarball_path, rw_only=False):
879 """Program firmware (EC, if applied, and BIOS) of the DUT.
880
881 @param model: The DUT model name.
882 @param tarball_path: The path of the downloaded build tarball.
883 @param rw_only: True to only install firmware to its RW portions. Keep
884 the RO portions unchanged.
885 """
886 ap_image_candidates = ('image.bin', 'image-%s.bin' % model)
887 ec_image_candidates = ('ec.bin', '%s/ec.bin' % model)
888
889 self._reprogram(tarball_path, 'EC', ec_image_candidates, rw_only)
890 self._reprogram(tarball_path, 'BIOS', ap_image_candidates, rw_only)
891
892 self.get_power_state_controller().reset()
893 time.sleep(Servo.BOOT_DELAY)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800894
Fang Dengafb88142013-05-30 17:44:31 -0700895
896 def _switch_usbkey_power(self, power_state, detection_delay=False):
897 """Switch usbkey power.
898
899 This function switches usbkey power by setting the value of
900 'prtctl4_pwren'. If the power is already in the
901 requested state, this function simply returns.
902
903 @param power_state: A string, 'on' or 'off'.
904 @param detection_delay: A boolean value, if True, sleep
905 for |USB_DETECTION_DELAY| after switching
906 the power on.
907 """
Kevin Chenga22c4a82016-10-07 14:13:25 -0700908 # TODO(kevcheng): Forgive me for this terrible hack. This is just to
909 # handle beaglebones that haven't yet updated and have the
910 # safe_switch_usbkey_power RPC. I'll remove this once all beaglebones
911 # have been updated and also think about a better way to handle
912 # situations like this.
913 try:
914 self._server.safe_switch_usbkey_power(power_state)
915 except Exception:
916 self.set('prtctl4_pwren', power_state)
Fang Dengafb88142013-05-30 17:44:31 -0700917 if power_state == 'off':
918 time.sleep(self.USB_POWEROFF_DELAY)
919 elif detection_delay:
920 time.sleep(self.USB_DETECTION_DELAY)
921
922
923 def switch_usbkey(self, usb_state):
924 """Connect USB flash stick to either host or DUT, or turn USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800925
926 This function switches the servo multiplexer to provide electrical
Fang Dengafb88142013-05-30 17:44:31 -0700927 connection between the USB port J3 and either host or DUT side. It
928 can also be used to turn the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800929
Fang Dengafb88142013-05-30 17:44:31 -0700930 Switching to 'dut' or 'host' is accompanied by powercycling
931 of the USB stick, because it sometimes gets wedged if the mux
932 is switched while the stick power is on.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800933
Fang Dengafb88142013-05-30 17:44:31 -0700934 @param usb_state: A string, one of 'dut', 'host', or 'off'.
935 'dut' and 'host' indicate which side the
936 USB flash device is required to be connected to.
937 'off' indicates turning the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800938
Fang Dengafb88142013-05-30 17:44:31 -0700939 @raise: error.TestError in case the parameter is not 'dut'
940 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800941 """
Fang Dengafb88142013-05-30 17:44:31 -0700942 if self.get_usbkey_direction() == usb_state:
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800943 return
944
Fang Dengafb88142013-05-30 17:44:31 -0700945 if usb_state == 'off':
Dan Shia5fef052015-05-18 23:28:47 -0700946 self._switch_usbkey_power('off')
947 self._usb_state = usb_state
948 return
Fang Dengafb88142013-05-30 17:44:31 -0700949 elif usb_state == 'host':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800950 mux_direction = 'servo_sees_usbkey'
Fang Dengafb88142013-05-30 17:44:31 -0700951 elif usb_state == 'dut':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800952 mux_direction = 'dut_sees_usbkey'
953 else:
Fang Dengafb88142013-05-30 17:44:31 -0700954 raise error.TestError('Unknown USB state request: %s' % usb_state)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800955
Fang Dengafb88142013-05-30 17:44:31 -0700956 self._switch_usbkey_power('off')
Kevin Chenga22c4a82016-10-07 14:13:25 -0700957 # TODO(kevcheng): Forgive me for this terrible hack. This is just to
958 # handle beaglebones that haven't yet updated and have the
959 # safe_switch_usbkey RPC. I'll remove this once all beaglebones have
960 # been updated and also think about a better way to handle situations
961 # like this.
962 try:
963 self._server.safe_switch_usbkey(mux_direction)
964 except Exception:
965 self.set('usb_mux_sel1', mux_direction)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800966 time.sleep(self.USB_POWEROFF_DELAY)
Fang Dengafb88142013-05-30 17:44:31 -0700967 self._switch_usbkey_power('on', usb_state == 'host')
968 self._usb_state = usb_state
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800969
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800970
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800971 def get_usbkey_direction(self):
Fang Dengafb88142013-05-30 17:44:31 -0700972 """Get which side USB is connected to or 'off' if usb power is off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800973
Fang Dengafb88142013-05-30 17:44:31 -0700974 @return: A string, one of 'dut', 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800975 """
Fang Dengafb88142013-05-30 17:44:31 -0700976 if not self._usb_state:
977 if self.get('prtctl4_pwren') == 'off':
978 self._usb_state = 'off'
979 elif self.get('usb_mux_sel1').startswith('dut'):
980 self._usb_state = 'dut'
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800981 else:
Fang Dengafb88142013-05-30 17:44:31 -0700982 self._usb_state = 'host'
983 return self._usb_state
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700984
985
Wai-Hong Tam60377262018-03-01 10:55:39 -0800986 def set_servo_v4_role(self, role):
987 """Set the power role of servo v4, either 'src' or 'snk'.
988
989 It does nothing if not a servo v4.
990
991 @param role: Power role for DUT port on servo v4, either 'src' or 'snk'.
992 """
993 servo_version = self.get_servo_version()
994 if servo_version.startswith('servo_v4'):
995 value = self.get('servo_v4_role')
996 if value != role:
997 self.set_nocheck('servo_v4_role', role)
998 else:
999 logging.debug('Already in the role: %s.', role)
1000 else:
1001 logging.debug('Not a servo v4, unable to set role to %s.', role)
1002
1003
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001004 def dump_uart_streams(self, output_dir):
1005 """Get buffered UART streams and append to log files.
1006
1007 @param output_dir: A string of directory name to save log files.
1008 """
1009 if self._uart:
1010 self._uart.dump(output_dir)
1011
1012
1013 def close(self):
1014 """Close the servo object."""
1015 if self._uart:
1016 self._uart.stop_capture()
1017 self._uart = None