blob: 7ec89b4bae8e08a0450024a82cd000b3f3c34891 [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
Jon Salzc88e5b62011-11-30 14:38:54 +0800145 # Time between an usb disk plugged-in and detected in the system.
146 USB_DETECTION_DELAY = 10
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800147 # Time to keep USB power off before and after USB mux direction is changed
148 USB_POWEROFF_DELAY = 2
Jon Salzc88e5b62011-11-30 14:38:54 +0800149
Simran Basib7850bb2013-07-24 12:33:42 -0700150 # Time to wait before timing out on servo initialization.
151 INIT_TIMEOUT_SECS = 10
Simran Basi59331342013-07-12 12:06:29 -0700152
J. Richard Barnette67ccb872012-04-19 16:34:56 -0700153
Ricky Liang0dd379c2014-04-23 16:29:08 +0800154 def __init__(self, servo_host, servo_serial=None):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700155 """Sets up the servo communication infrastructure.
156
Fang Deng5d518f42013-08-02 14:04:32 -0700157 @param servo_host: A ServoHost object representing
158 the host running servod.
Ricky Liang0dd379c2014-04-23 16:29:08 +0800159 @param servo_serial: Serial number of the servo board.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700160 """
Fang Deng5d518f42013-08-02 14:04:32 -0700161 # TODO(fdeng): crbug.com/298379
162 # We should move servo_host object out of servo object
163 # to minimize the dependencies on the rest of Autotest.
164 self._servo_host = servo_host
Ricky Liang0dd379c2014-04-23 16:29:08 +0800165 self._servo_serial = servo_serial
Fang Deng5d518f42013-08-02 14:04:32 -0700166 self._server = servo_host.get_servod_server_proxy()
J. Richard Barnette0c9c5882014-06-11 15:27:05 -0700167 self._power_state = _PowerStateController(self)
Fang Dengafb88142013-05-30 17:44:31 -0700168 self._usb_state = None
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700169 self._programmer = None
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800170
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800171
Ricky Liang0dd379c2014-04-23 16:29:08 +0800172 @property
173 def servo_serial(self):
174 """Returns the serial number of the servo board."""
175 return self._servo_serial
176
177
Tom Wai-Hong Tam22ee0e52013-04-03 13:36:39 +0800178 def get_power_state_controller(self):
J. Richard Barnette75136b32013-03-26 13:38:44 -0700179 """Return the power state controller for this Servo.
180
181 The power state controller provides board-independent
182 interfaces for reset, power-on, power-off operations.
183
184 """
185 return self._power_state
186
Fang Deng5d518f42013-08-02 14:04:32 -0700187
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700188 def initialize_dut(self, cold_reset=False):
189 """Initializes a dut for testing purposes.
190
191 This sets various servo signals back to default values
192 appropriate for the target board. By default, if the DUT
193 is already on, it stays on. If the DUT is powered off
194 before initialization, its state afterward is unspecified.
195
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700196 Rationale: Basic initialization of servo sets the lid open,
197 when there is a lid. This operation won't affect powered on
198 units; however, setting the lid open may power on a unit
J. Richard Barnette75136b32013-03-26 13:38:44 -0700199 that's off, depending on the board type and previous state
200 of the device.
201
J. Richard Barnette4b6af0d2014-06-05 09:57:20 -0700202 If `cold_reset` is a true value, the DUT and its EC will be
203 reset, and the DUT rebooted in normal mode.
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700204
205 @param cold_reset If True, cold reset the device after
206 initialization.
207 """
208 self._server.hwinit()
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700209 self.set('dut_hub_pwren', 'on')
210 self.set('usb_mux_oe1', 'on')
211 self.switch_usbkey('off')
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700212 if cold_reset:
J. Richard Barnette4b6af0d2014-06-05 09:57:20 -0700213 self._power_state.reset()
J. Richard Barnette1777f5d2014-09-03 15:18:19 -0700214 logging.debug('Servo initialized, version is %s',
215 self._server.get_version())
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700216
217
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800218 def is_localhost(self):
219 """Is the servod hosted locally?
220
221 Returns:
222 True if local hosted; otherwise, False.
223 """
Fang Deng5d518f42013-08-02 14:04:32 -0700224 return self._servo_host.is_localhost()
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800225
226
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700227 def power_long_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700228 """Simulate a long power button press."""
J. Richard Barnettef12bff22012-07-18 14:41:06 -0700229 # After a long power press, the EC may ignore the next power
230 # button press (at least on Alex). To guarantee that this
231 # won't happen, we need to allow the EC one second to
232 # collect itself.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800233 self._server.power_long_press()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700234
235
236 def power_normal_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700237 """Simulate a normal power button press."""
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800238 self._server.power_normal_press()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700239
240
241 def power_short_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700242 """Simulate a short power button press."""
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800243 self._server.power_short_press()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700244
245
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800246 def power_key(self, press_secs=''):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700247 """Simulate a power button press.
248
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800249 @param press_secs : Str. Time to press key.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700250 """
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800251 self._server.power_key(press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700252
253
254 def lid_open(self):
255 """Simulate opening the lid."""
Craig Harrison48997262011-06-27 14:31:10 -0700256 self.set_nocheck('lid_open', 'yes')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700257
258
259 def lid_close(self):
Craig Harrison48997262011-06-27 14:31:10 -0700260 """Simulate closing the lid.
261
262 Waits 6 seconds to ensure the device is fully asleep before returning.
263 """
264 self.set_nocheck('lid_open', 'no')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700265 time.sleep(Servo.SLEEP_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700266
267
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800268 def ctrl_d(self, press_secs=''):
269 """Simulate Ctrl-d simultaneous button presses.
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800270
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800271 @param press_secs : Str. Time to press key.
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800272 """
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800273 self._server.ctrl_d(press_secs)
Gediminas Ramanauskas9da80f22012-11-14 12:59:43 -0800274
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800275
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800276 def ctrl_u(self):
277 """Simulate Ctrl-u simultaneous button presses.
278
279 @param press_secs : Str. Time to press key.
280 """
281 self._server.ctrl_u()
Todd Broch9dfc3a82011-11-01 08:09:28 -0700282
283
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800284 def ctrl_enter(self, press_secs=''):
285 """Simulate Ctrl-enter simultaneous button presses.
286
287 @param press_secs : Str. Time to press key.
288 """
289 self._server.ctrl_enter(press_secs)
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -0700290
291
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800292 def d_key(self, press_secs=''):
293 """Simulate Enter key button press.
294
295 @param press_secs : Str. Time to press key.
296 """
297 self._server.d_key(press_secs)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700298
299
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800300 def ctrl_key(self, press_secs=''):
301 """Simulate Enter key button press.
302
303 @param press_secs : Str. Time to press key.
304 """
305 self._server.ctrl_key(press_secs)
Todd Broch9753bd42012-03-21 10:15:08 -0700306
307
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800308 def enter_key(self, press_secs=''):
309 """Simulate Enter key button press.
310
311 @param press_secs : Str. Time to press key.
312 """
313 self._server.enter_key(press_secs)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700314
315
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800316 def refresh_key(self, press_secs=''):
317 """Simulate Refresh key (F3) button press.
318
319 @param press_secs : Str. Time to press key.
320 """
321 self._server.refresh_key(press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700322
323
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800324 def ctrl_refresh_key(self, press_secs=''):
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800325 """Simulate Ctrl and Refresh (F3) simultaneous press.
326
327 This key combination is an alternative of Space key.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800328
329 @param press_secs : Str. Time to press key.
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800330 """
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800331 self._server.ctrl_refresh_key(press_secs)
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800332
333
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800334 def imaginary_key(self, press_secs=''):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700335 """Simulate imaginary key button press.
336
337 Maps to a key that doesn't physically exist.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800338
339 @param press_secs : Str. Time to press key.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700340 """
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800341 self._server.imaginary_key(press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700342
343
Craig Harrison6b36b122011-06-28 17:58:43 -0700344 def enable_recovery_mode(self):
345 """Enable recovery mode on device."""
346 self.set('rec_mode', 'on')
347
348
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800349 def custom_recovery_mode(self):
350 """Custom key combination to enter recovery mode."""
351 self._press_keys('rec_mode')
352 self.power_normal_press()
353 time.sleep(self.SERVO_KEY_PRESS_DELAY)
354 self.set_nocheck('kbd_en', 'off')
355
356
Craig Harrison6b36b122011-06-28 17:58:43 -0700357 def disable_recovery_mode(self):
358 """Disable recovery mode on device."""
359 self.set('rec_mode', 'off')
360
361
362 def enable_development_mode(self):
363 """Enable development mode on device."""
364 self.set('dev_mode', 'on')
365
366
367 def disable_development_mode(self):
368 """Disable development mode on device."""
369 self.set('dev_mode', 'off')
370
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700371 def boot_devmode(self):
372 """Boot a dev-mode device that is powered off."""
Tom Wai-Hong Tam23869102012-01-17 15:41:30 +0800373 self.power_short_press()
Craig Harrison48997262011-06-27 14:31:10 -0700374 self.pass_devmode()
375
376
377 def pass_devmode(self):
378 """Pass through boot screens in dev-mode."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700379 time.sleep(Servo.BOOT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700380 self.ctrl_d()
Chrome Bot9a1137d2011-07-19 14:35:00 -0700381 time.sleep(Servo.BOOT_DELAY)
Craig Harrison48997262011-06-27 14:31:10 -0700382
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700383
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800384 def get_board(self):
385 """Get the board connected to servod.
386
387 """
388 return self._server.get_board()
389
390
Todd Brochefe72cb2012-07-11 19:58:53 -0700391 def _get_xmlrpclib_exception(self, xmlexc):
392 """Get meaningful exception string from xmlrpc.
393
394 Args:
395 xmlexc: xmlrpclib.Fault object
396
397 xmlrpclib.Fault.faultString has the following format:
398
399 <type 'exception type'>:'actual error message'
400
401 Parse and return the real exception from the servod side instead of the
402 less meaningful one like,
403 <Fault 1: "<type 'exceptions.AttributeError'>:'tca6416' object has no
404 attribute 'hw_driver'">
405
406 Returns:
407 string of underlying exception raised in servod.
408 """
409 return re.sub('^.*>:', '', xmlexc.faultString)
410
411
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700412 def get(self, gpio_name):
413 """Get the value of a gpio from Servod."""
414 assert gpio_name
Todd Brochefe72cb2012-07-11 19:58:53 -0700415 try:
Simran Basi1cbc5f22013-07-17 14:23:58 -0700416 return self._server.get(gpio_name)
Todd Brochefe72cb2012-07-11 19:58:53 -0700417 except xmlrpclib.Fault as e:
418 err_msg = "Getting '%s' :: %s" % \
419 (gpio_name, self._get_xmlrpclib_exception(e))
420 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700421
422
423 def set(self, gpio_name, gpio_value):
424 """Set and check the value of a gpio using Servod."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700425 self.set_nocheck(gpio_name, gpio_value)
Todd Brochcf7c6652012-02-24 13:03:59 -0800426 retry_count = Servo.GET_RETRY_MAX
427 while gpio_value != self.get(gpio_name) and retry_count:
Ilja H. Friedel04be2bd2014-05-07 21:29:59 -0700428 logging.warning("%s != %s, retry %d", gpio_name, gpio_value,
Todd Brochcf7c6652012-02-24 13:03:59 -0800429 retry_count)
430 retry_count -= 1
431 time.sleep(Servo.SHORT_DELAY)
432 if not retry_count:
433 assert gpio_value == self.get(gpio_name), \
434 'Servo failed to set %s to %s' % (gpio_name, gpio_value)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700435
436
437 def set_nocheck(self, gpio_name, gpio_value):
438 """Set the value of a gpio using Servod."""
439 assert gpio_name and gpio_value
Todd Broch9753bd42012-03-21 10:15:08 -0700440 logging.info('Setting %s to %s', gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700441 try:
Simran Basi1cbc5f22013-07-17 14:23:58 -0700442 self._server.set(gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700443 except xmlrpclib.Fault as e:
444 err_msg = "Setting '%s' to '%s' :: %s" % \
445 (gpio_name, gpio_value, self._get_xmlrpclib_exception(e))
446 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700447
448
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800449 def set_get_all(self, controls):
450 """Set &| get one or more control values.
451
452 @param controls: list of strings, controls to set &| get.
453
454 @raise: error.TestError in case error occurs setting/getting values.
455 """
456 rv = []
457 try:
Vic Yangcad9acb2013-05-21 14:02:05 +0800458 logging.info('Set/get all: %s', str(controls))
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800459 rv = self._server.set_get_all(controls)
460 except xmlrpclib.Fault as e:
461 # TODO(waihong): Remove the following backward compatibility when
462 # the new versions of hdctools are deployed.
463 if 'not supported' in str(e):
464 logging.warning('The servod is too old that set_get_all '
465 'not supported. Use set and get instead.')
466 for control in controls:
467 if ':' in control:
468 (name, value) = control.split(':')
469 if name == 'sleep':
470 time.sleep(float(value))
471 else:
472 self.set_nocheck(name, value)
473 rv.append(True)
474 else:
475 rv.append(self.get(name))
476 else:
477 err_msg = "Problem with '%s' :: %s" % \
478 (controls, self._get_xmlrpclib_exception(e))
479 raise error.TestFail(err_msg)
480 return rv
481
482
Jon Salzc88e5b62011-11-30 14:38:54 +0800483 # TODO(waihong) It may fail if multiple servo's are connected to the same
484 # host. Should look for a better way, like the USB serial name, to identify
485 # the USB device.
Simran Basi741b5d42012-05-18 11:27:15 -0700486 # TODO(sbasi) Remove this code from autoserv once firmware tests have been
487 # updated.
Jon Salzc88e5b62011-11-30 14:38:54 +0800488 def probe_host_usb_dev(self):
489 """Probe the USB disk device plugged-in the servo from the host side.
490
491 It tries to switch the USB mux to make the host unable to see the
492 USB disk and compares the result difference.
493
Jon Salzc88e5b62011-11-30 14:38:54 +0800494 Returns:
495 A string of USB disk path, like '/dev/sdb', or None if not existed.
496 """
497 cmd = 'ls /dev/sd[a-z]'
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800498 original_value = self.get_usbkey_direction()
Jon Salzc88e5b62011-11-30 14:38:54 +0800499
500 # Make the host unable to see the USB disk.
Fang Dengafb88142013-05-30 17:44:31 -0700501 self.switch_usbkey('off')
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800502 no_usb_set = set(self.system_output(cmd, ignore_status=True).split())
Jon Salzc88e5b62011-11-30 14:38:54 +0800503
504 # Make the host able to see the USB disk.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800505 self.switch_usbkey('host')
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800506 has_usb_set = set(self.system_output(cmd, ignore_status=True).split())
Jon Salzc88e5b62011-11-30 14:38:54 +0800507
508 # Back to its original value.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800509 if original_value != self.get_usbkey_direction():
510 self.switch_usbkey(original_value)
Jon Salzc88e5b62011-11-30 14:38:54 +0800511
512 diff_set = has_usb_set - no_usb_set
513 if len(diff_set) == 1:
514 return diff_set.pop()
515 else:
516 return None
517
518
Mike Truty49153d82012-08-21 22:27:30 -0500519 def image_to_servo_usb(self, image_path=None,
520 make_image_noninteractive=False):
521 """Install an image to the USB key plugged into the servo.
522
523 This method may copy any image to the servo USB key including a
524 recovery image or a test image. These images are frequently used
525 for test purposes such as restoring a corrupted image or conducting
526 an upgrade of ec/fw/kernel as part of a test of a specific image part.
527
528 Args:
529 image_path: Path on the host to the recovery image.
530 make_image_noninteractive: Make the recovery image noninteractive,
531 therefore the DUT will reboot
532 automatically after installation.
533 """
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700534 # We're about to start plugging/unplugging the USB key. We
535 # don't know the state of the DUT, or what it might choose
536 # to do to the device after hotplug. To avoid surprises,
537 # force the DUT to be off.
538 self._server.hwinit()
539 self._power_state.power_off()
Mike Truty49153d82012-08-21 22:27:30 -0500540
541 # Set up Servo's usb mux.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800542 self.switch_usbkey('host')
Mike Truty49153d82012-08-21 22:27:30 -0500543 if image_path:
Tom Wai-Hong Tam42f136d2012-10-26 11:11:23 +0800544 logging.info('Searching for usb device and copying image to it. '
545 'Please wait a few minutes...')
Mike Truty49153d82012-08-21 22:27:30 -0500546 if not self._server.download_image_to_usb(image_path):
547 logging.error('Failed to transfer requested image to USB. '
548 'Please take a look at Servo Logs.')
549 raise error.AutotestError('Download image to usb failed.')
550 if make_image_noninteractive:
551 logging.info('Making image noninteractive')
552 if not self._server.make_image_noninteractive():
553 logging.error('Failed to make image noninteractive. '
554 'Please take a look at Servo Logs.')
555
556
Simran Basi741b5d42012-05-18 11:27:15 -0700557 def install_recovery_image(self, image_path=None,
J. Richard Barnetteb6de7e32013-02-14 13:28:04 -0800558 make_image_noninteractive=False):
Jon Salzc88e5b62011-11-30 14:38:54 +0800559 """Install the recovery image specied by the path onto the DUT.
560
561 This method uses google recovery mode to install a recovery image
562 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 +0800563 board specified by the usb_dev. If no image path is specified
Jon Salzc88e5b62011-11-30 14:38:54 +0800564 we use the recovery image already on the usb image.
565
566 Args:
567 image_path: Path on the host to the recovery image.
Simran Basi741b5d42012-05-18 11:27:15 -0700568 make_image_noninteractive: Make the recovery image noninteractive,
569 therefore the DUT will reboot
570 automatically after installation.
Jon Salzc88e5b62011-11-30 14:38:54 +0800571 """
Mike Truty49153d82012-08-21 22:27:30 -0500572 self.image_to_servo_usb(image_path, make_image_noninteractive)
J. Richard Barnette4b6af0d2014-06-05 09:57:20 -0700573 self._power_state.power_on(rec_mode=self._power_state.REC_ON)
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700574 self.switch_usbkey('dut')
Jon Salzc88e5b62011-11-30 14:38:54 +0800575
576
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800577 def _scp_image(self, image_path):
578 """Copy image to the servo host.
579
580 When programming a firmware image on the DUT, the image must be
581 located on the host to which the servo device is connected. Sometimes
582 servo is controlled by a remote host, in this case the image needs to
583 be transferred to the remote host.
584
585 @param image_path: a string, name of the firmware image file to be
586 transferred.
587 @return: a string, full path name of the copied file on the remote.
588 """
589
590 dest_path = os.path.join('/tmp', os.path.basename(image_path))
Fang Deng5d518f42013-08-02 14:04:32 -0700591 self._servo_host.send_file(image_path, dest_path)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800592 return dest_path
593
594
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800595 def system(self, command, timeout=None):
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800596 """Execute the passed in command on the servod host."""
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800597 logging.info('Will execute on servo host: %s', command)
Fang Deng5d518f42013-08-02 14:04:32 -0700598 self._servo_host.run(command, timeout=timeout)
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800599
600
601 def system_output(self, command, timeout=None,
602 ignore_status=False, args=()):
603 """Execute the passed in command on the servod host, return stdout.
604
605 @param command, a string, the command to execute
606 @param timeout, an int, max number of seconds to wait til command
607 execution completes
608 @ignore_status, a Boolean, if true - ignore command's nonzero exit
609 status, otherwise an exception will be thrown
610 @param args, a tuple of strings, each becoming a separate command line
611 parameter for the command
612 @return: command's stdout as a string.
613 """
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800614 logging.info('Will execute and collect output on servo host: %s %s',
615 command, ' '.join("'%s'" % x for x in args))
Fang Deng5d518f42013-08-02 14:04:32 -0700616 return self._servo_host.run(command, timeout=timeout,
617 ignore_status=ignore_status,
618 args=args).stdout.strip()
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800619
620
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700621 def _initialize_programmer(self):
622 if self._programmer:
623 return
624 # Initialize firmware programmer
J. Richard Barnette1777f5d2014-09-03 15:18:19 -0700625 servo_version = self._server.get_version()
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700626 if servo_version == 'servo_v2':
627 self._programmer = firmware_programmer.ProgrammerV2(self)
628 else:
629 raise error.TestError(
630 'No firmware programmer for servo version: %s' %
631 servo_version)
632
633
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800634 def program_bios(self, image):
635 """Program bios on DUT with given image.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800636
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800637 @param image: a string, file name of the BIOS image to program
638 on the DUT.
639
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800640 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700641 self._initialize_programmer()
Ricky Liangc31aab32014-07-03 16:23:29 +0800642 if not self.is_localhost():
643 image = self._scp_image(image)
644 self._programmer.program_bios(image)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800645
646
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800647 def program_ec(self, image):
648 """Program ec on DUT with given image.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800649
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800650 @param image: a string, file name of the EC image to program
651 on the DUT.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800652
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800653 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700654 self._initialize_programmer()
Ricky Liangc31aab32014-07-03 16:23:29 +0800655 if not self.is_localhost():
656 image = self._scp_image(image)
657 self._programmer.program_ec(image)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800658
Fang Dengafb88142013-05-30 17:44:31 -0700659
660 def _switch_usbkey_power(self, power_state, detection_delay=False):
661 """Switch usbkey power.
662
663 This function switches usbkey power by setting the value of
664 'prtctl4_pwren'. If the power is already in the
665 requested state, this function simply returns.
666
667 @param power_state: A string, 'on' or 'off'.
668 @param detection_delay: A boolean value, if True, sleep
669 for |USB_DETECTION_DELAY| after switching
670 the power on.
671 """
672 self.set('prtctl4_pwren', power_state)
673 if power_state == 'off':
674 time.sleep(self.USB_POWEROFF_DELAY)
675 elif detection_delay:
676 time.sleep(self.USB_DETECTION_DELAY)
677
678
679 def switch_usbkey(self, usb_state):
680 """Connect USB flash stick to either host or DUT, or turn USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800681
682 This function switches the servo multiplexer to provide electrical
Fang Dengafb88142013-05-30 17:44:31 -0700683 connection between the USB port J3 and either host or DUT side. It
684 can also be used to turn the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800685
Fang Dengafb88142013-05-30 17:44:31 -0700686 Switching to 'dut' or 'host' is accompanied by powercycling
687 of the USB stick, because it sometimes gets wedged if the mux
688 is switched while the stick power is on.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800689
Fang Dengafb88142013-05-30 17:44:31 -0700690 @param usb_state: A string, one of 'dut', 'host', or 'off'.
691 'dut' and 'host' indicate which side the
692 USB flash device is required to be connected to.
693 'off' indicates turning the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800694
Fang Dengafb88142013-05-30 17:44:31 -0700695 @raise: error.TestError in case the parameter is not 'dut'
696 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800697 """
Fang Dengafb88142013-05-30 17:44:31 -0700698 if self.get_usbkey_direction() == usb_state:
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800699 return
700
Fang Dengafb88142013-05-30 17:44:31 -0700701 if usb_state == 'off':
702 self._switch_usbkey_power('off')
703 self._usb_state = usb_state
704 return
705 elif usb_state == 'host':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800706 mux_direction = 'servo_sees_usbkey'
Fang Dengafb88142013-05-30 17:44:31 -0700707 elif usb_state == 'dut':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800708 mux_direction = 'dut_sees_usbkey'
709 else:
Fang Dengafb88142013-05-30 17:44:31 -0700710 raise error.TestError('Unknown USB state request: %s' % usb_state)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800711
Fang Dengafb88142013-05-30 17:44:31 -0700712 self._switch_usbkey_power('off')
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800713 self.set('usb_mux_sel1', mux_direction)
714 time.sleep(self.USB_POWEROFF_DELAY)
Fang Dengafb88142013-05-30 17:44:31 -0700715 self._switch_usbkey_power('on', usb_state == 'host')
716 self._usb_state = usb_state
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800717
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800718
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800719 def get_usbkey_direction(self):
Fang Dengafb88142013-05-30 17:44:31 -0700720 """Get which side USB is connected to or 'off' if usb power is off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800721
Fang Dengafb88142013-05-30 17:44:31 -0700722 @return: A string, one of 'dut', 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800723 """
Fang Dengafb88142013-05-30 17:44:31 -0700724 if not self._usb_state:
725 if self.get('prtctl4_pwren') == 'off':
726 self._usb_state = 'off'
727 elif self.get('usb_mux_sel1').startswith('dut'):
728 self._usb_state = 'dut'
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800729 else:
Fang Dengafb88142013-05-30 17:44:31 -0700730 self._usb_state = 'host'
731 return self._usb_state