blob: a0855f7b738121070f9fca41d375ac2f848aebc8 [file] [log] [blame]
J. Richard Barnette384056b2012-04-16 11:04:46 -07001# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -07002# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4#
5# Expects to be run in an environment with sudo and no interactive password
6# prompt, such as within the Chromium OS development chroot.
7
Congbin Guoa1f9cba2018-07-03 11:36:59 -07008import ast
9import logging
Vadim Bendeburyb80ba592012-12-07 15:02:34 -080010import os
Congbin Guoa1f9cba2018-07-03 11:36:59 -070011import re
12import time
13import xmlrpclib
Vadim Bendeburyb80ba592012-12-07 15:02:34 -080014
Simran Basi741b5d42012-05-18 11:27:15 -070015from autotest_lib.client.common_lib import error
Congbin Guo42427612019-02-12 10:22:06 -080016from autotest_lib.server import utils as server_utils
Ricky Liangc31aab32014-07-03 16:23:29 +080017from autotest_lib.server.cros.servo import firmware_programmer
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070018
Kevin Chenga22c4a82016-10-07 14:13:25 -070019# Time to wait when probing for a usb device, it takes on avg 17 seconds
20# to do a full probe.
21_USB_PROBE_TIMEOUT = 40
22
J. Richard Barnette41320ee2013-03-11 13:00:13 -070023
Congbin Guo42427612019-02-12 10:22:06 -080024def _extract_image_from_tarball(tarball, dest_dir, image_candidates):
25 """Try extracting the image_candidates from the tarball.
26
27 @param tarball: The path of the tarball.
28 @param dest_path: The path of the destination.
29 @param image_candidates: A tuple of the paths of image candidates.
30
31 @return: The first path from the image candidates, which succeeds, or None
32 if all the image candidates fail.
33 """
34 for image in image_candidates:
35 status = server_utils.system(
36 ('tar xf %s -C %s %s' % (tarball, dest_dir, image)),
37 timeout=60, ignore_status=True)
38 if status == 0:
39 return image
40 return None
41
42
J. Richard Barnette0c9c5882014-06-11 15:27:05 -070043class _PowerStateController(object):
44
45 """Class to provide board-specific power operations.
46
47 This class is responsible for "power on" and "power off"
48 operations that can operate without making assumptions in
49 advance about board state. It offers an interface that
50 abstracts out the different sequences required for different
51 board types.
52
53 """
54
55 # Constants acceptable to be passed for the `rec_mode` parameter
56 # to power_on().
57 #
58 # REC_ON: Boot the DUT in recovery mode, i.e. boot from USB or
59 # SD card.
60 # REC_OFF: Boot in normal mode, i.e. boot from internal storage.
61
62 REC_ON = 'rec'
63 REC_OFF = 'on'
Shelley Chen65938622018-05-16 07:45:54 -070064 REC_ON_FORCE_MRC = 'rec_force_mrc'
J. Richard Barnette0c9c5882014-06-11 15:27:05 -070065
66 # Delay in seconds needed between asserting and de-asserting
67 # warm reset.
68 _RESET_HOLD_TIME = 0.5
69
70 def __init__(self, servo):
71 """Initialize the power state control.
72
73 @param servo Servo object providing the underlying `set` and `get`
74 methods for the target controls.
75
76 """
77 self._servo = servo
78
79 def reset(self):
80 """Force the DUT to reset.
81
82 The DUT is guaranteed to be on at the end of this call,
83 regardless of its previous state, provided that there is
84 working OS software. This also guarantees that the EC has
85 been restarted.
86
87 """
88 self._servo.set_nocheck('power_state', 'reset')
89
90 def warm_reset(self):
91 """Apply warm reset to the DUT.
92
93 This asserts, then de-asserts the 'warm_reset' signal.
94 Generally, this causes the board to restart.
95
96 """
97 self._servo.set_get_all(['warm_reset:on',
98 'sleep:%.4f' % self._RESET_HOLD_TIME,
99 'warm_reset:off'])
100
J. Richard Barnette0c9c5882014-06-11 15:27:05 -0700101 def power_off(self):
102 """Force the DUT to power off.
103
104 The DUT is guaranteed to be off at the end of this call,
105 regardless of its previous state, provided that there is
106 working EC and boot firmware. There is no requirement for
107 working OS software.
108
109 """
110 self._servo.set_nocheck('power_state', 'off')
111
112 def power_on(self, rec_mode=REC_OFF):
113 """Force the DUT to power on.
114
115 Prior to calling this function, the DUT must be powered off,
116 e.g. with a call to `power_off()`.
117
118 At power on, recovery mode is set as specified by the
119 corresponding argument. When booting with recovery mode on, it
120 is the caller's responsibility to unplug/plug in a bootable
121 external storage device.
122
123 If the DUT requires a delay after powering on but before
124 processing inputs such as USB stick insertion, the delay is
125 handled by this method; the caller is not responsible for such
126 delays.
127
128 @param rec_mode Setting of recovery mode to be applied at
129 power on. default: REC_OFF aka 'off'
130
131 """
132 self._servo.set_nocheck('power_state', rec_mode)
133
134
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700135class _Uart(object):
Congbin Guo0f00be82019-04-18 17:51:14 -0700136 """Class to capture UART streams of CPU, EC, Cr50, etc."""
137 _UartToCapture = ('cpu', 'ec', 'cr50', 'servo_v4', 'servo_micro', 'usbpd')
138
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700139 def __init__(self, servo):
140 self._servo = servo
141 self._streams = []
Congbin Guo0f00be82019-04-18 17:51:14 -0700142 self.logs_dir = None
Congbin Guofe4d4e82019-04-18 17:51:14 -0700143
Congbin Guo0f00be82019-04-18 17:51:14 -0700144 def _start_stop_capture(self, uart, start):
145 """Helper function to start/stop capturing on specified UART.
146
147 @param uart: The UART name to start/stop capturing.
148 @param start: True to start capturing, otherwise stop.
149
150 @returns True if the operation completes successfully. False if the UART
151 capturing is not supported or failed due to an error.
152 """
153 logging.debug('%s capturing %s UART.', 'Start' if start else 'Stop',
154 uart)
Congbin Guo39dc6f22019-04-23 23:01:21 +0000155 try:
Congbin Guo0f00be82019-04-18 17:51:14 -0700156 self._servo.set('%s_uart_capture' % uart,
157 'on' if start else 'off')
Congbin Guo39dc6f22019-04-23 23:01:21 +0000158 except error.TestFail as err:
159 if 'No control named' in str(err):
Congbin Guo0f00be82019-04-18 17:51:14 -0700160 logging.debug("The servod doesn't support %s_uart_capture.",
161 uart)
162 else:
163 logging.debug("Can't caputre UART for %s: %s", uart, err)
164 return False
165
166 return True
167
168 def start_capture(self):
169 """Start capturing UART streams."""
170 for uart in self._UartToCapture:
171 if self._start_stop_capture(uart, True):
172 self._streams.append(('%s_uart_stream' % uart, '%s_uart.log' %
173 uart))
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700174
Congbin Guofc3b8962019-03-22 17:38:46 -0700175 def dump(self):
176 """Dump UART streams to log files accordingly."""
Congbin Guo0f00be82019-04-18 17:51:14 -0700177 if not self.logs_dir:
Congbin Guofc3b8962019-03-22 17:38:46 -0700178 return
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700179
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700180 for stream, logfile in self._streams:
Congbin Guo0f00be82019-04-18 17:51:14 -0700181 logfile_fullname = os.path.join(self.logs_dir, logfile)
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700182 try:
183 content = self._servo.get(stream)
184 except Exception as err:
185 logging.warn('Failed to get UART log for %s: %s', stream, err)
186 continue
187
188 # The UART stream may contain non-printable characters, and servo
189 # returns it in string representation. We use `ast.leteral_eval`
190 # to revert it back.
191 with open(logfile_fullname, 'a') as fd:
192 fd.write(ast.literal_eval(content))
193
194 def stop_capture(self):
195 """Stop capturing UART streams."""
Congbin Guo0f00be82019-04-18 17:51:14 -0700196 for uart in self._UartToCapture:
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700197 try:
Congbin Guo0f00be82019-04-18 17:51:14 -0700198 self._start_stop_capture(uart, False)
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700199 except Exception as err:
200 logging.warn('Failed to stop UART logging for %s: %s', uart,
201 err)
202
203
J. Richard Barnette384056b2012-04-16 11:04:46 -0700204class Servo(object):
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700205
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700206 """Manages control of a Servo board.
207
208 Servo is a board developed by hardware group to aide in the debug and
209 control of various partner devices. Servo's features include the simulation
210 of pressing the power button, closing the lid, and pressing Ctrl-d. This
211 class manages setting up and communicating with a servo demon (servod)
212 process. It provides both high-level functions for common servo tasks and
213 low-level functions for directly setting and reading gpios.
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700214
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700215 """
216
Chrome Bot9a1137d2011-07-19 14:35:00 -0700217 # Power button press delays in seconds.
J. Richard Barnetted2e4cbd2012-06-29 12:18:40 -0700218 #
J. Richard Barnette5383f072012-07-26 17:35:40 -0700219 # The EC specification says that 8.0 seconds should be enough
220 # for the long power press. However, some platforms need a bit
221 # more time. Empirical testing has found these requirements:
222 # Alex: 8.2 seconds
223 # ZGB: 8.5 seconds
224 # The actual value is set to the largest known necessary value.
225 #
226 # TODO(jrbarnette) Being generous is the right thing to do for
227 # existing platforms, but if this code is to be used for
228 # qualification of new hardware, we should be less generous.
Chrome Bot9a1137d2011-07-19 14:35:00 -0700229 SHORT_DELAY = 0.1
J. Richard Barnette5383f072012-07-26 17:35:40 -0700230
Todd Broch31c82502011-08-29 08:14:39 -0700231 # Maximum number of times to re-read power button on release.
Todd Brochcf7c6652012-02-24 13:03:59 -0800232 GET_RETRY_MAX = 10
Chrome Bot9a1137d2011-07-19 14:35:00 -0700233
J. Richard Barnette5383f072012-07-26 17:35:40 -0700234 # Delays to deal with DUT state transitions.
Chrome Bot9a1137d2011-07-19 14:35:00 -0700235 SLEEP_DELAY = 6
236 BOOT_DELAY = 10
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700237
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700238 # Default minimum time interval between 'press' and 'release'
239 # keyboard events.
Vic Yang0aca1c22012-11-19 18:33:56 -0800240 SERVO_KEY_PRESS_DELAY = 0.1
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700241
Tom Wai-Hong Tam93b5c092015-05-14 02:50:43 +0800242 # Time to toggle recovery switch on and off.
243 REC_TOGGLE_DELAY = 0.1
244
Tom Wai-Hong Tamf9ded092015-05-20 05:37:22 +0800245 # Time to toggle development switch on and off.
246 DEV_TOGGLE_DELAY = 0.1
247
Jon Salzc88e5b62011-11-30 14:38:54 +0800248 # Time between an usb disk plugged-in and detected in the system.
249 USB_DETECTION_DELAY = 10
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800250 # Time to keep USB power off before and after USB mux direction is changed
251 USB_POWEROFF_DELAY = 2
Jon Salzc88e5b62011-11-30 14:38:54 +0800252
Simran Basib7850bb2013-07-24 12:33:42 -0700253 # Time to wait before timing out on servo initialization.
254 INIT_TIMEOUT_SECS = 10
Simran Basi59331342013-07-12 12:06:29 -0700255
J. Richard Barnette67ccb872012-04-19 16:34:56 -0700256
Ricky Liang0dd379c2014-04-23 16:29:08 +0800257 def __init__(self, servo_host, servo_serial=None):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700258 """Sets up the servo communication infrastructure.
259
Fang Deng5d518f42013-08-02 14:04:32 -0700260 @param servo_host: A ServoHost object representing
261 the host running servod.
Ricky Liang0dd379c2014-04-23 16:29:08 +0800262 @param servo_serial: Serial number of the servo board.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700263 """
Fang Deng5d518f42013-08-02 14:04:32 -0700264 # TODO(fdeng): crbug.com/298379
265 # We should move servo_host object out of servo object
266 # to minimize the dependencies on the rest of Autotest.
267 self._servo_host = servo_host
Ricky Liang0dd379c2014-04-23 16:29:08 +0800268 self._servo_serial = servo_serial
Richard Barnette180efe62016-12-02 23:20:44 +0000269 self._server = servo_host.get_servod_server_proxy()
J. Richard Barnette0c9c5882014-06-11 15:27:05 -0700270 self._power_state = _PowerStateController(self)
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700271 self._uart = _Uart(self)
Fang Dengafb88142013-05-30 17:44:31 -0700272 self._usb_state = None
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700273 self._programmer = None
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800274
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800275
Ricky Liang0dd379c2014-04-23 16:29:08 +0800276 @property
277 def servo_serial(self):
278 """Returns the serial number of the servo board."""
279 return self._servo_serial
280
281
Tom Wai-Hong Tam22ee0e52013-04-03 13:36:39 +0800282 def get_power_state_controller(self):
J. Richard Barnette75136b32013-03-26 13:38:44 -0700283 """Return the power state controller for this Servo.
284
285 The power state controller provides board-independent
286 interfaces for reset, power-on, power-off operations.
287
288 """
289 return self._power_state
290
Fang Deng5d518f42013-08-02 14:04:32 -0700291
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700292 def initialize_dut(self, cold_reset=False):
293 """Initializes a dut for testing purposes.
294
295 This sets various servo signals back to default values
296 appropriate for the target board. By default, if the DUT
297 is already on, it stays on. If the DUT is powered off
298 before initialization, its state afterward is unspecified.
299
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700300 Rationale: Basic initialization of servo sets the lid open,
301 when there is a lid. This operation won't affect powered on
302 units; however, setting the lid open may power on a unit
J. Richard Barnette75136b32013-03-26 13:38:44 -0700303 that's off, depending on the board type and previous state
304 of the device.
305
J. Richard Barnette4b6af0d2014-06-05 09:57:20 -0700306 If `cold_reset` is a true value, the DUT and its EC will be
307 reset, and the DUT rebooted in normal mode.
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700308
309 @param cold_reset If True, cold reset the device after
310 initialization.
311 """
312 self._server.hwinit()
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700313 self.set('usb_mux_oe1', 'on')
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700314 self._usb_state = None
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700315 self.switch_usbkey('off')
Congbin Guoa1f9cba2018-07-03 11:36:59 -0700316 self._uart.start_capture()
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700317 if cold_reset:
J. Richard Barnette4b6af0d2014-06-05 09:57:20 -0700318 self._power_state.reset()
J. Richard Barnette1777f5d2014-09-03 15:18:19 -0700319 logging.debug('Servo initialized, version is %s',
320 self._server.get_version())
Ruben Rodriguez Buchillon7121b092019-04-29 12:39:34 -0700321 try:
Ruben Rodriguez Buchillon05298242019-05-20 11:29:06 -0700322 # TODO(coconutruben): change this back to set() about a month
323 # after crrev.com/c/1586239 has been merged (or whenever that
324 # logic is in the labstation images).
325 self.set_nocheck('init_keyboard','on')
Ruben Rodriguez Buchillon7121b092019-04-29 12:39:34 -0700326 except error.TestFail as err:
327 if 'No control named' in str(err):
328 # This indicates the servod version does not
329 # have explicit keyboard initialization yet.
330 # Ignore this.
331 pass
332 else:
333 raise err
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700334
335
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800336 def is_localhost(self):
337 """Is the servod hosted locally?
338
339 Returns:
340 True if local hosted; otherwise, False.
341 """
Fang Deng5d518f42013-08-02 14:04:32 -0700342 return self._servo_host.is_localhost()
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800343
344
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700345 def power_long_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700346 """Simulate a long power button press."""
J. Richard Barnettef12bff22012-07-18 14:41:06 -0700347 # After a long power press, the EC may ignore the next power
348 # button press (at least on Alex). To guarantee that this
349 # won't happen, we need to allow the EC one second to
350 # collect itself.
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800351 # long_press is defined as 8.5s in servod
352 self.set_nocheck('power_key', 'long_press')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700353
354
355 def power_normal_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700356 """Simulate a normal power button press."""
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800357 # press is defined as 1.2s in servod
358 self.set_nocheck('power_key', 'press')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700359
360
361 def power_short_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700362 """Simulate a short power button press."""
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800363 # tab is defined as 0.2s in servod
364 self.set_nocheck('power_key', 'tab')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700365
366
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800367 def power_key(self, press_secs='tab'):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700368 """Simulate a power button press.
369
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800370 @param press_secs: int, float, str; time to press key in seconds or
371 known shorthand: 'tab' 'press' 'long_press'
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700372 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800373 self.set_nocheck('power_key', press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700374
375
376 def lid_open(self):
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800377 """Simulate opening the lid and raise exception if all attempts fail"""
378 self.set('lid_open', 'yes')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700379
380
381 def lid_close(self):
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800382 """Simulate closing the lid and raise exception if all attempts fail
Craig Harrison48997262011-06-27 14:31:10 -0700383
384 Waits 6 seconds to ensure the device is fully asleep before returning.
385 """
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800386 self.set('lid_open', 'no')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700387 time.sleep(Servo.SLEEP_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700388
Shelley Chenc26575a2015-09-18 10:56:16 -0700389 def volume_up(self, timeout=300):
Kevin Chenge448a8b2016-09-09 10:18:11 -0700390 """Simulate pushing the volume down button.
391
392 @param timeout: Timeout for setting the volume.
393 """
Shelley Chenc26575a2015-09-18 10:56:16 -0700394 self.set_get_all(['volume_up:yes',
395 'sleep:%.4f' % self.SERVO_KEY_PRESS_DELAY,
396 'volume_up:no'])
397 # we need to wait for commands to take effect before moving on
398 time_left = float(timeout)
399 while time_left > 0.0:
400 value = self.get('volume_up')
401 if value == 'no':
402 return
403 time.sleep(self.SHORT_DELAY)
404 time_left = time_left - self.SHORT_DELAY
405 raise error.TestFail("Failed setting volume_up to no")
406
407 def volume_down(self, timeout=300):
Kevin Chenge448a8b2016-09-09 10:18:11 -0700408 """Simulate pushing the volume down button.
409
410 @param timeout: Timeout for setting the volume.
411 """
Shelley Chenc26575a2015-09-18 10:56:16 -0700412 self.set_get_all(['volume_down:yes',
413 'sleep:%.4f' % self.SERVO_KEY_PRESS_DELAY,
414 'volume_down:no'])
415 # we need to wait for commands to take effect before moving on
416 time_left = float(timeout)
417 while time_left > 0.0:
418 value = self.get('volume_down')
419 if value == 'no':
420 return
421 time.sleep(self.SHORT_DELAY)
422 time_left = time_left - self.SHORT_DELAY
423 raise error.TestFail("Failed setting volume_down to no")
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700424
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800425 def ctrl_d(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800426 """Simulate Ctrl-d simultaneous button presses.
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800427
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800428 @param press_secs: int, float, str; time to press key in seconds or
429 known shorthand: 'tab' 'press' 'long_press'
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800430 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800431 self.set_nocheck('ctrl_d', press_secs)
Gediminas Ramanauskas9da80f22012-11-14 12:59:43 -0800432
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800433
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800434 def ctrl_u(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800435 """Simulate Ctrl-u simultaneous button presses.
436
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800437 @param press_secs: int, float, str; time to press key in seconds or
438 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800439 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800440 self.set_nocheck('ctrl_u', press_secs)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700441
442
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800443 def ctrl_enter(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800444 """Simulate Ctrl-enter simultaneous button presses.
445
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800446 @param press_secs: int, float, str; time to press key in seconds or
447 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800448 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800449 self.set_nocheck('ctrl_enter', press_secs)
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -0700450
451
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800452 def ctrl_key(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800453 """Simulate Enter key button press.
454
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800455 @param press_secs: int, float, str; time to press key in seconds or
456 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800457 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800458 self.set_nocheck('ctrl_key', press_secs)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700459
460
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800461 def enter_key(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800462 """Simulate Enter key button press.
463
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800464 @param press_secs: int, float, str; time to press key in seconds or
465 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800466 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800467 self.set_nocheck('enter_key', press_secs)
Todd Broch9753bd42012-03-21 10:15:08 -0700468
469
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800470 def refresh_key(self, press_secs='tab'):
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800471 """Simulate Refresh key (F3) button press.
472
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800473 @param press_secs: int, float, str; time to press key in seconds or
474 known shorthand: 'tab' 'press' 'long_press'
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800475 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800476 self.set_nocheck('refresh_key', press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700477
478
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800479 def ctrl_refresh_key(self, press_secs='tab'):
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800480 """Simulate Ctrl and Refresh (F3) simultaneous press.
481
482 This key combination is an alternative of Space key.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800483
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800484 @param press_secs: int, float, str; time to press key in seconds or
485 known shorthand: 'tab' 'press' 'long_press'
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800486 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800487 self.set_nocheck('ctrl_refresh_key', press_secs)
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800488
489
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800490 def imaginary_key(self, press_secs='tab'):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700491 """Simulate imaginary key button press.
492
493 Maps to a key that doesn't physically exist.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800494
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800495 @param press_secs: int, float, str; time to press key in seconds or
496 known shorthand: 'tab' 'press' 'long_press'
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700497 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800498 self.set_nocheck('imaginary_key', press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700499
500
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800501 def sysrq_x(self, press_secs='tab'):
Vincent Palatine7dc9282016-07-14 11:31:58 +0200502 """Simulate Alt VolumeUp X simulataneous press.
503
504 This key combination is the kernel system request (sysrq) X.
505
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800506 @param press_secs: int, float, str; time to press key in seconds or
507 known shorthand: 'tab' 'press' 'long_press'
Vincent Palatine7dc9282016-07-14 11:31:58 +0200508 """
Ruben Rodriguez Buchillon84eba752018-07-27 17:59:48 +0800509 self.set_nocheck('sysrq_x', press_secs)
Vincent Palatine7dc9282016-07-14 11:31:58 +0200510
511
Tom Wai-Hong Tam93b5c092015-05-14 02:50:43 +0800512 def toggle_recovery_switch(self):
513 """Toggle recovery switch on and off."""
514 self.enable_recovery_mode()
515 time.sleep(self.REC_TOGGLE_DELAY)
516 self.disable_recovery_mode()
517
518
Craig Harrison6b36b122011-06-28 17:58:43 -0700519 def enable_recovery_mode(self):
520 """Enable recovery mode on device."""
521 self.set('rec_mode', 'on')
522
523
524 def disable_recovery_mode(self):
525 """Disable recovery mode on device."""
526 self.set('rec_mode', 'off')
527
528
Tom Wai-Hong Tamf9ded092015-05-20 05:37:22 +0800529 def toggle_development_switch(self):
530 """Toggle development switch on and off."""
531 self.enable_development_mode()
532 time.sleep(self.DEV_TOGGLE_DELAY)
533 self.disable_development_mode()
534
535
Craig Harrison6b36b122011-06-28 17:58:43 -0700536 def enable_development_mode(self):
537 """Enable development mode on device."""
538 self.set('dev_mode', 'on')
539
540
541 def disable_development_mode(self):
542 """Disable development mode on device."""
543 self.set('dev_mode', 'off')
544
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700545 def boot_devmode(self):
546 """Boot a dev-mode device that is powered off."""
Tom Wai-Hong Tam23869102012-01-17 15:41:30 +0800547 self.power_short_press()
Craig Harrison48997262011-06-27 14:31:10 -0700548 self.pass_devmode()
549
550
551 def pass_devmode(self):
552 """Pass through boot screens in dev-mode."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700553 time.sleep(Servo.BOOT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700554 self.ctrl_d()
Chrome Bot9a1137d2011-07-19 14:35:00 -0700555 time.sleep(Servo.BOOT_DELAY)
Craig Harrison48997262011-06-27 14:31:10 -0700556
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700557
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800558 def get_board(self):
Wai-Hong Tam0f904002017-09-19 15:52:22 -0700559 """Get the board connected to servod."""
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800560 return self._server.get_board()
561
562
Wai-Hong Tam0f904002017-09-19 15:52:22 -0700563 def get_base_board(self):
564 """Get the board of the base connected to servod."""
Wai-Hong Tam7f6169e2017-09-29 09:23:48 -0700565 try:
566 return self._server.get_base_board()
567 except xmlrpclib.Fault as e:
568 # TODO(waihong): Remove the following compatibility check when
569 # the new versions of hdctools are deployed.
570 if 'not supported' in str(e):
571 logging.warning('The servod is too old that get_base_board '
572 'not supported.')
573 return ''
574 raise
Wai-Hong Tam0f904002017-09-19 15:52:22 -0700575
576
Wai-Hong Tamcc399ee2017-12-08 12:43:28 -0800577 def get_ec_active_copy(self):
578 """Get the active copy of the EC image."""
579 return self.get('ec_active_copy')
580
581
Todd Brochefe72cb2012-07-11 19:58:53 -0700582 def _get_xmlrpclib_exception(self, xmlexc):
583 """Get meaningful exception string from xmlrpc.
584
585 Args:
586 xmlexc: xmlrpclib.Fault object
587
588 xmlrpclib.Fault.faultString has the following format:
589
590 <type 'exception type'>:'actual error message'
591
592 Parse and return the real exception from the servod side instead of the
593 less meaningful one like,
594 <Fault 1: "<type 'exceptions.AttributeError'>:'tca6416' object has no
595 attribute 'hw_driver'">
596
597 Returns:
598 string of underlying exception raised in servod.
599 """
600 return re.sub('^.*>:', '', xmlexc.faultString)
601
602
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700603 def get(self, gpio_name):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700604 """Get the value of a gpio from Servod.
605
606 @param gpio_name Name of the gpio.
607 """
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700608 assert gpio_name
Todd Brochefe72cb2012-07-11 19:58:53 -0700609 try:
Simran Basi1cbc5f22013-07-17 14:23:58 -0700610 return self._server.get(gpio_name)
Todd Brochefe72cb2012-07-11 19:58:53 -0700611 except xmlrpclib.Fault as e:
612 err_msg = "Getting '%s' :: %s" % \
613 (gpio_name, self._get_xmlrpclib_exception(e))
614 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700615
616
617 def set(self, gpio_name, gpio_value):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700618 """Set and check the value of a gpio using Servod.
619
620 @param gpio_name Name of the gpio.
621 @param gpio_value New setting for the gpio.
622 """
Chrome Bot9a1137d2011-07-19 14:35:00 -0700623 self.set_nocheck(gpio_name, gpio_value)
Todd Brochcf7c6652012-02-24 13:03:59 -0800624 retry_count = Servo.GET_RETRY_MAX
625 while gpio_value != self.get(gpio_name) and retry_count:
Ilja H. Friedel04be2bd2014-05-07 21:29:59 -0700626 logging.warning("%s != %s, retry %d", gpio_name, gpio_value,
Todd Brochcf7c6652012-02-24 13:03:59 -0800627 retry_count)
628 retry_count -= 1
629 time.sleep(Servo.SHORT_DELAY)
630 if not retry_count:
631 assert gpio_value == self.get(gpio_name), \
632 'Servo failed to set %s to %s' % (gpio_name, gpio_value)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700633
634
635 def set_nocheck(self, gpio_name, gpio_value):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700636 """Set the value of a gpio using Servod.
637
638 @param gpio_name Name of the gpio.
639 @param gpio_value New setting for the gpio.
640 """
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700641 assert gpio_name and gpio_value
Mary Ruthvendfa45342019-06-25 10:10:17 -0700642 logging.debug('Setting %s to %r', gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700643 try:
Simran Basi1cbc5f22013-07-17 14:23:58 -0700644 self._server.set(gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700645 except xmlrpclib.Fault as e:
Mary Ruthven73d3a352018-05-10 15:35:20 -0700646 err_msg = "Setting '%s' to %r :: %s" % \
Todd Brochefe72cb2012-07-11 19:58:53 -0700647 (gpio_name, gpio_value, self._get_xmlrpclib_exception(e))
648 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700649
650
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800651 def set_get_all(self, controls):
652 """Set &| get one or more control values.
653
654 @param controls: list of strings, controls to set &| get.
655
656 @raise: error.TestError in case error occurs setting/getting values.
657 """
658 rv = []
659 try:
Mary Ruthvendfa45342019-06-25 10:10:17 -0700660 logging.debug('Set/get all: %s', str(controls))
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800661 rv = self._server.set_get_all(controls)
662 except xmlrpclib.Fault as e:
663 # TODO(waihong): Remove the following backward compatibility when
664 # the new versions of hdctools are deployed.
665 if 'not supported' in str(e):
666 logging.warning('The servod is too old that set_get_all '
667 'not supported. Use set and get instead.')
668 for control in controls:
669 if ':' in control:
670 (name, value) = control.split(':')
671 if name == 'sleep':
672 time.sleep(float(value))
673 else:
674 self.set_nocheck(name, value)
675 rv.append(True)
676 else:
677 rv.append(self.get(name))
678 else:
679 err_msg = "Problem with '%s' :: %s" % \
680 (controls, self._get_xmlrpclib_exception(e))
681 raise error.TestFail(err_msg)
682 return rv
683
684
Jon Salzc88e5b62011-11-30 14:38:54 +0800685 # TODO(waihong) It may fail if multiple servo's are connected to the same
686 # host. Should look for a better way, like the USB serial name, to identify
687 # the USB device.
Simran Basi741b5d42012-05-18 11:27:15 -0700688 # TODO(sbasi) Remove this code from autoserv once firmware tests have been
689 # updated.
Kevin Chenga22c4a82016-10-07 14:13:25 -0700690 def probe_host_usb_dev(self, timeout=_USB_PROBE_TIMEOUT):
Jon Salzc88e5b62011-11-30 14:38:54 +0800691 """Probe the USB disk device plugged-in the servo from the host side.
692
Kevin Chengeb06fe72016-08-22 15:26:32 -0700693 It uses servod to discover if there is a usb device attached to it.
Jon Salzc88e5b62011-11-30 14:38:54 +0800694
Kevin Chenga22c4a82016-10-07 14:13:25 -0700695 @param timeout The timeout period when probing for the usb host device.
696
697 @return: String of USB disk path (e.g. '/dev/sdb') or None.
Jon Salzc88e5b62011-11-30 14:38:54 +0800698 """
Ruben Rodriguez Buchillon9a4bfc32018-10-16 20:46:25 +0800699 # Set up Servo's usb mux.
700 self.switch_usbkey('host')
Kevin Chenga22c4a82016-10-07 14:13:25 -0700701 return self._server.probe_host_usb_dev(timeout) or None
Jon Salzc88e5b62011-11-30 14:38:54 +0800702
703
Mike Truty49153d82012-08-21 22:27:30 -0500704 def image_to_servo_usb(self, image_path=None,
705 make_image_noninteractive=False):
706 """Install an image to the USB key plugged into the servo.
707
708 This method may copy any image to the servo USB key including a
709 recovery image or a test image. These images are frequently used
710 for test purposes such as restoring a corrupted image or conducting
711 an upgrade of ec/fw/kernel as part of a test of a specific image part.
712
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700713 @param image_path Path on the host to the recovery image.
714 @param make_image_noninteractive Make the recovery image
715 noninteractive, therefore the DUT
716 will reboot automatically after
717 installation.
Mike Truty49153d82012-08-21 22:27:30 -0500718 """
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700719 # We're about to start plugging/unplugging the USB key. We
720 # don't know the state of the DUT, or what it might choose
721 # to do to the device after hotplug. To avoid surprises,
722 # force the DUT to be off.
723 self._server.hwinit()
724 self._power_state.power_off()
Mike Truty49153d82012-08-21 22:27:30 -0500725
726 # Set up Servo's usb mux.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800727 self.switch_usbkey('host')
Mike Truty49153d82012-08-21 22:27:30 -0500728 if image_path:
Tom Wai-Hong Tam42f136d2012-10-26 11:11:23 +0800729 logging.info('Searching for usb device and copying image to it. '
730 'Please wait a few minutes...')
Mike Truty49153d82012-08-21 22:27:30 -0500731 if not self._server.download_image_to_usb(image_path):
732 logging.error('Failed to transfer requested image to USB. '
733 'Please take a look at Servo Logs.')
734 raise error.AutotestError('Download image to usb failed.')
735 if make_image_noninteractive:
736 logging.info('Making image noninteractive')
737 if not self._server.make_image_noninteractive():
738 logging.error('Failed to make image noninteractive. '
739 'Please take a look at Servo Logs.')
740
Kalin Stoyanovb3c11f32018-05-11 09:02:00 -0700741 def boot_in_recovery_mode(self):
742 """Boot host DUT in recovery mode."""
743 self._power_state.power_on(rec_mode=self._power_state.REC_ON)
744 self.switch_usbkey('dut')
745
Mike Truty49153d82012-08-21 22:27:30 -0500746
Simran Basi741b5d42012-05-18 11:27:15 -0700747 def install_recovery_image(self, image_path=None,
J. Richard Barnetteb6de7e32013-02-14 13:28:04 -0800748 make_image_noninteractive=False):
Dan Shic67f1332016-04-06 12:38:06 -0700749 """Install the recovery image specified by the path onto the DUT.
Jon Salzc88e5b62011-11-30 14:38:54 +0800750
751 This method uses google recovery mode to install a recovery image
752 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 +0800753 board specified by the usb_dev. If no image path is specified
Jon Salzc88e5b62011-11-30 14:38:54 +0800754 we use the recovery image already on the usb image.
755
Dan Shic67f1332016-04-06 12:38:06 -0700756 @param image_path: Path on the host to the recovery image.
757 @param make_image_noninteractive: Make the recovery image
758 noninteractive, therefore the DUT will reboot automatically
759 after installation.
Jon Salzc88e5b62011-11-30 14:38:54 +0800760 """
Mike Truty49153d82012-08-21 22:27:30 -0500761 self.image_to_servo_usb(image_path, make_image_noninteractive)
Kalin Stoyanovb3c11f32018-05-11 09:02:00 -0700762 self.boot_in_recovery_mode()
Jon Salzc88e5b62011-11-30 14:38:54 +0800763
764
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800765 def _scp_image(self, image_path):
766 """Copy image to the servo host.
767
768 When programming a firmware image on the DUT, the image must be
769 located on the host to which the servo device is connected. Sometimes
770 servo is controlled by a remote host, in this case the image needs to
771 be transferred to the remote host.
772
773 @param image_path: a string, name of the firmware image file to be
774 transferred.
775 @return: a string, full path name of the copied file on the remote.
776 """
777
778 dest_path = os.path.join('/tmp', os.path.basename(image_path))
Fang Deng5d518f42013-08-02 14:04:32 -0700779 self._servo_host.send_file(image_path, dest_path)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800780 return dest_path
781
782
Dan Shifecdaf42015-07-28 10:17:26 -0700783 def system(self, command, timeout=3600):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700784 """Execute the passed in command on the servod host.
785
786 @param command Command to be executed.
Dan Shifecdaf42015-07-28 10:17:26 -0700787 @param timeout Maximum number of seconds of runtime allowed. Default to
788 1 hour.
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700789 """
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800790 logging.info('Will execute on servo host: %s', command)
Fang Deng5d518f42013-08-02 14:04:32 -0700791 self._servo_host.run(command, timeout=timeout)
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800792
793
Dan Shifecdaf42015-07-28 10:17:26 -0700794 def system_output(self, command, timeout=3600,
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800795 ignore_status=False, args=()):
796 """Execute the passed in command on the servod host, return stdout.
797
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700798 @param command a string, the command to execute
799 @param timeout an int, max number of seconds to wait til command
Dan Shifecdaf42015-07-28 10:17:26 -0700800 execution completes. Default to 1 hour.
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700801 @param ignore_status a Boolean, if true - ignore command's nonzero exit
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800802 status, otherwise an exception will be thrown
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700803 @param args a tuple of strings, each becoming a separate command line
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800804 parameter for the command
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700805 @return command's stdout as a string.
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800806 """
Fang Deng5d518f42013-08-02 14:04:32 -0700807 return self._servo_host.run(command, timeout=timeout,
808 ignore_status=ignore_status,
809 args=args).stdout.strip()
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800810
811
Dan Shia5fef052015-05-18 23:28:47 -0700812 def get_servo_version(self):
813 """Get the version of the servo, e.g., servo_v2 or servo_v3.
814
815 @return: The version of the servo.
816
817 """
818 return self._server.get_version()
819
820
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800821 def _initialize_programmer(self, rw_only=False):
822 """Initialize the firmware programmer.
823
824 @param rw_only: True to initialize a programmer which only
825 programs the RW portions.
826 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700827 if self._programmer:
828 return
829 # Initialize firmware programmer
Dan Shia5fef052015-05-18 23:28:47 -0700830 servo_version = self.get_servo_version()
Dan Shi9cb0eec2014-06-03 09:04:50 -0700831 if servo_version.startswith('servo_v2'):
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700832 self._programmer = firmware_programmer.ProgrammerV2(self)
Wai-Hong Tamc36c4d22016-09-09 10:39:45 -0700833 self._programmer_rw = firmware_programmer.ProgrammerV2RwOnly(self)
Kevin Chengdf2e29f2016-09-09 02:31:22 -0700834 # Both servo v3 and v4 use the same programming methods so just leverage
835 # ProgrammerV3 for servo v4 as well.
836 elif (servo_version.startswith('servo_v3') or
837 servo_version.startswith('servo_v4')):
Dan Shia5fef052015-05-18 23:28:47 -0700838 self._programmer = firmware_programmer.ProgrammerV3(self)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800839 self._programmer_rw = firmware_programmer.ProgrammerV3RwOnly(self)
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700840 else:
841 raise error.TestError(
842 'No firmware programmer for servo version: %s' %
Congbin Guo42427612019-02-12 10:22:06 -0800843 servo_version)
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700844
845
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800846 def program_bios(self, image, rw_only=False):
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800847 """Program bios on DUT with given image.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800848
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800849 @param image: a string, file name of the BIOS image to program
850 on the DUT.
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800851 @param rw_only: True to only program the RW portion of BIOS.
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800852
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800853 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700854 self._initialize_programmer()
Ricky Liangc31aab32014-07-03 16:23:29 +0800855 if not self.is_localhost():
856 image = self._scp_image(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800857 if rw_only:
858 self._programmer_rw.program_bios(image)
859 else:
860 self._programmer.program_bios(image)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800861
862
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800863 def program_ec(self, image, rw_only=False):
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800864 """Program ec on DUT with given image.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800865
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800866 @param image: a string, file name of the EC image to program
867 on the DUT.
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800868 @param rw_only: True to only program the RW portion of EC.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800869
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800870 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700871 self._initialize_programmer()
Ricky Liangc31aab32014-07-03 16:23:29 +0800872 if not self.is_localhost():
873 image = self._scp_image(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800874 if rw_only:
Congbin Guo42427612019-02-12 10:22:06 -0800875 self._programmer_rw.program_ec(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800876 else:
Congbin Guo42427612019-02-12 10:22:06 -0800877 self._programmer.program_ec(image)
878
879
880 def _reprogram(self, tarball_path, firmware_name, image_candidates,
881 rw_only):
882 """Helper function to reprogram firmware for EC or BIOS.
883
884 @param tarball_path: The path of the downloaded build tarball.
885 @param: firmware_name: either 'EC' or 'BIOS'.
886 @param image_candidates: A tuple of the paths of image candidates.
887 @param rw_only: True to only install firmware to its RW portions. Keep
888 the RO portions unchanged.
889
890 @raise: TestError if cannot extract firmware from the tarball.
891 """
892 dest_dir = os.path.dirname(tarball_path)
893 image = _extract_image_from_tarball(tarball_path, dest_dir,
894 image_candidates)
895 if not image:
896 if firmware_name == 'EC':
897 logging.info('Not a Chrome EC, ignore re-programming it')
898 return
899 else:
900 raise error.TestError('Failed to extract the %s image from '
901 'tarball' % firmware_name)
902
903 logging.info('Will re-program %s %snow', firmware_name,
904 'RW ' if rw_only else '')
905
906 if firmware_name == 'EC':
907 self.program_ec(os.path.join(dest_dir, image), rw_only)
908 else:
909 self.program_bios(os.path.join(dest_dir, image), rw_only)
910
911
912 def program_firmware(self, model, tarball_path, rw_only=False):
913 """Program firmware (EC, if applied, and BIOS) of the DUT.
914
915 @param model: The DUT model name.
916 @param tarball_path: The path of the downloaded build tarball.
917 @param rw_only: True to only install firmware to its RW portions. Keep
918 the RO portions unchanged.
919 """
920 ap_image_candidates = ('image.bin', 'image-%s.bin' % model)
921 ec_image_candidates = ('ec.bin', '%s/ec.bin' % model)
922
923 self._reprogram(tarball_path, 'EC', ec_image_candidates, rw_only)
924 self._reprogram(tarball_path, 'BIOS', ap_image_candidates, rw_only)
925
926 self.get_power_state_controller().reset()
927 time.sleep(Servo.BOOT_DELAY)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800928
Fang Dengafb88142013-05-30 17:44:31 -0700929
930 def _switch_usbkey_power(self, power_state, detection_delay=False):
931 """Switch usbkey power.
932
933 This function switches usbkey power by setting the value of
934 'prtctl4_pwren'. If the power is already in the
935 requested state, this function simply returns.
936
937 @param power_state: A string, 'on' or 'off'.
938 @param detection_delay: A boolean value, if True, sleep
939 for |USB_DETECTION_DELAY| after switching
940 the power on.
941 """
Kevin Chenga22c4a82016-10-07 14:13:25 -0700942 # TODO(kevcheng): Forgive me for this terrible hack. This is just to
943 # handle beaglebones that haven't yet updated and have the
944 # safe_switch_usbkey_power RPC. I'll remove this once all beaglebones
945 # have been updated and also think about a better way to handle
946 # situations like this.
947 try:
948 self._server.safe_switch_usbkey_power(power_state)
949 except Exception:
950 self.set('prtctl4_pwren', power_state)
Fang Dengafb88142013-05-30 17:44:31 -0700951 if power_state == 'off':
952 time.sleep(self.USB_POWEROFF_DELAY)
953 elif detection_delay:
954 time.sleep(self.USB_DETECTION_DELAY)
955
956
957 def switch_usbkey(self, usb_state):
958 """Connect USB flash stick to either host or DUT, or turn USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800959
960 This function switches the servo multiplexer to provide electrical
Fang Dengafb88142013-05-30 17:44:31 -0700961 connection between the USB port J3 and either host or DUT side. It
962 can also be used to turn the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800963
Fang Dengafb88142013-05-30 17:44:31 -0700964 Switching to 'dut' or 'host' is accompanied by powercycling
965 of the USB stick, because it sometimes gets wedged if the mux
966 is switched while the stick power is on.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800967
Fang Dengafb88142013-05-30 17:44:31 -0700968 @param usb_state: A string, one of 'dut', 'host', or 'off'.
969 'dut' and 'host' indicate which side the
970 USB flash device is required to be connected to.
971 'off' indicates turning the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800972
Fang Dengafb88142013-05-30 17:44:31 -0700973 @raise: error.TestError in case the parameter is not 'dut'
974 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800975 """
Fang Dengafb88142013-05-30 17:44:31 -0700976 if self.get_usbkey_direction() == usb_state:
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800977 return
978
Fang Dengafb88142013-05-30 17:44:31 -0700979 if usb_state == 'off':
Dan Shia5fef052015-05-18 23:28:47 -0700980 self._switch_usbkey_power('off')
981 self._usb_state = usb_state
982 return
Fang Dengafb88142013-05-30 17:44:31 -0700983 elif usb_state == 'host':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800984 mux_direction = 'servo_sees_usbkey'
Fang Dengafb88142013-05-30 17:44:31 -0700985 elif usb_state == 'dut':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800986 mux_direction = 'dut_sees_usbkey'
987 else:
Fang Dengafb88142013-05-30 17:44:31 -0700988 raise error.TestError('Unknown USB state request: %s' % usb_state)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800989
Fang Dengafb88142013-05-30 17:44:31 -0700990 self._switch_usbkey_power('off')
Kevin Chenga22c4a82016-10-07 14:13:25 -0700991 # TODO(kevcheng): Forgive me for this terrible hack. This is just to
992 # handle beaglebones that haven't yet updated and have the
993 # safe_switch_usbkey RPC. I'll remove this once all beaglebones have
994 # been updated and also think about a better way to handle situations
995 # like this.
996 try:
997 self._server.safe_switch_usbkey(mux_direction)
998 except Exception:
999 self.set('usb_mux_sel1', mux_direction)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001000 time.sleep(self.USB_POWEROFF_DELAY)
Fang Dengafb88142013-05-30 17:44:31 -07001001 self._switch_usbkey_power('on', usb_state == 'host')
1002 self._usb_state = usb_state
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001003
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001004
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001005 def get_usbkey_direction(self):
Fang Dengafb88142013-05-30 17:44:31 -07001006 """Get which side USB is connected to or 'off' if usb power is off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001007
Fang Dengafb88142013-05-30 17:44:31 -07001008 @return: A string, one of 'dut', 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001009 """
Fang Dengafb88142013-05-30 17:44:31 -07001010 if not self._usb_state:
1011 if self.get('prtctl4_pwren') == 'off':
1012 self._usb_state = 'off'
1013 elif self.get('usb_mux_sel1').startswith('dut'):
1014 self._usb_state = 'dut'
Vadim Bendeburye7bd9362012-12-19 14:35:20 -08001015 else:
Fang Dengafb88142013-05-30 17:44:31 -07001016 self._usb_state = 'host'
1017 return self._usb_state
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001018
1019
Wai-Hong Tam60377262018-03-01 10:55:39 -08001020 def set_servo_v4_role(self, role):
1021 """Set the power role of servo v4, either 'src' or 'snk'.
1022
1023 It does nothing if not a servo v4.
1024
1025 @param role: Power role for DUT port on servo v4, either 'src' or 'snk'.
1026 """
1027 servo_version = self.get_servo_version()
1028 if servo_version.startswith('servo_v4'):
1029 value = self.get('servo_v4_role')
1030 if value != role:
1031 self.set_nocheck('servo_v4_role', role)
1032 else:
1033 logging.debug('Already in the role: %s.', role)
1034 else:
1035 logging.debug('Not a servo v4, unable to set role to %s.', role)
1036
1037
Congbin Guofc3b8962019-03-22 17:38:46 -07001038 @property
1039 def uart_logs_dir(self):
1040 """Return the directory to save UART logs."""
1041 return self._uart.logs_dir if self._uart else ""
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001042
Congbin Guofc3b8962019-03-22 17:38:46 -07001043
1044 @uart_logs_dir.setter
1045 def uart_logs_dir(self, logs_dir):
1046 """Set directory to save UART logs.
1047
1048 @param logs_dir String of directory name."""
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001049 if self._uart:
Congbin Guofc3b8962019-03-22 17:38:46 -07001050 self._uart.logs_dir = logs_dir
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001051
1052
1053 def close(self):
1054 """Close the servo object."""
1055 if self._uart:
1056 self._uart.stop_capture()
Congbin Guofc3b8962019-03-22 17:38:46 -07001057 self._uart.dump()
Congbin Guoa1f9cba2018-07-03 11:36:59 -07001058 self._uart = None