blob: e0dd8329d34b01ab95bf2266083cbefc7e05c55a [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
Jon Salzc88e5b62011-11-30 14:38:54 +0800149 # Time between an usb disk plugged-in and detected in the system.
150 USB_DETECTION_DELAY = 10
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800151 # Time to keep USB power off before and after USB mux direction is changed
152 USB_POWEROFF_DELAY = 2
Jon Salzc88e5b62011-11-30 14:38:54 +0800153
Simran Basib7850bb2013-07-24 12:33:42 -0700154 # Time to wait before timing out on servo initialization.
155 INIT_TIMEOUT_SECS = 10
Simran Basi59331342013-07-12 12:06:29 -0700156
J. Richard Barnette67ccb872012-04-19 16:34:56 -0700157
Ricky Liang0dd379c2014-04-23 16:29:08 +0800158 def __init__(self, servo_host, servo_serial=None):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700159 """Sets up the servo communication infrastructure.
160
Fang Deng5d518f42013-08-02 14:04:32 -0700161 @param servo_host: A ServoHost object representing
162 the host running servod.
Ricky Liang0dd379c2014-04-23 16:29:08 +0800163 @param servo_serial: Serial number of the servo board.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700164 """
Fang Deng5d518f42013-08-02 14:04:32 -0700165 # TODO(fdeng): crbug.com/298379
166 # We should move servo_host object out of servo object
167 # to minimize the dependencies on the rest of Autotest.
168 self._servo_host = servo_host
Ricky Liang0dd379c2014-04-23 16:29:08 +0800169 self._servo_serial = servo_serial
Fang Deng5d518f42013-08-02 14:04:32 -0700170 self._server = servo_host.get_servod_server_proxy()
J. Richard Barnette0c9c5882014-06-11 15:27:05 -0700171 self._power_state = _PowerStateController(self)
Fang Dengafb88142013-05-30 17:44:31 -0700172 self._usb_state = None
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700173 self._programmer = None
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800174
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800175
Ricky Liang0dd379c2014-04-23 16:29:08 +0800176 @property
177 def servo_serial(self):
178 """Returns the serial number of the servo board."""
179 return self._servo_serial
180
181
Tom Wai-Hong Tam22ee0e52013-04-03 13:36:39 +0800182 def get_power_state_controller(self):
J. Richard Barnette75136b32013-03-26 13:38:44 -0700183 """Return the power state controller for this Servo.
184
185 The power state controller provides board-independent
186 interfaces for reset, power-on, power-off operations.
187
188 """
189 return self._power_state
190
Fang Deng5d518f42013-08-02 14:04:32 -0700191
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700192 def initialize_dut(self, cold_reset=False):
193 """Initializes a dut for testing purposes.
194
195 This sets various servo signals back to default values
196 appropriate for the target board. By default, if the DUT
197 is already on, it stays on. If the DUT is powered off
198 before initialization, its state afterward is unspecified.
199
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700200 Rationale: Basic initialization of servo sets the lid open,
201 when there is a lid. This operation won't affect powered on
202 units; however, setting the lid open may power on a unit
J. Richard Barnette75136b32013-03-26 13:38:44 -0700203 that's off, depending on the board type and previous state
204 of the device.
205
J. Richard Barnette4b6af0d2014-06-05 09:57:20 -0700206 If `cold_reset` is a true value, the DUT and its EC will be
207 reset, and the DUT rebooted in normal mode.
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700208
209 @param cold_reset If True, cold reset the device after
210 initialization.
211 """
212 self._server.hwinit()
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700213 self.set('dut_hub_pwren', 'on')
214 self.set('usb_mux_oe1', 'on')
215 self.switch_usbkey('off')
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700216 if cold_reset:
J. Richard Barnette4b6af0d2014-06-05 09:57:20 -0700217 self._power_state.reset()
J. Richard Barnette1777f5d2014-09-03 15:18:19 -0700218 logging.debug('Servo initialized, version is %s',
219 self._server.get_version())
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700220
221
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800222 def is_localhost(self):
223 """Is the servod hosted locally?
224
225 Returns:
226 True if local hosted; otherwise, False.
227 """
Fang Deng5d518f42013-08-02 14:04:32 -0700228 return self._servo_host.is_localhost()
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800229
230
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700231 def power_long_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700232 """Simulate a long power button press."""
J. Richard Barnettef12bff22012-07-18 14:41:06 -0700233 # After a long power press, the EC may ignore the next power
234 # button press (at least on Alex). To guarantee that this
235 # won't happen, we need to allow the EC one second to
236 # collect itself.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800237 self._server.power_long_press()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700238
239
240 def power_normal_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700241 """Simulate a normal power button press."""
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800242 self._server.power_normal_press()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700243
244
245 def power_short_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700246 """Simulate a short power button press."""
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800247 self._server.power_short_press()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700248
249
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800250 def power_key(self, press_secs=''):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700251 """Simulate a power button press.
252
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800253 @param press_secs : Str. Time to press key.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700254 """
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800255 self._server.power_key(press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700256
257
258 def lid_open(self):
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800259 """Simulate opening the lid and raise exception if all attempts fail"""
260 self.set('lid_open', 'yes')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700261
262
263 def lid_close(self):
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800264 """Simulate closing the lid and raise exception if all attempts fail
Craig Harrison48997262011-06-27 14:31:10 -0700265
266 Waits 6 seconds to ensure the device is fully asleep before returning.
267 """
Yuli Huang7b82dcf2015-05-13 17:58:52 +0800268 self.set('lid_open', 'no')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700269 time.sleep(Servo.SLEEP_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700270
271
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800272 def ctrl_d(self, press_secs=''):
273 """Simulate Ctrl-d simultaneous button presses.
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800274
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800275 @param press_secs : Str. Time to press key.
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800276 """
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800277 self._server.ctrl_d(press_secs)
Gediminas Ramanauskas9da80f22012-11-14 12:59:43 -0800278
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800279
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800280 def ctrl_u(self):
281 """Simulate Ctrl-u simultaneous button presses.
282
283 @param press_secs : Str. Time to press key.
284 """
285 self._server.ctrl_u()
Todd Broch9dfc3a82011-11-01 08:09:28 -0700286
287
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800288 def ctrl_enter(self, press_secs=''):
289 """Simulate Ctrl-enter simultaneous button presses.
290
291 @param press_secs : Str. Time to press key.
292 """
293 self._server.ctrl_enter(press_secs)
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -0700294
295
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800296 def d_key(self, press_secs=''):
297 """Simulate Enter key button press.
298
299 @param press_secs : Str. Time to press key.
300 """
301 self._server.d_key(press_secs)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700302
303
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800304 def ctrl_key(self, press_secs=''):
305 """Simulate Enter key button press.
306
307 @param press_secs : Str. Time to press key.
308 """
309 self._server.ctrl_key(press_secs)
Todd Broch9753bd42012-03-21 10:15:08 -0700310
311
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800312 def enter_key(self, press_secs=''):
313 """Simulate Enter key button press.
314
315 @param press_secs : Str. Time to press key.
316 """
317 self._server.enter_key(press_secs)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700318
319
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800320 def refresh_key(self, press_secs=''):
321 """Simulate Refresh key (F3) button press.
322
323 @param press_secs : Str. Time to press key.
324 """
325 self._server.refresh_key(press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700326
327
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800328 def ctrl_refresh_key(self, press_secs=''):
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800329 """Simulate Ctrl and Refresh (F3) simultaneous press.
330
331 This key combination is an alternative of Space key.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800332
333 @param press_secs : Str. Time to press key.
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800334 """
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800335 self._server.ctrl_refresh_key(press_secs)
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800336
337
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800338 def imaginary_key(self, press_secs=''):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700339 """Simulate imaginary key button press.
340
341 Maps to a key that doesn't physically exist.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800342
343 @param press_secs : Str. Time to press key.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700344 """
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800345 self._server.imaginary_key(press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700346
347
Tom Wai-Hong Tam93b5c092015-05-14 02:50:43 +0800348 def toggle_recovery_switch(self):
349 """Toggle recovery switch on and off."""
350 self.enable_recovery_mode()
351 time.sleep(self.REC_TOGGLE_DELAY)
352 self.disable_recovery_mode()
353
354
Craig Harrison6b36b122011-06-28 17:58:43 -0700355 def enable_recovery_mode(self):
356 """Enable recovery mode on device."""
357 self.set('rec_mode', 'on')
358
359
360 def disable_recovery_mode(self):
361 """Disable recovery mode on device."""
362 self.set('rec_mode', 'off')
363
364
365 def enable_development_mode(self):
366 """Enable development mode on device."""
367 self.set('dev_mode', 'on')
368
369
370 def disable_development_mode(self):
371 """Disable development mode on device."""
372 self.set('dev_mode', 'off')
373
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700374 def boot_devmode(self):
375 """Boot a dev-mode device that is powered off."""
Tom Wai-Hong Tam23869102012-01-17 15:41:30 +0800376 self.power_short_press()
Craig Harrison48997262011-06-27 14:31:10 -0700377 self.pass_devmode()
378
379
380 def pass_devmode(self):
381 """Pass through boot screens in dev-mode."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700382 time.sleep(Servo.BOOT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700383 self.ctrl_d()
Chrome Bot9a1137d2011-07-19 14:35:00 -0700384 time.sleep(Servo.BOOT_DELAY)
Craig Harrison48997262011-06-27 14:31:10 -0700385
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700386
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800387 def get_board(self):
388 """Get the board connected to servod.
389
390 """
391 return self._server.get_board()
392
393
Todd Brochefe72cb2012-07-11 19:58:53 -0700394 def _get_xmlrpclib_exception(self, xmlexc):
395 """Get meaningful exception string from xmlrpc.
396
397 Args:
398 xmlexc: xmlrpclib.Fault object
399
400 xmlrpclib.Fault.faultString has the following format:
401
402 <type 'exception type'>:'actual error message'
403
404 Parse and return the real exception from the servod side instead of the
405 less meaningful one like,
406 <Fault 1: "<type 'exceptions.AttributeError'>:'tca6416' object has no
407 attribute 'hw_driver'">
408
409 Returns:
410 string of underlying exception raised in servod.
411 """
412 return re.sub('^.*>:', '', xmlexc.faultString)
413
414
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700415 def get(self, gpio_name):
416 """Get the value of a gpio from Servod."""
417 assert gpio_name
Todd Brochefe72cb2012-07-11 19:58:53 -0700418 try:
Simran Basi1cbc5f22013-07-17 14:23:58 -0700419 return self._server.get(gpio_name)
Todd Brochefe72cb2012-07-11 19:58:53 -0700420 except xmlrpclib.Fault as e:
421 err_msg = "Getting '%s' :: %s" % \
422 (gpio_name, self._get_xmlrpclib_exception(e))
423 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700424
425
426 def set(self, gpio_name, gpio_value):
427 """Set and check the value of a gpio using Servod."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700428 self.set_nocheck(gpio_name, gpio_value)
Todd Brochcf7c6652012-02-24 13:03:59 -0800429 retry_count = Servo.GET_RETRY_MAX
430 while gpio_value != self.get(gpio_name) and retry_count:
Ilja H. Friedel04be2bd2014-05-07 21:29:59 -0700431 logging.warning("%s != %s, retry %d", gpio_name, gpio_value,
Todd Brochcf7c6652012-02-24 13:03:59 -0800432 retry_count)
433 retry_count -= 1
434 time.sleep(Servo.SHORT_DELAY)
435 if not retry_count:
436 assert gpio_value == self.get(gpio_name), \
437 'Servo failed to set %s to %s' % (gpio_name, gpio_value)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700438
439
440 def set_nocheck(self, gpio_name, gpio_value):
441 """Set the value of a gpio using Servod."""
442 assert gpio_name and gpio_value
Todd Broch9753bd42012-03-21 10:15:08 -0700443 logging.info('Setting %s to %s', gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700444 try:
Simran Basi1cbc5f22013-07-17 14:23:58 -0700445 self._server.set(gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700446 except xmlrpclib.Fault as e:
447 err_msg = "Setting '%s' to '%s' :: %s" % \
448 (gpio_name, gpio_value, self._get_xmlrpclib_exception(e))
449 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700450
451
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800452 def set_get_all(self, controls):
453 """Set &| get one or more control values.
454
455 @param controls: list of strings, controls to set &| get.
456
457 @raise: error.TestError in case error occurs setting/getting values.
458 """
459 rv = []
460 try:
Vic Yangcad9acb2013-05-21 14:02:05 +0800461 logging.info('Set/get all: %s', str(controls))
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800462 rv = self._server.set_get_all(controls)
463 except xmlrpclib.Fault as e:
464 # TODO(waihong): Remove the following backward compatibility when
465 # the new versions of hdctools are deployed.
466 if 'not supported' in str(e):
467 logging.warning('The servod is too old that set_get_all '
468 'not supported. Use set and get instead.')
469 for control in controls:
470 if ':' in control:
471 (name, value) = control.split(':')
472 if name == 'sleep':
473 time.sleep(float(value))
474 else:
475 self.set_nocheck(name, value)
476 rv.append(True)
477 else:
478 rv.append(self.get(name))
479 else:
480 err_msg = "Problem with '%s' :: %s" % \
481 (controls, self._get_xmlrpclib_exception(e))
482 raise error.TestFail(err_msg)
483 return rv
484
485
Jon Salzc88e5b62011-11-30 14:38:54 +0800486 # TODO(waihong) It may fail if multiple servo's are connected to the same
487 # host. Should look for a better way, like the USB serial name, to identify
488 # the USB device.
Simran Basi741b5d42012-05-18 11:27:15 -0700489 # TODO(sbasi) Remove this code from autoserv once firmware tests have been
490 # updated.
Jon Salzc88e5b62011-11-30 14:38:54 +0800491 def probe_host_usb_dev(self):
492 """Probe the USB disk device plugged-in the servo from the host side.
493
494 It tries to switch the USB mux to make the host unable to see the
495 USB disk and compares the result difference.
496
Jon Salzc88e5b62011-11-30 14:38:54 +0800497 Returns:
498 A string of USB disk path, like '/dev/sdb', or None if not existed.
499 """
500 cmd = 'ls /dev/sd[a-z]'
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800501 original_value = self.get_usbkey_direction()
Jon Salzc88e5b62011-11-30 14:38:54 +0800502
503 # Make the host unable to see the USB disk.
Fang Dengafb88142013-05-30 17:44:31 -0700504 self.switch_usbkey('off')
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800505 no_usb_set = set(self.system_output(cmd, ignore_status=True).split())
Jon Salzc88e5b62011-11-30 14:38:54 +0800506
507 # Make the host able to see the USB disk.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800508 self.switch_usbkey('host')
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800509 has_usb_set = set(self.system_output(cmd, ignore_status=True).split())
Jon Salzc88e5b62011-11-30 14:38:54 +0800510
511 # Back to its original value.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800512 if original_value != self.get_usbkey_direction():
513 self.switch_usbkey(original_value)
Jon Salzc88e5b62011-11-30 14:38:54 +0800514
515 diff_set = has_usb_set - no_usb_set
516 if len(diff_set) == 1:
517 return diff_set.pop()
518 else:
519 return None
520
521
Mike Truty49153d82012-08-21 22:27:30 -0500522 def image_to_servo_usb(self, image_path=None,
523 make_image_noninteractive=False):
524 """Install an image to the USB key plugged into the servo.
525
526 This method may copy any image to the servo USB key including a
527 recovery image or a test image. These images are frequently used
528 for test purposes such as restoring a corrupted image or conducting
529 an upgrade of ec/fw/kernel as part of a test of a specific image part.
530
531 Args:
532 image_path: Path on the host to the recovery image.
533 make_image_noninteractive: Make the recovery image noninteractive,
534 therefore the DUT will reboot
535 automatically after installation.
536 """
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700537 # We're about to start plugging/unplugging the USB key. We
538 # don't know the state of the DUT, or what it might choose
539 # to do to the device after hotplug. To avoid surprises,
540 # force the DUT to be off.
541 self._server.hwinit()
542 self._power_state.power_off()
Mike Truty49153d82012-08-21 22:27:30 -0500543
544 # Set up Servo's usb mux.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800545 self.switch_usbkey('host')
Mike Truty49153d82012-08-21 22:27:30 -0500546 if image_path:
Tom Wai-Hong Tam42f136d2012-10-26 11:11:23 +0800547 logging.info('Searching for usb device and copying image to it. '
548 'Please wait a few minutes...')
Mike Truty49153d82012-08-21 22:27:30 -0500549 if not self._server.download_image_to_usb(image_path):
550 logging.error('Failed to transfer requested image to USB. '
551 'Please take a look at Servo Logs.')
552 raise error.AutotestError('Download image to usb failed.')
553 if make_image_noninteractive:
554 logging.info('Making image noninteractive')
555 if not self._server.make_image_noninteractive():
556 logging.error('Failed to make image noninteractive. '
557 'Please take a look at Servo Logs.')
558
559
Simran Basi741b5d42012-05-18 11:27:15 -0700560 def install_recovery_image(self, image_path=None,
J. Richard Barnetteb6de7e32013-02-14 13:28:04 -0800561 make_image_noninteractive=False):
Jon Salzc88e5b62011-11-30 14:38:54 +0800562 """Install the recovery image specied by the path onto the DUT.
563
564 This method uses google recovery mode to install a recovery image
565 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 +0800566 board specified by the usb_dev. If no image path is specified
Jon Salzc88e5b62011-11-30 14:38:54 +0800567 we use the recovery image already on the usb image.
568
569 Args:
570 image_path: Path on the host to the recovery image.
Simran Basi741b5d42012-05-18 11:27:15 -0700571 make_image_noninteractive: Make the recovery image noninteractive,
572 therefore the DUT will reboot
573 automatically after installation.
Jon Salzc88e5b62011-11-30 14:38:54 +0800574 """
Mike Truty49153d82012-08-21 22:27:30 -0500575 self.image_to_servo_usb(image_path, make_image_noninteractive)
J. Richard Barnette4b6af0d2014-06-05 09:57:20 -0700576 self._power_state.power_on(rec_mode=self._power_state.REC_ON)
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700577 self.switch_usbkey('dut')
Jon Salzc88e5b62011-11-30 14:38:54 +0800578
579
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800580 def _scp_image(self, image_path):
581 """Copy image to the servo host.
582
583 When programming a firmware image on the DUT, the image must be
584 located on the host to which the servo device is connected. Sometimes
585 servo is controlled by a remote host, in this case the image needs to
586 be transferred to the remote host.
587
588 @param image_path: a string, name of the firmware image file to be
589 transferred.
590 @return: a string, full path name of the copied file on the remote.
591 """
592
593 dest_path = os.path.join('/tmp', os.path.basename(image_path))
Fang Deng5d518f42013-08-02 14:04:32 -0700594 self._servo_host.send_file(image_path, dest_path)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800595 return dest_path
596
597
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800598 def system(self, command, timeout=None):
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800599 """Execute the passed in command on the servod host."""
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800600 logging.info('Will execute on servo host: %s', command)
Fang Deng5d518f42013-08-02 14:04:32 -0700601 self._servo_host.run(command, timeout=timeout)
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800602
603
604 def system_output(self, command, timeout=None,
605 ignore_status=False, args=()):
606 """Execute the passed in command on the servod host, return stdout.
607
608 @param command, a string, the command to execute
609 @param timeout, an int, max number of seconds to wait til command
610 execution completes
611 @ignore_status, a Boolean, if true - ignore command's nonzero exit
612 status, otherwise an exception will be thrown
613 @param args, a tuple of strings, each becoming a separate command line
614 parameter for the command
615 @return: command's stdout as a string.
616 """
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800617 logging.info('Will execute and collect output on servo host: %s %s',
618 command, ' '.join("'%s'" % x for x in args))
Fang Deng5d518f42013-08-02 14:04:32 -0700619 return self._servo_host.run(command, timeout=timeout,
620 ignore_status=ignore_status,
621 args=args).stdout.strip()
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800622
623
Dan Shia5fef052015-05-18 23:28:47 -0700624 def get_servo_version(self):
625 """Get the version of the servo, e.g., servo_v2 or servo_v3.
626
627 @return: The version of the servo.
628
629 """
630 return self._server.get_version()
631
632
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700633 def _initialize_programmer(self):
634 if self._programmer:
635 return
636 # Initialize firmware programmer
Dan Shia5fef052015-05-18 23:28:47 -0700637 servo_version = self.get_servo_version()
Dan Shi9cb0eec2014-06-03 09:04:50 -0700638 if servo_version.startswith('servo_v2'):
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700639 self._programmer = firmware_programmer.ProgrammerV2(self)
Dan Shia5fef052015-05-18 23:28:47 -0700640 elif servo_version.startswith('servo_v3'):
641 self._programmer = firmware_programmer.ProgrammerV3(self)
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700642 else:
643 raise error.TestError(
644 'No firmware programmer for servo version: %s' %
645 servo_version)
646
647
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800648 def program_bios(self, image):
649 """Program bios on DUT with given image.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800650
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800651 @param image: a string, file name of the BIOS image to program
652 on the DUT.
653
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800654 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700655 self._initialize_programmer()
Ricky Liangc31aab32014-07-03 16:23:29 +0800656 if not self.is_localhost():
657 image = self._scp_image(image)
658 self._programmer.program_bios(image)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800659
660
Dan Shia5fef052015-05-18 23:28:47 -0700661 def _get_board_from_ec(self, image):
662 """Get the board name from the EC image file.
663
664 @param image: string with the location of the image file
665
666 @return: Board name used in the EC image file.
667
668 """
669 cmd = ('strings "%s" | grep -oP "[a-z\-_0-9]*(?=_v[0-9]\.[0-9]\.)" | '
670 'head -n 1' % image)
671 return utils.run(cmd).stdout.strip()
672
673
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800674 def program_ec(self, image):
675 """Program ec on DUT with given image.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800676
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800677 @param image: a string, file name of the EC image to program
678 on the DUT.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800679
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800680 """
J. Richard Barnette8f19b392014-08-11 14:05:39 -0700681 self._initialize_programmer()
Dan Shia5fef052015-05-18 23:28:47 -0700682 board = self._get_board_from_ec(image)
683 logging.info('board name from ec image file is "%s".', board)
Ricky Liangc31aab32014-07-03 16:23:29 +0800684 if not self.is_localhost():
685 image = self._scp_image(image)
Dan Shia5fef052015-05-18 23:28:47 -0700686 self._programmer.program_ec(image, board=board)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800687
Fang Dengafb88142013-05-30 17:44:31 -0700688
689 def _switch_usbkey_power(self, power_state, detection_delay=False):
690 """Switch usbkey power.
691
692 This function switches usbkey power by setting the value of
693 'prtctl4_pwren'. If the power is already in the
694 requested state, this function simply returns.
695
696 @param power_state: A string, 'on' or 'off'.
697 @param detection_delay: A boolean value, if True, sleep
698 for |USB_DETECTION_DELAY| after switching
699 the power on.
700 """
701 self.set('prtctl4_pwren', power_state)
702 if power_state == 'off':
703 time.sleep(self.USB_POWEROFF_DELAY)
704 elif detection_delay:
705 time.sleep(self.USB_DETECTION_DELAY)
706
707
708 def switch_usbkey(self, usb_state):
709 """Connect USB flash stick to either host or DUT, or turn USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800710
711 This function switches the servo multiplexer to provide electrical
Fang Dengafb88142013-05-30 17:44:31 -0700712 connection between the USB port J3 and either host or DUT side. It
713 can also be used to turn the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800714
Fang Dengafb88142013-05-30 17:44:31 -0700715 Switching to 'dut' or 'host' is accompanied by powercycling
716 of the USB stick, because it sometimes gets wedged if the mux
717 is switched while the stick power is on.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800718
Fang Dengafb88142013-05-30 17:44:31 -0700719 @param usb_state: A string, one of 'dut', 'host', or 'off'.
720 'dut' and 'host' indicate which side the
721 USB flash device is required to be connected to.
722 'off' indicates turning the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800723
Fang Dengafb88142013-05-30 17:44:31 -0700724 @raise: error.TestError in case the parameter is not 'dut'
725 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800726 """
Fang Dengafb88142013-05-30 17:44:31 -0700727 if self.get_usbkey_direction() == usb_state:
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800728 return
729
Fang Dengafb88142013-05-30 17:44:31 -0700730 if usb_state == 'off':
Dan Shia5fef052015-05-18 23:28:47 -0700731 self._switch_usbkey_power('off')
732 self._usb_state = usb_state
733 return
Fang Dengafb88142013-05-30 17:44:31 -0700734 elif usb_state == 'host':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800735 mux_direction = 'servo_sees_usbkey'
Fang Dengafb88142013-05-30 17:44:31 -0700736 elif usb_state == 'dut':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800737 mux_direction = 'dut_sees_usbkey'
738 else:
Fang Dengafb88142013-05-30 17:44:31 -0700739 raise error.TestError('Unknown USB state request: %s' % usb_state)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800740
Fang Dengafb88142013-05-30 17:44:31 -0700741 self._switch_usbkey_power('off')
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800742 self.set('usb_mux_sel1', mux_direction)
743 time.sleep(self.USB_POWEROFF_DELAY)
Fang Dengafb88142013-05-30 17:44:31 -0700744 self._switch_usbkey_power('on', usb_state == 'host')
745 self._usb_state = usb_state
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800746
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800747
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800748 def get_usbkey_direction(self):
Fang Dengafb88142013-05-30 17:44:31 -0700749 """Get which side USB is connected to or 'off' if usb power is off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800750
Fang Dengafb88142013-05-30 17:44:31 -0700751 @return: A string, one of 'dut', 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800752 """
Fang Dengafb88142013-05-30 17:44:31 -0700753 if not self._usb_state:
754 if self.get('prtctl4_pwren') == 'off':
755 self._usb_state = 'off'
756 elif self.get('usb_mux_sel1').startswith('dut'):
757 self._usb_state = 'dut'
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800758 else:
Fang Dengafb88142013-05-30 17:44:31 -0700759 self._usb_state = 'host'
760 return self._usb_state