blob: 6159a95bb87fbff5a18c5a608c9acea26400fb6e [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
Jon Salzc88e5b62011-11-30 14:38:54 +0800148 # Time between an usb disk plugged-in and detected in the system.
149 USB_DETECTION_DELAY = 10
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800150 # Time to keep USB power off before and after USB mux direction is changed
151 USB_POWEROFF_DELAY = 2
Jon Salzc88e5b62011-11-30 14:38:54 +0800152
Simran Basib7850bb2013-07-24 12:33:42 -0700153 # Time to wait before timing out on servo initialization.
154 INIT_TIMEOUT_SECS = 10
Simran Basi59331342013-07-12 12:06:29 -0700155
J. Richard Barnette67ccb872012-04-19 16:34:56 -0700156
Ricky Liang0dd379c2014-04-23 16:29:08 +0800157 def __init__(self, servo_host, servo_serial=None):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700158 """Sets up the servo communication infrastructure.
159
Fang Deng5d518f42013-08-02 14:04:32 -0700160 @param servo_host: A ServoHost object representing
161 the host running servod.
Ricky Liang0dd379c2014-04-23 16:29:08 +0800162 @param servo_serial: Serial number of the servo board.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700163 """
Fang Deng5d518f42013-08-02 14:04:32 -0700164 # TODO(fdeng): crbug.com/298379
165 # We should move servo_host object out of servo object
166 # to minimize the dependencies on the rest of Autotest.
167 self._servo_host = servo_host
Ricky Liang0dd379c2014-04-23 16:29:08 +0800168 self._servo_serial = servo_serial
Fang Deng5d518f42013-08-02 14:04:32 -0700169 self._server = servo_host.get_servod_server_proxy()
J. Richard Barnette0c9c5882014-06-11 15:27:05 -0700170 self._power_state = _PowerStateController(self)
Fang Dengafb88142013-05-30 17:44:31 -0700171 self._usb_state = None
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700172 self._programmer = None
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800173
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800174
Ricky Liang0dd379c2014-04-23 16:29:08 +0800175 @property
176 def servo_serial(self):
177 """Returns the serial number of the servo board."""
178 return self._servo_serial
179
180
Tom Wai-Hong Tam22ee0e52013-04-03 13:36:39 +0800181 def get_power_state_controller(self):
J. Richard Barnette75136b32013-03-26 13:38:44 -0700182 """Return the power state controller for this Servo.
183
184 The power state controller provides board-independent
185 interfaces for reset, power-on, power-off operations.
186
187 """
188 return self._power_state
189
Fang Deng5d518f42013-08-02 14:04:32 -0700190
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700191 def initialize_dut(self, cold_reset=False):
192 """Initializes a dut for testing purposes.
193
194 This sets various servo signals back to default values
195 appropriate for the target board. By default, if the DUT
196 is already on, it stays on. If the DUT is powered off
197 before initialization, its state afterward is unspecified.
198
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700199 Rationale: Basic initialization of servo sets the lid open,
200 when there is a lid. This operation won't affect powered on
201 units; however, setting the lid open may power on a unit
J. Richard Barnette75136b32013-03-26 13:38:44 -0700202 that's off, depending on the board type and previous state
203 of the device.
204
J. Richard Barnette4b6af0d2014-06-05 09:57:20 -0700205 If `cold_reset` is a true value, the DUT and its EC will be
206 reset, and the DUT rebooted in normal mode.
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700207
208 @param cold_reset If True, cold reset the device after
209 initialization.
210 """
211 self._server.hwinit()
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700212 self.set('dut_hub_pwren', 'on')
213 self.set('usb_mux_oe1', 'on')
214 self.switch_usbkey('off')
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700215 if cold_reset:
J. Richard Barnette4b6af0d2014-06-05 09:57:20 -0700216 self._power_state.reset()
J. Richard Barnette1777f5d2014-09-03 15:18:19 -0700217 logging.debug('Servo initialized, version is %s',
218 self._server.get_version())
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700219
220
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800221 def is_localhost(self):
222 """Is the servod hosted locally?
223
224 Returns:
225 True if local hosted; otherwise, False.
226 """
Fang Deng5d518f42013-08-02 14:04:32 -0700227 return self._servo_host.is_localhost()
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800228
229
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700230 def power_long_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700231 """Simulate a long power button press."""
J. Richard Barnettef12bff22012-07-18 14:41:06 -0700232 # After a long power press, the EC may ignore the next power
233 # button press (at least on Alex). To guarantee that this
234 # won't happen, we need to allow the EC one second to
235 # collect itself.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800236 self._server.power_long_press()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700237
238
239 def power_normal_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700240 """Simulate a normal power button press."""
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800241 self._server.power_normal_press()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700242
243
244 def power_short_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700245 """Simulate a short power button press."""
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800246 self._server.power_short_press()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700247
248
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800249 def power_key(self, press_secs=''):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700250 """Simulate a power button press.
251
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800252 @param press_secs : Str. Time to press key.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700253 """
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800254 self._server.power_key(press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700255
256
257 def lid_open(self):
258 """Simulate opening the lid."""
Craig Harrison48997262011-06-27 14:31:10 -0700259 self.set_nocheck('lid_open', 'yes')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700260
261
262 def lid_close(self):
Craig Harrison48997262011-06-27 14:31:10 -0700263 """Simulate closing the lid.
264
265 Waits 6 seconds to ensure the device is fully asleep before returning.
266 """
267 self.set_nocheck('lid_open', 'no')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700268 time.sleep(Servo.SLEEP_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700269
270
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800271 def ctrl_d(self, press_secs=''):
272 """Simulate Ctrl-d simultaneous button presses.
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800273
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800274 @param press_secs : Str. Time to press key.
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800275 """
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800276 self._server.ctrl_d(press_secs)
Gediminas Ramanauskas9da80f22012-11-14 12:59:43 -0800277
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800278
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800279 def ctrl_u(self):
280 """Simulate Ctrl-u simultaneous button presses.
281
282 @param press_secs : Str. Time to press key.
283 """
284 self._server.ctrl_u()
Todd Broch9dfc3a82011-11-01 08:09:28 -0700285
286
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800287 def ctrl_enter(self, press_secs=''):
288 """Simulate Ctrl-enter simultaneous button presses.
289
290 @param press_secs : Str. Time to press key.
291 """
292 self._server.ctrl_enter(press_secs)
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -0700293
294
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800295 def d_key(self, press_secs=''):
296 """Simulate Enter key button press.
297
298 @param press_secs : Str. Time to press key.
299 """
300 self._server.d_key(press_secs)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700301
302
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800303 def ctrl_key(self, press_secs=''):
304 """Simulate Enter key button press.
305
306 @param press_secs : Str. Time to press key.
307 """
308 self._server.ctrl_key(press_secs)
Todd Broch9753bd42012-03-21 10:15:08 -0700309
310
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800311 def enter_key(self, press_secs=''):
312 """Simulate Enter key button press.
313
314 @param press_secs : Str. Time to press key.
315 """
316 self._server.enter_key(press_secs)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700317
318
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800319 def refresh_key(self, press_secs=''):
320 """Simulate Refresh key (F3) button press.
321
322 @param press_secs : Str. Time to press key.
323 """
324 self._server.refresh_key(press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700325
326
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800327 def ctrl_refresh_key(self, press_secs=''):
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800328 """Simulate Ctrl and Refresh (F3) simultaneous press.
329
330 This key combination is an alternative of Space key.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800331
332 @param press_secs : Str. Time to press key.
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800333 """
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800334 self._server.ctrl_refresh_key(press_secs)
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800335
336
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800337 def imaginary_key(self, press_secs=''):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700338 """Simulate imaginary key button press.
339
340 Maps to a key that doesn't physically exist.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800341
342 @param press_secs : Str. Time to press key.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700343 """
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800344 self._server.imaginary_key(press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700345
346
Tom Wai-Hong Tam93b5c092015-05-14 02:50:43 +0800347 def toggle_recovery_switch(self):
348 """Toggle recovery switch on and off."""
349 self.enable_recovery_mode()
350 time.sleep(self.REC_TOGGLE_DELAY)
351 self.disable_recovery_mode()
352
353
Craig Harrison6b36b122011-06-28 17:58:43 -0700354 def enable_recovery_mode(self):
355 """Enable recovery mode on device."""
356 self.set('rec_mode', 'on')
357
358
359 def disable_recovery_mode(self):
360 """Disable recovery mode on device."""
361 self.set('rec_mode', 'off')
362
363
364 def enable_development_mode(self):
365 """Enable development mode on device."""
366 self.set('dev_mode', 'on')
367
368
369 def disable_development_mode(self):
370 """Disable development mode on device."""
371 self.set('dev_mode', 'off')
372
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700373 def boot_devmode(self):
374 """Boot a dev-mode device that is powered off."""
Tom Wai-Hong Tam23869102012-01-17 15:41:30 +0800375 self.power_short_press()
Craig Harrison48997262011-06-27 14:31:10 -0700376 self.pass_devmode()
377
378
379 def pass_devmode(self):
380 """Pass through boot screens in dev-mode."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700381 time.sleep(Servo.BOOT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700382 self.ctrl_d()
Chrome Bot9a1137d2011-07-19 14:35:00 -0700383 time.sleep(Servo.BOOT_DELAY)
Craig Harrison48997262011-06-27 14:31:10 -0700384
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700385
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800386 def get_board(self):
387 """Get the board connected to servod.
388
389 """
390 return self._server.get_board()
391
392
Todd Brochefe72cb2012-07-11 19:58:53 -0700393 def _get_xmlrpclib_exception(self, xmlexc):
394 """Get meaningful exception string from xmlrpc.
395
396 Args:
397 xmlexc: xmlrpclib.Fault object
398
399 xmlrpclib.Fault.faultString has the following format:
400
401 <type 'exception type'>:'actual error message'
402
403 Parse and return the real exception from the servod side instead of the
404 less meaningful one like,
405 <Fault 1: "<type 'exceptions.AttributeError'>:'tca6416' object has no
406 attribute 'hw_driver'">
407
408 Returns:
409 string of underlying exception raised in servod.
410 """
411 return re.sub('^.*>:', '', xmlexc.faultString)
412
413
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700414 def get(self, gpio_name):
415 """Get the value of a gpio from Servod."""
416 assert gpio_name
Todd Brochefe72cb2012-07-11 19:58:53 -0700417 try:
Simran Basi1cbc5f22013-07-17 14:23:58 -0700418 return self._server.get(gpio_name)
Todd Brochefe72cb2012-07-11 19:58:53 -0700419 except xmlrpclib.Fault as e:
420 err_msg = "Getting '%s' :: %s" % \
421 (gpio_name, self._get_xmlrpclib_exception(e))
422 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700423
424
425 def set(self, gpio_name, gpio_value):
426 """Set and check the value of a gpio using Servod."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700427 self.set_nocheck(gpio_name, gpio_value)
Todd Brochcf7c6652012-02-24 13:03:59 -0800428 retry_count = Servo.GET_RETRY_MAX
429 while gpio_value != self.get(gpio_name) and retry_count:
Ilja H. Friedel04be2bd2014-05-07 21:29:59 -0700430 logging.warning("%s != %s, retry %d", gpio_name, gpio_value,
Todd Brochcf7c6652012-02-24 13:03:59 -0800431 retry_count)
432 retry_count -= 1
433 time.sleep(Servo.SHORT_DELAY)
434 if not retry_count:
435 assert gpio_value == self.get(gpio_name), \
436 'Servo failed to set %s to %s' % (gpio_name, gpio_value)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700437
438
439 def set_nocheck(self, gpio_name, gpio_value):
440 """Set the value of a gpio using Servod."""
441 assert gpio_name and gpio_value
Todd Broch9753bd42012-03-21 10:15:08 -0700442 logging.info('Setting %s to %s', gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700443 try:
Simran Basi1cbc5f22013-07-17 14:23:58 -0700444 self._server.set(gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700445 except xmlrpclib.Fault as e:
446 err_msg = "Setting '%s' to '%s' :: %s" % \
447 (gpio_name, gpio_value, self._get_xmlrpclib_exception(e))
448 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700449
450
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800451 def set_get_all(self, controls):
452 """Set &| get one or more control values.
453
454 @param controls: list of strings, controls to set &| get.
455
456 @raise: error.TestError in case error occurs setting/getting values.
457 """
458 rv = []
459 try:
Vic Yangcad9acb2013-05-21 14:02:05 +0800460 logging.info('Set/get all: %s', str(controls))
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800461 rv = self._server.set_get_all(controls)
462 except xmlrpclib.Fault as e:
463 # TODO(waihong): Remove the following backward compatibility when
464 # the new versions of hdctools are deployed.
465 if 'not supported' in str(e):
466 logging.warning('The servod is too old that set_get_all '
467 'not supported. Use set and get instead.')
468 for control in controls:
469 if ':' in control:
470 (name, value) = control.split(':')
471 if name == 'sleep':
472 time.sleep(float(value))
473 else:
474 self.set_nocheck(name, value)
475 rv.append(True)
476 else:
477 rv.append(self.get(name))
478 else:
479 err_msg = "Problem with '%s' :: %s" % \
480 (controls, self._get_xmlrpclib_exception(e))
481 raise error.TestFail(err_msg)
482 return rv
483
484
Jon Salzc88e5b62011-11-30 14:38:54 +0800485 # TODO(waihong) It may fail if multiple servo's are connected to the same
486 # host. Should look for a better way, like the USB serial name, to identify
487 # the USB device.
Simran Basi741b5d42012-05-18 11:27:15 -0700488 # TODO(sbasi) Remove this code from autoserv once firmware tests have been
489 # updated.
Jon Salzc88e5b62011-11-30 14:38:54 +0800490 def probe_host_usb_dev(self):
491 """Probe the USB disk device plugged-in the servo from the host side.
492
493 It tries to switch the USB mux to make the host unable to see the
494 USB disk and compares the result difference.
495
Jon Salzc88e5b62011-11-30 14:38:54 +0800496 Returns:
497 A string of USB disk path, like '/dev/sdb', or None if not existed.
498 """
499 cmd = 'ls /dev/sd[a-z]'
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800500 original_value = self.get_usbkey_direction()
Jon Salzc88e5b62011-11-30 14:38:54 +0800501
502 # Make the host unable to see the USB disk.
Fang Dengafb88142013-05-30 17:44:31 -0700503 self.switch_usbkey('off')
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800504 no_usb_set = set(self.system_output(cmd, ignore_status=True).split())
Jon Salzc88e5b62011-11-30 14:38:54 +0800505
506 # Make the host able to see the USB disk.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800507 self.switch_usbkey('host')
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800508 has_usb_set = set(self.system_output(cmd, ignore_status=True).split())
Jon Salzc88e5b62011-11-30 14:38:54 +0800509
510 # Back to its original value.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800511 if original_value != self.get_usbkey_direction():
512 self.switch_usbkey(original_value)
Jon Salzc88e5b62011-11-30 14:38:54 +0800513
514 diff_set = has_usb_set - no_usb_set
515 if len(diff_set) == 1:
516 return diff_set.pop()
517 else:
518 return None
519
520
Mike Truty49153d82012-08-21 22:27:30 -0500521 def image_to_servo_usb(self, image_path=None,
522 make_image_noninteractive=False):
523 """Install an image to the USB key plugged into the servo.
524
525 This method may copy any image to the servo USB key including a
526 recovery image or a test image. These images are frequently used
527 for test purposes such as restoring a corrupted image or conducting
528 an upgrade of ec/fw/kernel as part of a test of a specific image part.
529
530 Args:
531 image_path: Path on the host to the recovery image.
532 make_image_noninteractive: Make the recovery image noninteractive,
533 therefore the DUT will reboot
534 automatically after installation.
535 """
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700536 # We're about to start plugging/unplugging the USB key. We
537 # don't know the state of the DUT, or what it might choose
538 # to do to the device after hotplug. To avoid surprises,
539 # force the DUT to be off.
540 self._server.hwinit()
541 self._power_state.power_off()
Mike Truty49153d82012-08-21 22:27:30 -0500542
543 # Set up Servo's usb mux.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800544 self.switch_usbkey('host')
Mike Truty49153d82012-08-21 22:27:30 -0500545 if image_path:
Tom Wai-Hong Tam42f136d2012-10-26 11:11:23 +0800546 logging.info('Searching for usb device and copying image to it. '
547 'Please wait a few minutes...')
Mike Truty49153d82012-08-21 22:27:30 -0500548 if not self._server.download_image_to_usb(image_path):
549 logging.error('Failed to transfer requested image to USB. '
550 'Please take a look at Servo Logs.')
551 raise error.AutotestError('Download image to usb failed.')
552 if make_image_noninteractive:
553 logging.info('Making image noninteractive')
554 if not self._server.make_image_noninteractive():
555 logging.error('Failed to make image noninteractive. '
556 'Please take a look at Servo Logs.')
557
558
Simran Basi741b5d42012-05-18 11:27:15 -0700559 def install_recovery_image(self, image_path=None,
J. Richard Barnetteb6de7e32013-02-14 13:28:04 -0800560 make_image_noninteractive=False):
Jon Salzc88e5b62011-11-30 14:38:54 +0800561 """Install the recovery image specied by the path onto the DUT.
562
563 This method uses google recovery mode to install a recovery image
564 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 +0800565 board specified by the usb_dev. If no image path is specified
Jon Salzc88e5b62011-11-30 14:38:54 +0800566 we use the recovery image already on the usb image.
567
568 Args:
569 image_path: Path on the host to the recovery image.
Simran Basi741b5d42012-05-18 11:27:15 -0700570 make_image_noninteractive: Make the recovery image noninteractive,
571 therefore the DUT will reboot
572 automatically after installation.
Jon Salzc88e5b62011-11-30 14:38:54 +0800573 """
Mike Truty49153d82012-08-21 22:27:30 -0500574 self.image_to_servo_usb(image_path, make_image_noninteractive)
J. Richard Barnette4b6af0d2014-06-05 09:57:20 -0700575 self._power_state.power_on(rec_mode=self._power_state.REC_ON)
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700576 self.switch_usbkey('dut')
Jon Salzc88e5b62011-11-30 14:38:54 +0800577
578
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800579 def _scp_image(self, image_path):
580 """Copy image to the servo host.
581
582 When programming a firmware image on the DUT, the image must be
583 located on the host to which the servo device is connected. Sometimes
584 servo is controlled by a remote host, in this case the image needs to
585 be transferred to the remote host.
586
587 @param image_path: a string, name of the firmware image file to be
588 transferred.
589 @return: a string, full path name of the copied file on the remote.
590 """
591
592 dest_path = os.path.join('/tmp', os.path.basename(image_path))
Fang Deng5d518f42013-08-02 14:04:32 -0700593 self._servo_host.send_file(image_path, dest_path)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800594 return dest_path
595
596
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800597 def system(self, command, timeout=None):
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800598 """Execute the passed in command on the servod host."""
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800599 logging.info('Will execute on servo host: %s', command)
Fang Deng5d518f42013-08-02 14:04:32 -0700600 self._servo_host.run(command, timeout=timeout)
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800601
602
603 def system_output(self, command, timeout=None,
604 ignore_status=False, args=()):
605 """Execute the passed in command on the servod host, return stdout.
606
607 @param command, a string, the command to execute
608 @param timeout, an int, max number of seconds to wait til command
609 execution completes
610 @ignore_status, a Boolean, if true - ignore command's nonzero exit
611 status, otherwise an exception will be thrown
612 @param args, a tuple of strings, each becoming a separate command line
613 parameter for the command
614 @return: command's stdout as a string.
615 """
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800616 logging.info('Will execute and collect output on servo host: %s %s',
617 command, ' '.join("'%s'" % x for x in args))
Fang Deng5d518f42013-08-02 14:04:32 -0700618 return self._servo_host.run(command, timeout=timeout,
619 ignore_status=ignore_status,
620 args=args).stdout.strip()
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800621
622
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700623 def _initialize_programmer(self):
624 if self._programmer:
625 return
626 # Initialize firmware programmer
J. Richard Barnette1777f5d2014-09-03 15:18:19 -0700627 servo_version = self._server.get_version()
Dan Shi9cb0eec2014-06-03 09:04:50 -0700628 if servo_version.startswith('servo_v2'):
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700629 self._programmer = firmware_programmer.ProgrammerV2(self)
630 else:
631 raise error.TestError(
632 'No firmware programmer for servo version: %s' %
633 servo_version)
634
635
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800636 def program_bios(self, image):
637 """Program bios on DUT with given image.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800638
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800639 @param image: a string, file name of the BIOS image to program
640 on the DUT.
641
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800642 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700643 self._initialize_programmer()
Ricky Liangc31aab32014-07-03 16:23:29 +0800644 if not self.is_localhost():
645 image = self._scp_image(image)
646 self._programmer.program_bios(image)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800647
648
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800649 def program_ec(self, image):
650 """Program ec on DUT with given image.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800651
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800652 @param image: a string, file name of the EC image to program
653 on the DUT.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800654
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800655 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700656 self._initialize_programmer()
Ricky Liangc31aab32014-07-03 16:23:29 +0800657 if not self.is_localhost():
658 image = self._scp_image(image)
659 self._programmer.program_ec(image)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800660
Fang Dengafb88142013-05-30 17:44:31 -0700661
662 def _switch_usbkey_power(self, power_state, detection_delay=False):
663 """Switch usbkey power.
664
665 This function switches usbkey power by setting the value of
666 'prtctl4_pwren'. If the power is already in the
667 requested state, this function simply returns.
668
669 @param power_state: A string, 'on' or 'off'.
670 @param detection_delay: A boolean value, if True, sleep
671 for |USB_DETECTION_DELAY| after switching
672 the power on.
673 """
674 self.set('prtctl4_pwren', power_state)
675 if power_state == 'off':
676 time.sleep(self.USB_POWEROFF_DELAY)
677 elif detection_delay:
678 time.sleep(self.USB_DETECTION_DELAY)
679
680
681 def switch_usbkey(self, usb_state):
682 """Connect USB flash stick to either host or DUT, or turn USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800683
684 This function switches the servo multiplexer to provide electrical
Fang Dengafb88142013-05-30 17:44:31 -0700685 connection between the USB port J3 and either host or DUT side. It
686 can also be used to turn the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800687
Fang Dengafb88142013-05-30 17:44:31 -0700688 Switching to 'dut' or 'host' is accompanied by powercycling
689 of the USB stick, because it sometimes gets wedged if the mux
690 is switched while the stick power is on.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800691
Fang Dengafb88142013-05-30 17:44:31 -0700692 @param usb_state: A string, one of 'dut', 'host', or 'off'.
693 'dut' and 'host' indicate which side the
694 USB flash device is required to be connected to.
695 'off' indicates turning the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800696
Fang Dengafb88142013-05-30 17:44:31 -0700697 @raise: error.TestError in case the parameter is not 'dut'
698 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800699 """
Fang Dengafb88142013-05-30 17:44:31 -0700700 if self.get_usbkey_direction() == usb_state:
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800701 return
702
Fang Dengafb88142013-05-30 17:44:31 -0700703 if usb_state == 'off':
704 self._switch_usbkey_power('off')
705 self._usb_state = usb_state
706 return
707 elif usb_state == 'host':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800708 mux_direction = 'servo_sees_usbkey'
Fang Dengafb88142013-05-30 17:44:31 -0700709 elif usb_state == 'dut':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800710 mux_direction = 'dut_sees_usbkey'
711 else:
Fang Dengafb88142013-05-30 17:44:31 -0700712 raise error.TestError('Unknown USB state request: %s' % usb_state)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800713
Fang Dengafb88142013-05-30 17:44:31 -0700714 self._switch_usbkey_power('off')
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800715 self.set('usb_mux_sel1', mux_direction)
716 time.sleep(self.USB_POWEROFF_DELAY)
Fang Dengafb88142013-05-30 17:44:31 -0700717 self._switch_usbkey_power('on', usb_state == 'host')
718 self._usb_state = usb_state
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800719
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800720
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800721 def get_usbkey_direction(self):
Fang Dengafb88142013-05-30 17:44:31 -0700722 """Get which side USB is connected to or 'off' if usb power is off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800723
Fang Dengafb88142013-05-30 17:44:31 -0700724 @return: A string, one of 'dut', 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800725 """
Fang Dengafb88142013-05-30 17:44:31 -0700726 if not self._usb_state:
727 if self.get('prtctl4_pwren') == 'off':
728 self._usb_state = 'off'
729 elif self.get('usb_mux_sel1').startswith('dut'):
730 self._usb_state = 'dut'
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800731 else:
Fang Dengafb88142013-05-30 17:44:31 -0700732 self._usb_state = 'host'
733 return self._usb_state