blob: 0ecccc519e4b83bd8504c52ed5d73971ad51e88a [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 = []
Congbin Guofc3b8962019-03-22 17:38:46 -0700140 self._logs_dir = None
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700141
142 def start_capture(self):
143 """Start capturing Uart streams."""
144 logging.debug('Start capturing CPU/EC UART.')
145 self._servo.set('cpu_uart_capture', 'on')
146 self._streams.append(('cpu_uart_stream', 'cpu_uart.log'))
147 try:
148 self._servo.set('ec_uart_capture', 'on')
149 self._streams.append(('ec_uart_stream', 'ec_uart.log'))
150 except error.TestFail as err:
151 if 'No control named' in str(err):
152 logging.debug('The servod is too old that ec_uart_capture not '
153 'supported.')
154
Congbin Guofc3b8962019-03-22 17:38:46 -0700155 def dump(self):
156 """Dump UART streams to log files accordingly."""
157 if not self._logs_dir:
158 return
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700159
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700160 for stream, logfile in self._streams:
Congbin Guofc3b8962019-03-22 17:38:46 -0700161 logfile_fullname = os.path.join(self._logs_dir, logfile)
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700162 try:
163 content = self._servo.get(stream)
164 except Exception as err:
165 logging.warn('Failed to get UART log for %s: %s', stream, err)
166 continue
167
168 # The UART stream may contain non-printable characters, and servo
169 # returns it in string representation. We use `ast.leteral_eval`
170 # to revert it back.
171 with open(logfile_fullname, 'a') as fd:
172 fd.write(ast.literal_eval(content))
173
174 def stop_capture(self):
175 """Stop capturing UART streams."""
176 logging.debug('Stop capturing CPU/EC UART.')
177 for uart in ('cpu_uart_capture', 'ec_uart_capture'):
178 try:
179 self._servo.set(uart, 'off')
180 except error.TestFail as err:
181 if 'No control named' in str(err):
182 logging.debug('The servod is too old that %s not '
183 'supported.', uart)
184 except Exception as err:
185 logging.warn('Failed to stop UART logging for %s: %s', uart,
186 err)
187
Congbin Guofc3b8962019-03-22 17:38:46 -0700188 @property
189 def logs_dir(self):
190 """Return the directory to save UART logs."""
191 return self._logs_dir
192
193 @logs_dir.setter
194 def logs_dir(self, a_dir):
195 """Set directory to save UART logs.
196
197 @param a_dir String of logs directory name."""
198 self._logs_dir = a_dir
199
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700200
J. Richard Barnette384056b2012-04-16 11:04:46 -0700201class Servo(object):
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700202
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700203 """Manages control of a Servo board.
204
205 Servo is a board developed by hardware group to aide in the debug and
206 control of various partner devices. Servo's features include the simulation
207 of pressing the power button, closing the lid, and pressing Ctrl-d. This
208 class manages setting up and communicating with a servo demon (servod)
209 process. It provides both high-level functions for common servo tasks and
210 low-level functions for directly setting and reading gpios.
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700211
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700212 """
213
Chrome Bot9a1137d2011-07-19 14:35:00 -0700214 # Power button press delays in seconds.
J. Richard Barnetted2e4cbd2012-06-29 12:18:40 -0700215 #
J. Richard Barnette5383f072012-07-26 17:35:40 -0700216 # The EC specification says that 8.0 seconds should be enough
217 # for the long power press. However, some platforms need a bit
218 # more time. Empirical testing has found these requirements:
219 # Alex: 8.2 seconds
220 # ZGB: 8.5 seconds
221 # The actual value is set to the largest known necessary value.
222 #
223 # TODO(jrbarnette) Being generous is the right thing to do for
224 # existing platforms, but if this code is to be used for
225 # qualification of new hardware, we should be less generous.
Chrome Bot9a1137d2011-07-19 14:35:00 -0700226 SHORT_DELAY = 0.1
J. Richard Barnette5383f072012-07-26 17:35:40 -0700227
Todd Broch31c82502011-08-29 08:14:39 -0700228 # Maximum number of times to re-read power button on release.
Todd Brochcf7c6652012-02-24 13:03:59 -0800229 GET_RETRY_MAX = 10
Chrome Bot9a1137d2011-07-19 14:35:00 -0700230
J. Richard Barnette5383f072012-07-26 17:35:40 -0700231 # Delays to deal with DUT state transitions.
Chrome Bot9a1137d2011-07-19 14:35:00 -0700232 SLEEP_DELAY = 6
233 BOOT_DELAY = 10
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700234
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700235 # Default minimum time interval between 'press' and 'release'
236 # keyboard events.
Vic Yang0aca1c22012-11-19 18:33:56 -0800237 SERVO_KEY_PRESS_DELAY = 0.1
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700238
Tom Wai-Hong Tam93b5c092015-05-14 02:50:43 +0800239 # Time to toggle recovery switch on and off.
240 REC_TOGGLE_DELAY = 0.1
241
Tom Wai-Hong Tamf9ded092015-05-20 05:37:22 +0800242 # Time to toggle development switch on and off.
243 DEV_TOGGLE_DELAY = 0.1
244
Jon Salzc88e5b62011-11-30 14:38:54 +0800245 # Time between an usb disk plugged-in and detected in the system.
246 USB_DETECTION_DELAY = 10
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800247 # Time to keep USB power off before and after USB mux direction is changed
248 USB_POWEROFF_DELAY = 2
Jon Salzc88e5b62011-11-30 14:38:54 +0800249
Simran Basib7850bb2013-07-24 12:33:42 -0700250 # Time to wait before timing out on servo initialization.
251 INIT_TIMEOUT_SECS = 10
Simran Basi59331342013-07-12 12:06:29 -0700252
J. Richard Barnette67ccb872012-04-19 16:34:56 -0700253
Ricky Liang0dd379c2014-04-23 16:29:08 +0800254 def __init__(self, servo_host, servo_serial=None):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700255 """Sets up the servo communication infrastructure.
256
Fang Deng5d518f42013-08-02 14:04:32 -0700257 @param servo_host: A ServoHost object representing
258 the host running servod.
Ricky Liang0dd379c2014-04-23 16:29:08 +0800259 @param servo_serial: Serial number of the servo board.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700260 """
Fang Deng5d518f42013-08-02 14:04:32 -0700261 # TODO(fdeng): crbug.com/298379
262 # We should move servo_host object out of servo object
263 # to minimize the dependencies on the rest of Autotest.
264 self._servo_host = servo_host
Ricky Liang0dd379c2014-04-23 16:29:08 +0800265 self._servo_serial = servo_serial
Richard Barnette180efe62016-12-02 23:20:44 +0000266 self._server = servo_host.get_servod_server_proxy()
J. Richard Barnette0c9c5882014-06-11 15:27:05 -0700267 self._power_state = _PowerStateController(self)
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700268 self._uart = _Uart(self)
Fang Dengafb88142013-05-30 17:44:31 -0700269 self._usb_state = None
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700270 self._programmer = None
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800271
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800272
Ricky Liang0dd379c2014-04-23 16:29:08 +0800273 @property
274 def servo_serial(self):
275 """Returns the serial number of the servo board."""
276 return self._servo_serial
277
278
Tom Wai-Hong Tam22ee0e52013-04-03 13:36:39 +0800279 def get_power_state_controller(self):
J. Richard Barnette75136b32013-03-26 13:38:44 -0700280 """Return the power state controller for this Servo.
281
282 The power state controller provides board-independent
283 interfaces for reset, power-on, power-off operations.
284
285 """
286 return self._power_state
287
Fang Deng5d518f42013-08-02 14:04:32 -0700288
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700289 def initialize_dut(self, cold_reset=False):
290 """Initializes a dut for testing purposes.
291
292 This sets various servo signals back to default values
293 appropriate for the target board. By default, if the DUT
294 is already on, it stays on. If the DUT is powered off
295 before initialization, its state afterward is unspecified.
296
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700297 Rationale: Basic initialization of servo sets the lid open,
298 when there is a lid. This operation won't affect powered on
299 units; however, setting the lid open may power on a unit
J. Richard Barnette75136b32013-03-26 13:38:44 -0700300 that's off, depending on the board type and previous state
301 of the device.
302
J. Richard Barnette4b6af0d2014-06-05 09:57:20 -0700303 If `cold_reset` is a true value, the DUT and its EC will be
304 reset, and the DUT rebooted in normal mode.
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700305
306 @param cold_reset If True, cold reset the device after
307 initialization.
308 """
309 self._server.hwinit()
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700310 self.set('usb_mux_oe1', 'on')
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700311 self._usb_state = None
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700312 self.switch_usbkey('off')
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700313 self._uart.start_capture()
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700314 if cold_reset:
J. Richard Barnette4b6af0d2014-06-05 09:57:20 -0700315 self._power_state.reset()
J. Richard Barnette1777f5d2014-09-03 15:18:19 -0700316 logging.debug('Servo initialized, version is %s',
317 self._server.get_version())
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700318
319
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800320 def is_localhost(self):
321 """Is the servod hosted locally?
322
323 Returns:
324 True if local hosted; otherwise, False.
325 """
Fang Deng5d518f42013-08-02 14:04:32 -0700326 return self._servo_host.is_localhost()
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800327
328
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700329 def power_long_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700330 """Simulate a long power button press."""
J. Richard Barnettef12bff22012-07-18 14:41:06 -0700331 # After a long power press, the EC may ignore the next power
332 # button press (at least on Alex). To guarantee that this
333 # won't happen, we need to allow the EC one second to
334 # collect itself.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800335 self._server.power_long_press()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700336
337
338 def power_normal_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700339 """Simulate a normal power button press."""
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800340 self._server.power_normal_press()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700341
342
343 def power_short_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700344 """Simulate a short power button press."""
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800345 self._server.power_short_press()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700346
347
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800348 def power_key(self, press_secs=''):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700349 """Simulate a power button press.
350
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800351 @param press_secs : Str. Time to press key.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700352 """
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800353 self._server.power_key(press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700354
355
356 def lid_open(self):
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800357 """Simulate opening the lid and raise exception if all attempts fail"""
358 self.set('lid_open', 'yes')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700359
360
361 def lid_close(self):
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800362 """Simulate closing the lid and raise exception if all attempts fail
Craig Harrison48997262011-06-27 14:31:10 -0700363
364 Waits 6 seconds to ensure the device is fully asleep before returning.
365 """
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800366 self.set('lid_open', 'no')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700367 time.sleep(Servo.SLEEP_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700368
Shelley Chenc26575a2015-09-18 10:56:16 -0700369 def volume_up(self, timeout=300):
Kevin Chenge448a8b2016-09-09 10:18:11 -0700370 """Simulate pushing the volume down button.
371
372 @param timeout: Timeout for setting the volume.
373 """
Shelley Chenc26575a2015-09-18 10:56:16 -0700374 self.set_get_all(['volume_up:yes',
375 'sleep:%.4f' % self.SERVO_KEY_PRESS_DELAY,
376 'volume_up:no'])
377 # we need to wait for commands to take effect before moving on
378 time_left = float(timeout)
379 while time_left > 0.0:
380 value = self.get('volume_up')
381 if value == 'no':
382 return
383 time.sleep(self.SHORT_DELAY)
384 time_left = time_left - self.SHORT_DELAY
385 raise error.TestFail("Failed setting volume_up to no")
386
387 def volume_down(self, timeout=300):
Kevin Chenge448a8b2016-09-09 10:18:11 -0700388 """Simulate pushing the volume down button.
389
390 @param timeout: Timeout for setting the volume.
391 """
Shelley Chenc26575a2015-09-18 10:56:16 -0700392 self.set_get_all(['volume_down:yes',
393 'sleep:%.4f' % self.SERVO_KEY_PRESS_DELAY,
394 'volume_down:no'])
395 # we need to wait for commands to take effect before moving on
396 time_left = float(timeout)
397 while time_left > 0.0:
398 value = self.get('volume_down')
399 if value == 'no':
400 return
401 time.sleep(self.SHORT_DELAY)
402 time_left = time_left - self.SHORT_DELAY
403 raise error.TestFail("Failed setting volume_down to no")
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700404
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800405 def ctrl_d(self, press_secs=''):
406 """Simulate Ctrl-d simultaneous button presses.
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800407
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800408 @param press_secs : Str. Time to press key.
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800409 """
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800410 self._server.ctrl_d(press_secs)
Gediminas Ramanauskas9da80f22012-11-14 12:59:43 -0800411
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800412
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800413 def ctrl_u(self):
414 """Simulate Ctrl-u simultaneous button presses.
415
416 @param press_secs : Str. Time to press key.
417 """
418 self._server.ctrl_u()
Todd Broch9dfc3a82011-11-01 08:09:28 -0700419
420
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800421 def ctrl_enter(self, press_secs=''):
422 """Simulate Ctrl-enter simultaneous button presses.
423
424 @param press_secs : Str. Time to press key.
425 """
426 self._server.ctrl_enter(press_secs)
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -0700427
428
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800429 def d_key(self, press_secs=''):
430 """Simulate Enter key button press.
431
432 @param press_secs : Str. Time to press key.
433 """
434 self._server.d_key(press_secs)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700435
436
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800437 def ctrl_key(self, press_secs=''):
438 """Simulate Enter key button press.
439
440 @param press_secs : Str. Time to press key.
441 """
442 self._server.ctrl_key(press_secs)
Todd Broch9753bd42012-03-21 10:15:08 -0700443
444
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800445 def enter_key(self, press_secs=''):
446 """Simulate Enter key button press.
447
448 @param press_secs : Str. Time to press key.
449 """
450 self._server.enter_key(press_secs)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700451
452
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800453 def refresh_key(self, press_secs=''):
454 """Simulate Refresh key (F3) button press.
455
456 @param press_secs : Str. Time to press key.
457 """
458 self._server.refresh_key(press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700459
460
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800461 def ctrl_refresh_key(self, press_secs=''):
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800462 """Simulate Ctrl and Refresh (F3) simultaneous press.
463
464 This key combination is an alternative of Space key.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800465
466 @param press_secs : Str. Time to press key.
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800467 """
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800468 self._server.ctrl_refresh_key(press_secs)
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800469
470
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800471 def imaginary_key(self, press_secs=''):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700472 """Simulate imaginary key button press.
473
474 Maps to a key that doesn't physically exist.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800475
476 @param press_secs : Str. Time to press key.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700477 """
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800478 self._server.imaginary_key(press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700479
480
Vincent Palatine7dc9282016-07-14 11:31:58 +0200481 def sysrq_x(self, press_secs=''):
482 """Simulate Alt VolumeUp X simulataneous press.
483
484 This key combination is the kernel system request (sysrq) X.
485
486 @param press_secs : Str. Time to press key.
487 """
488 self._server.sysrq_x(press_secs)
489
490
Tom Wai-Hong Tam93b5c092015-05-14 02:50:43 +0800491 def toggle_recovery_switch(self):
492 """Toggle recovery switch on and off."""
493 self.enable_recovery_mode()
494 time.sleep(self.REC_TOGGLE_DELAY)
495 self.disable_recovery_mode()
496
497
Craig Harrison6b36b122011-06-28 17:58:43 -0700498 def enable_recovery_mode(self):
499 """Enable recovery mode on device."""
500 self.set('rec_mode', 'on')
501
502
503 def disable_recovery_mode(self):
504 """Disable recovery mode on device."""
505 self.set('rec_mode', 'off')
506
507
Tom Wai-Hong Tamf9ded092015-05-20 05:37:22 +0800508 def toggle_development_switch(self):
509 """Toggle development switch on and off."""
510 self.enable_development_mode()
511 time.sleep(self.DEV_TOGGLE_DELAY)
512 self.disable_development_mode()
513
514
Craig Harrison6b36b122011-06-28 17:58:43 -0700515 def enable_development_mode(self):
516 """Enable development mode on device."""
517 self.set('dev_mode', 'on')
518
519
520 def disable_development_mode(self):
521 """Disable development mode on device."""
522 self.set('dev_mode', 'off')
523
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700524 def boot_devmode(self):
525 """Boot a dev-mode device that is powered off."""
Tom Wai-Hong Tam23869102012-01-17 15:41:30 +0800526 self.power_short_press()
Craig Harrison48997262011-06-27 14:31:10 -0700527 self.pass_devmode()
528
529
530 def pass_devmode(self):
531 """Pass through boot screens in dev-mode."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700532 time.sleep(Servo.BOOT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700533 self.ctrl_d()
Chrome Bot9a1137d2011-07-19 14:35:00 -0700534 time.sleep(Servo.BOOT_DELAY)
Craig Harrison48997262011-06-27 14:31:10 -0700535
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700536
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800537 def get_board(self):
Wai-Hong Tam0f904002017-09-19 15:52:22 -0700538 """Get the board connected to servod."""
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800539 return self._server.get_board()
540
541
Wai-Hong Tam0f904002017-09-19 15:52:22 -0700542 def get_base_board(self):
543 """Get the board of the base connected to servod."""
Wai-Hong Tam7f6169e2017-09-29 09:23:48 -0700544 try:
545 return self._server.get_base_board()
546 except xmlrpclib.Fault as e:
547 # TODO(waihong): Remove the following compatibility check when
548 # the new versions of hdctools are deployed.
549 if 'not supported' in str(e):
550 logging.warning('The servod is too old that get_base_board '
551 'not supported.')
552 return ''
553 raise
Wai-Hong Tam0f904002017-09-19 15:52:22 -0700554
555
Wai-Hong Tamcc399ee2017-12-08 12:43:28 -0800556 def get_ec_active_copy(self):
557 """Get the active copy of the EC image."""
558 return self.get('ec_active_copy')
559
560
Todd Brochefe72cb2012-07-11 19:58:53 -0700561 def _get_xmlrpclib_exception(self, xmlexc):
562 """Get meaningful exception string from xmlrpc.
563
564 Args:
565 xmlexc: xmlrpclib.Fault object
566
567 xmlrpclib.Fault.faultString has the following format:
568
569 <type 'exception type'>:'actual error message'
570
571 Parse and return the real exception from the servod side instead of the
572 less meaningful one like,
573 <Fault 1: "<type 'exceptions.AttributeError'>:'tca6416' object has no
574 attribute 'hw_driver'">
575
576 Returns:
577 string of underlying exception raised in servod.
578 """
579 return re.sub('^.*>:', '', xmlexc.faultString)
580
581
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700582 def get(self, gpio_name):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700583 """Get the value of a gpio from Servod.
584
585 @param gpio_name Name of the gpio.
586 """
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700587 assert gpio_name
Todd Brochefe72cb2012-07-11 19:58:53 -0700588 try:
Simran Basi1cbc5f22013-07-17 14:23:58 -0700589 return self._server.get(gpio_name)
Todd Brochefe72cb2012-07-11 19:58:53 -0700590 except xmlrpclib.Fault as e:
591 err_msg = "Getting '%s' :: %s" % \
592 (gpio_name, self._get_xmlrpclib_exception(e))
593 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700594
595
596 def set(self, gpio_name, gpio_value):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700597 """Set and check the value of a gpio using Servod.
598
599 @param gpio_name Name of the gpio.
600 @param gpio_value New setting for the gpio.
601 """
Chrome Bot9a1137d2011-07-19 14:35:00 -0700602 self.set_nocheck(gpio_name, gpio_value)
Todd Brochcf7c6652012-02-24 13:03:59 -0800603 retry_count = Servo.GET_RETRY_MAX
604 while gpio_value != self.get(gpio_name) and retry_count:
Ilja H. Friedel04be2bd2014-05-07 21:29:59 -0700605 logging.warning("%s != %s, retry %d", gpio_name, gpio_value,
Todd Brochcf7c6652012-02-24 13:03:59 -0800606 retry_count)
607 retry_count -= 1
608 time.sleep(Servo.SHORT_DELAY)
609 if not retry_count:
610 assert gpio_value == self.get(gpio_name), \
611 'Servo failed to set %s to %s' % (gpio_name, gpio_value)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700612
613
614 def set_nocheck(self, gpio_name, gpio_value):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700615 """Set the value of a gpio using Servod.
616
617 @param gpio_name Name of the gpio.
618 @param gpio_value New setting for the gpio.
619 """
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700620 assert gpio_name and gpio_value
Mary Ruthven2f72e2a2018-05-01 17:12:58 -0700621 logging.info('Setting %s to %r', gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700622 try:
Simran Basi1cbc5f22013-07-17 14:23:58 -0700623 self._server.set(gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700624 except xmlrpclib.Fault as e:
Mary Ruthven73d3a352018-05-10 15:35:20 -0700625 err_msg = "Setting '%s' to %r :: %s" % \
Todd Brochefe72cb2012-07-11 19:58:53 -0700626 (gpio_name, gpio_value, self._get_xmlrpclib_exception(e))
627 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700628
629
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800630 def set_get_all(self, controls):
631 """Set &| get one or more control values.
632
633 @param controls: list of strings, controls to set &| get.
634
635 @raise: error.TestError in case error occurs setting/getting values.
636 """
637 rv = []
638 try:
Vic Yangcad9acb2013-05-21 14:02:05 +0800639 logging.info('Set/get all: %s', str(controls))
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800640 rv = self._server.set_get_all(controls)
641 except xmlrpclib.Fault as e:
642 # TODO(waihong): Remove the following backward compatibility when
643 # the new versions of hdctools are deployed.
644 if 'not supported' in str(e):
645 logging.warning('The servod is too old that set_get_all '
646 'not supported. Use set and get instead.')
647 for control in controls:
648 if ':' in control:
649 (name, value) = control.split(':')
650 if name == 'sleep':
651 time.sleep(float(value))
652 else:
653 self.set_nocheck(name, value)
654 rv.append(True)
655 else:
656 rv.append(self.get(name))
657 else:
658 err_msg = "Problem with '%s' :: %s" % \
659 (controls, self._get_xmlrpclib_exception(e))
660 raise error.TestFail(err_msg)
661 return rv
662
663
Jon Salzc88e5b62011-11-30 14:38:54 +0800664 # TODO(waihong) It may fail if multiple servo's are connected to the same
665 # host. Should look for a better way, like the USB serial name, to identify
666 # the USB device.
Simran Basi741b5d42012-05-18 11:27:15 -0700667 # TODO(sbasi) Remove this code from autoserv once firmware tests have been
668 # updated.
Kevin Chenga22c4a82016-10-07 14:13:25 -0700669 def probe_host_usb_dev(self, timeout=_USB_PROBE_TIMEOUT):
Jon Salzc88e5b62011-11-30 14:38:54 +0800670 """Probe the USB disk device plugged-in the servo from the host side.
671
Kevin Chengeb06fe72016-08-22 15:26:32 -0700672 It uses servod to discover if there is a usb device attached to it.
Jon Salzc88e5b62011-11-30 14:38:54 +0800673
Kevin Chenga22c4a82016-10-07 14:13:25 -0700674 @param timeout The timeout period when probing for the usb host device.
675
676 @return: String of USB disk path (e.g. '/dev/sdb') or None.
Jon Salzc88e5b62011-11-30 14:38:54 +0800677 """
Ruben Rodriguez Buchillon9a4bfc32018-10-16 20:46:25 +0800678 # Set up Servo's usb mux.
679 self.switch_usbkey('host')
Kevin Chenga22c4a82016-10-07 14:13:25 -0700680 return self._server.probe_host_usb_dev(timeout) or None
Jon Salzc88e5b62011-11-30 14:38:54 +0800681
682
Mike Truty49153d82012-08-21 22:27:30 -0500683 def image_to_servo_usb(self, image_path=None,
684 make_image_noninteractive=False):
685 """Install an image to the USB key plugged into the servo.
686
687 This method may copy any image to the servo USB key including a
688 recovery image or a test image. These images are frequently used
689 for test purposes such as restoring a corrupted image or conducting
690 an upgrade of ec/fw/kernel as part of a test of a specific image part.
691
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700692 @param image_path Path on the host to the recovery image.
693 @param make_image_noninteractive Make the recovery image
694 noninteractive, therefore the DUT
695 will reboot automatically after
696 installation.
Mike Truty49153d82012-08-21 22:27:30 -0500697 """
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700698 # We're about to start plugging/unplugging the USB key. We
699 # don't know the state of the DUT, or what it might choose
700 # to do to the device after hotplug. To avoid surprises,
701 # force the DUT to be off.
702 self._server.hwinit()
703 self._power_state.power_off()
Mike Truty49153d82012-08-21 22:27:30 -0500704
705 # Set up Servo's usb mux.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800706 self.switch_usbkey('host')
Mike Truty49153d82012-08-21 22:27:30 -0500707 if image_path:
Tom Wai-Hong Tam42f136d2012-10-26 11:11:23 +0800708 logging.info('Searching for usb device and copying image to it. '
709 'Please wait a few minutes...')
Mike Truty49153d82012-08-21 22:27:30 -0500710 if not self._server.download_image_to_usb(image_path):
711 logging.error('Failed to transfer requested image to USB. '
712 'Please take a look at Servo Logs.')
713 raise error.AutotestError('Download image to usb failed.')
714 if make_image_noninteractive:
715 logging.info('Making image noninteractive')
716 if not self._server.make_image_noninteractive():
717 logging.error('Failed to make image noninteractive. '
718 'Please take a look at Servo Logs.')
719
Kalin Stoyanovb3c11f32018-05-11 09:02:00 -0700720 def boot_in_recovery_mode(self):
721 """Boot host DUT in recovery mode."""
722 self._power_state.power_on(rec_mode=self._power_state.REC_ON)
723 self.switch_usbkey('dut')
724
Mike Truty49153d82012-08-21 22:27:30 -0500725
Simran Basi741b5d42012-05-18 11:27:15 -0700726 def install_recovery_image(self, image_path=None,
J. Richard Barnetteb6de7e32013-02-14 13:28:04 -0800727 make_image_noninteractive=False):
Dan Shic67f1332016-04-06 12:38:06 -0700728 """Install the recovery image specified by the path onto the DUT.
Jon Salzc88e5b62011-11-30 14:38:54 +0800729
730 This method uses google recovery mode to install a recovery image
731 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 +0800732 board specified by the usb_dev. If no image path is specified
Jon Salzc88e5b62011-11-30 14:38:54 +0800733 we use the recovery image already on the usb image.
734
Dan Shic67f1332016-04-06 12:38:06 -0700735 @param image_path: Path on the host to the recovery image.
736 @param make_image_noninteractive: Make the recovery image
737 noninteractive, therefore the DUT will reboot automatically
738 after installation.
Jon Salzc88e5b62011-11-30 14:38:54 +0800739 """
Mike Truty49153d82012-08-21 22:27:30 -0500740 self.image_to_servo_usb(image_path, make_image_noninteractive)
Kalin Stoyanovb3c11f32018-05-11 09:02:00 -0700741 self.boot_in_recovery_mode()
Jon Salzc88e5b62011-11-30 14:38:54 +0800742
743
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800744 def _scp_image(self, image_path):
745 """Copy image to the servo host.
746
747 When programming a firmware image on the DUT, the image must be
748 located on the host to which the servo device is connected. Sometimes
749 servo is controlled by a remote host, in this case the image needs to
750 be transferred to the remote host.
751
752 @param image_path: a string, name of the firmware image file to be
753 transferred.
754 @return: a string, full path name of the copied file on the remote.
755 """
756
757 dest_path = os.path.join('/tmp', os.path.basename(image_path))
Fang Deng5d518f42013-08-02 14:04:32 -0700758 self._servo_host.send_file(image_path, dest_path)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800759 return dest_path
760
761
Dan Shifecdaf42015-07-28 10:17:26 -0700762 def system(self, command, timeout=3600):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700763 """Execute the passed in command on the servod host.
764
765 @param command Command to be executed.
Dan Shifecdaf42015-07-28 10:17:26 -0700766 @param timeout Maximum number of seconds of runtime allowed. Default to
767 1 hour.
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700768 """
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800769 logging.info('Will execute on servo host: %s', command)
Fang Deng5d518f42013-08-02 14:04:32 -0700770 self._servo_host.run(command, timeout=timeout)
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800771
772
Dan Shifecdaf42015-07-28 10:17:26 -0700773 def system_output(self, command, timeout=3600,
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800774 ignore_status=False, args=()):
775 """Execute the passed in command on the servod host, return stdout.
776
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700777 @param command a string, the command to execute
778 @param timeout an int, max number of seconds to wait til command
Dan Shifecdaf42015-07-28 10:17:26 -0700779 execution completes. Default to 1 hour.
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700780 @param ignore_status a Boolean, if true - ignore command's nonzero exit
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800781 status, otherwise an exception will be thrown
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700782 @param args a tuple of strings, each becoming a separate command line
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800783 parameter for the command
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700784 @return command's stdout as a string.
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800785 """
Fang Deng5d518f42013-08-02 14:04:32 -0700786 return self._servo_host.run(command, timeout=timeout,
787 ignore_status=ignore_status,
788 args=args).stdout.strip()
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800789
790
Dan Shia5fef052015-05-18 23:28:47 -0700791 def get_servo_version(self):
792 """Get the version of the servo, e.g., servo_v2 or servo_v3.
793
794 @return: The version of the servo.
795
796 """
797 return self._server.get_version()
798
799
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800800 def _initialize_programmer(self, rw_only=False):
801 """Initialize the firmware programmer.
802
803 @param rw_only: True to initialize a programmer which only
804 programs the RW portions.
805 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700806 if self._programmer:
807 return
808 # Initialize firmware programmer
Dan Shia5fef052015-05-18 23:28:47 -0700809 servo_version = self.get_servo_version()
Dan Shi9cb0eec2014-06-03 09:04:50 -0700810 if servo_version.startswith('servo_v2'):
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700811 self._programmer = firmware_programmer.ProgrammerV2(self)
Wai-Hong Tamc36c4d22016-09-09 10:39:45 -0700812 self._programmer_rw = firmware_programmer.ProgrammerV2RwOnly(self)
Kevin Chengdf2e29f2016-09-09 02:31:22 -0700813 # Both servo v3 and v4 use the same programming methods so just leverage
814 # ProgrammerV3 for servo v4 as well.
815 elif (servo_version.startswith('servo_v3') or
816 servo_version.startswith('servo_v4')):
Dan Shia5fef052015-05-18 23:28:47 -0700817 self._programmer = firmware_programmer.ProgrammerV3(self)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800818 self._programmer_rw = firmware_programmer.ProgrammerV3RwOnly(self)
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700819 else:
820 raise error.TestError(
821 'No firmware programmer for servo version: %s' %
Congbin Guo42427612019-02-12 10:22:06 -0800822 servo_version)
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700823
824
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800825 def program_bios(self, image, rw_only=False):
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800826 """Program bios on DUT with given image.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800827
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800828 @param image: a string, file name of the BIOS image to program
829 on the DUT.
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800830 @param rw_only: True to only program the RW portion of BIOS.
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800831
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800832 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700833 self._initialize_programmer()
Ricky Liangc31aab32014-07-03 16:23:29 +0800834 if not self.is_localhost():
835 image = self._scp_image(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800836 if rw_only:
837 self._programmer_rw.program_bios(image)
838 else:
839 self._programmer.program_bios(image)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800840
841
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800842 def program_ec(self, image, rw_only=False):
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800843 """Program ec on DUT with given image.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800844
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800845 @param image: a string, file name of the EC image to program
846 on the DUT.
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800847 @param rw_only: True to only program the RW portion of EC.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800848
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800849 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700850 self._initialize_programmer()
Ricky Liangc31aab32014-07-03 16:23:29 +0800851 if not self.is_localhost():
852 image = self._scp_image(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800853 if rw_only:
Congbin Guo42427612019-02-12 10:22:06 -0800854 self._programmer_rw.program_ec(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800855 else:
Congbin Guo42427612019-02-12 10:22:06 -0800856 self._programmer.program_ec(image)
857
858
859 def _reprogram(self, tarball_path, firmware_name, image_candidates,
860 rw_only):
861 """Helper function to reprogram firmware for EC or BIOS.
862
863 @param tarball_path: The path of the downloaded build tarball.
864 @param: firmware_name: either 'EC' or 'BIOS'.
865 @param image_candidates: A tuple of the paths of image candidates.
866 @param rw_only: True to only install firmware to its RW portions. Keep
867 the RO portions unchanged.
868
869 @raise: TestError if cannot extract firmware from the tarball.
870 """
871 dest_dir = os.path.dirname(tarball_path)
872 image = _extract_image_from_tarball(tarball_path, dest_dir,
873 image_candidates)
874 if not image:
875 if firmware_name == 'EC':
876 logging.info('Not a Chrome EC, ignore re-programming it')
877 return
878 else:
879 raise error.TestError('Failed to extract the %s image from '
880 'tarball' % firmware_name)
881
882 logging.info('Will re-program %s %snow', firmware_name,
883 'RW ' if rw_only else '')
884
885 if firmware_name == 'EC':
886 self.program_ec(os.path.join(dest_dir, image), rw_only)
887 else:
888 self.program_bios(os.path.join(dest_dir, image), rw_only)
889
890
891 def program_firmware(self, model, tarball_path, rw_only=False):
892 """Program firmware (EC, if applied, and BIOS) of the DUT.
893
894 @param model: The DUT model name.
895 @param tarball_path: The path of the downloaded build tarball.
896 @param rw_only: True to only install firmware to its RW portions. Keep
897 the RO portions unchanged.
898 """
899 ap_image_candidates = ('image.bin', 'image-%s.bin' % model)
900 ec_image_candidates = ('ec.bin', '%s/ec.bin' % model)
901
902 self._reprogram(tarball_path, 'EC', ec_image_candidates, rw_only)
903 self._reprogram(tarball_path, 'BIOS', ap_image_candidates, rw_only)
904
905 self.get_power_state_controller().reset()
906 time.sleep(Servo.BOOT_DELAY)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800907
Fang Dengafb88142013-05-30 17:44:31 -0700908
909 def _switch_usbkey_power(self, power_state, detection_delay=False):
910 """Switch usbkey power.
911
912 This function switches usbkey power by setting the value of
913 'prtctl4_pwren'. If the power is already in the
914 requested state, this function simply returns.
915
916 @param power_state: A string, 'on' or 'off'.
917 @param detection_delay: A boolean value, if True, sleep
918 for |USB_DETECTION_DELAY| after switching
919 the power on.
920 """
Kevin Chenga22c4a82016-10-07 14:13:25 -0700921 # TODO(kevcheng): Forgive me for this terrible hack. This is just to
922 # handle beaglebones that haven't yet updated and have the
923 # safe_switch_usbkey_power RPC. I'll remove this once all beaglebones
924 # have been updated and also think about a better way to handle
925 # situations like this.
926 try:
927 self._server.safe_switch_usbkey_power(power_state)
928 except Exception:
929 self.set('prtctl4_pwren', power_state)
Fang Dengafb88142013-05-30 17:44:31 -0700930 if power_state == 'off':
931 time.sleep(self.USB_POWEROFF_DELAY)
932 elif detection_delay:
933 time.sleep(self.USB_DETECTION_DELAY)
934
935
936 def switch_usbkey(self, usb_state):
937 """Connect USB flash stick to either host or DUT, or turn USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800938
939 This function switches the servo multiplexer to provide electrical
Fang Dengafb88142013-05-30 17:44:31 -0700940 connection between the USB port J3 and either host or DUT side. It
941 can also be used to turn the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800942
Fang Dengafb88142013-05-30 17:44:31 -0700943 Switching to 'dut' or 'host' is accompanied by powercycling
944 of the USB stick, because it sometimes gets wedged if the mux
945 is switched while the stick power is on.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800946
Fang Dengafb88142013-05-30 17:44:31 -0700947 @param usb_state: A string, one of 'dut', 'host', or 'off'.
948 'dut' and 'host' indicate which side the
949 USB flash device is required to be connected to.
950 'off' indicates turning the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800951
Fang Dengafb88142013-05-30 17:44:31 -0700952 @raise: error.TestError in case the parameter is not 'dut'
953 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800954 """
Fang Dengafb88142013-05-30 17:44:31 -0700955 if self.get_usbkey_direction() == usb_state:
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800956 return
957
Fang Dengafb88142013-05-30 17:44:31 -0700958 if usb_state == 'off':
Dan Shia5fef052015-05-18 23:28:47 -0700959 self._switch_usbkey_power('off')
960 self._usb_state = usb_state
961 return
Fang Dengafb88142013-05-30 17:44:31 -0700962 elif usb_state == 'host':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800963 mux_direction = 'servo_sees_usbkey'
Fang Dengafb88142013-05-30 17:44:31 -0700964 elif usb_state == 'dut':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800965 mux_direction = 'dut_sees_usbkey'
966 else:
Fang Dengafb88142013-05-30 17:44:31 -0700967 raise error.TestError('Unknown USB state request: %s' % usb_state)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800968
Fang Dengafb88142013-05-30 17:44:31 -0700969 self._switch_usbkey_power('off')
Kevin Chenga22c4a82016-10-07 14:13:25 -0700970 # TODO(kevcheng): Forgive me for this terrible hack. This is just to
971 # handle beaglebones that haven't yet updated and have the
972 # safe_switch_usbkey RPC. I'll remove this once all beaglebones have
973 # been updated and also think about a better way to handle situations
974 # like this.
975 try:
976 self._server.safe_switch_usbkey(mux_direction)
977 except Exception:
978 self.set('usb_mux_sel1', mux_direction)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800979 time.sleep(self.USB_POWEROFF_DELAY)
Fang Dengafb88142013-05-30 17:44:31 -0700980 self._switch_usbkey_power('on', usb_state == 'host')
981 self._usb_state = usb_state
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800982
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800983
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800984 def get_usbkey_direction(self):
Fang Dengafb88142013-05-30 17:44:31 -0700985 """Get which side USB is connected to or 'off' if usb power is off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800986
Fang Dengafb88142013-05-30 17:44:31 -0700987 @return: A string, one of 'dut', 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800988 """
Fang Dengafb88142013-05-30 17:44:31 -0700989 if not self._usb_state:
990 if self.get('prtctl4_pwren') == 'off':
991 self._usb_state = 'off'
992 elif self.get('usb_mux_sel1').startswith('dut'):
993 self._usb_state = 'dut'
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800994 else:
Fang Dengafb88142013-05-30 17:44:31 -0700995 self._usb_state = 'host'
996 return self._usb_state
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700997
998
Wai-Hong Tam60377262018-03-01 10:55:39 -0800999 def set_servo_v4_role(self, role):
1000 """Set the power role of servo v4, either 'src' or 'snk'.
1001
1002 It does nothing if not a servo v4.
1003
1004 @param role: Power role for DUT port on servo v4, either 'src' or 'snk'.
1005 """
1006 servo_version = self.get_servo_version()
1007 if servo_version.startswith('servo_v4'):
1008 value = self.get('servo_v4_role')
1009 if value != role:
1010 self.set_nocheck('servo_v4_role', role)
1011 else:
1012 logging.debug('Already in the role: %s.', role)
1013 else:
1014 logging.debug('Not a servo v4, unable to set role to %s.', role)
1015
1016
Congbin Guofc3b8962019-03-22 17:38:46 -07001017 @property
1018 def uart_logs_dir(self):
1019 """Return the directory to save UART logs."""
1020 return self._uart.logs_dir if self._uart else ""
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001021
Congbin Guofc3b8962019-03-22 17:38:46 -07001022
1023 @uart_logs_dir.setter
1024 def uart_logs_dir(self, logs_dir):
1025 """Set directory to save UART logs.
1026
1027 @param logs_dir String of directory name."""
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001028 if self._uart:
Congbin Guofc3b8962019-03-22 17:38:46 -07001029 self._uart.logs_dir = logs_dir
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001030
1031
1032 def close(self):
1033 """Close the servo object."""
1034 if self._uart:
1035 self._uart.stop_capture()
Congbin Guofc3b8962019-03-22 17:38:46 -07001036 self._uart.dump()
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001037 self._uart = None