blob: 46795c45aab0b590e66f8845bbd60a15a3d9f767 [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
Dan Shia5fef052015-05-18 23:28:47 -070013from autotest_lib.client.common_lib import utils
Ricky Liangc31aab32014-07-03 16:23:29 +080014from autotest_lib.server.cros.servo import firmware_programmer
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070015
J. Richard Barnette41320ee2013-03-11 13:00:13 -070016
J. Richard Barnette0c9c5882014-06-11 15:27:05 -070017class _PowerStateController(object):
18
19 """Class to provide board-specific power operations.
20
21 This class is responsible for "power on" and "power off"
22 operations that can operate without making assumptions in
23 advance about board state. It offers an interface that
24 abstracts out the different sequences required for different
25 board types.
26
27 """
28
29 # Constants acceptable to be passed for the `rec_mode` parameter
30 # to power_on().
31 #
32 # REC_ON: Boot the DUT in recovery mode, i.e. boot from USB or
33 # SD card.
34 # REC_OFF: Boot in normal mode, i.e. boot from internal storage.
35
36 REC_ON = 'rec'
37 REC_OFF = 'on'
38
39 # Delay in seconds needed between asserting and de-asserting
40 # warm reset.
41 _RESET_HOLD_TIME = 0.5
42
43 def __init__(self, servo):
44 """Initialize the power state control.
45
46 @param servo Servo object providing the underlying `set` and `get`
47 methods for the target controls.
48
49 """
50 self._servo = servo
51
52 def reset(self):
53 """Force the DUT to reset.
54
55 The DUT is guaranteed to be on at the end of this call,
56 regardless of its previous state, provided that there is
57 working OS software. This also guarantees that the EC has
58 been restarted.
59
60 """
61 self._servo.set_nocheck('power_state', 'reset')
62
63 def warm_reset(self):
64 """Apply warm reset to the DUT.
65
66 This asserts, then de-asserts the 'warm_reset' signal.
67 Generally, this causes the board to restart.
68
69 """
70 self._servo.set_get_all(['warm_reset:on',
71 'sleep:%.4f' % self._RESET_HOLD_TIME,
72 'warm_reset:off'])
73
J. Richard Barnette0c9c5882014-06-11 15:27:05 -070074 def power_off(self):
75 """Force the DUT to power off.
76
77 The DUT is guaranteed to be off at the end of this call,
78 regardless of its previous state, provided that there is
79 working EC and boot firmware. There is no requirement for
80 working OS software.
81
82 """
83 self._servo.set_nocheck('power_state', 'off')
84
85 def power_on(self, rec_mode=REC_OFF):
86 """Force the DUT to power on.
87
88 Prior to calling this function, the DUT must be powered off,
89 e.g. with a call to `power_off()`.
90
91 At power on, recovery mode is set as specified by the
92 corresponding argument. When booting with recovery mode on, it
93 is the caller's responsibility to unplug/plug in a bootable
94 external storage device.
95
96 If the DUT requires a delay after powering on but before
97 processing inputs such as USB stick insertion, the delay is
98 handled by this method; the caller is not responsible for such
99 delays.
100
101 @param rec_mode Setting of recovery mode to be applied at
102 power on. default: REC_OFF aka 'off'
103
104 """
105 self._servo.set_nocheck('power_state', rec_mode)
106
107
J. Richard Barnette384056b2012-04-16 11:04:46 -0700108class Servo(object):
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700109
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700110 """Manages control of a Servo board.
111
112 Servo is a board developed by hardware group to aide in the debug and
113 control of various partner devices. Servo's features include the simulation
114 of pressing the power button, closing the lid, and pressing Ctrl-d. This
115 class manages setting up and communicating with a servo demon (servod)
116 process. It provides both high-level functions for common servo tasks and
117 low-level functions for directly setting and reading gpios.
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700118
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700119 """
120
Chrome Bot9a1137d2011-07-19 14:35:00 -0700121 # Power button press delays in seconds.
J. Richard Barnetted2e4cbd2012-06-29 12:18:40 -0700122 #
J. Richard Barnette5383f072012-07-26 17:35:40 -0700123 # The EC specification says that 8.0 seconds should be enough
124 # for the long power press. However, some platforms need a bit
125 # more time. Empirical testing has found these requirements:
126 # Alex: 8.2 seconds
127 # ZGB: 8.5 seconds
128 # The actual value is set to the largest known necessary value.
129 #
130 # TODO(jrbarnette) Being generous is the right thing to do for
131 # existing platforms, but if this code is to be used for
132 # qualification of new hardware, we should be less generous.
Chrome Bot9a1137d2011-07-19 14:35:00 -0700133 SHORT_DELAY = 0.1
J. Richard Barnette5383f072012-07-26 17:35:40 -0700134
Todd Broch31c82502011-08-29 08:14:39 -0700135 # Maximum number of times to re-read power button on release.
Todd Brochcf7c6652012-02-24 13:03:59 -0800136 GET_RETRY_MAX = 10
Chrome Bot9a1137d2011-07-19 14:35:00 -0700137
J. Richard Barnette5383f072012-07-26 17:35:40 -0700138 # Delays to deal with DUT state transitions.
Chrome Bot9a1137d2011-07-19 14:35:00 -0700139 SLEEP_DELAY = 6
140 BOOT_DELAY = 10
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700141
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700142 # Default minimum time interval between 'press' and 'release'
143 # keyboard events.
Vic Yang0aca1c22012-11-19 18:33:56 -0800144 SERVO_KEY_PRESS_DELAY = 0.1
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700145
Tom Wai-Hong Tam93b5c092015-05-14 02:50:43 +0800146 # Time to toggle recovery switch on and off.
147 REC_TOGGLE_DELAY = 0.1
148
Tom Wai-Hong Tamf9ded092015-05-20 05:37:22 +0800149 # Time to toggle development switch on and off.
150 DEV_TOGGLE_DELAY = 0.1
151
Jon Salzc88e5b62011-11-30 14:38:54 +0800152 # Time between an usb disk plugged-in and detected in the system.
153 USB_DETECTION_DELAY = 10
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800154 # Time to keep USB power off before and after USB mux direction is changed
155 USB_POWEROFF_DELAY = 2
Jon Salzc88e5b62011-11-30 14:38:54 +0800156
Simran Basib7850bb2013-07-24 12:33:42 -0700157 # Time to wait before timing out on servo initialization.
158 INIT_TIMEOUT_SECS = 10
Simran Basi59331342013-07-12 12:06:29 -0700159
J. Richard Barnette67ccb872012-04-19 16:34:56 -0700160
Ricky Liang0dd379c2014-04-23 16:29:08 +0800161 def __init__(self, servo_host, servo_serial=None):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700162 """Sets up the servo communication infrastructure.
163
Fang Deng5d518f42013-08-02 14:04:32 -0700164 @param servo_host: A ServoHost object representing
165 the host running servod.
Ricky Liang0dd379c2014-04-23 16:29:08 +0800166 @param servo_serial: Serial number of the servo board.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700167 """
Fang Deng5d518f42013-08-02 14:04:32 -0700168 # TODO(fdeng): crbug.com/298379
169 # We should move servo_host object out of servo object
170 # to minimize the dependencies on the rest of Autotest.
171 self._servo_host = servo_host
Ricky Liang0dd379c2014-04-23 16:29:08 +0800172 self._servo_serial = servo_serial
Fang Deng5d518f42013-08-02 14:04:32 -0700173 self._server = servo_host.get_servod_server_proxy()
J. Richard Barnette0c9c5882014-06-11 15:27:05 -0700174 self._power_state = _PowerStateController(self)
Fang Dengafb88142013-05-30 17:44:31 -0700175 self._usb_state = None
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700176 self._programmer = None
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800177
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800178
Ricky Liang0dd379c2014-04-23 16:29:08 +0800179 @property
180 def servo_serial(self):
181 """Returns the serial number of the servo board."""
182 return self._servo_serial
183
184
Tom Wai-Hong Tam22ee0e52013-04-03 13:36:39 +0800185 def get_power_state_controller(self):
J. Richard Barnette75136b32013-03-26 13:38:44 -0700186 """Return the power state controller for this Servo.
187
188 The power state controller provides board-independent
189 interfaces for reset, power-on, power-off operations.
190
191 """
192 return self._power_state
193
Fang Deng5d518f42013-08-02 14:04:32 -0700194
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700195 def initialize_dut(self, cold_reset=False):
196 """Initializes a dut for testing purposes.
197
198 This sets various servo signals back to default values
199 appropriate for the target board. By default, if the DUT
200 is already on, it stays on. If the DUT is powered off
201 before initialization, its state afterward is unspecified.
202
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700203 Rationale: Basic initialization of servo sets the lid open,
204 when there is a lid. This operation won't affect powered on
205 units; however, setting the lid open may power on a unit
J. Richard Barnette75136b32013-03-26 13:38:44 -0700206 that's off, depending on the board type and previous state
207 of the device.
208
J. Richard Barnette4b6af0d2014-06-05 09:57:20 -0700209 If `cold_reset` is a true value, the DUT and its EC will be
210 reset, and the DUT rebooted in normal mode.
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700211
212 @param cold_reset If True, cold reset the device after
213 initialization.
214 """
215 self._server.hwinit()
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700216 self.set('dut_hub_pwren', 'on')
217 self.set('usb_mux_oe1', 'on')
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700218 self._usb_state = None
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700219 self.switch_usbkey('off')
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700220 if cold_reset:
J. Richard Barnette4b6af0d2014-06-05 09:57:20 -0700221 self._power_state.reset()
J. Richard Barnette1777f5d2014-09-03 15:18:19 -0700222 logging.debug('Servo initialized, version is %s',
223 self._server.get_version())
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700224
225
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800226 def is_localhost(self):
227 """Is the servod hosted locally?
228
229 Returns:
230 True if local hosted; otherwise, False.
231 """
Fang Deng5d518f42013-08-02 14:04:32 -0700232 return self._servo_host.is_localhost()
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800233
234
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700235 def power_long_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700236 """Simulate a long power button press."""
J. Richard Barnettef12bff22012-07-18 14:41:06 -0700237 # After a long power press, the EC may ignore the next power
238 # button press (at least on Alex). To guarantee that this
239 # won't happen, we need to allow the EC one second to
240 # collect itself.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800241 self._server.power_long_press()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700242
243
244 def power_normal_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700245 """Simulate a normal power button press."""
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800246 self._server.power_normal_press()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700247
248
249 def power_short_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700250 """Simulate a short power button press."""
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800251 self._server.power_short_press()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700252
253
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800254 def power_key(self, press_secs=''):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700255 """Simulate a power button press.
256
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800257 @param press_secs : Str. Time to press key.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700258 """
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800259 self._server.power_key(press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700260
261
262 def lid_open(self):
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800263 """Simulate opening the lid and raise exception if all attempts fail"""
264 self.set('lid_open', 'yes')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700265
266
267 def lid_close(self):
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800268 """Simulate closing the lid and raise exception if all attempts fail
Craig Harrison48997262011-06-27 14:31:10 -0700269
270 Waits 6 seconds to ensure the device is fully asleep before returning.
271 """
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800272 self.set('lid_open', 'no')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700273 time.sleep(Servo.SLEEP_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700274
275
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800276 def ctrl_d(self, press_secs=''):
277 """Simulate Ctrl-d simultaneous button presses.
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800278
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800279 @param press_secs : Str. Time to press key.
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800280 """
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800281 self._server.ctrl_d(press_secs)
Gediminas Ramanauskas9da80f22012-11-14 12:59:43 -0800282
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800283
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800284 def ctrl_u(self):
285 """Simulate Ctrl-u simultaneous button presses.
286
287 @param press_secs : Str. Time to press key.
288 """
289 self._server.ctrl_u()
Todd Broch9dfc3a82011-11-01 08:09:28 -0700290
291
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800292 def ctrl_enter(self, press_secs=''):
293 """Simulate Ctrl-enter simultaneous button presses.
294
295 @param press_secs : Str. Time to press key.
296 """
297 self._server.ctrl_enter(press_secs)
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -0700298
299
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800300 def d_key(self, press_secs=''):
301 """Simulate Enter key button press.
302
303 @param press_secs : Str. Time to press key.
304 """
305 self._server.d_key(press_secs)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700306
307
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800308 def ctrl_key(self, press_secs=''):
309 """Simulate Enter key button press.
310
311 @param press_secs : Str. Time to press key.
312 """
313 self._server.ctrl_key(press_secs)
Todd Broch9753bd42012-03-21 10:15:08 -0700314
315
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800316 def enter_key(self, press_secs=''):
317 """Simulate Enter key button press.
318
319 @param press_secs : Str. Time to press key.
320 """
321 self._server.enter_key(press_secs)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700322
323
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800324 def refresh_key(self, press_secs=''):
325 """Simulate Refresh key (F3) button press.
326
327 @param press_secs : Str. Time to press key.
328 """
329 self._server.refresh_key(press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700330
331
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800332 def ctrl_refresh_key(self, press_secs=''):
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800333 """Simulate Ctrl and Refresh (F3) simultaneous press.
334
335 This key combination is an alternative of Space key.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800336
337 @param press_secs : Str. Time to press key.
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800338 """
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800339 self._server.ctrl_refresh_key(press_secs)
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800340
341
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800342 def imaginary_key(self, press_secs=''):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700343 """Simulate imaginary key button press.
344
345 Maps to a key that doesn't physically exist.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800346
347 @param press_secs : Str. Time to press key.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700348 """
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800349 self._server.imaginary_key(press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700350
351
Tom Wai-Hong Tam93b5c092015-05-14 02:50:43 +0800352 def toggle_recovery_switch(self):
353 """Toggle recovery switch on and off."""
354 self.enable_recovery_mode()
355 time.sleep(self.REC_TOGGLE_DELAY)
356 self.disable_recovery_mode()
357
358
Craig Harrison6b36b122011-06-28 17:58:43 -0700359 def enable_recovery_mode(self):
360 """Enable recovery mode on device."""
361 self.set('rec_mode', 'on')
362
363
364 def disable_recovery_mode(self):
365 """Disable recovery mode on device."""
366 self.set('rec_mode', 'off')
367
368
Tom Wai-Hong Tamf9ded092015-05-20 05:37:22 +0800369 def toggle_development_switch(self):
370 """Toggle development switch on and off."""
371 self.enable_development_mode()
372 time.sleep(self.DEV_TOGGLE_DELAY)
373 self.disable_development_mode()
374
375
Craig Harrison6b36b122011-06-28 17:58:43 -0700376 def enable_development_mode(self):
377 """Enable development mode on device."""
378 self.set('dev_mode', 'on')
379
380
381 def disable_development_mode(self):
382 """Disable development mode on device."""
383 self.set('dev_mode', 'off')
384
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700385 def boot_devmode(self):
386 """Boot a dev-mode device that is powered off."""
Tom Wai-Hong Tam23869102012-01-17 15:41:30 +0800387 self.power_short_press()
Craig Harrison48997262011-06-27 14:31:10 -0700388 self.pass_devmode()
389
390
391 def pass_devmode(self):
392 """Pass through boot screens in dev-mode."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700393 time.sleep(Servo.BOOT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700394 self.ctrl_d()
Chrome Bot9a1137d2011-07-19 14:35:00 -0700395 time.sleep(Servo.BOOT_DELAY)
Craig Harrison48997262011-06-27 14:31:10 -0700396
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700397
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800398 def get_board(self):
399 """Get the board connected to servod.
400
401 """
402 return self._server.get_board()
403
404
Todd Brochefe72cb2012-07-11 19:58:53 -0700405 def _get_xmlrpclib_exception(self, xmlexc):
406 """Get meaningful exception string from xmlrpc.
407
408 Args:
409 xmlexc: xmlrpclib.Fault object
410
411 xmlrpclib.Fault.faultString has the following format:
412
413 <type 'exception type'>:'actual error message'
414
415 Parse and return the real exception from the servod side instead of the
416 less meaningful one like,
417 <Fault 1: "<type 'exceptions.AttributeError'>:'tca6416' object has no
418 attribute 'hw_driver'">
419
420 Returns:
421 string of underlying exception raised in servod.
422 """
423 return re.sub('^.*>:', '', xmlexc.faultString)
424
425
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700426 def get(self, gpio_name):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700427 """Get the value of a gpio from Servod.
428
429 @param gpio_name Name of the gpio.
430 """
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700431 assert gpio_name
Todd Brochefe72cb2012-07-11 19:58:53 -0700432 try:
Simran Basi1cbc5f22013-07-17 14:23:58 -0700433 return self._server.get(gpio_name)
Todd Brochefe72cb2012-07-11 19:58:53 -0700434 except xmlrpclib.Fault as e:
435 err_msg = "Getting '%s' :: %s" % \
436 (gpio_name, self._get_xmlrpclib_exception(e))
437 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700438
439
440 def set(self, gpio_name, gpio_value):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700441 """Set and check the value of a gpio using Servod.
442
443 @param gpio_name Name of the gpio.
444 @param gpio_value New setting for the gpio.
445 """
Chrome Bot9a1137d2011-07-19 14:35:00 -0700446 self.set_nocheck(gpio_name, gpio_value)
Todd Brochcf7c6652012-02-24 13:03:59 -0800447 retry_count = Servo.GET_RETRY_MAX
448 while gpio_value != self.get(gpio_name) and retry_count:
Ilja H. Friedel04be2bd2014-05-07 21:29:59 -0700449 logging.warning("%s != %s, retry %d", gpio_name, gpio_value,
Todd Brochcf7c6652012-02-24 13:03:59 -0800450 retry_count)
451 retry_count -= 1
452 time.sleep(Servo.SHORT_DELAY)
453 if not retry_count:
454 assert gpio_value == self.get(gpio_name), \
455 'Servo failed to set %s to %s' % (gpio_name, gpio_value)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700456
457
458 def set_nocheck(self, gpio_name, gpio_value):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700459 """Set the value of a gpio using Servod.
460
461 @param gpio_name Name of the gpio.
462 @param gpio_value New setting for the gpio.
463 """
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700464 assert gpio_name and gpio_value
Todd Broch9753bd42012-03-21 10:15:08 -0700465 logging.info('Setting %s to %s', gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700466 try:
Simran Basi1cbc5f22013-07-17 14:23:58 -0700467 self._server.set(gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700468 except xmlrpclib.Fault as e:
469 err_msg = "Setting '%s' to '%s' :: %s" % \
470 (gpio_name, gpio_value, self._get_xmlrpclib_exception(e))
471 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700472
473
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800474 def set_get_all(self, controls):
475 """Set &| get one or more control values.
476
477 @param controls: list of strings, controls to set &| get.
478
479 @raise: error.TestError in case error occurs setting/getting values.
480 """
481 rv = []
482 try:
Vic Yangcad9acb2013-05-21 14:02:05 +0800483 logging.info('Set/get all: %s', str(controls))
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800484 rv = self._server.set_get_all(controls)
485 except xmlrpclib.Fault as e:
486 # TODO(waihong): Remove the following backward compatibility when
487 # the new versions of hdctools are deployed.
488 if 'not supported' in str(e):
489 logging.warning('The servod is too old that set_get_all '
490 'not supported. Use set and get instead.')
491 for control in controls:
492 if ':' in control:
493 (name, value) = control.split(':')
494 if name == 'sleep':
495 time.sleep(float(value))
496 else:
497 self.set_nocheck(name, value)
498 rv.append(True)
499 else:
500 rv.append(self.get(name))
501 else:
502 err_msg = "Problem with '%s' :: %s" % \
503 (controls, self._get_xmlrpclib_exception(e))
504 raise error.TestFail(err_msg)
505 return rv
506
507
Jon Salzc88e5b62011-11-30 14:38:54 +0800508 # TODO(waihong) It may fail if multiple servo's are connected to the same
509 # host. Should look for a better way, like the USB serial name, to identify
510 # the USB device.
Simran Basi741b5d42012-05-18 11:27:15 -0700511 # TODO(sbasi) Remove this code from autoserv once firmware tests have been
512 # updated.
Jon Salzc88e5b62011-11-30 14:38:54 +0800513 def probe_host_usb_dev(self):
514 """Probe the USB disk device plugged-in the servo from the host side.
515
516 It tries to switch the USB mux to make the host unable to see the
517 USB disk and compares the result difference.
518
Jon Salzc88e5b62011-11-30 14:38:54 +0800519 Returns:
520 A string of USB disk path, like '/dev/sdb', or None if not existed.
521 """
522 cmd = 'ls /dev/sd[a-z]'
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800523 original_value = self.get_usbkey_direction()
Jon Salzc88e5b62011-11-30 14:38:54 +0800524
525 # Make the host unable to see the USB disk.
Fang Dengafb88142013-05-30 17:44:31 -0700526 self.switch_usbkey('off')
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800527 no_usb_set = set(self.system_output(cmd, ignore_status=True).split())
Jon Salzc88e5b62011-11-30 14:38:54 +0800528
529 # Make the host able to see the USB disk.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800530 self.switch_usbkey('host')
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800531 has_usb_set = set(self.system_output(cmd, ignore_status=True).split())
Jon Salzc88e5b62011-11-30 14:38:54 +0800532
533 # Back to its original value.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800534 if original_value != self.get_usbkey_direction():
535 self.switch_usbkey(original_value)
Jon Salzc88e5b62011-11-30 14:38:54 +0800536
537 diff_set = has_usb_set - no_usb_set
538 if len(diff_set) == 1:
539 return diff_set.pop()
540 else:
541 return None
542
543
Mike Truty49153d82012-08-21 22:27:30 -0500544 def image_to_servo_usb(self, image_path=None,
545 make_image_noninteractive=False):
546 """Install an image to the USB key plugged into the servo.
547
548 This method may copy any image to the servo USB key including a
549 recovery image or a test image. These images are frequently used
550 for test purposes such as restoring a corrupted image or conducting
551 an upgrade of ec/fw/kernel as part of a test of a specific image part.
552
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700553 @param image_path Path on the host to the recovery image.
554 @param make_image_noninteractive Make the recovery image
555 noninteractive, therefore the DUT
556 will reboot automatically after
557 installation.
Mike Truty49153d82012-08-21 22:27:30 -0500558 """
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700559 # We're about to start plugging/unplugging the USB key. We
560 # don't know the state of the DUT, or what it might choose
561 # to do to the device after hotplug. To avoid surprises,
562 # force the DUT to be off.
563 self._server.hwinit()
564 self._power_state.power_off()
Mike Truty49153d82012-08-21 22:27:30 -0500565
566 # Set up Servo's usb mux.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800567 self.switch_usbkey('host')
Mike Truty49153d82012-08-21 22:27:30 -0500568 if image_path:
Tom Wai-Hong Tam42f136d2012-10-26 11:11:23 +0800569 logging.info('Searching for usb device and copying image to it. '
570 'Please wait a few minutes...')
Mike Truty49153d82012-08-21 22:27:30 -0500571 if not self._server.download_image_to_usb(image_path):
572 logging.error('Failed to transfer requested image to USB. '
573 'Please take a look at Servo Logs.')
574 raise error.AutotestError('Download image to usb failed.')
575 if make_image_noninteractive:
576 logging.info('Making image noninteractive')
577 if not self._server.make_image_noninteractive():
578 logging.error('Failed to make image noninteractive. '
579 'Please take a look at Servo Logs.')
580
581
Simran Basi741b5d42012-05-18 11:27:15 -0700582 def install_recovery_image(self, image_path=None,
J. Richard Barnetteb6de7e32013-02-14 13:28:04 -0800583 make_image_noninteractive=False):
Jon Salzc88e5b62011-11-30 14:38:54 +0800584 """Install the recovery image specied by the path onto the DUT.
585
586 This method uses google recovery mode to install a recovery image
587 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 +0800588 board specified by the usb_dev. If no image path is specified
Jon Salzc88e5b62011-11-30 14:38:54 +0800589 we use the recovery image already on the usb image.
590
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700591 @param image_path Path on the host to the recovery image.
592 @param make_image_noninteractive Make the recovery image
593 noninteractive, therefore
594 the DUT will reboot
595 automatically after
596 installation.
Jon Salzc88e5b62011-11-30 14:38:54 +0800597 """
Mike Truty49153d82012-08-21 22:27:30 -0500598 self.image_to_servo_usb(image_path, make_image_noninteractive)
J. Richard Barnette4b6af0d2014-06-05 09:57:20 -0700599 self._power_state.power_on(rec_mode=self._power_state.REC_ON)
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700600 self.switch_usbkey('dut')
Jon Salzc88e5b62011-11-30 14:38:54 +0800601
602
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800603 def _scp_image(self, image_path):
604 """Copy image to the servo host.
605
606 When programming a firmware image on the DUT, the image must be
607 located on the host to which the servo device is connected. Sometimes
608 servo is controlled by a remote host, in this case the image needs to
609 be transferred to the remote host.
610
611 @param image_path: a string, name of the firmware image file to be
612 transferred.
613 @return: a string, full path name of the copied file on the remote.
614 """
615
616 dest_path = os.path.join('/tmp', os.path.basename(image_path))
Fang Deng5d518f42013-08-02 14:04:32 -0700617 self._servo_host.send_file(image_path, dest_path)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800618 return dest_path
619
620
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800621 def system(self, command, timeout=None):
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700622 """Execute the passed in command on the servod host.
623
624 @param command Command to be executed.
625 @param timeout Maximum number of seconds of runtime allowed.
626 """
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800627 logging.info('Will execute on servo host: %s', command)
Fang Deng5d518f42013-08-02 14:04:32 -0700628 self._servo_host.run(command, timeout=timeout)
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800629
630
631 def system_output(self, command, timeout=None,
632 ignore_status=False, args=()):
633 """Execute the passed in command on the servod host, return stdout.
634
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700635 @param command a string, the command to execute
636 @param timeout an int, max number of seconds to wait til command
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800637 execution completes
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700638 @param ignore_status a Boolean, if true - ignore command's nonzero exit
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800639 status, otherwise an exception will be thrown
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700640 @param args a tuple of strings, each becoming a separate command line
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800641 parameter for the command
J. Richard Barnettecdd1edf2015-07-24 11:39:46 -0700642 @return command's stdout as a string.
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800643 """
Fang Deng5d518f42013-08-02 14:04:32 -0700644 return self._servo_host.run(command, timeout=timeout,
645 ignore_status=ignore_status,
646 args=args).stdout.strip()
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800647
648
Dan Shia5fef052015-05-18 23:28:47 -0700649 def get_servo_version(self):
650 """Get the version of the servo, e.g., servo_v2 or servo_v3.
651
652 @return: The version of the servo.
653
654 """
655 return self._server.get_version()
656
657
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700658 def _initialize_programmer(self):
659 if self._programmer:
660 return
661 # Initialize firmware programmer
Dan Shia5fef052015-05-18 23:28:47 -0700662 servo_version = self.get_servo_version()
Dan Shi9cb0eec2014-06-03 09:04:50 -0700663 if servo_version.startswith('servo_v2'):
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700664 self._programmer = firmware_programmer.ProgrammerV2(self)
Dan Shia5fef052015-05-18 23:28:47 -0700665 elif servo_version.startswith('servo_v3'):
666 self._programmer = firmware_programmer.ProgrammerV3(self)
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700667 else:
668 raise error.TestError(
669 'No firmware programmer for servo version: %s' %
670 servo_version)
671
672
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800673 def program_bios(self, image):
674 """Program bios on DUT with given image.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800675
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800676 @param image: a string, file name of the BIOS image to program
677 on the DUT.
678
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800679 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700680 self._initialize_programmer()
Ricky Liangc31aab32014-07-03 16:23:29 +0800681 if not self.is_localhost():
682 image = self._scp_image(image)
683 self._programmer.program_bios(image)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800684
685
Dan Shia5fef052015-05-18 23:28:47 -0700686 def _get_board_from_ec(self, image):
687 """Get the board name from the EC image file.
688
689 @param image: string with the location of the image file
690
691 @return: Board name used in the EC image file.
692
693 """
694 cmd = ('strings "%s" | grep -oP "[a-z\-_0-9]*(?=_v[0-9]\.[0-9]\.)" | '
695 'head -n 1' % image)
696 return utils.run(cmd).stdout.strip()
697
698
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800699 def program_ec(self, image):
700 """Program ec on DUT with given image.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800701
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800702 @param image: a string, file name of the EC image to program
703 on the DUT.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800704
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800705 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700706 self._initialize_programmer()
Dan Shia5fef052015-05-18 23:28:47 -0700707 board = self._get_board_from_ec(image)
708 logging.info('board name from ec image file is "%s".', board)
Ricky Liangc31aab32014-07-03 16:23:29 +0800709 if not self.is_localhost():
710 image = self._scp_image(image)
Dan Shia5fef052015-05-18 23:28:47 -0700711 self._programmer.program_ec(image, board=board)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800712
Fang Dengafb88142013-05-30 17:44:31 -0700713
714 def _switch_usbkey_power(self, power_state, detection_delay=False):
715 """Switch usbkey power.
716
717 This function switches usbkey power by setting the value of
718 'prtctl4_pwren'. If the power is already in the
719 requested state, this function simply returns.
720
721 @param power_state: A string, 'on' or 'off'.
722 @param detection_delay: A boolean value, if True, sleep
723 for |USB_DETECTION_DELAY| after switching
724 the power on.
725 """
726 self.set('prtctl4_pwren', power_state)
727 if power_state == 'off':
728 time.sleep(self.USB_POWEROFF_DELAY)
729 elif detection_delay:
730 time.sleep(self.USB_DETECTION_DELAY)
731
732
733 def switch_usbkey(self, usb_state):
734 """Connect USB flash stick to either host or DUT, or turn USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800735
736 This function switches the servo multiplexer to provide electrical
Fang Dengafb88142013-05-30 17:44:31 -0700737 connection between the USB port J3 and either host or DUT side. It
738 can also be used to turn the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800739
Fang Dengafb88142013-05-30 17:44:31 -0700740 Switching to 'dut' or 'host' is accompanied by powercycling
741 of the USB stick, because it sometimes gets wedged if the mux
742 is switched while the stick power is on.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800743
Fang Dengafb88142013-05-30 17:44:31 -0700744 @param usb_state: A string, one of 'dut', 'host', or 'off'.
745 'dut' and 'host' indicate which side the
746 USB flash device is required to be connected to.
747 'off' indicates turning the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800748
Fang Dengafb88142013-05-30 17:44:31 -0700749 @raise: error.TestError in case the parameter is not 'dut'
750 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800751 """
Fang Dengafb88142013-05-30 17:44:31 -0700752 if self.get_usbkey_direction() == usb_state:
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800753 return
754
Fang Dengafb88142013-05-30 17:44:31 -0700755 if usb_state == 'off':
Dan Shia5fef052015-05-18 23:28:47 -0700756 self._switch_usbkey_power('off')
757 self._usb_state = usb_state
758 return
Fang Dengafb88142013-05-30 17:44:31 -0700759 elif usb_state == 'host':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800760 mux_direction = 'servo_sees_usbkey'
Fang Dengafb88142013-05-30 17:44:31 -0700761 elif usb_state == 'dut':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800762 mux_direction = 'dut_sees_usbkey'
763 else:
Fang Dengafb88142013-05-30 17:44:31 -0700764 raise error.TestError('Unknown USB state request: %s' % usb_state)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800765
Fang Dengafb88142013-05-30 17:44:31 -0700766 self._switch_usbkey_power('off')
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800767 self.set('usb_mux_sel1', mux_direction)
768 time.sleep(self.USB_POWEROFF_DELAY)
Fang Dengafb88142013-05-30 17:44:31 -0700769 self._switch_usbkey_power('on', usb_state == 'host')
770 self._usb_state = usb_state
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800771
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800772
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800773 def get_usbkey_direction(self):
Fang Dengafb88142013-05-30 17:44:31 -0700774 """Get which side USB is connected to or 'off' if usb power is off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800775
Fang Dengafb88142013-05-30 17:44:31 -0700776 @return: A string, one of 'dut', 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800777 """
Fang Dengafb88142013-05-30 17:44:31 -0700778 if not self._usb_state:
779 if self.get('prtctl4_pwren') == 'off':
780 self._usb_state = 'off'
781 elif self.get('usb_mux_sel1').startswith('dut'):
782 self._usb_state = 'dut'
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800783 else:
Fang Dengafb88142013-05-30 17:44:31 -0700784 self._usb_state = 'host'
785 return self._usb_state