blob: 3f12981e83bfdd0a00c00e0fce25ed44f5137dbd [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
J. Richard Barnette41320ee2013-03-11 13:00:13 -070015
J. Richard Barnette0c9c5882014-06-11 15:27:05 -070016class _PowerStateController(object):
17
18 """Class to provide board-specific power operations.
19
20 This class is responsible for "power on" and "power off"
21 operations that can operate without making assumptions in
22 advance about board state. It offers an interface that
23 abstracts out the different sequences required for different
24 board types.
25
26 """
27
28 # Constants acceptable to be passed for the `rec_mode` parameter
29 # to power_on().
30 #
31 # REC_ON: Boot the DUT in recovery mode, i.e. boot from USB or
32 # SD card.
33 # REC_OFF: Boot in normal mode, i.e. boot from internal storage.
34
35 REC_ON = 'rec'
36 REC_OFF = 'on'
37
38 # Delay in seconds needed between asserting and de-asserting
39 # warm reset.
40 _RESET_HOLD_TIME = 0.5
41
42 def __init__(self, servo):
43 """Initialize the power state control.
44
45 @param servo Servo object providing the underlying `set` and `get`
46 methods for the target controls.
47
48 """
49 self._servo = servo
50
51 def reset(self):
52 """Force the DUT to reset.
53
54 The DUT is guaranteed to be on at the end of this call,
55 regardless of its previous state, provided that there is
56 working OS software. This also guarantees that the EC has
57 been restarted.
58
59 """
60 self._servo.set_nocheck('power_state', 'reset')
61
62 def warm_reset(self):
63 """Apply warm reset to the DUT.
64
65 This asserts, then de-asserts the 'warm_reset' signal.
66 Generally, this causes the board to restart.
67
68 """
69 self._servo.set_get_all(['warm_reset:on',
70 'sleep:%.4f' % self._RESET_HOLD_TIME,
71 'warm_reset:off'])
72
J. Richard Barnette0c9c5882014-06-11 15:27:05 -070073 def power_off(self):
74 """Force the DUT to power off.
75
76 The DUT is guaranteed to be off at the end of this call,
77 regardless of its previous state, provided that there is
78 working EC and boot firmware. There is no requirement for
79 working OS software.
80
81 """
82 self._servo.set_nocheck('power_state', 'off')
83
84 def power_on(self, rec_mode=REC_OFF):
85 """Force the DUT to power on.
86
87 Prior to calling this function, the DUT must be powered off,
88 e.g. with a call to `power_off()`.
89
90 At power on, recovery mode is set as specified by the
91 corresponding argument. When booting with recovery mode on, it
92 is the caller's responsibility to unplug/plug in a bootable
93 external storage device.
94
95 If the DUT requires a delay after powering on but before
96 processing inputs such as USB stick insertion, the delay is
97 handled by this method; the caller is not responsible for such
98 delays.
99
100 @param rec_mode Setting of recovery mode to be applied at
101 power on. default: REC_OFF aka 'off'
102
103 """
104 self._servo.set_nocheck('power_state', rec_mode)
105
106
J. Richard Barnette384056b2012-04-16 11:04:46 -0700107class Servo(object):
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700108
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700109 """Manages control of a Servo board.
110
111 Servo is a board developed by hardware group to aide in the debug and
112 control of various partner devices. Servo's features include the simulation
113 of pressing the power button, closing the lid, and pressing Ctrl-d. This
114 class manages setting up and communicating with a servo demon (servod)
115 process. It provides both high-level functions for common servo tasks and
116 low-level functions for directly setting and reading gpios.
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700117
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700118 """
119
Chrome Bot9a1137d2011-07-19 14:35:00 -0700120 # Power button press delays in seconds.
J. Richard Barnetted2e4cbd2012-06-29 12:18:40 -0700121 #
J. Richard Barnette5383f072012-07-26 17:35:40 -0700122 # The EC specification says that 8.0 seconds should be enough
123 # for the long power press. However, some platforms need a bit
124 # more time. Empirical testing has found these requirements:
125 # Alex: 8.2 seconds
126 # ZGB: 8.5 seconds
127 # The actual value is set to the largest known necessary value.
128 #
129 # TODO(jrbarnette) Being generous is the right thing to do for
130 # existing platforms, but if this code is to be used for
131 # qualification of new hardware, we should be less generous.
Chrome Bot9a1137d2011-07-19 14:35:00 -0700132 SHORT_DELAY = 0.1
J. Richard Barnette5383f072012-07-26 17:35:40 -0700133
Todd Broch31c82502011-08-29 08:14:39 -0700134 # Maximum number of times to re-read power button on release.
Todd Brochcf7c6652012-02-24 13:03:59 -0800135 GET_RETRY_MAX = 10
Chrome Bot9a1137d2011-07-19 14:35:00 -0700136
J. Richard Barnette5383f072012-07-26 17:35:40 -0700137 # Delays to deal with DUT state transitions.
Chrome Bot9a1137d2011-07-19 14:35:00 -0700138 SLEEP_DELAY = 6
139 BOOT_DELAY = 10
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700140
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700141 # Default minimum time interval between 'press' and 'release'
142 # keyboard events.
Vic Yang0aca1c22012-11-19 18:33:56 -0800143 SERVO_KEY_PRESS_DELAY = 0.1
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700144
Tom Wai-Hong Tam93b5c092015-05-14 02:50:43 +0800145 # Time to toggle recovery switch on and off.
146 REC_TOGGLE_DELAY = 0.1
147
Tom Wai-Hong Tamf9ded092015-05-20 05:37:22 +0800148 # Time to toggle development switch on and off.
149 DEV_TOGGLE_DELAY = 0.1
150
Jon Salzc88e5b62011-11-30 14:38:54 +0800151 # Time between an usb disk plugged-in and detected in the system.
152 USB_DETECTION_DELAY = 10
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800153 # Time to keep USB power off before and after USB mux direction is changed
154 USB_POWEROFF_DELAY = 2
Jon Salzc88e5b62011-11-30 14:38:54 +0800155
Simran Basib7850bb2013-07-24 12:33:42 -0700156 # Time to wait before timing out on servo initialization.
157 INIT_TIMEOUT_SECS = 10
Simran Basi59331342013-07-12 12:06:29 -0700158
J. Richard Barnette67ccb872012-04-19 16:34:56 -0700159
Ricky Liang0dd379c2014-04-23 16:29:08 +0800160 def __init__(self, servo_host, servo_serial=None):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700161 """Sets up the servo communication infrastructure.
162
Fang Deng5d518f42013-08-02 14:04:32 -0700163 @param servo_host: A ServoHost object representing
164 the host running servod.
Ricky Liang0dd379c2014-04-23 16:29:08 +0800165 @param servo_serial: Serial number of the servo board.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700166 """
Fang Deng5d518f42013-08-02 14:04:32 -0700167 # TODO(fdeng): crbug.com/298379
168 # We should move servo_host object out of servo object
169 # to minimize the dependencies on the rest of Autotest.
170 self._servo_host = servo_host
Ricky Liang0dd379c2014-04-23 16:29:08 +0800171 self._servo_serial = servo_serial
Fang Deng5d518f42013-08-02 14:04:32 -0700172 self._server = servo_host.get_servod_server_proxy()
J. Richard Barnette0c9c5882014-06-11 15:27:05 -0700173 self._power_state = _PowerStateController(self)
Fang Dengafb88142013-05-30 17:44:31 -0700174 self._usb_state = None
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700175 self._programmer = None
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800176
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800177
Ricky Liang0dd379c2014-04-23 16:29:08 +0800178 @property
179 def servo_serial(self):
180 """Returns the serial number of the servo board."""
181 return self._servo_serial
182
183
Tom Wai-Hong Tam22ee0e52013-04-03 13:36:39 +0800184 def get_power_state_controller(self):
J. Richard Barnette75136b32013-03-26 13:38:44 -0700185 """Return the power state controller for this Servo.
186
187 The power state controller provides board-independent
188 interfaces for reset, power-on, power-off operations.
189
190 """
191 return self._power_state
192
Fang Deng5d518f42013-08-02 14:04:32 -0700193
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700194 def initialize_dut(self, cold_reset=False):
195 """Initializes a dut for testing purposes.
196
197 This sets various servo signals back to default values
198 appropriate for the target board. By default, if the DUT
199 is already on, it stays on. If the DUT is powered off
200 before initialization, its state afterward is unspecified.
201
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700202 Rationale: Basic initialization of servo sets the lid open,
203 when there is a lid. This operation won't affect powered on
204 units; however, setting the lid open may power on a unit
J. Richard Barnette75136b32013-03-26 13:38:44 -0700205 that's off, depending on the board type and previous state
206 of the device.
207
J. Richard Barnette4b6af0d2014-06-05 09:57:20 -0700208 If `cold_reset` is a true value, the DUT and its EC will be
209 reset, and the DUT rebooted in normal mode.
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700210
211 @param cold_reset If True, cold reset the device after
212 initialization.
213 """
214 self._server.hwinit()
John Carey82638c72015-08-20 15:55:16 -0700215 # Workaround for bug chrome-os-partner:42349. Without this check, the
216 # gpio will briefly pulse low if we set it from high to high.
217 if self.get('dut_hub_pwren') != 'on':
218 self.set('dut_hub_pwren', 'on')
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):
278 """Simulate pushing the volume down button"""
279 self.set_get_all(['volume_up:yes',
280 'sleep:%.4f' % self.SERVO_KEY_PRESS_DELAY,
281 'volume_up:no'])
282 # we need to wait for commands to take effect before moving on
283 time_left = float(timeout)
284 while time_left > 0.0:
285 value = self.get('volume_up')
286 if value == 'no':
287 return
288 time.sleep(self.SHORT_DELAY)
289 time_left = time_left - self.SHORT_DELAY
290 raise error.TestFail("Failed setting volume_up to no")
291
292 def volume_down(self, timeout=300):
293 """Simulate pushing the volume down button"""
294 self.set_get_all(['volume_down:yes',
295 'sleep:%.4f' % self.SERVO_KEY_PRESS_DELAY,
296 'volume_down:no'])
297 # we need to wait for commands to take effect before moving on
298 time_left = float(timeout)
299 while time_left > 0.0:
300 value = self.get('volume_down')
301 if value == 'no':
302 return
303 time.sleep(self.SHORT_DELAY)
304 time_left = time_left - self.SHORT_DELAY
305 raise error.TestFail("Failed setting volume_down to no")
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700306
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800307 def ctrl_d(self, press_secs=''):
308 """Simulate Ctrl-d simultaneous button presses.
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800309
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800310 @param press_secs : Str. Time to press key.
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800311 """
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800312 self._server.ctrl_d(press_secs)
Gediminas Ramanauskas9da80f22012-11-14 12:59:43 -0800313
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800314
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800315 def ctrl_u(self):
316 """Simulate Ctrl-u simultaneous button presses.
317
318 @param press_secs : Str. Time to press key.
319 """
320 self._server.ctrl_u()
Todd Broch9dfc3a82011-11-01 08:09:28 -0700321
322
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800323 def ctrl_enter(self, press_secs=''):
324 """Simulate Ctrl-enter simultaneous button presses.
325
326 @param press_secs : Str. Time to press key.
327 """
328 self._server.ctrl_enter(press_secs)
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -0700329
330
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800331 def d_key(self, press_secs=''):
332 """Simulate Enter key button press.
333
334 @param press_secs : Str. Time to press key.
335 """
336 self._server.d_key(press_secs)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700337
338
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800339 def ctrl_key(self, press_secs=''):
340 """Simulate Enter key button press.
341
342 @param press_secs : Str. Time to press key.
343 """
344 self._server.ctrl_key(press_secs)
Todd Broch9753bd42012-03-21 10:15:08 -0700345
346
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800347 def enter_key(self, press_secs=''):
348 """Simulate Enter key button press.
349
350 @param press_secs : Str. Time to press key.
351 """
352 self._server.enter_key(press_secs)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700353
354
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800355 def refresh_key(self, press_secs=''):
356 """Simulate Refresh key (F3) button press.
357
358 @param press_secs : Str. Time to press key.
359 """
360 self._server.refresh_key(press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700361
362
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800363 def ctrl_refresh_key(self, press_secs=''):
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800364 """Simulate Ctrl and Refresh (F3) simultaneous press.
365
366 This key combination is an alternative of Space key.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800367
368 @param press_secs : Str. Time to press key.
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800369 """
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800370 self._server.ctrl_refresh_key(press_secs)
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800371
372
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800373 def imaginary_key(self, press_secs=''):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700374 """Simulate imaginary key button press.
375
376 Maps to a key that doesn't physically exist.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800377
378 @param press_secs : Str. Time to press key.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700379 """
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800380 self._server.imaginary_key(press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700381
382
Vincent Palatine7dc9282016-07-14 11:31:58 +0200383 def sysrq_x(self, press_secs=''):
384 """Simulate Alt VolumeUp X simulataneous press.
385
386 This key combination is the kernel system request (sysrq) X.
387
388 @param press_secs : Str. Time to press key.
389 """
390 self._server.sysrq_x(press_secs)
391
392
Tom Wai-Hong Tam93b5c092015-05-14 02:50:43 +0800393 def toggle_recovery_switch(self):
394 """Toggle recovery switch on and off."""
395 self.enable_recovery_mode()
396 time.sleep(self.REC_TOGGLE_DELAY)
397 self.disable_recovery_mode()
398
399
Craig Harrison6b36b122011-06-28 17:58:43 -0700400 def enable_recovery_mode(self):
401 """Enable recovery mode on device."""
402 self.set('rec_mode', 'on')
403
404
405 def disable_recovery_mode(self):
406 """Disable recovery mode on device."""
407 self.set('rec_mode', 'off')
408
409
Tom Wai-Hong Tamf9ded092015-05-20 05:37:22 +0800410 def toggle_development_switch(self):
411 """Toggle development switch on and off."""
412 self.enable_development_mode()
413 time.sleep(self.DEV_TOGGLE_DELAY)
414 self.disable_development_mode()
415
416
Craig Harrison6b36b122011-06-28 17:58:43 -0700417 def enable_development_mode(self):
418 """Enable development mode on device."""
419 self.set('dev_mode', 'on')
420
421
422 def disable_development_mode(self):
423 """Disable development mode on device."""
424 self.set('dev_mode', 'off')
425
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700426 def boot_devmode(self):
427 """Boot a dev-mode device that is powered off."""
Tom Wai-Hong Tam23869102012-01-17 15:41:30 +0800428 self.power_short_press()
Craig Harrison48997262011-06-27 14:31:10 -0700429 self.pass_devmode()
430
431
432 def pass_devmode(self):
433 """Pass through boot screens in dev-mode."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700434 time.sleep(Servo.BOOT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700435 self.ctrl_d()
Chrome Bot9a1137d2011-07-19 14:35:00 -0700436 time.sleep(Servo.BOOT_DELAY)
Craig Harrison48997262011-06-27 14:31:10 -0700437
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700438
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800439 def get_board(self):
440 """Get the board connected to servod.
441
442 """
443 return self._server.get_board()
444
445
Todd Brochefe72cb2012-07-11 19:58:53 -0700446 def _get_xmlrpclib_exception(self, xmlexc):
447 """Get meaningful exception string from xmlrpc.
448
449 Args:
450 xmlexc: xmlrpclib.Fault object
451
452 xmlrpclib.Fault.faultString has the following format:
453
454 <type 'exception type'>:'actual error message'
455
456 Parse and return the real exception from the servod side instead of the
457 less meaningful one like,
458 <Fault 1: "<type 'exceptions.AttributeError'>:'tca6416' object has no
459 attribute 'hw_driver'">
460
461 Returns:
462 string of underlying exception raised in servod.
463 """
464 return re.sub('^.*>:', '', xmlexc.faultString)
465
466
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700467 def get(self, gpio_name):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700468 """Get the value of a gpio from Servod.
469
470 @param gpio_name Name of the gpio.
471 """
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700472 assert gpio_name
Todd Brochefe72cb2012-07-11 19:58:53 -0700473 try:
Simran Basi1cbc5f22013-07-17 14:23:58 -0700474 return self._server.get(gpio_name)
Todd Brochefe72cb2012-07-11 19:58:53 -0700475 except xmlrpclib.Fault as e:
476 err_msg = "Getting '%s' :: %s" % \
477 (gpio_name, self._get_xmlrpclib_exception(e))
478 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700479
480
481 def set(self, gpio_name, gpio_value):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700482 """Set and check the value of a gpio using Servod.
483
484 @param gpio_name Name of the gpio.
485 @param gpio_value New setting for the gpio.
486 """
Chrome Bot9a1137d2011-07-19 14:35:00 -0700487 self.set_nocheck(gpio_name, gpio_value)
Todd Brochcf7c6652012-02-24 13:03:59 -0800488 retry_count = Servo.GET_RETRY_MAX
489 while gpio_value != self.get(gpio_name) and retry_count:
Ilja H. Friedel04be2bd2014-05-07 21:29:59 -0700490 logging.warning("%s != %s, retry %d", gpio_name, gpio_value,
Todd Brochcf7c6652012-02-24 13:03:59 -0800491 retry_count)
492 retry_count -= 1
493 time.sleep(Servo.SHORT_DELAY)
494 if not retry_count:
495 assert gpio_value == self.get(gpio_name), \
496 'Servo failed to set %s to %s' % (gpio_name, gpio_value)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700497
498
499 def set_nocheck(self, gpio_name, gpio_value):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700500 """Set the value of a gpio using Servod.
501
502 @param gpio_name Name of the gpio.
503 @param gpio_value New setting for the gpio.
504 """
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700505 assert gpio_name and gpio_value
Todd Broch9753bd42012-03-21 10:15:08 -0700506 logging.info('Setting %s to %s', gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700507 try:
Simran Basi1cbc5f22013-07-17 14:23:58 -0700508 self._server.set(gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700509 except xmlrpclib.Fault as e:
510 err_msg = "Setting '%s' to '%s' :: %s" % \
511 (gpio_name, gpio_value, self._get_xmlrpclib_exception(e))
512 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700513
514
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800515 def set_get_all(self, controls):
516 """Set &| get one or more control values.
517
518 @param controls: list of strings, controls to set &| get.
519
520 @raise: error.TestError in case error occurs setting/getting values.
521 """
522 rv = []
523 try:
Vic Yangcad9acb2013-05-21 14:02:05 +0800524 logging.info('Set/get all: %s', str(controls))
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800525 rv = self._server.set_get_all(controls)
526 except xmlrpclib.Fault as e:
527 # TODO(waihong): Remove the following backward compatibility when
528 # the new versions of hdctools are deployed.
529 if 'not supported' in str(e):
530 logging.warning('The servod is too old that set_get_all '
531 'not supported. Use set and get instead.')
532 for control in controls:
533 if ':' in control:
534 (name, value) = control.split(':')
535 if name == 'sleep':
536 time.sleep(float(value))
537 else:
538 self.set_nocheck(name, value)
539 rv.append(True)
540 else:
541 rv.append(self.get(name))
542 else:
543 err_msg = "Problem with '%s' :: %s" % \
544 (controls, self._get_xmlrpclib_exception(e))
545 raise error.TestFail(err_msg)
546 return rv
547
548
Jon Salzc88e5b62011-11-30 14:38:54 +0800549 # TODO(waihong) It may fail if multiple servo's are connected to the same
550 # host. Should look for a better way, like the USB serial name, to identify
551 # the USB device.
Simran Basi741b5d42012-05-18 11:27:15 -0700552 # TODO(sbasi) Remove this code from autoserv once firmware tests have been
553 # updated.
Jon Salzc88e5b62011-11-30 14:38:54 +0800554 def probe_host_usb_dev(self):
555 """Probe the USB disk device plugged-in the servo from the host side.
556
557 It tries to switch the USB mux to make the host unable to see the
558 USB disk and compares the result difference.
559
Jon Salzc88e5b62011-11-30 14:38:54 +0800560 Returns:
561 A string of USB disk path, like '/dev/sdb', or None if not existed.
562 """
563 cmd = 'ls /dev/sd[a-z]'
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800564 original_value = self.get_usbkey_direction()
Jon Salzc88e5b62011-11-30 14:38:54 +0800565
566 # Make the host unable to see the USB disk.
Fang Dengafb88142013-05-30 17:44:31 -0700567 self.switch_usbkey('off')
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800568 no_usb_set = set(self.system_output(cmd, ignore_status=True).split())
Jon Salzc88e5b62011-11-30 14:38:54 +0800569
570 # Make the host able to see the USB disk.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800571 self.switch_usbkey('host')
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800572 has_usb_set = set(self.system_output(cmd, ignore_status=True).split())
Jon Salzc88e5b62011-11-30 14:38:54 +0800573
574 # Back to its original value.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800575 if original_value != self.get_usbkey_direction():
576 self.switch_usbkey(original_value)
Jon Salzc88e5b62011-11-30 14:38:54 +0800577
578 diff_set = has_usb_set - no_usb_set
579 if len(diff_set) == 1:
580 return diff_set.pop()
581 else:
582 return None
583
584
Mike Truty49153d82012-08-21 22:27:30 -0500585 def image_to_servo_usb(self, image_path=None,
586 make_image_noninteractive=False):
587 """Install an image to the USB key plugged into the servo.
588
589 This method may copy any image to the servo USB key including a
590 recovery image or a test image. These images are frequently used
591 for test purposes such as restoring a corrupted image or conducting
592 an upgrade of ec/fw/kernel as part of a test of a specific image part.
593
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700594 @param image_path Path on the host to the recovery image.
595 @param make_image_noninteractive Make the recovery image
596 noninteractive, therefore the DUT
597 will reboot automatically after
598 installation.
Mike Truty49153d82012-08-21 22:27:30 -0500599 """
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700600 # We're about to start plugging/unplugging the USB key. We
601 # don't know the state of the DUT, or what it might choose
602 # to do to the device after hotplug. To avoid surprises,
603 # force the DUT to be off.
604 self._server.hwinit()
605 self._power_state.power_off()
Mike Truty49153d82012-08-21 22:27:30 -0500606
607 # Set up Servo's usb mux.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800608 self.switch_usbkey('host')
Mike Truty49153d82012-08-21 22:27:30 -0500609 if image_path:
Tom Wai-Hong Tam42f136d2012-10-26 11:11:23 +0800610 logging.info('Searching for usb device and copying image to it. '
611 'Please wait a few minutes...')
Mike Truty49153d82012-08-21 22:27:30 -0500612 if not self._server.download_image_to_usb(image_path):
613 logging.error('Failed to transfer requested image to USB. '
614 'Please take a look at Servo Logs.')
615 raise error.AutotestError('Download image to usb failed.')
616 if make_image_noninteractive:
617 logging.info('Making image noninteractive')
618 if not self._server.make_image_noninteractive():
619 logging.error('Failed to make image noninteractive. '
620 'Please take a look at Servo Logs.')
621
622
Simran Basi741b5d42012-05-18 11:27:15 -0700623 def install_recovery_image(self, image_path=None,
J. Richard Barnetteb6de7e32013-02-14 13:28:04 -0800624 make_image_noninteractive=False):
Dan Shic67f1332016-04-06 12:38:06 -0700625 """Install the recovery image specified by the path onto the DUT.
Jon Salzc88e5b62011-11-30 14:38:54 +0800626
627 This method uses google recovery mode to install a recovery image
628 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 +0800629 board specified by the usb_dev. If no image path is specified
Jon Salzc88e5b62011-11-30 14:38:54 +0800630 we use the recovery image already on the usb image.
631
Dan Shic67f1332016-04-06 12:38:06 -0700632 @param image_path: Path on the host to the recovery image.
633 @param make_image_noninteractive: Make the recovery image
634 noninteractive, therefore the DUT will reboot automatically
635 after installation.
Jon Salzc88e5b62011-11-30 14:38:54 +0800636 """
Mike Truty49153d82012-08-21 22:27:30 -0500637 self.image_to_servo_usb(image_path, make_image_noninteractive)
J. Richard Barnette4b6af0d2014-06-05 09:57:20 -0700638 self._power_state.power_on(rec_mode=self._power_state.REC_ON)
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700639 self.switch_usbkey('dut')
Jon Salzc88e5b62011-11-30 14:38:54 +0800640
641
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800642 def _scp_image(self, image_path):
643 """Copy image to the servo host.
644
645 When programming a firmware image on the DUT, the image must be
646 located on the host to which the servo device is connected. Sometimes
647 servo is controlled by a remote host, in this case the image needs to
648 be transferred to the remote host.
649
650 @param image_path: a string, name of the firmware image file to be
651 transferred.
652 @return: a string, full path name of the copied file on the remote.
653 """
654
655 dest_path = os.path.join('/tmp', os.path.basename(image_path))
Fang Deng5d518f42013-08-02 14:04:32 -0700656 self._servo_host.send_file(image_path, dest_path)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800657 return dest_path
658
659
Dan Shifecdaf42015-07-28 10:17:26 -0700660 def system(self, command, timeout=3600):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700661 """Execute the passed in command on the servod host.
662
663 @param command Command to be executed.
Dan Shifecdaf42015-07-28 10:17:26 -0700664 @param timeout Maximum number of seconds of runtime allowed. Default to
665 1 hour.
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700666 """
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800667 logging.info('Will execute on servo host: %s', command)
Fang Deng5d518f42013-08-02 14:04:32 -0700668 self._servo_host.run(command, timeout=timeout)
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800669
670
Dan Shifecdaf42015-07-28 10:17:26 -0700671 def system_output(self, command, timeout=3600,
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800672 ignore_status=False, args=()):
673 """Execute the passed in command on the servod host, return stdout.
674
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700675 @param command a string, the command to execute
676 @param timeout an int, max number of seconds to wait til command
Dan Shifecdaf42015-07-28 10:17:26 -0700677 execution completes. Default to 1 hour.
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700678 @param ignore_status a Boolean, if true - ignore command's nonzero exit
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800679 status, otherwise an exception will be thrown
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700680 @param args a tuple of strings, each becoming a separate command line
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800681 parameter for the command
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700682 @return command's stdout as a string.
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800683 """
Fang Deng5d518f42013-08-02 14:04:32 -0700684 return self._servo_host.run(command, timeout=timeout,
685 ignore_status=ignore_status,
686 args=args).stdout.strip()
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800687
688
Dan Shia5fef052015-05-18 23:28:47 -0700689 def get_servo_version(self):
690 """Get the version of the servo, e.g., servo_v2 or servo_v3.
691
692 @return: The version of the servo.
693
694 """
695 return self._server.get_version()
696
697
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800698 def _initialize_programmer(self, rw_only=False):
699 """Initialize the firmware programmer.
700
701 @param rw_only: True to initialize a programmer which only
702 programs the RW portions.
703 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700704 if self._programmer:
705 return
706 # Initialize firmware programmer
Dan Shia5fef052015-05-18 23:28:47 -0700707 servo_version = self.get_servo_version()
Dan Shi9cb0eec2014-06-03 09:04:50 -0700708 if servo_version.startswith('servo_v2'):
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700709 self._programmer = firmware_programmer.ProgrammerV2(self)
Dan Shia5fef052015-05-18 23:28:47 -0700710 elif servo_version.startswith('servo_v3'):
711 self._programmer = firmware_programmer.ProgrammerV3(self)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800712 self._programmer_rw = firmware_programmer.ProgrammerV3RwOnly(self)
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700713 else:
714 raise error.TestError(
715 'No firmware programmer for servo version: %s' %
716 servo_version)
717
718
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800719 def program_bios(self, image, rw_only=False):
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800720 """Program bios on DUT with given image.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800721
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800722 @param image: a string, file name of the BIOS image to program
723 on the DUT.
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800724 @param rw_only: True to only program the RW portion of BIOS.
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800725
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800726 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700727 self._initialize_programmer()
Ricky Liangc31aab32014-07-03 16:23:29 +0800728 if not self.is_localhost():
729 image = self._scp_image(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800730 if rw_only:
731 self._programmer_rw.program_bios(image)
732 else:
733 self._programmer.program_bios(image)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800734
735
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800736 def program_ec(self, image, rw_only=False):
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800737 """Program ec on DUT with given image.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800738
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800739 @param image: a string, file name of the EC image to program
740 on the DUT.
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800741 @param rw_only: True to only program the RW portion of EC.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800742
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800743 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700744 self._initialize_programmer()
Ricky Liangc31aab32014-07-03 16:23:29 +0800745 if not self.is_localhost():
746 image = self._scp_image(image)
Tom Wai-Hong Tam4ac78982016-01-08 02:34:37 +0800747 if rw_only:
748 self._programmer_rw.program_ec(image)
749 else:
750 self._programmer.program_ec(image)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800751
Fang Dengafb88142013-05-30 17:44:31 -0700752
753 def _switch_usbkey_power(self, power_state, detection_delay=False):
754 """Switch usbkey power.
755
756 This function switches usbkey power by setting the value of
757 'prtctl4_pwren'. If the power is already in the
758 requested state, this function simply returns.
759
760 @param power_state: A string, 'on' or 'off'.
761 @param detection_delay: A boolean value, if True, sleep
762 for |USB_DETECTION_DELAY| after switching
763 the power on.
764 """
765 self.set('prtctl4_pwren', power_state)
766 if power_state == 'off':
767 time.sleep(self.USB_POWEROFF_DELAY)
768 elif detection_delay:
769 time.sleep(self.USB_DETECTION_DELAY)
770
771
772 def switch_usbkey(self, usb_state):
773 """Connect USB flash stick to either host or DUT, or turn USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800774
775 This function switches the servo multiplexer to provide electrical
Fang Dengafb88142013-05-30 17:44:31 -0700776 connection between the USB port J3 and either host or DUT side. It
777 can also be used to turn the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800778
Fang Dengafb88142013-05-30 17:44:31 -0700779 Switching to 'dut' or 'host' is accompanied by powercycling
780 of the USB stick, because it sometimes gets wedged if the mux
781 is switched while the stick power is on.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800782
Fang Dengafb88142013-05-30 17:44:31 -0700783 @param usb_state: A string, one of 'dut', 'host', or 'off'.
784 'dut' and 'host' indicate which side the
785 USB flash device is required to be connected to.
786 'off' indicates turning the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800787
Fang Dengafb88142013-05-30 17:44:31 -0700788 @raise: error.TestError in case the parameter is not 'dut'
789 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800790 """
Fang Dengafb88142013-05-30 17:44:31 -0700791 if self.get_usbkey_direction() == usb_state:
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800792 return
793
Fang Dengafb88142013-05-30 17:44:31 -0700794 if usb_state == 'off':
Dan Shia5fef052015-05-18 23:28:47 -0700795 self._switch_usbkey_power('off')
796 self._usb_state = usb_state
797 return
Fang Dengafb88142013-05-30 17:44:31 -0700798 elif usb_state == 'host':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800799 mux_direction = 'servo_sees_usbkey'
Fang Dengafb88142013-05-30 17:44:31 -0700800 elif usb_state == 'dut':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800801 mux_direction = 'dut_sees_usbkey'
802 else:
Fang Dengafb88142013-05-30 17:44:31 -0700803 raise error.TestError('Unknown USB state request: %s' % usb_state)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800804
Fang Dengafb88142013-05-30 17:44:31 -0700805 self._switch_usbkey_power('off')
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800806 self.set('usb_mux_sel1', mux_direction)
807 time.sleep(self.USB_POWEROFF_DELAY)
Fang Dengafb88142013-05-30 17:44:31 -0700808 self._switch_usbkey_power('on', usb_state == 'host')
809 self._usb_state = usb_state
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800810
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800811
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800812 def get_usbkey_direction(self):
Fang Dengafb88142013-05-30 17:44:31 -0700813 """Get which side USB is connected to or 'off' if usb power is off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800814
Fang Dengafb88142013-05-30 17:44:31 -0700815 @return: A string, one of 'dut', 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800816 """
Fang Dengafb88142013-05-30 17:44:31 -0700817 if not self._usb_state:
818 if self.get('prtctl4_pwren') == 'off':
819 self._usb_state = 'off'
820 elif self.get('usb_mux_sel1').startswith('dut'):
821 self._usb_state = 'dut'
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800822 else:
Fang Dengafb88142013-05-30 17:44:31 -0700823 self._usb_state = 'host'
824 return self._usb_state