blob: aca058293135c7c1cefe02194bec5e628b0979f7 [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
Vadim Bendeburyb80ba592012-12-07 15:02:34 -08008import os
9
Vic Yang3a7cf602012-11-07 17:28:39 +080010import logging, re, time, xmlrpclib
Vadim Bendeburyb80ba592012-12-07 15:02:34 -080011
Simran Basi741b5d42012-05-18 11:27:15 -070012from autotest_lib.client.common_lib import error
Ricky Liangc31aab32014-07-03 16:23:29 +080013from autotest_lib.server.cros.servo import firmware_programmer
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070014
Kevin Chenga22c4a82016-10-07 14:13:25 -070015# Time to wait when probing for a usb device, it takes on avg 17 seconds
16# to do a full probe.
17_USB_PROBE_TIMEOUT = 40
18
J. Richard Barnette41320ee2013-03-11 13:00:13 -070019
J. Richard Barnette0c9c5882014-06-11 15:27:05 -070020class _PowerStateController(object):
21
22 """Class to provide board-specific power operations.
23
24 This class is responsible for "power on" and "power off"
25 operations that can operate without making assumptions in
26 advance about board state. It offers an interface that
27 abstracts out the different sequences required for different
28 board types.
29
30 """
31
32 # Constants acceptable to be passed for the `rec_mode` parameter
33 # to power_on().
34 #
35 # REC_ON: Boot the DUT in recovery mode, i.e. boot from USB or
36 # SD card.
37 # REC_OFF: Boot in normal mode, i.e. boot from internal storage.
38
39 REC_ON = 'rec'
40 REC_OFF = 'on'
41
42 # Delay in seconds needed between asserting and de-asserting
43 # warm reset.
44 _RESET_HOLD_TIME = 0.5
45
46 def __init__(self, servo):
47 """Initialize the power state control.
48
49 @param servo Servo object providing the underlying `set` and `get`
50 methods for the target controls.
51
52 """
53 self._servo = servo
54
55 def reset(self):
56 """Force the DUT to reset.
57
58 The DUT is guaranteed to be on at the end of this call,
59 regardless of its previous state, provided that there is
60 working OS software. This also guarantees that the EC has
61 been restarted.
62
63 """
64 self._servo.set_nocheck('power_state', 'reset')
65
66 def warm_reset(self):
67 """Apply warm reset to the DUT.
68
69 This asserts, then de-asserts the 'warm_reset' signal.
70 Generally, this causes the board to restart.
71
72 """
73 self._servo.set_get_all(['warm_reset:on',
74 'sleep:%.4f' % self._RESET_HOLD_TIME,
75 'warm_reset:off'])
76
J. Richard Barnette0c9c5882014-06-11 15:27:05 -070077 def power_off(self):
78 """Force the DUT to power off.
79
80 The DUT is guaranteed to be off at the end of this call,
81 regardless of its previous state, provided that there is
82 working EC and boot firmware. There is no requirement for
83 working OS software.
84
85 """
86 self._servo.set_nocheck('power_state', 'off')
87
88 def power_on(self, rec_mode=REC_OFF):
89 """Force the DUT to power on.
90
91 Prior to calling this function, the DUT must be powered off,
92 e.g. with a call to `power_off()`.
93
94 At power on, recovery mode is set as specified by the
95 corresponding argument. When booting with recovery mode on, it
96 is the caller's responsibility to unplug/plug in a bootable
97 external storage device.
98
99 If the DUT requires a delay after powering on but before
100 processing inputs such as USB stick insertion, the delay is
101 handled by this method; the caller is not responsible for such
102 delays.
103
104 @param rec_mode Setting of recovery mode to be applied at
105 power on. default: REC_OFF aka 'off'
106
107 """
108 self._servo.set_nocheck('power_state', rec_mode)
109
110
J. Richard Barnette384056b2012-04-16 11:04:46 -0700111class Servo(object):
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700112
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700113 """Manages control of a Servo board.
114
115 Servo is a board developed by hardware group to aide in the debug and
116 control of various partner devices. Servo's features include the simulation
117 of pressing the power button, closing the lid, and pressing Ctrl-d. This
118 class manages setting up and communicating with a servo demon (servod)
119 process. It provides both high-level functions for common servo tasks and
120 low-level functions for directly setting and reading gpios.
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700121
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700122 """
123
Chrome Bot9a1137d2011-07-19 14:35:00 -0700124 # Power button press delays in seconds.
J. Richard Barnetted2e4cbd2012-06-29 12:18:40 -0700125 #
J. Richard Barnette5383f072012-07-26 17:35:40 -0700126 # The EC specification says that 8.0 seconds should be enough
127 # for the long power press. However, some platforms need a bit
128 # more time. Empirical testing has found these requirements:
129 # Alex: 8.2 seconds
130 # ZGB: 8.5 seconds
131 # The actual value is set to the largest known necessary value.
132 #
133 # TODO(jrbarnette) Being generous is the right thing to do for
134 # existing platforms, but if this code is to be used for
135 # qualification of new hardware, we should be less generous.
Chrome Bot9a1137d2011-07-19 14:35:00 -0700136 SHORT_DELAY = 0.1
J. Richard Barnette5383f072012-07-26 17:35:40 -0700137
Todd Broch31c82502011-08-29 08:14:39 -0700138 # Maximum number of times to re-read power button on release.
Todd Brochcf7c6652012-02-24 13:03:59 -0800139 GET_RETRY_MAX = 10
Chrome Bot9a1137d2011-07-19 14:35:00 -0700140
J. Richard Barnette5383f072012-07-26 17:35:40 -0700141 # Delays to deal with DUT state transitions.
Chrome Bot9a1137d2011-07-19 14:35:00 -0700142 SLEEP_DELAY = 6
143 BOOT_DELAY = 10
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700144
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700145 # Default minimum time interval between 'press' and 'release'
146 # keyboard events.
Vic Yang0aca1c22012-11-19 18:33:56 -0800147 SERVO_KEY_PRESS_DELAY = 0.1
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700148
Tom Wai-Hong Tam93b5c092015-05-14 02:50:43 +0800149 # Time to toggle recovery switch on and off.
150 REC_TOGGLE_DELAY = 0.1
151
Tom Wai-Hong Tamf9ded092015-05-20 05:37:22 +0800152 # Time to toggle development switch on and off.
153 DEV_TOGGLE_DELAY = 0.1
154
Jon Salzc88e5b62011-11-30 14:38:54 +0800155 # Time between an usb disk plugged-in and detected in the system.
156 USB_DETECTION_DELAY = 10
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800157 # Time to keep USB power off before and after USB mux direction is changed
158 USB_POWEROFF_DELAY = 2
Jon Salzc88e5b62011-11-30 14:38:54 +0800159
Simran Basib7850bb2013-07-24 12:33:42 -0700160 # Time to wait before timing out on servo initialization.
161 INIT_TIMEOUT_SECS = 10
Simran Basi59331342013-07-12 12:06:29 -0700162
J. Richard Barnette67ccb872012-04-19 16:34:56 -0700163
Ricky Liang0dd379c2014-04-23 16:29:08 +0800164 def __init__(self, servo_host, servo_serial=None):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700165 """Sets up the servo communication infrastructure.
166
Fang Deng5d518f42013-08-02 14:04:32 -0700167 @param servo_host: A ServoHost object representing
168 the host running servod.
Ricky Liang0dd379c2014-04-23 16:29:08 +0800169 @param servo_serial: Serial number of the servo board.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700170 """
Fang Deng5d518f42013-08-02 14:04:32 -0700171 # TODO(fdeng): crbug.com/298379
172 # We should move servo_host object out of servo object
173 # to minimize the dependencies on the rest of Autotest.
174 self._servo_host = servo_host
Ricky Liang0dd379c2014-04-23 16:29:08 +0800175 self._servo_serial = servo_serial
Richard Barnette180efe62016-12-02 23:20:44 +0000176 self._server = servo_host.get_servod_server_proxy()
J. Richard Barnette0c9c5882014-06-11 15:27:05 -0700177 self._power_state = _PowerStateController(self)
Fang Dengafb88142013-05-30 17:44:31 -0700178 self._usb_state = None
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700179 self._programmer = None
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800180
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800181
Ricky Liang0dd379c2014-04-23 16:29:08 +0800182 @property
183 def servo_serial(self):
184 """Returns the serial number of the servo board."""
185 return self._servo_serial
186
187
Tom Wai-Hong Tam22ee0e52013-04-03 13:36:39 +0800188 def get_power_state_controller(self):
J. Richard Barnette75136b32013-03-26 13:38:44 -0700189 """Return the power state controller for this Servo.
190
191 The power state controller provides board-independent
192 interfaces for reset, power-on, power-off operations.
193
194 """
195 return self._power_state
196
Fang Deng5d518f42013-08-02 14:04:32 -0700197
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700198 def initialize_dut(self, cold_reset=False):
199 """Initializes a dut for testing purposes.
200
201 This sets various servo signals back to default values
202 appropriate for the target board. By default, if the DUT
203 is already on, it stays on. If the DUT is powered off
204 before initialization, its state afterward is unspecified.
205
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700206 Rationale: Basic initialization of servo sets the lid open,
207 when there is a lid. This operation won't affect powered on
208 units; however, setting the lid open may power on a unit
J. Richard Barnette75136b32013-03-26 13:38:44 -0700209 that's off, depending on the board type and previous state
210 of the device.
211
J. Richard Barnette4b6af0d2014-06-05 09:57:20 -0700212 If `cold_reset` is a true value, the DUT and its EC will be
213 reset, and the DUT rebooted in normal mode.
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700214
215 @param cold_reset If True, cold reset the device after
216 initialization.
217 """
218 self._server.hwinit()
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700219 self.set('usb_mux_oe1', 'on')
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700220 self._usb_state = None
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700221 self.switch_usbkey('off')
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700222 if cold_reset:
J. Richard Barnette4b6af0d2014-06-05 09:57:20 -0700223 self._power_state.reset()
J. Richard Barnette1777f5d2014-09-03 15:18:19 -0700224 logging.debug('Servo initialized, version is %s',
225 self._server.get_version())
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700226
227
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800228 def is_localhost(self):
229 """Is the servod hosted locally?
230
231 Returns:
232 True if local hosted; otherwise, False.
233 """
Fang Deng5d518f42013-08-02 14:04:32 -0700234 return self._servo_host.is_localhost()
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800235
236
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700237 def power_long_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700238 """Simulate a long power button press."""
J. Richard Barnettef12bff22012-07-18 14:41:06 -0700239 # After a long power press, the EC may ignore the next power
240 # button press (at least on Alex). To guarantee that this
241 # won't happen, we need to allow the EC one second to
242 # collect itself.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800243 self._server.power_long_press()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700244
245
246 def power_normal_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700247 """Simulate a normal power button press."""
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800248 self._server.power_normal_press()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700249
250
251 def power_short_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700252 """Simulate a short power button press."""
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800253 self._server.power_short_press()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700254
255
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800256 def power_key(self, press_secs=''):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700257 """Simulate a power button press.
258
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800259 @param press_secs : Str. Time to press key.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700260 """
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800261 self._server.power_key(press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700262
263
264 def lid_open(self):
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800265 """Simulate opening the lid and raise exception if all attempts fail"""
266 self.set('lid_open', 'yes')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700267
268
269 def lid_close(self):
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800270 """Simulate closing the lid and raise exception if all attempts fail
Craig Harrison48997262011-06-27 14:31:10 -0700271
272 Waits 6 seconds to ensure the device is fully asleep before returning.
273 """
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800274 self.set('lid_open', 'no')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700275 time.sleep(Servo.SLEEP_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700276
Shelley Chenc26575a2015-09-18 10:56:16 -0700277 def volume_up(self, timeout=300):
Kevin Chenge448a8b2016-09-09 10:18:11 -0700278 """Simulate pushing the volume down button.
279
280 @param timeout: Timeout for setting the volume.
281 """
Shelley Chenc26575a2015-09-18 10:56:16 -0700282 self.set_get_all(['volume_up:yes',
283 'sleep:%.4f' % self.SERVO_KEY_PRESS_DELAY,
284 'volume_up:no'])
285 # we need to wait for commands to take effect before moving on
286 time_left = float(timeout)
287 while time_left > 0.0:
288 value = self.get('volume_up')
289 if value == 'no':
290 return
291 time.sleep(self.SHORT_DELAY)
292 time_left = time_left - self.SHORT_DELAY
293 raise error.TestFail("Failed setting volume_up to no")
294
295 def volume_down(self, timeout=300):
Kevin Chenge448a8b2016-09-09 10:18:11 -0700296 """Simulate pushing the volume down button.
297
298 @param timeout: Timeout for setting the volume.
299 """
Shelley Chenc26575a2015-09-18 10:56:16 -0700300 self.set_get_all(['volume_down:yes',
301 'sleep:%.4f' % self.SERVO_KEY_PRESS_DELAY,
302 'volume_down:no'])
303 # we need to wait for commands to take effect before moving on
304 time_left = float(timeout)
305 while time_left > 0.0:
306 value = self.get('volume_down')
307 if value == 'no':
308 return
309 time.sleep(self.SHORT_DELAY)
310 time_left = time_left - self.SHORT_DELAY
311 raise error.TestFail("Failed setting volume_down to no")
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700312
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800313 def ctrl_d(self, press_secs=''):
314 """Simulate Ctrl-d simultaneous button presses.
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800315
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800316 @param press_secs : Str. Time to press key.
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800317 """
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800318 self._server.ctrl_d(press_secs)
Gediminas Ramanauskas9da80f22012-11-14 12:59:43 -0800319
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800320
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800321 def ctrl_u(self):
322 """Simulate Ctrl-u simultaneous button presses.
323
324 @param press_secs : Str. Time to press key.
325 """
326 self._server.ctrl_u()
Todd Broch9dfc3a82011-11-01 08:09:28 -0700327
328
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800329 def ctrl_enter(self, press_secs=''):
330 """Simulate Ctrl-enter simultaneous button presses.
331
332 @param press_secs : Str. Time to press key.
333 """
334 self._server.ctrl_enter(press_secs)
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -0700335
336
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800337 def d_key(self, press_secs=''):
338 """Simulate Enter key button press.
339
340 @param press_secs : Str. Time to press key.
341 """
342 self._server.d_key(press_secs)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700343
344
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800345 def ctrl_key(self, press_secs=''):
346 """Simulate Enter key button press.
347
348 @param press_secs : Str. Time to press key.
349 """
350 self._server.ctrl_key(press_secs)
Todd Broch9753bd42012-03-21 10:15:08 -0700351
352
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800353 def enter_key(self, press_secs=''):
354 """Simulate Enter key button press.
355
356 @param press_secs : Str. Time to press key.
357 """
358 self._server.enter_key(press_secs)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700359
360
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800361 def refresh_key(self, press_secs=''):
362 """Simulate Refresh key (F3) button press.
363
364 @param press_secs : Str. Time to press key.
365 """
366 self._server.refresh_key(press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700367
368
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800369 def ctrl_refresh_key(self, press_secs=''):
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800370 """Simulate Ctrl and Refresh (F3) simultaneous press.
371
372 This key combination is an alternative of Space key.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800373
374 @param press_secs : Str. Time to press key.
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800375 """
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800376 self._server.ctrl_refresh_key(press_secs)
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800377
378
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800379 def imaginary_key(self, press_secs=''):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700380 """Simulate imaginary key button press.
381
382 Maps to a key that doesn't physically exist.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800383
384 @param press_secs : Str. Time to press key.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700385 """
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800386 self._server.imaginary_key(press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700387
388
Vincent Palatine7dc9282016-07-14 11:31:58 +0200389 def sysrq_x(self, press_secs=''):
390 """Simulate Alt VolumeUp X simulataneous press.
391
392 This key combination is the kernel system request (sysrq) X.
393
394 @param press_secs : Str. Time to press key.
395 """
396 self._server.sysrq_x(press_secs)
397
398
Tom Wai-Hong Tam93b5c092015-05-14 02:50:43 +0800399 def toggle_recovery_switch(self):
400 """Toggle recovery switch on and off."""
401 self.enable_recovery_mode()
402 time.sleep(self.REC_TOGGLE_DELAY)
403 self.disable_recovery_mode()
404
405
Craig Harrison6b36b122011-06-28 17:58:43 -0700406 def enable_recovery_mode(self):
407 """Enable recovery mode on device."""
408 self.set('rec_mode', 'on')
409
410
411 def disable_recovery_mode(self):
412 """Disable recovery mode on device."""
413 self.set('rec_mode', 'off')
414
415
Tom Wai-Hong Tamf9ded092015-05-20 05:37:22 +0800416 def toggle_development_switch(self):
417 """Toggle development switch on and off."""
418 self.enable_development_mode()
419 time.sleep(self.DEV_TOGGLE_DELAY)
420 self.disable_development_mode()
421
422
Craig Harrison6b36b122011-06-28 17:58:43 -0700423 def enable_development_mode(self):
424 """Enable development mode on device."""
425 self.set('dev_mode', 'on')
426
427
428 def disable_development_mode(self):
429 """Disable development mode on device."""
430 self.set('dev_mode', 'off')
431
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700432 def boot_devmode(self):
433 """Boot a dev-mode device that is powered off."""
Tom Wai-Hong Tam23869102012-01-17 15:41:30 +0800434 self.power_short_press()
Craig Harrison48997262011-06-27 14:31:10 -0700435 self.pass_devmode()
436
437
438 def pass_devmode(self):
439 """Pass through boot screens in dev-mode."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700440 time.sleep(Servo.BOOT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700441 self.ctrl_d()
Chrome Bot9a1137d2011-07-19 14:35:00 -0700442 time.sleep(Servo.BOOT_DELAY)
Craig Harrison48997262011-06-27 14:31:10 -0700443
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700444
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800445 def get_board(self):
Wai-Hong Tam0f904002017-09-19 15:52:22 -0700446 """Get the board connected to servod."""
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800447 return self._server.get_board()
448
449
Wai-Hong Tam0f904002017-09-19 15:52:22 -0700450 def get_base_board(self):
451 """Get the board of the base connected to servod."""
Wai-Hong Tam7f6169e2017-09-29 09:23:48 -0700452 try:
453 return self._server.get_base_board()
454 except xmlrpclib.Fault as e:
455 # TODO(waihong): Remove the following compatibility check when
456 # the new versions of hdctools are deployed.
457 if 'not supported' in str(e):
458 logging.warning('The servod is too old that get_base_board '
459 'not supported.')
460 return ''
461 raise
Wai-Hong Tam0f904002017-09-19 15:52:22 -0700462
463
Wai-Hong Tamcc399ee2017-12-08 12:43:28 -0800464 def get_ec_active_copy(self):
465 """Get the active copy of the EC image."""
466 return self.get('ec_active_copy')
467
468
Todd Brochefe72cb2012-07-11 19:58:53 -0700469 def _get_xmlrpclib_exception(self, xmlexc):
470 """Get meaningful exception string from xmlrpc.
471
472 Args:
473 xmlexc: xmlrpclib.Fault object
474
475 xmlrpclib.Fault.faultString has the following format:
476
477 <type 'exception type'>:'actual error message'
478
479 Parse and return the real exception from the servod side instead of the
480 less meaningful one like,
481 <Fault 1: "<type 'exceptions.AttributeError'>:'tca6416' object has no
482 attribute 'hw_driver'">
483
484 Returns:
485 string of underlying exception raised in servod.
486 """
487 return re.sub('^.*>:', '', xmlexc.faultString)
488
489
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700490 def get(self, gpio_name):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700491 """Get the value of a gpio from Servod.
492
493 @param gpio_name Name of the gpio.
494 """
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700495 assert gpio_name
Todd Brochefe72cb2012-07-11 19:58:53 -0700496 try:
Simran Basi1cbc5f22013-07-17 14:23:58 -0700497 return self._server.get(gpio_name)
Todd Brochefe72cb2012-07-11 19:58:53 -0700498 except xmlrpclib.Fault as e:
499 err_msg = "Getting '%s' :: %s" % \
500 (gpio_name, self._get_xmlrpclib_exception(e))
501 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700502
503
504 def set(self, gpio_name, gpio_value):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700505 """Set and check the value of a gpio using Servod.
506
507 @param gpio_name Name of the gpio.
508 @param gpio_value New setting for the gpio.
509 """
Chrome Bot9a1137d2011-07-19 14:35:00 -0700510 self.set_nocheck(gpio_name, gpio_value)
Todd Brochcf7c6652012-02-24 13:03:59 -0800511 retry_count = Servo.GET_RETRY_MAX
512 while gpio_value != self.get(gpio_name) and retry_count:
Ilja H. Friedel04be2bd2014-05-07 21:29:59 -0700513 logging.warning("%s != %s, retry %d", gpio_name, gpio_value,
Todd Brochcf7c6652012-02-24 13:03:59 -0800514 retry_count)
515 retry_count -= 1
516 time.sleep(Servo.SHORT_DELAY)
517 if not retry_count:
518 assert gpio_value == self.get(gpio_name), \
519 'Servo failed to set %s to %s' % (gpio_name, gpio_value)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700520
521
522 def set_nocheck(self, gpio_name, gpio_value):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700523 """Set the value of a gpio using Servod.
524
525 @param gpio_name Name of the gpio.
526 @param gpio_value New setting for the gpio.
527 """
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700528 assert gpio_name and gpio_value
Mary Ruthven2f72e2a2018-05-01 17:12:58 -0700529 logging.info('Setting %s to %r', gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700530 try:
Simran Basi1cbc5f22013-07-17 14:23:58 -0700531 self._server.set(gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700532 except xmlrpclib.Fault as e:
Mary Ruthven73d3a352018-05-10 15:35:20 -0700533 err_msg = "Setting '%s' to %r :: %s" % \
Todd Brochefe72cb2012-07-11 19:58:53 -0700534 (gpio_name, gpio_value, self._get_xmlrpclib_exception(e))
535 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700536
537
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800538 def set_get_all(self, controls):
539 """Set &| get one or more control values.
540
541 @param controls: list of strings, controls to set &| get.
542
543 @raise: error.TestError in case error occurs setting/getting values.
544 """
545 rv = []
546 try:
Vic Yangcad9acb2013-05-21 14:02:05 +0800547 logging.info('Set/get all: %s', str(controls))
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800548 rv = self._server.set_get_all(controls)
549 except xmlrpclib.Fault as e:
550 # TODO(waihong): Remove the following backward compatibility when
551 # the new versions of hdctools are deployed.
552 if 'not supported' in str(e):
553 logging.warning('The servod is too old that set_get_all '
554 'not supported. Use set and get instead.')
555 for control in controls:
556 if ':' in control:
557 (name, value) = control.split(':')
558 if name == 'sleep':
559 time.sleep(float(value))
560 else:
561 self.set_nocheck(name, value)
562 rv.append(True)
563 else:
564 rv.append(self.get(name))
565 else:
566 err_msg = "Problem with '%s' :: %s" % \
567 (controls, self._get_xmlrpclib_exception(e))
568 raise error.TestFail(err_msg)
569 return rv
570
571
Jon Salzc88e5b62011-11-30 14:38:54 +0800572 # TODO(waihong) It may fail if multiple servo's are connected to the same
573 # host. Should look for a better way, like the USB serial name, to identify
574 # the USB device.
Simran Basi741b5d42012-05-18 11:27:15 -0700575 # TODO(sbasi) Remove this code from autoserv once firmware tests have been
576 # updated.
Kevin Chenga22c4a82016-10-07 14:13:25 -0700577 def probe_host_usb_dev(self, timeout=_USB_PROBE_TIMEOUT):
Jon Salzc88e5b62011-11-30 14:38:54 +0800578 """Probe the USB disk device plugged-in the servo from the host side.
579
Kevin Chengeb06fe72016-08-22 15:26:32 -0700580 It uses servod to discover if there is a usb device attached to it.
Jon Salzc88e5b62011-11-30 14:38:54 +0800581
Kevin Chenga22c4a82016-10-07 14:13:25 -0700582 @param timeout The timeout period when probing for the usb host device.
583
584 @return: String of USB disk path (e.g. '/dev/sdb') or None.
Jon Salzc88e5b62011-11-30 14:38:54 +0800585 """
Kevin Chenga22c4a82016-10-07 14:13:25 -0700586 return self._server.probe_host_usb_dev(timeout) or None
Jon Salzc88e5b62011-11-30 14:38:54 +0800587
588
Mike Truty49153d82012-08-21 22:27:30 -0500589 def image_to_servo_usb(self, image_path=None,
590 make_image_noninteractive=False):
591 """Install an image to the USB key plugged into the servo.
592
593 This method may copy any image to the servo USB key including a
594 recovery image or a test image. These images are frequently used
595 for test purposes such as restoring a corrupted image or conducting
596 an upgrade of ec/fw/kernel as part of a test of a specific image part.
597
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700598 @param image_path Path on the host to the recovery image.
599 @param make_image_noninteractive Make the recovery image
600 noninteractive, therefore the DUT
601 will reboot automatically after
602 installation.
Mike Truty49153d82012-08-21 22:27:30 -0500603 """
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700604 # We're about to start plugging/unplugging the USB key. We
605 # don't know the state of the DUT, or what it might choose
606 # to do to the device after hotplug. To avoid surprises,
607 # force the DUT to be off.
608 self._server.hwinit()
609 self._power_state.power_off()
Mike Truty49153d82012-08-21 22:27:30 -0500610
611 # Set up Servo's usb mux.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800612 self.switch_usbkey('host')
Mike Truty49153d82012-08-21 22:27:30 -0500613 if image_path:
Tom Wai-Hong Tam42f136d2012-10-26 11:11:23 +0800614 logging.info('Searching for usb device and copying image to it. '
615 'Please wait a few minutes...')
Mike Truty49153d82012-08-21 22:27:30 -0500616 if not self._server.download_image_to_usb(image_path):
617 logging.error('Failed to transfer requested image to USB. '
618 'Please take a look at Servo Logs.')
619 raise error.AutotestError('Download image to usb failed.')
620 if make_image_noninteractive:
621 logging.info('Making image noninteractive')
622 if not self._server.make_image_noninteractive():
623 logging.error('Failed to make image noninteractive. '
624 'Please take a look at Servo Logs.')
625
Kalin Stoyanovb3c11f32018-05-11 09:02:00 -0700626 def boot_in_recovery_mode(self):
627 """Boot host DUT in recovery mode."""
628 self._power_state.power_on(rec_mode=self._power_state.REC_ON)
629 self.switch_usbkey('dut')
630
Mike Truty49153d82012-08-21 22:27:30 -0500631
Simran Basi741b5d42012-05-18 11:27:15 -0700632 def install_recovery_image(self, image_path=None,
J. Richard Barnetteb6de7e32013-02-14 13:28:04 -0800633 make_image_noninteractive=False):
Dan Shic67f1332016-04-06 12:38:06 -0700634 """Install the recovery image specified by the path onto the DUT.
Jon Salzc88e5b62011-11-30 14:38:54 +0800635
636 This method uses google recovery mode to install a recovery image
637 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 +0800638 board specified by the usb_dev. If no image path is specified
Jon Salzc88e5b62011-11-30 14:38:54 +0800639 we use the recovery image already on the usb image.
640
Dan Shic67f1332016-04-06 12:38:06 -0700641 @param image_path: Path on the host to the recovery image.
642 @param make_image_noninteractive: Make the recovery image
643 noninteractive, therefore the DUT will reboot automatically
644 after installation.
Jon Salzc88e5b62011-11-30 14:38:54 +0800645 """
Mike Truty49153d82012-08-21 22:27:30 -0500646 self.image_to_servo_usb(image_path, make_image_noninteractive)
Kalin Stoyanovb3c11f32018-05-11 09:02:00 -0700647 self.boot_in_recovery_mode()
Jon Salzc88e5b62011-11-30 14:38:54 +0800648
649
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800650 def _scp_image(self, image_path):
651 """Copy image to the servo host.
652
653 When programming a firmware image on the DUT, the image must be
654 located on the host to which the servo device is connected. Sometimes
655 servo is controlled by a remote host, in this case the image needs to
656 be transferred to the remote host.
657
658 @param image_path: a string, name of the firmware image file to be
659 transferred.
660 @return: a string, full path name of the copied file on the remote.
661 """
662
663 dest_path = os.path.join('/tmp', os.path.basename(image_path))
Fang Deng5d518f42013-08-02 14:04:32 -0700664 self._servo_host.send_file(image_path, dest_path)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800665 return dest_path
666
667
Dan Shifecdaf42015-07-28 10:17:26 -0700668 def system(self, command, timeout=3600):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700669 """Execute the passed in command on the servod host.
670
671 @param command Command to be executed.
Dan Shifecdaf42015-07-28 10:17:26 -0700672 @param timeout Maximum number of seconds of runtime allowed. Default to
673 1 hour.
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700674 """
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800675 logging.info('Will execute on servo host: %s', command)
Fang Deng5d518f42013-08-02 14:04:32 -0700676 self._servo_host.run(command, timeout=timeout)
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800677
678
Dan Shifecdaf42015-07-28 10:17:26 -0700679 def system_output(self, command, timeout=3600,
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800680 ignore_status=False, args=()):
681 """Execute the passed in command on the servod host, return stdout.
682
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700683 @param command a string, the command to execute
684 @param timeout an int, max number of seconds to wait til command
Dan Shifecdaf42015-07-28 10:17:26 -0700685 execution completes. Default to 1 hour.
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700686 @param ignore_status a Boolean, if true - ignore command's nonzero exit
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800687 status, otherwise an exception will be thrown
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700688 @param args a tuple of strings, each becoming a separate command line
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800689 parameter for the command
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700690 @return command's stdout as a string.
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800691 """
Fang Deng5d518f42013-08-02 14:04:32 -0700692 return self._servo_host.run(command, timeout=timeout,
693 ignore_status=ignore_status,
694 args=args).stdout.strip()
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800695
696
Dan Shia5fef052015-05-18 23:28:47 -0700697 def get_servo_version(self):
698 """Get the version of the servo, e.g., servo_v2 or servo_v3.
699
700 @return: The version of the servo.
701
702 """
703 return self._server.get_version()
704
705
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800706 def _initialize_programmer(self, rw_only=False):
707 """Initialize the firmware programmer.
708
709 @param rw_only: True to initialize a programmer which only
710 programs the RW portions.
711 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700712 if self._programmer:
713 return
714 # Initialize firmware programmer
Dan Shia5fef052015-05-18 23:28:47 -0700715 servo_version = self.get_servo_version()
Dan Shi9cb0eec2014-06-03 09:04:50 -0700716 if servo_version.startswith('servo_v2'):
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700717 self._programmer = firmware_programmer.ProgrammerV2(self)
Wai-Hong Tamc36c4d22016-09-09 10:39:45 -0700718 self._programmer_rw = firmware_programmer.ProgrammerV2RwOnly(self)
Kevin Chengdf2e29f2016-09-09 02:31:22 -0700719 # Both servo v3 and v4 use the same programming methods so just leverage
720 # ProgrammerV3 for servo v4 as well.
721 elif (servo_version.startswith('servo_v3') or
722 servo_version.startswith('servo_v4')):
Dan Shia5fef052015-05-18 23:28:47 -0700723 self._programmer = firmware_programmer.ProgrammerV3(self)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800724 self._programmer_rw = firmware_programmer.ProgrammerV3RwOnly(self)
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700725 else:
726 raise error.TestError(
727 'No firmware programmer for servo version: %s' %
728 servo_version)
729
730
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800731 def program_bios(self, image, rw_only=False):
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800732 """Program bios on DUT with given image.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800733
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800734 @param image: a string, file name of the BIOS image to program
735 on the DUT.
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800736 @param rw_only: True to only program the RW portion of BIOS.
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800737
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800738 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700739 self._initialize_programmer()
Ricky Liangc31aab32014-07-03 16:23:29 +0800740 if not self.is_localhost():
741 image = self._scp_image(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800742 if rw_only:
743 self._programmer_rw.program_bios(image)
744 else:
745 self._programmer.program_bios(image)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800746
747
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800748 def program_ec(self, image, rw_only=False):
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800749 """Program ec on DUT with given image.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800750
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800751 @param image: a string, file name of the EC image to program
752 on the DUT.
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800753 @param rw_only: True to only program the RW portion of EC.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800754
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800755 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700756 self._initialize_programmer()
Ricky Liangc31aab32014-07-03 16:23:29 +0800757 if not self.is_localhost():
758 image = self._scp_image(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800759 if rw_only:
760 self._programmer_rw.program_ec(image)
761 else:
762 self._programmer.program_ec(image)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800763
Fang Dengafb88142013-05-30 17:44:31 -0700764
765 def _switch_usbkey_power(self, power_state, detection_delay=False):
766 """Switch usbkey power.
767
768 This function switches usbkey power by setting the value of
769 'prtctl4_pwren'. If the power is already in the
770 requested state, this function simply returns.
771
772 @param power_state: A string, 'on' or 'off'.
773 @param detection_delay: A boolean value, if True, sleep
774 for |USB_DETECTION_DELAY| after switching
775 the power on.
776 """
Kevin Chenga22c4a82016-10-07 14:13:25 -0700777 # TODO(kevcheng): Forgive me for this terrible hack. This is just to
778 # handle beaglebones that haven't yet updated and have the
779 # safe_switch_usbkey_power RPC. I'll remove this once all beaglebones
780 # have been updated and also think about a better way to handle
781 # situations like this.
782 try:
783 self._server.safe_switch_usbkey_power(power_state)
784 except Exception:
785 self.set('prtctl4_pwren', power_state)
Fang Dengafb88142013-05-30 17:44:31 -0700786 if power_state == 'off':
787 time.sleep(self.USB_POWEROFF_DELAY)
788 elif detection_delay:
789 time.sleep(self.USB_DETECTION_DELAY)
790
791
792 def switch_usbkey(self, usb_state):
793 """Connect USB flash stick to either host or DUT, or turn USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800794
795 This function switches the servo multiplexer to provide electrical
Fang Dengafb88142013-05-30 17:44:31 -0700796 connection between the USB port J3 and either host or DUT side. It
797 can also be used to turn the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800798
Fang Dengafb88142013-05-30 17:44:31 -0700799 Switching to 'dut' or 'host' is accompanied by powercycling
800 of the USB stick, because it sometimes gets wedged if the mux
801 is switched while the stick power is on.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800802
Fang Dengafb88142013-05-30 17:44:31 -0700803 @param usb_state: A string, one of 'dut', 'host', or 'off'.
804 'dut' and 'host' indicate which side the
805 USB flash device is required to be connected to.
806 'off' indicates turning the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800807
Fang Dengafb88142013-05-30 17:44:31 -0700808 @raise: error.TestError in case the parameter is not 'dut'
809 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800810 """
Fang Dengafb88142013-05-30 17:44:31 -0700811 if self.get_usbkey_direction() == usb_state:
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800812 return
813
Fang Dengafb88142013-05-30 17:44:31 -0700814 if usb_state == 'off':
Dan Shia5fef052015-05-18 23:28:47 -0700815 self._switch_usbkey_power('off')
816 self._usb_state = usb_state
817 return
Fang Dengafb88142013-05-30 17:44:31 -0700818 elif usb_state == 'host':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800819 mux_direction = 'servo_sees_usbkey'
Fang Dengafb88142013-05-30 17:44:31 -0700820 elif usb_state == 'dut':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800821 mux_direction = 'dut_sees_usbkey'
822 else:
Fang Dengafb88142013-05-30 17:44:31 -0700823 raise error.TestError('Unknown USB state request: %s' % usb_state)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800824
Fang Dengafb88142013-05-30 17:44:31 -0700825 self._switch_usbkey_power('off')
Kevin Chenga22c4a82016-10-07 14:13:25 -0700826 # TODO(kevcheng): Forgive me for this terrible hack. This is just to
827 # handle beaglebones that haven't yet updated and have the
828 # safe_switch_usbkey RPC. I'll remove this once all beaglebones have
829 # been updated and also think about a better way to handle situations
830 # like this.
831 try:
832 self._server.safe_switch_usbkey(mux_direction)
833 except Exception:
834 self.set('usb_mux_sel1', mux_direction)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800835 time.sleep(self.USB_POWEROFF_DELAY)
Fang Dengafb88142013-05-30 17:44:31 -0700836 self._switch_usbkey_power('on', usb_state == 'host')
837 self._usb_state = usb_state
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800838
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800839
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800840 def get_usbkey_direction(self):
Fang Dengafb88142013-05-30 17:44:31 -0700841 """Get which side USB is connected to or 'off' if usb power is off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800842
Fang Dengafb88142013-05-30 17:44:31 -0700843 @return: A string, one of 'dut', 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800844 """
Fang Dengafb88142013-05-30 17:44:31 -0700845 if not self._usb_state:
846 if self.get('prtctl4_pwren') == 'off':
847 self._usb_state = 'off'
848 elif self.get('usb_mux_sel1').startswith('dut'):
849 self._usb_state = 'dut'
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800850 else:
Fang Dengafb88142013-05-30 17:44:31 -0700851 self._usb_state = 'host'
852 return self._usb_state