blob: cdb86f37c30819dc8deb1f6b283e8a34a70e32c2 [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'
Shelley Chen65938622018-05-16 07:45:54 -070041 REC_ON_FORCE_MRC = 'rec_force_mrc'
J. Richard Barnette0c9c5882014-06-11 15:27:05 -070042
43 # Delay in seconds needed between asserting and de-asserting
44 # warm reset.
45 _RESET_HOLD_TIME = 0.5
46
47 def __init__(self, servo):
48 """Initialize the power state control.
49
50 @param servo Servo object providing the underlying `set` and `get`
51 methods for the target controls.
52
53 """
54 self._servo = servo
55
56 def reset(self):
57 """Force the DUT to reset.
58
59 The DUT is guaranteed to be on at the end of this call,
60 regardless of its previous state, provided that there is
61 working OS software. This also guarantees that the EC has
62 been restarted.
63
64 """
65 self._servo.set_nocheck('power_state', 'reset')
66
67 def warm_reset(self):
68 """Apply warm reset to the DUT.
69
70 This asserts, then de-asserts the 'warm_reset' signal.
71 Generally, this causes the board to restart.
72
73 """
74 self._servo.set_get_all(['warm_reset:on',
75 'sleep:%.4f' % self._RESET_HOLD_TIME,
76 'warm_reset:off'])
77
J. Richard Barnette0c9c5882014-06-11 15:27:05 -070078 def power_off(self):
79 """Force the DUT to power off.
80
81 The DUT is guaranteed to be off at the end of this call,
82 regardless of its previous state, provided that there is
83 working EC and boot firmware. There is no requirement for
84 working OS software.
85
86 """
87 self._servo.set_nocheck('power_state', 'off')
88
89 def power_on(self, rec_mode=REC_OFF):
90 """Force the DUT to power on.
91
92 Prior to calling this function, the DUT must be powered off,
93 e.g. with a call to `power_off()`.
94
95 At power on, recovery mode is set as specified by the
96 corresponding argument. When booting with recovery mode on, it
97 is the caller's responsibility to unplug/plug in a bootable
98 external storage device.
99
100 If the DUT requires a delay after powering on but before
101 processing inputs such as USB stick insertion, the delay is
102 handled by this method; the caller is not responsible for such
103 delays.
104
105 @param rec_mode Setting of recovery mode to be applied at
106 power on. default: REC_OFF aka 'off'
107
108 """
109 self._servo.set_nocheck('power_state', rec_mode)
110
111
J. Richard Barnette384056b2012-04-16 11:04:46 -0700112class Servo(object):
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700113
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700114 """Manages control of a Servo board.
115
116 Servo is a board developed by hardware group to aide in the debug and
117 control of various partner devices. Servo's features include the simulation
118 of pressing the power button, closing the lid, and pressing Ctrl-d. This
119 class manages setting up and communicating with a servo demon (servod)
120 process. It provides both high-level functions for common servo tasks and
121 low-level functions for directly setting and reading gpios.
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700122
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700123 """
124
Chrome Bot9a1137d2011-07-19 14:35:00 -0700125 # Power button press delays in seconds.
J. Richard Barnetted2e4cbd2012-06-29 12:18:40 -0700126 #
J. Richard Barnette5383f072012-07-26 17:35:40 -0700127 # The EC specification says that 8.0 seconds should be enough
128 # for the long power press. However, some platforms need a bit
129 # more time. Empirical testing has found these requirements:
130 # Alex: 8.2 seconds
131 # ZGB: 8.5 seconds
132 # The actual value is set to the largest known necessary value.
133 #
134 # TODO(jrbarnette) Being generous is the right thing to do for
135 # existing platforms, but if this code is to be used for
136 # qualification of new hardware, we should be less generous.
Chrome Bot9a1137d2011-07-19 14:35:00 -0700137 SHORT_DELAY = 0.1
J. Richard Barnette5383f072012-07-26 17:35:40 -0700138
Todd Broch31c82502011-08-29 08:14:39 -0700139 # Maximum number of times to re-read power button on release.
Todd Brochcf7c6652012-02-24 13:03:59 -0800140 GET_RETRY_MAX = 10
Chrome Bot9a1137d2011-07-19 14:35:00 -0700141
J. Richard Barnette5383f072012-07-26 17:35:40 -0700142 # Delays to deal with DUT state transitions.
Chrome Bot9a1137d2011-07-19 14:35:00 -0700143 SLEEP_DELAY = 6
144 BOOT_DELAY = 10
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700145
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700146 # Default minimum time interval between 'press' and 'release'
147 # keyboard events.
Vic Yang0aca1c22012-11-19 18:33:56 -0800148 SERVO_KEY_PRESS_DELAY = 0.1
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700149
Tom Wai-Hong Tam93b5c092015-05-14 02:50:43 +0800150 # Time to toggle recovery switch on and off.
151 REC_TOGGLE_DELAY = 0.1
152
Tom Wai-Hong Tamf9ded092015-05-20 05:37:22 +0800153 # Time to toggle development switch on and off.
154 DEV_TOGGLE_DELAY = 0.1
155
Jon Salzc88e5b62011-11-30 14:38:54 +0800156 # Time between an usb disk plugged-in and detected in the system.
157 USB_DETECTION_DELAY = 10
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800158 # Time to keep USB power off before and after USB mux direction is changed
159 USB_POWEROFF_DELAY = 2
Jon Salzc88e5b62011-11-30 14:38:54 +0800160
Simran Basib7850bb2013-07-24 12:33:42 -0700161 # Time to wait before timing out on servo initialization.
162 INIT_TIMEOUT_SECS = 10
Simran Basi59331342013-07-12 12:06:29 -0700163
J. Richard Barnette67ccb872012-04-19 16:34:56 -0700164
Ricky Liang0dd379c2014-04-23 16:29:08 +0800165 def __init__(self, servo_host, servo_serial=None):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700166 """Sets up the servo communication infrastructure.
167
Fang Deng5d518f42013-08-02 14:04:32 -0700168 @param servo_host: A ServoHost object representing
169 the host running servod.
Ricky Liang0dd379c2014-04-23 16:29:08 +0800170 @param servo_serial: Serial number of the servo board.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700171 """
Fang Deng5d518f42013-08-02 14:04:32 -0700172 # TODO(fdeng): crbug.com/298379
173 # We should move servo_host object out of servo object
174 # to minimize the dependencies on the rest of Autotest.
175 self._servo_host = servo_host
Ricky Liang0dd379c2014-04-23 16:29:08 +0800176 self._servo_serial = servo_serial
Richard Barnette180efe62016-12-02 23:20:44 +0000177 self._server = servo_host.get_servod_server_proxy()
J. Richard Barnette0c9c5882014-06-11 15:27:05 -0700178 self._power_state = _PowerStateController(self)
Fang Dengafb88142013-05-30 17:44:31 -0700179 self._usb_state = None
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700180 self._programmer = None
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800181
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800182
Ricky Liang0dd379c2014-04-23 16:29:08 +0800183 @property
184 def servo_serial(self):
185 """Returns the serial number of the servo board."""
186 return self._servo_serial
187
188
Tom Wai-Hong Tam22ee0e52013-04-03 13:36:39 +0800189 def get_power_state_controller(self):
J. Richard Barnette75136b32013-03-26 13:38:44 -0700190 """Return the power state controller for this Servo.
191
192 The power state controller provides board-independent
193 interfaces for reset, power-on, power-off operations.
194
195 """
196 return self._power_state
197
Fang Deng5d518f42013-08-02 14:04:32 -0700198
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700199 def initialize_dut(self, cold_reset=False):
200 """Initializes a dut for testing purposes.
201
202 This sets various servo signals back to default values
203 appropriate for the target board. By default, if the DUT
204 is already on, it stays on. If the DUT is powered off
205 before initialization, its state afterward is unspecified.
206
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700207 Rationale: Basic initialization of servo sets the lid open,
208 when there is a lid. This operation won't affect powered on
209 units; however, setting the lid open may power on a unit
J. Richard Barnette75136b32013-03-26 13:38:44 -0700210 that's off, depending on the board type and previous state
211 of the device.
212
J. Richard Barnette4b6af0d2014-06-05 09:57:20 -0700213 If `cold_reset` is a true value, the DUT and its EC will be
214 reset, and the DUT rebooted in normal mode.
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700215
216 @param cold_reset If True, cold reset the device after
217 initialization.
218 """
219 self._server.hwinit()
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700220 self.set('usb_mux_oe1', 'on')
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700221 self._usb_state = None
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700222 self.switch_usbkey('off')
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700223 if cold_reset:
J. Richard Barnette4b6af0d2014-06-05 09:57:20 -0700224 self._power_state.reset()
J. Richard Barnette1777f5d2014-09-03 15:18:19 -0700225 logging.debug('Servo initialized, version is %s',
226 self._server.get_version())
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700227
228
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800229 def is_localhost(self):
230 """Is the servod hosted locally?
231
232 Returns:
233 True if local hosted; otherwise, False.
234 """
Fang Deng5d518f42013-08-02 14:04:32 -0700235 return self._servo_host.is_localhost()
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800236
237
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700238 def power_long_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700239 """Simulate a long power button press."""
J. Richard Barnettef12bff22012-07-18 14:41:06 -0700240 # After a long power press, the EC may ignore the next power
241 # button press (at least on Alex). To guarantee that this
242 # won't happen, we need to allow the EC one second to
243 # collect itself.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800244 self._server.power_long_press()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700245
246
247 def power_normal_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700248 """Simulate a normal power button press."""
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800249 self._server.power_normal_press()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700250
251
252 def power_short_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700253 """Simulate a short power button press."""
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800254 self._server.power_short_press()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700255
256
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800257 def power_key(self, press_secs=''):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700258 """Simulate a power button press.
259
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800260 @param press_secs : Str. Time to press key.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700261 """
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800262 self._server.power_key(press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700263
264
265 def lid_open(self):
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800266 """Simulate opening the lid and raise exception if all attempts fail"""
267 self.set('lid_open', 'yes')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700268
269
270 def lid_close(self):
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800271 """Simulate closing the lid and raise exception if all attempts fail
Craig Harrison48997262011-06-27 14:31:10 -0700272
273 Waits 6 seconds to ensure the device is fully asleep before returning.
274 """
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800275 self.set('lid_open', 'no')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700276 time.sleep(Servo.SLEEP_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700277
Shelley Chenc26575a2015-09-18 10:56:16 -0700278 def volume_up(self, timeout=300):
Kevin Chenge448a8b2016-09-09 10:18:11 -0700279 """Simulate pushing the volume down button.
280
281 @param timeout: Timeout for setting the volume.
282 """
Shelley Chenc26575a2015-09-18 10:56:16 -0700283 self.set_get_all(['volume_up:yes',
284 'sleep:%.4f' % self.SERVO_KEY_PRESS_DELAY,
285 'volume_up:no'])
286 # we need to wait for commands to take effect before moving on
287 time_left = float(timeout)
288 while time_left > 0.0:
289 value = self.get('volume_up')
290 if value == 'no':
291 return
292 time.sleep(self.SHORT_DELAY)
293 time_left = time_left - self.SHORT_DELAY
294 raise error.TestFail("Failed setting volume_up to no")
295
296 def volume_down(self, timeout=300):
Kevin Chenge448a8b2016-09-09 10:18:11 -0700297 """Simulate pushing the volume down button.
298
299 @param timeout: Timeout for setting the volume.
300 """
Shelley Chenc26575a2015-09-18 10:56:16 -0700301 self.set_get_all(['volume_down:yes',
302 'sleep:%.4f' % self.SERVO_KEY_PRESS_DELAY,
303 'volume_down:no'])
304 # we need to wait for commands to take effect before moving on
305 time_left = float(timeout)
306 while time_left > 0.0:
307 value = self.get('volume_down')
308 if value == 'no':
309 return
310 time.sleep(self.SHORT_DELAY)
311 time_left = time_left - self.SHORT_DELAY
312 raise error.TestFail("Failed setting volume_down to no")
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700313
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800314 def ctrl_d(self, press_secs=''):
315 """Simulate Ctrl-d simultaneous button presses.
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800316
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800317 @param press_secs : Str. Time to press key.
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800318 """
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800319 self._server.ctrl_d(press_secs)
Gediminas Ramanauskas9da80f22012-11-14 12:59:43 -0800320
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800321
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800322 def ctrl_u(self):
323 """Simulate Ctrl-u simultaneous button presses.
324
325 @param press_secs : Str. Time to press key.
326 """
327 self._server.ctrl_u()
Todd Broch9dfc3a82011-11-01 08:09:28 -0700328
329
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800330 def ctrl_enter(self, press_secs=''):
331 """Simulate Ctrl-enter simultaneous button presses.
332
333 @param press_secs : Str. Time to press key.
334 """
335 self._server.ctrl_enter(press_secs)
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -0700336
337
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800338 def d_key(self, press_secs=''):
339 """Simulate Enter key button press.
340
341 @param press_secs : Str. Time to press key.
342 """
343 self._server.d_key(press_secs)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700344
345
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800346 def ctrl_key(self, press_secs=''):
347 """Simulate Enter key button press.
348
349 @param press_secs : Str. Time to press key.
350 """
351 self._server.ctrl_key(press_secs)
Todd Broch9753bd42012-03-21 10:15:08 -0700352
353
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800354 def enter_key(self, press_secs=''):
355 """Simulate Enter key button press.
356
357 @param press_secs : Str. Time to press key.
358 """
359 self._server.enter_key(press_secs)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700360
361
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800362 def refresh_key(self, press_secs=''):
363 """Simulate Refresh key (F3) button press.
364
365 @param press_secs : Str. Time to press key.
366 """
367 self._server.refresh_key(press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700368
369
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800370 def ctrl_refresh_key(self, press_secs=''):
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800371 """Simulate Ctrl and Refresh (F3) simultaneous press.
372
373 This key combination is an alternative of Space key.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800374
375 @param press_secs : Str. Time to press key.
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800376 """
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800377 self._server.ctrl_refresh_key(press_secs)
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800378
379
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800380 def imaginary_key(self, press_secs=''):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700381 """Simulate imaginary key button press.
382
383 Maps to a key that doesn't physically exist.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800384
385 @param press_secs : Str. Time to press key.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700386 """
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800387 self._server.imaginary_key(press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700388
389
Vincent Palatine7dc9282016-07-14 11:31:58 +0200390 def sysrq_x(self, press_secs=''):
391 """Simulate Alt VolumeUp X simulataneous press.
392
393 This key combination is the kernel system request (sysrq) X.
394
395 @param press_secs : Str. Time to press key.
396 """
397 self._server.sysrq_x(press_secs)
398
399
Tom Wai-Hong Tam93b5c092015-05-14 02:50:43 +0800400 def toggle_recovery_switch(self):
401 """Toggle recovery switch on and off."""
402 self.enable_recovery_mode()
403 time.sleep(self.REC_TOGGLE_DELAY)
404 self.disable_recovery_mode()
405
406
Craig Harrison6b36b122011-06-28 17:58:43 -0700407 def enable_recovery_mode(self):
408 """Enable recovery mode on device."""
409 self.set('rec_mode', 'on')
410
411
412 def disable_recovery_mode(self):
413 """Disable recovery mode on device."""
414 self.set('rec_mode', 'off')
415
416
Tom Wai-Hong Tamf9ded092015-05-20 05:37:22 +0800417 def toggle_development_switch(self):
418 """Toggle development switch on and off."""
419 self.enable_development_mode()
420 time.sleep(self.DEV_TOGGLE_DELAY)
421 self.disable_development_mode()
422
423
Craig Harrison6b36b122011-06-28 17:58:43 -0700424 def enable_development_mode(self):
425 """Enable development mode on device."""
426 self.set('dev_mode', 'on')
427
428
429 def disable_development_mode(self):
430 """Disable development mode on device."""
431 self.set('dev_mode', 'off')
432
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700433 def boot_devmode(self):
434 """Boot a dev-mode device that is powered off."""
Tom Wai-Hong Tam23869102012-01-17 15:41:30 +0800435 self.power_short_press()
Craig Harrison48997262011-06-27 14:31:10 -0700436 self.pass_devmode()
437
438
439 def pass_devmode(self):
440 """Pass through boot screens in dev-mode."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700441 time.sleep(Servo.BOOT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700442 self.ctrl_d()
Chrome Bot9a1137d2011-07-19 14:35:00 -0700443 time.sleep(Servo.BOOT_DELAY)
Craig Harrison48997262011-06-27 14:31:10 -0700444
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700445
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800446 def get_board(self):
Wai-Hong Tam0f904002017-09-19 15:52:22 -0700447 """Get the board connected to servod."""
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800448 return self._server.get_board()
449
450
Wai-Hong Tam0f904002017-09-19 15:52:22 -0700451 def get_base_board(self):
452 """Get the board of the base connected to servod."""
Wai-Hong Tam7f6169e2017-09-29 09:23:48 -0700453 try:
454 return self._server.get_base_board()
455 except xmlrpclib.Fault as e:
456 # TODO(waihong): Remove the following compatibility check when
457 # the new versions of hdctools are deployed.
458 if 'not supported' in str(e):
459 logging.warning('The servod is too old that get_base_board '
460 'not supported.')
461 return ''
462 raise
Wai-Hong Tam0f904002017-09-19 15:52:22 -0700463
464
Wai-Hong Tamcc399ee2017-12-08 12:43:28 -0800465 def get_ec_active_copy(self):
466 """Get the active copy of the EC image."""
467 return self.get('ec_active_copy')
468
469
Todd Brochefe72cb2012-07-11 19:58:53 -0700470 def _get_xmlrpclib_exception(self, xmlexc):
471 """Get meaningful exception string from xmlrpc.
472
473 Args:
474 xmlexc: xmlrpclib.Fault object
475
476 xmlrpclib.Fault.faultString has the following format:
477
478 <type 'exception type'>:'actual error message'
479
480 Parse and return the real exception from the servod side instead of the
481 less meaningful one like,
482 <Fault 1: "<type 'exceptions.AttributeError'>:'tca6416' object has no
483 attribute 'hw_driver'">
484
485 Returns:
486 string of underlying exception raised in servod.
487 """
488 return re.sub('^.*>:', '', xmlexc.faultString)
489
490
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700491 def get(self, gpio_name):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700492 """Get the value of a gpio from Servod.
493
494 @param gpio_name Name of the gpio.
495 """
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700496 assert gpio_name
Todd Brochefe72cb2012-07-11 19:58:53 -0700497 try:
Simran Basi1cbc5f22013-07-17 14:23:58 -0700498 return self._server.get(gpio_name)
Todd Brochefe72cb2012-07-11 19:58:53 -0700499 except xmlrpclib.Fault as e:
500 err_msg = "Getting '%s' :: %s" % \
501 (gpio_name, self._get_xmlrpclib_exception(e))
502 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700503
504
505 def set(self, gpio_name, gpio_value):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700506 """Set and check the value of a gpio using Servod.
507
508 @param gpio_name Name of the gpio.
509 @param gpio_value New setting for the gpio.
510 """
Chrome Bot9a1137d2011-07-19 14:35:00 -0700511 self.set_nocheck(gpio_name, gpio_value)
Todd Brochcf7c6652012-02-24 13:03:59 -0800512 retry_count = Servo.GET_RETRY_MAX
513 while gpio_value != self.get(gpio_name) and retry_count:
Ilja H. Friedel04be2bd2014-05-07 21:29:59 -0700514 logging.warning("%s != %s, retry %d", gpio_name, gpio_value,
Todd Brochcf7c6652012-02-24 13:03:59 -0800515 retry_count)
516 retry_count -= 1
517 time.sleep(Servo.SHORT_DELAY)
518 if not retry_count:
519 assert gpio_value == self.get(gpio_name), \
520 'Servo failed to set %s to %s' % (gpio_name, gpio_value)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700521
522
523 def set_nocheck(self, gpio_name, gpio_value):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700524 """Set the value of a gpio using Servod.
525
526 @param gpio_name Name of the gpio.
527 @param gpio_value New setting for the gpio.
528 """
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700529 assert gpio_name and gpio_value
Mary Ruthven2f72e2a2018-05-01 17:12:58 -0700530 logging.info('Setting %s to %r', gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700531 try:
Simran Basi1cbc5f22013-07-17 14:23:58 -0700532 self._server.set(gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700533 except xmlrpclib.Fault as e:
Mary Ruthven73d3a352018-05-10 15:35:20 -0700534 err_msg = "Setting '%s' to %r :: %s" % \
Todd Brochefe72cb2012-07-11 19:58:53 -0700535 (gpio_name, gpio_value, self._get_xmlrpclib_exception(e))
536 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700537
538
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800539 def set_get_all(self, controls):
540 """Set &| get one or more control values.
541
542 @param controls: list of strings, controls to set &| get.
543
544 @raise: error.TestError in case error occurs setting/getting values.
545 """
546 rv = []
547 try:
Vic Yangcad9acb2013-05-21 14:02:05 +0800548 logging.info('Set/get all: %s', str(controls))
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800549 rv = self._server.set_get_all(controls)
550 except xmlrpclib.Fault as e:
551 # TODO(waihong): Remove the following backward compatibility when
552 # the new versions of hdctools are deployed.
553 if 'not supported' in str(e):
554 logging.warning('The servod is too old that set_get_all '
555 'not supported. Use set and get instead.')
556 for control in controls:
557 if ':' in control:
558 (name, value) = control.split(':')
559 if name == 'sleep':
560 time.sleep(float(value))
561 else:
562 self.set_nocheck(name, value)
563 rv.append(True)
564 else:
565 rv.append(self.get(name))
566 else:
567 err_msg = "Problem with '%s' :: %s" % \
568 (controls, self._get_xmlrpclib_exception(e))
569 raise error.TestFail(err_msg)
570 return rv
571
572
Jon Salzc88e5b62011-11-30 14:38:54 +0800573 # TODO(waihong) It may fail if multiple servo's are connected to the same
574 # host. Should look for a better way, like the USB serial name, to identify
575 # the USB device.
Simran Basi741b5d42012-05-18 11:27:15 -0700576 # TODO(sbasi) Remove this code from autoserv once firmware tests have been
577 # updated.
Kevin Chenga22c4a82016-10-07 14:13:25 -0700578 def probe_host_usb_dev(self, timeout=_USB_PROBE_TIMEOUT):
Jon Salzc88e5b62011-11-30 14:38:54 +0800579 """Probe the USB disk device plugged-in the servo from the host side.
580
Kevin Chengeb06fe72016-08-22 15:26:32 -0700581 It uses servod to discover if there is a usb device attached to it.
Jon Salzc88e5b62011-11-30 14:38:54 +0800582
Kevin Chenga22c4a82016-10-07 14:13:25 -0700583 @param timeout The timeout period when probing for the usb host device.
584
585 @return: String of USB disk path (e.g. '/dev/sdb') or None.
Jon Salzc88e5b62011-11-30 14:38:54 +0800586 """
Kevin Chenga22c4a82016-10-07 14:13:25 -0700587 return self._server.probe_host_usb_dev(timeout) or None
Jon Salzc88e5b62011-11-30 14:38:54 +0800588
589
Mike Truty49153d82012-08-21 22:27:30 -0500590 def image_to_servo_usb(self, image_path=None,
591 make_image_noninteractive=False):
592 """Install an image to the USB key plugged into the servo.
593
594 This method may copy any image to the servo USB key including a
595 recovery image or a test image. These images are frequently used
596 for test purposes such as restoring a corrupted image or conducting
597 an upgrade of ec/fw/kernel as part of a test of a specific image part.
598
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700599 @param image_path Path on the host to the recovery image.
600 @param make_image_noninteractive Make the recovery image
601 noninteractive, therefore the DUT
602 will reboot automatically after
603 installation.
Mike Truty49153d82012-08-21 22:27:30 -0500604 """
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700605 # We're about to start plugging/unplugging the USB key. We
606 # don't know the state of the DUT, or what it might choose
607 # to do to the device after hotplug. To avoid surprises,
608 # force the DUT to be off.
609 self._server.hwinit()
610 self._power_state.power_off()
Mike Truty49153d82012-08-21 22:27:30 -0500611
612 # Set up Servo's usb mux.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800613 self.switch_usbkey('host')
Mike Truty49153d82012-08-21 22:27:30 -0500614 if image_path:
Tom Wai-Hong Tam42f136d2012-10-26 11:11:23 +0800615 logging.info('Searching for usb device and copying image to it. '
616 'Please wait a few minutes...')
Mike Truty49153d82012-08-21 22:27:30 -0500617 if not self._server.download_image_to_usb(image_path):
618 logging.error('Failed to transfer requested image to USB. '
619 'Please take a look at Servo Logs.')
620 raise error.AutotestError('Download image to usb failed.')
621 if make_image_noninteractive:
622 logging.info('Making image noninteractive')
623 if not self._server.make_image_noninteractive():
624 logging.error('Failed to make image noninteractive. '
625 'Please take a look at Servo Logs.')
626
Kalin Stoyanovb3c11f32018-05-11 09:02:00 -0700627 def boot_in_recovery_mode(self):
628 """Boot host DUT in recovery mode."""
629 self._power_state.power_on(rec_mode=self._power_state.REC_ON)
630 self.switch_usbkey('dut')
631
Mike Truty49153d82012-08-21 22:27:30 -0500632
Simran Basi741b5d42012-05-18 11:27:15 -0700633 def install_recovery_image(self, image_path=None,
J. Richard Barnetteb6de7e32013-02-14 13:28:04 -0800634 make_image_noninteractive=False):
Dan Shic67f1332016-04-06 12:38:06 -0700635 """Install the recovery image specified by the path onto the DUT.
Jon Salzc88e5b62011-11-30 14:38:54 +0800636
637 This method uses google recovery mode to install a recovery image
638 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 +0800639 board specified by the usb_dev. If no image path is specified
Jon Salzc88e5b62011-11-30 14:38:54 +0800640 we use the recovery image already on the usb image.
641
Dan Shic67f1332016-04-06 12:38:06 -0700642 @param image_path: Path on the host to the recovery image.
643 @param make_image_noninteractive: Make the recovery image
644 noninteractive, therefore the DUT will reboot automatically
645 after installation.
Jon Salzc88e5b62011-11-30 14:38:54 +0800646 """
Mike Truty49153d82012-08-21 22:27:30 -0500647 self.image_to_servo_usb(image_path, make_image_noninteractive)
Kalin Stoyanovb3c11f32018-05-11 09:02:00 -0700648 self.boot_in_recovery_mode()
Jon Salzc88e5b62011-11-30 14:38:54 +0800649
650
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800651 def _scp_image(self, image_path):
652 """Copy image to the servo host.
653
654 When programming a firmware image on the DUT, the image must be
655 located on the host to which the servo device is connected. Sometimes
656 servo is controlled by a remote host, in this case the image needs to
657 be transferred to the remote host.
658
659 @param image_path: a string, name of the firmware image file to be
660 transferred.
661 @return: a string, full path name of the copied file on the remote.
662 """
663
664 dest_path = os.path.join('/tmp', os.path.basename(image_path))
Fang Deng5d518f42013-08-02 14:04:32 -0700665 self._servo_host.send_file(image_path, dest_path)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800666 return dest_path
667
668
Dan Shifecdaf42015-07-28 10:17:26 -0700669 def system(self, command, timeout=3600):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700670 """Execute the passed in command on the servod host.
671
672 @param command Command to be executed.
Dan Shifecdaf42015-07-28 10:17:26 -0700673 @param timeout Maximum number of seconds of runtime allowed. Default to
674 1 hour.
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700675 """
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800676 logging.info('Will execute on servo host: %s', command)
Fang Deng5d518f42013-08-02 14:04:32 -0700677 self._servo_host.run(command, timeout=timeout)
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800678
679
Dan Shifecdaf42015-07-28 10:17:26 -0700680 def system_output(self, command, timeout=3600,
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800681 ignore_status=False, args=()):
682 """Execute the passed in command on the servod host, return stdout.
683
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700684 @param command a string, the command to execute
685 @param timeout an int, max number of seconds to wait til command
Dan Shifecdaf42015-07-28 10:17:26 -0700686 execution completes. Default to 1 hour.
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700687 @param ignore_status a Boolean, if true - ignore command's nonzero exit
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800688 status, otherwise an exception will be thrown
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700689 @param args a tuple of strings, each becoming a separate command line
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800690 parameter for the command
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700691 @return command's stdout as a string.
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800692 """
Fang Deng5d518f42013-08-02 14:04:32 -0700693 return self._servo_host.run(command, timeout=timeout,
694 ignore_status=ignore_status,
695 args=args).stdout.strip()
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800696
697
Dan Shia5fef052015-05-18 23:28:47 -0700698 def get_servo_version(self):
699 """Get the version of the servo, e.g., servo_v2 or servo_v3.
700
701 @return: The version of the servo.
702
703 """
704 return self._server.get_version()
705
706
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800707 def _initialize_programmer(self, rw_only=False):
708 """Initialize the firmware programmer.
709
710 @param rw_only: True to initialize a programmer which only
711 programs the RW portions.
712 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700713 if self._programmer:
714 return
715 # Initialize firmware programmer
Dan Shia5fef052015-05-18 23:28:47 -0700716 servo_version = self.get_servo_version()
Dan Shi9cb0eec2014-06-03 09:04:50 -0700717 if servo_version.startswith('servo_v2'):
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700718 self._programmer = firmware_programmer.ProgrammerV2(self)
Wai-Hong Tamc36c4d22016-09-09 10:39:45 -0700719 self._programmer_rw = firmware_programmer.ProgrammerV2RwOnly(self)
Kevin Chengdf2e29f2016-09-09 02:31:22 -0700720 # Both servo v3 and v4 use the same programming methods so just leverage
721 # ProgrammerV3 for servo v4 as well.
722 elif (servo_version.startswith('servo_v3') or
723 servo_version.startswith('servo_v4')):
Dan Shia5fef052015-05-18 23:28:47 -0700724 self._programmer = firmware_programmer.ProgrammerV3(self)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800725 self._programmer_rw = firmware_programmer.ProgrammerV3RwOnly(self)
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700726 else:
727 raise error.TestError(
728 'No firmware programmer for servo version: %s' %
729 servo_version)
730
731
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800732 def program_bios(self, image, rw_only=False):
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800733 """Program bios on DUT with given image.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800734
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800735 @param image: a string, file name of the BIOS image to program
736 on the DUT.
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800737 @param rw_only: True to only program the RW portion of BIOS.
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800738
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800739 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700740 self._initialize_programmer()
Ricky Liangc31aab32014-07-03 16:23:29 +0800741 if not self.is_localhost():
742 image = self._scp_image(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800743 if rw_only:
744 self._programmer_rw.program_bios(image)
745 else:
746 self._programmer.program_bios(image)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800747
748
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800749 def program_ec(self, image, rw_only=False):
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800750 """Program ec on DUT with given image.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800751
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800752 @param image: a string, file name of the EC image to program
753 on the DUT.
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800754 @param rw_only: True to only program the RW portion of EC.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800755
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800756 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700757 self._initialize_programmer()
Ricky Liangc31aab32014-07-03 16:23:29 +0800758 if not self.is_localhost():
759 image = self._scp_image(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800760 if rw_only:
761 self._programmer_rw.program_ec(image)
762 else:
763 self._programmer.program_ec(image)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800764
Fang Dengafb88142013-05-30 17:44:31 -0700765
766 def _switch_usbkey_power(self, power_state, detection_delay=False):
767 """Switch usbkey power.
768
769 This function switches usbkey power by setting the value of
770 'prtctl4_pwren'. If the power is already in the
771 requested state, this function simply returns.
772
773 @param power_state: A string, 'on' or 'off'.
774 @param detection_delay: A boolean value, if True, sleep
775 for |USB_DETECTION_DELAY| after switching
776 the power on.
777 """
Kevin Chenga22c4a82016-10-07 14:13:25 -0700778 # TODO(kevcheng): Forgive me for this terrible hack. This is just to
779 # handle beaglebones that haven't yet updated and have the
780 # safe_switch_usbkey_power RPC. I'll remove this once all beaglebones
781 # have been updated and also think about a better way to handle
782 # situations like this.
783 try:
784 self._server.safe_switch_usbkey_power(power_state)
785 except Exception:
786 self.set('prtctl4_pwren', power_state)
Fang Dengafb88142013-05-30 17:44:31 -0700787 if power_state == 'off':
788 time.sleep(self.USB_POWEROFF_DELAY)
789 elif detection_delay:
790 time.sleep(self.USB_DETECTION_DELAY)
791
792
793 def switch_usbkey(self, usb_state):
794 """Connect USB flash stick to either host or DUT, or turn USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800795
796 This function switches the servo multiplexer to provide electrical
Fang Dengafb88142013-05-30 17:44:31 -0700797 connection between the USB port J3 and either host or DUT side. It
798 can also be used to turn the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800799
Fang Dengafb88142013-05-30 17:44:31 -0700800 Switching to 'dut' or 'host' is accompanied by powercycling
801 of the USB stick, because it sometimes gets wedged if the mux
802 is switched while the stick power is on.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800803
Fang Dengafb88142013-05-30 17:44:31 -0700804 @param usb_state: A string, one of 'dut', 'host', or 'off'.
805 'dut' and 'host' indicate which side the
806 USB flash device is required to be connected to.
807 'off' indicates turning the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800808
Fang Dengafb88142013-05-30 17:44:31 -0700809 @raise: error.TestError in case the parameter is not 'dut'
810 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800811 """
Fang Dengafb88142013-05-30 17:44:31 -0700812 if self.get_usbkey_direction() == usb_state:
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800813 return
814
Fang Dengafb88142013-05-30 17:44:31 -0700815 if usb_state == 'off':
Dan Shia5fef052015-05-18 23:28:47 -0700816 self._switch_usbkey_power('off')
817 self._usb_state = usb_state
818 return
Fang Dengafb88142013-05-30 17:44:31 -0700819 elif usb_state == 'host':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800820 mux_direction = 'servo_sees_usbkey'
Fang Dengafb88142013-05-30 17:44:31 -0700821 elif usb_state == 'dut':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800822 mux_direction = 'dut_sees_usbkey'
823 else:
Fang Dengafb88142013-05-30 17:44:31 -0700824 raise error.TestError('Unknown USB state request: %s' % usb_state)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800825
Fang Dengafb88142013-05-30 17:44:31 -0700826 self._switch_usbkey_power('off')
Kevin Chenga22c4a82016-10-07 14:13:25 -0700827 # TODO(kevcheng): Forgive me for this terrible hack. This is just to
828 # handle beaglebones that haven't yet updated and have the
829 # safe_switch_usbkey RPC. I'll remove this once all beaglebones have
830 # been updated and also think about a better way to handle situations
831 # like this.
832 try:
833 self._server.safe_switch_usbkey(mux_direction)
834 except Exception:
835 self.set('usb_mux_sel1', mux_direction)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800836 time.sleep(self.USB_POWEROFF_DELAY)
Fang Dengafb88142013-05-30 17:44:31 -0700837 self._switch_usbkey_power('on', usb_state == 'host')
838 self._usb_state = usb_state
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800839
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800840
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800841 def get_usbkey_direction(self):
Fang Dengafb88142013-05-30 17:44:31 -0700842 """Get which side USB is connected to or 'off' if usb power is off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800843
Fang Dengafb88142013-05-30 17:44:31 -0700844 @return: A string, one of 'dut', 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800845 """
Fang Dengafb88142013-05-30 17:44:31 -0700846 if not self._usb_state:
847 if self.get('prtctl4_pwren') == 'off':
848 self._usb_state = 'off'
849 elif self.get('usb_mux_sel1').startswith('dut'):
850 self._usb_state = 'dut'
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800851 else:
Fang Dengafb88142013-05-30 17:44:31 -0700852 self._usb_state = 'host'
853 return self._usb_state