blob: f59a88e7e49b37bcf224790c4c976ff80961faca [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
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -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
73 def recovery_supported(self):
74 """Return whether the power on/off methods are supported.
75
76 @return True means the power_on() and power_off() methods will
77 not raise a NotImplementedError. False means they will.
78
79 """
80 return True
81
82 def power_off(self):
83 """Force the DUT to power off.
84
85 The DUT is guaranteed to be off at the end of this call,
86 regardless of its previous state, provided that there is
87 working EC and boot firmware. There is no requirement for
88 working OS software.
89
90 """
91 self._servo.set_nocheck('power_state', 'off')
92
93 def power_on(self, rec_mode=REC_OFF):
94 """Force the DUT to power on.
95
96 Prior to calling this function, the DUT must be powered off,
97 e.g. with a call to `power_off()`.
98
99 At power on, recovery mode is set as specified by the
100 corresponding argument. When booting with recovery mode on, it
101 is the caller's responsibility to unplug/plug in a bootable
102 external storage device.
103
104 If the DUT requires a delay after powering on but before
105 processing inputs such as USB stick insertion, the delay is
106 handled by this method; the caller is not responsible for such
107 delays.
108
109 @param rec_mode Setting of recovery mode to be applied at
110 power on. default: REC_OFF aka 'off'
111
112 """
113 self._servo.set_nocheck('power_state', rec_mode)
114
115
J. Richard Barnette384056b2012-04-16 11:04:46 -0700116class Servo(object):
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700117
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700118 """Manages control of a Servo board.
119
120 Servo is a board developed by hardware group to aide in the debug and
121 control of various partner devices. Servo's features include the simulation
122 of pressing the power button, closing the lid, and pressing Ctrl-d. This
123 class manages setting up and communicating with a servo demon (servod)
124 process. It provides both high-level functions for common servo tasks and
125 low-level functions for directly setting and reading gpios.
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700126
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700127 """
128
Chrome Bot9a1137d2011-07-19 14:35:00 -0700129 # Power button press delays in seconds.
J. Richard Barnetted2e4cbd2012-06-29 12:18:40 -0700130 #
J. Richard Barnette5383f072012-07-26 17:35:40 -0700131 # The EC specification says that 8.0 seconds should be enough
132 # for the long power press. However, some platforms need a bit
133 # more time. Empirical testing has found these requirements:
134 # Alex: 8.2 seconds
135 # ZGB: 8.5 seconds
136 # The actual value is set to the largest known necessary value.
137 #
138 # TODO(jrbarnette) Being generous is the right thing to do for
139 # existing platforms, but if this code is to be used for
140 # qualification of new hardware, we should be less generous.
Chrome Bot9a1137d2011-07-19 14:35:00 -0700141 SHORT_DELAY = 0.1
J. Richard Barnette5383f072012-07-26 17:35:40 -0700142
Todd Broch31c82502011-08-29 08:14:39 -0700143 # Maximum number of times to re-read power button on release.
Todd Brochcf7c6652012-02-24 13:03:59 -0800144 GET_RETRY_MAX = 10
Chrome Bot9a1137d2011-07-19 14:35:00 -0700145
J. Richard Barnette5383f072012-07-26 17:35:40 -0700146 # Delays to deal with DUT state transitions.
Chrome Bot9a1137d2011-07-19 14:35:00 -0700147 SLEEP_DELAY = 6
148 BOOT_DELAY = 10
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700149
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700150 # Default minimum time interval between 'press' and 'release'
151 # keyboard events.
Vic Yang0aca1c22012-11-19 18:33:56 -0800152 SERVO_KEY_PRESS_DELAY = 0.1
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700153
Jon Salzc88e5b62011-11-30 14:38:54 +0800154 # Time between an usb disk plugged-in and detected in the system.
155 USB_DETECTION_DELAY = 10
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800156 # Time to keep USB power off before and after USB mux direction is changed
157 USB_POWEROFF_DELAY = 2
Jon Salzc88e5b62011-11-30 14:38:54 +0800158
Simran Basib7850bb2013-07-24 12:33:42 -0700159 # Time to wait before timing out on servo initialization.
160 INIT_TIMEOUT_SECS = 10
Simran Basi59331342013-07-12 12:06:29 -0700161
J. Richard Barnette67ccb872012-04-19 16:34:56 -0700162
Ricky Liang0dd379c2014-04-23 16:29:08 +0800163 def __init__(self, servo_host, servo_serial=None):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700164 """Sets up the servo communication infrastructure.
165
Fang Deng5d518f42013-08-02 14:04:32 -0700166 @param servo_host: A ServoHost object representing
167 the host running servod.
Ricky Liang0dd379c2014-04-23 16:29:08 +0800168 @param servo_serial: Serial number of the servo board.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700169 """
Fang Deng5d518f42013-08-02 14:04:32 -0700170 # TODO(fdeng): crbug.com/298379
171 # We should move servo_host object out of servo object
172 # to minimize the dependencies on the rest of Autotest.
173 self._servo_host = servo_host
Ricky Liang0dd379c2014-04-23 16:29:08 +0800174 self._servo_serial = servo_serial
Fang Deng5d518f42013-08-02 14:04:32 -0700175 self._server = servo_host.get_servod_server_proxy()
J. Richard Barnette0c9c5882014-06-11 15:27:05 -0700176 self._power_state = _PowerStateController(self)
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800177
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800178 # a string, showing what interface (host or dut) the USB device is
179 # connected to.
Fang Dengafb88142013-05-30 17:44:31 -0700180 self._usb_state = None
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800181 self.set('dut_hub_pwren', 'on')
182 self.set('usb_mux_oe1', 'on')
Fang Dengafb88142013-05-30 17:44:31 -0700183 self.switch_usbkey('off')
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800184
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800185 # Initialize firmware programmer
Yusuf Mohsinally6a837572014-03-10 14:35:45 -0700186 if self.get_version() == "servo_v2":
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800187 self._programmer = firmware_programmer.ProgrammerV2(self)
188 else:
Ilja H. Friedel04be2bd2014-05-07 21:29:59 -0700189 logging.warning("No firmware programmer for servo version: %s",
Yusuf Mohsinally6a837572014-03-10 14:35:45 -0700190 self.get_version())
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800191
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800192
Ricky Liang0dd379c2014-04-23 16:29:08 +0800193 @property
194 def servo_serial(self):
195 """Returns the serial number of the servo board."""
196 return self._servo_serial
197
198
Tom Wai-Hong Tam22ee0e52013-04-03 13:36:39 +0800199 def get_power_state_controller(self):
J. Richard Barnette75136b32013-03-26 13:38:44 -0700200 """Return the power state controller for this Servo.
201
202 The power state controller provides board-independent
203 interfaces for reset, power-on, power-off operations.
204
205 """
206 return self._power_state
207
Fang Deng5d518f42013-08-02 14:04:32 -0700208
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700209 def initialize_dut(self, cold_reset=False):
210 """Initializes a dut for testing purposes.
211
212 This sets various servo signals back to default values
213 appropriate for the target board. By default, if the DUT
214 is already on, it stays on. If the DUT is powered off
215 before initialization, its state afterward is unspecified.
216
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700217 Rationale: Basic initialization of servo sets the lid open,
218 when there is a lid. This operation won't affect powered on
219 units; however, setting the lid open may power on a unit
J. Richard Barnette75136b32013-03-26 13:38:44 -0700220 that's off, depending on the board type and previous state
221 of the device.
222
J. Richard Barnette4b6af0d2014-06-05 09:57:20 -0700223 If `cold_reset` is a true value, the DUT and its EC will be
224 reset, and the DUT rebooted in normal mode.
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700225
226 @param cold_reset If True, cold reset the device after
227 initialization.
228 """
229 self._server.hwinit()
230 if cold_reset:
J. Richard Barnette4b6af0d2014-06-05 09:57:20 -0700231 self._power_state.reset()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700232
233
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800234 def is_localhost(self):
235 """Is the servod hosted locally?
236
237 Returns:
238 True if local hosted; otherwise, False.
239 """
Fang Deng5d518f42013-08-02 14:04:32 -0700240 return self._servo_host.is_localhost()
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800241
242
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700243 def power_long_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700244 """Simulate a long power button press."""
J. Richard Barnettef12bff22012-07-18 14:41:06 -0700245 # After a long power press, the EC may ignore the next power
246 # button press (at least on Alex). To guarantee that this
247 # won't happen, we need to allow the EC one second to
248 # collect itself.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800249 self._server.power_long_press()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700250
251
252 def power_normal_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700253 """Simulate a normal power button press."""
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800254 self._server.power_normal_press()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700255
256
257 def power_short_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700258 """Simulate a short power button press."""
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800259 self._server.power_short_press()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700260
261
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800262 def power_key(self, press_secs=''):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700263 """Simulate a power button press.
264
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800265 @param press_secs : Str. Time to press key.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700266 """
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800267 self._server.power_key(press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700268
269
270 def lid_open(self):
271 """Simulate opening the lid."""
Craig Harrison48997262011-06-27 14:31:10 -0700272 self.set_nocheck('lid_open', 'yes')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700273
274
275 def lid_close(self):
Craig Harrison48997262011-06-27 14:31:10 -0700276 """Simulate closing the lid.
277
278 Waits 6 seconds to ensure the device is fully asleep before returning.
279 """
280 self.set_nocheck('lid_open', 'no')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700281 time.sleep(Servo.SLEEP_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700282
283
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800284 def ctrl_d(self, press_secs=''):
285 """Simulate Ctrl-d simultaneous button presses.
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800286
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800287 @param press_secs : Str. Time to press key.
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800288 """
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800289 self._server.ctrl_d(press_secs)
Gediminas Ramanauskas9da80f22012-11-14 12:59:43 -0800290
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800291
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800292 def ctrl_u(self):
293 """Simulate Ctrl-u simultaneous button presses.
294
295 @param press_secs : Str. Time to press key.
296 """
297 self._server.ctrl_u()
Todd Broch9dfc3a82011-11-01 08:09:28 -0700298
299
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800300 def ctrl_enter(self, press_secs=''):
301 """Simulate Ctrl-enter simultaneous button presses.
302
303 @param press_secs : Str. Time to press key.
304 """
305 self._server.ctrl_enter(press_secs)
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -0700306
307
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800308 def d_key(self, press_secs=''):
309 """Simulate Enter key button press.
310
311 @param press_secs : Str. Time to press key.
312 """
313 self._server.d_key(press_secs)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700314
315
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800316 def ctrl_key(self, press_secs=''):
317 """Simulate Enter key button press.
318
319 @param press_secs : Str. Time to press key.
320 """
321 self._server.ctrl_key(press_secs)
Todd Broch9753bd42012-03-21 10:15:08 -0700322
323
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800324 def enter_key(self, press_secs=''):
325 """Simulate Enter key button press.
326
327 @param press_secs : Str. Time to press key.
328 """
329 self._server.enter_key(press_secs)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700330
331
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800332 def refresh_key(self, press_secs=''):
333 """Simulate Refresh key (F3) button press.
334
335 @param press_secs : Str. Time to press key.
336 """
337 self._server.refresh_key(press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700338
339
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800340 def ctrl_refresh_key(self, press_secs=''):
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800341 """Simulate Ctrl and Refresh (F3) simultaneous press.
342
343 This key combination is an alternative of Space key.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800344
345 @param press_secs : Str. Time to press key.
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800346 """
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800347 self._server.ctrl_refresh_key(press_secs)
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800348
349
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800350 def imaginary_key(self, press_secs=''):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700351 """Simulate imaginary key button press.
352
353 Maps to a key that doesn't physically exist.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800354
355 @param press_secs : Str. Time to press key.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700356 """
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800357 self._server.imaginary_key(press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700358
359
Craig Harrison6b36b122011-06-28 17:58:43 -0700360 def enable_recovery_mode(self):
361 """Enable recovery mode on device."""
362 self.set('rec_mode', 'on')
363
364
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800365 def custom_recovery_mode(self):
366 """Custom key combination to enter recovery mode."""
367 self._press_keys('rec_mode')
368 self.power_normal_press()
369 time.sleep(self.SERVO_KEY_PRESS_DELAY)
370 self.set_nocheck('kbd_en', 'off')
371
372
Craig Harrison6b36b122011-06-28 17:58:43 -0700373 def disable_recovery_mode(self):
374 """Disable recovery mode on device."""
375 self.set('rec_mode', 'off')
376
377
378 def enable_development_mode(self):
379 """Enable development mode on device."""
380 self.set('dev_mode', 'on')
381
382
383 def disable_development_mode(self):
384 """Disable development mode on device."""
385 self.set('dev_mode', 'off')
386
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700387 def boot_devmode(self):
388 """Boot a dev-mode device that is powered off."""
Tom Wai-Hong Tam23869102012-01-17 15:41:30 +0800389 self.power_short_press()
Craig Harrison48997262011-06-27 14:31:10 -0700390 self.pass_devmode()
391
392
393 def pass_devmode(self):
394 """Pass through boot screens in dev-mode."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700395 time.sleep(Servo.BOOT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700396 self.ctrl_d()
Chrome Bot9a1137d2011-07-19 14:35:00 -0700397 time.sleep(Servo.BOOT_DELAY)
Craig Harrison48997262011-06-27 14:31:10 -0700398
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700399
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800400 def get_version(self):
401 """Get the version of servod board.
402
403 """
404 return self._server.get_version()
405
406
407 def get_board(self):
408 """Get the board connected to servod.
409
410 """
411 return self._server.get_board()
412
413
Todd Brochefe72cb2012-07-11 19:58:53 -0700414 def _get_xmlrpclib_exception(self, xmlexc):
415 """Get meaningful exception string from xmlrpc.
416
417 Args:
418 xmlexc: xmlrpclib.Fault object
419
420 xmlrpclib.Fault.faultString has the following format:
421
422 <type 'exception type'>:'actual error message'
423
424 Parse and return the real exception from the servod side instead of the
425 less meaningful one like,
426 <Fault 1: "<type 'exceptions.AttributeError'>:'tca6416' object has no
427 attribute 'hw_driver'">
428
429 Returns:
430 string of underlying exception raised in servod.
431 """
432 return re.sub('^.*>:', '', xmlexc.faultString)
433
434
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700435 def get(self, gpio_name):
436 """Get the value of a gpio from Servod."""
437 assert gpio_name
Todd Brochefe72cb2012-07-11 19:58:53 -0700438 try:
Simran Basi1cbc5f22013-07-17 14:23:58 -0700439 return self._server.get(gpio_name)
Todd Brochefe72cb2012-07-11 19:58:53 -0700440 except xmlrpclib.Fault as e:
441 err_msg = "Getting '%s' :: %s" % \
442 (gpio_name, self._get_xmlrpclib_exception(e))
443 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700444
445
446 def set(self, gpio_name, gpio_value):
447 """Set and check the value of a gpio using Servod."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700448 self.set_nocheck(gpio_name, gpio_value)
Todd Brochcf7c6652012-02-24 13:03:59 -0800449 retry_count = Servo.GET_RETRY_MAX
450 while gpio_value != self.get(gpio_name) and retry_count:
Ilja H. Friedel04be2bd2014-05-07 21:29:59 -0700451 logging.warning("%s != %s, retry %d", gpio_name, gpio_value,
Todd Brochcf7c6652012-02-24 13:03:59 -0800452 retry_count)
453 retry_count -= 1
454 time.sleep(Servo.SHORT_DELAY)
455 if not retry_count:
456 assert gpio_value == self.get(gpio_name), \
457 'Servo failed to set %s to %s' % (gpio_name, gpio_value)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700458
459
460 def set_nocheck(self, gpio_name, gpio_value):
461 """Set the value of a gpio using Servod."""
462 assert gpio_name and gpio_value
Todd Broch9753bd42012-03-21 10:15:08 -0700463 logging.info('Setting %s to %s', gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700464 try:
Simran Basi1cbc5f22013-07-17 14:23:58 -0700465 self._server.set(gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700466 except xmlrpclib.Fault as e:
467 err_msg = "Setting '%s' to '%s' :: %s" % \
468 (gpio_name, gpio_value, self._get_xmlrpclib_exception(e))
469 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700470
471
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800472 def set_get_all(self, controls):
473 """Set &| get one or more control values.
474
475 @param controls: list of strings, controls to set &| get.
476
477 @raise: error.TestError in case error occurs setting/getting values.
478 """
479 rv = []
480 try:
Vic Yangcad9acb2013-05-21 14:02:05 +0800481 logging.info('Set/get all: %s', str(controls))
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800482 rv = self._server.set_get_all(controls)
483 except xmlrpclib.Fault as e:
484 # TODO(waihong): Remove the following backward compatibility when
485 # the new versions of hdctools are deployed.
486 if 'not supported' in str(e):
487 logging.warning('The servod is too old that set_get_all '
488 'not supported. Use set and get instead.')
489 for control in controls:
490 if ':' in control:
491 (name, value) = control.split(':')
492 if name == 'sleep':
493 time.sleep(float(value))
494 else:
495 self.set_nocheck(name, value)
496 rv.append(True)
497 else:
498 rv.append(self.get(name))
499 else:
500 err_msg = "Problem with '%s' :: %s" % \
501 (controls, self._get_xmlrpclib_exception(e))
502 raise error.TestFail(err_msg)
503 return rv
504
505
Jon Salzc88e5b62011-11-30 14:38:54 +0800506 # TODO(waihong) It may fail if multiple servo's are connected to the same
507 # host. Should look for a better way, like the USB serial name, to identify
508 # the USB device.
Simran Basi741b5d42012-05-18 11:27:15 -0700509 # TODO(sbasi) Remove this code from autoserv once firmware tests have been
510 # updated.
Jon Salzc88e5b62011-11-30 14:38:54 +0800511 def probe_host_usb_dev(self):
512 """Probe the USB disk device plugged-in the servo from the host side.
513
514 It tries to switch the USB mux to make the host unable to see the
515 USB disk and compares the result difference.
516
Jon Salzc88e5b62011-11-30 14:38:54 +0800517 Returns:
518 A string of USB disk path, like '/dev/sdb', or None if not existed.
519 """
520 cmd = 'ls /dev/sd[a-z]'
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800521 original_value = self.get_usbkey_direction()
Jon Salzc88e5b62011-11-30 14:38:54 +0800522
523 # Make the host unable to see the USB disk.
Fang Dengafb88142013-05-30 17:44:31 -0700524 self.switch_usbkey('off')
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800525 no_usb_set = set(self.system_output(cmd, ignore_status=True).split())
Jon Salzc88e5b62011-11-30 14:38:54 +0800526
527 # Make the host able to see the USB disk.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800528 self.switch_usbkey('host')
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800529 has_usb_set = set(self.system_output(cmd, ignore_status=True).split())
Jon Salzc88e5b62011-11-30 14:38:54 +0800530
531 # Back to its original value.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800532 if original_value != self.get_usbkey_direction():
533 self.switch_usbkey(original_value)
Jon Salzc88e5b62011-11-30 14:38:54 +0800534
535 diff_set = has_usb_set - no_usb_set
536 if len(diff_set) == 1:
537 return diff_set.pop()
538 else:
539 return None
540
541
J. Richard Barnette69929a52013-03-15 13:22:11 -0700542 def recovery_supported(self):
543 """Return whether servo-based recovery should work.
544
545 Use of `image_to_servo_usb()` and `install_recovery_image()`
546 relies on DUT-board specific behaviors, and is not supported
547 for all types of board. Return whether these two operations
548 are expected to succeed for the current DUT.
549
550 @return `True` iff the recovery related methods are supported
551 for this servo and DUT.
552
553 """
J. Richard Barnette75136b32013-03-26 13:38:44 -0700554 return self._power_state.recovery_supported()
J. Richard Barnette69929a52013-03-15 13:22:11 -0700555
556
Mike Truty49153d82012-08-21 22:27:30 -0500557 def image_to_servo_usb(self, image_path=None,
558 make_image_noninteractive=False):
559 """Install an image to the USB key plugged into the servo.
560
561 This method may copy any image to the servo USB key including a
562 recovery image or a test image. These images are frequently used
563 for test purposes such as restoring a corrupted image or conducting
564 an upgrade of ec/fw/kernel as part of a test of a specific image part.
565
566 Args:
567 image_path: Path on the host to the recovery image.
568 make_image_noninteractive: Make the recovery image noninteractive,
569 therefore the DUT will reboot
570 automatically after installation.
571 """
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700572 # We're about to start plugging/unplugging the USB key. We
573 # don't know the state of the DUT, or what it might choose
574 # to do to the device after hotplug. To avoid surprises,
575 # force the DUT to be off.
576 self._server.hwinit()
577 self._power_state.power_off()
Mike Truty49153d82012-08-21 22:27:30 -0500578
579 # Set up Servo's usb mux.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800580 self.switch_usbkey('host')
Mike Truty49153d82012-08-21 22:27:30 -0500581 if image_path:
Tom Wai-Hong Tam42f136d2012-10-26 11:11:23 +0800582 logging.info('Searching for usb device and copying image to it. '
583 'Please wait a few minutes...')
Mike Truty49153d82012-08-21 22:27:30 -0500584 if not self._server.download_image_to_usb(image_path):
585 logging.error('Failed to transfer requested image to USB. '
586 'Please take a look at Servo Logs.')
587 raise error.AutotestError('Download image to usb failed.')
588 if make_image_noninteractive:
589 logging.info('Making image noninteractive')
590 if not self._server.make_image_noninteractive():
591 logging.error('Failed to make image noninteractive. '
592 'Please take a look at Servo Logs.')
593
594
Simran Basi741b5d42012-05-18 11:27:15 -0700595 def install_recovery_image(self, image_path=None,
J. Richard Barnetteb6de7e32013-02-14 13:28:04 -0800596 make_image_noninteractive=False):
Jon Salzc88e5b62011-11-30 14:38:54 +0800597 """Install the recovery image specied by the path onto the DUT.
598
599 This method uses google recovery mode to install a recovery image
600 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 +0800601 board specified by the usb_dev. If no image path is specified
Jon Salzc88e5b62011-11-30 14:38:54 +0800602 we use the recovery image already on the usb image.
603
604 Args:
605 image_path: Path on the host to the recovery image.
Simran Basi741b5d42012-05-18 11:27:15 -0700606 make_image_noninteractive: Make the recovery image noninteractive,
607 therefore the DUT will reboot
608 automatically after installation.
Jon Salzc88e5b62011-11-30 14:38:54 +0800609 """
Mike Truty49153d82012-08-21 22:27:30 -0500610 self.image_to_servo_usb(image_path, make_image_noninteractive)
J. Richard Barnette4b6af0d2014-06-05 09:57:20 -0700611 self._power_state.power_on(rec_mode=self._power_state.REC_ON)
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700612 self.switch_usbkey('dut')
Jon Salzc88e5b62011-11-30 14:38:54 +0800613
614
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800615 def _scp_image(self, image_path):
616 """Copy image to the servo host.
617
618 When programming a firmware image on the DUT, the image must be
619 located on the host to which the servo device is connected. Sometimes
620 servo is controlled by a remote host, in this case the image needs to
621 be transferred to the remote host.
622
623 @param image_path: a string, name of the firmware image file to be
624 transferred.
625 @return: a string, full path name of the copied file on the remote.
626 """
627
628 dest_path = os.path.join('/tmp', os.path.basename(image_path))
Fang Deng5d518f42013-08-02 14:04:32 -0700629 self._servo_host.send_file(image_path, dest_path)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800630 return dest_path
631
632
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800633 def system(self, command, timeout=None):
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800634 """Execute the passed in command on the servod host."""
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800635 logging.info('Will execute on servo host: %s', command)
Fang Deng5d518f42013-08-02 14:04:32 -0700636 self._servo_host.run(command, timeout=timeout)
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800637
638
639 def system_output(self, command, timeout=None,
640 ignore_status=False, args=()):
641 """Execute the passed in command on the servod host, return stdout.
642
643 @param command, a string, the command to execute
644 @param timeout, an int, max number of seconds to wait til command
645 execution completes
646 @ignore_status, a Boolean, if true - ignore command's nonzero exit
647 status, otherwise an exception will be thrown
648 @param args, a tuple of strings, each becoming a separate command line
649 parameter for the command
650 @return: command's stdout as a string.
651 """
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800652 logging.info('Will execute and collect output on servo host: %s %s',
653 command, ' '.join("'%s'" % x for x in args))
Fang Deng5d518f42013-08-02 14:04:32 -0700654 return self._servo_host.run(command, timeout=timeout,
655 ignore_status=ignore_status,
656 args=args).stdout.strip()
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800657
658
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800659 def program_bios(self, image):
660 """Program bios on DUT with given image.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800661
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800662 @param image: a string, file name of the BIOS image to program
663 on the DUT.
664
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800665 """
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800666 if not self.is_localhost():
667 image = self._scp_image(image)
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800668 self._programmer.program_bios(image)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800669
670
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800671 def program_ec(self, image):
672 """Program ec on DUT with given image.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800673
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800674 @param image: a string, file name of the EC image to program
675 on the DUT.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800676
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800677 """
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800678 if not self.is_localhost():
679 image = self._scp_image(image)
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800680 self._programmer.program_ec(image)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800681
Fang Dengafb88142013-05-30 17:44:31 -0700682
683 def _switch_usbkey_power(self, power_state, detection_delay=False):
684 """Switch usbkey power.
685
686 This function switches usbkey power by setting the value of
687 'prtctl4_pwren'. If the power is already in the
688 requested state, this function simply returns.
689
690 @param power_state: A string, 'on' or 'off'.
691 @param detection_delay: A boolean value, if True, sleep
692 for |USB_DETECTION_DELAY| after switching
693 the power on.
694 """
695 self.set('prtctl4_pwren', power_state)
696 if power_state == 'off':
697 time.sleep(self.USB_POWEROFF_DELAY)
698 elif detection_delay:
699 time.sleep(self.USB_DETECTION_DELAY)
700
701
702 def switch_usbkey(self, usb_state):
703 """Connect USB flash stick to either host or DUT, or turn USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800704
705 This function switches the servo multiplexer to provide electrical
Fang Dengafb88142013-05-30 17:44:31 -0700706 connection between the USB port J3 and either host or DUT side. It
707 can also be used to turn the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800708
Fang Dengafb88142013-05-30 17:44:31 -0700709 Switching to 'dut' or 'host' is accompanied by powercycling
710 of the USB stick, because it sometimes gets wedged if the mux
711 is switched while the stick power is on.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800712
Fang Dengafb88142013-05-30 17:44:31 -0700713 @param usb_state: A string, one of 'dut', 'host', or 'off'.
714 'dut' and 'host' indicate which side the
715 USB flash device is required to be connected to.
716 'off' indicates turning the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800717
Fang Dengafb88142013-05-30 17:44:31 -0700718 @raise: error.TestError in case the parameter is not 'dut'
719 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800720 """
Fang Dengafb88142013-05-30 17:44:31 -0700721 if self.get_usbkey_direction() == usb_state:
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800722 return
723
Fang Dengafb88142013-05-30 17:44:31 -0700724 if usb_state == 'off':
725 self._switch_usbkey_power('off')
726 self._usb_state = usb_state
727 return
728 elif usb_state == 'host':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800729 mux_direction = 'servo_sees_usbkey'
Fang Dengafb88142013-05-30 17:44:31 -0700730 elif usb_state == 'dut':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800731 mux_direction = 'dut_sees_usbkey'
732 else:
Fang Dengafb88142013-05-30 17:44:31 -0700733 raise error.TestError('Unknown USB state request: %s' % usb_state)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800734
Fang Dengafb88142013-05-30 17:44:31 -0700735 self._switch_usbkey_power('off')
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800736 self.set('usb_mux_sel1', mux_direction)
737 time.sleep(self.USB_POWEROFF_DELAY)
Fang Dengafb88142013-05-30 17:44:31 -0700738 self._switch_usbkey_power('on', usb_state == 'host')
739 self._usb_state = usb_state
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800740
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800741
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800742 def get_usbkey_direction(self):
Fang Dengafb88142013-05-30 17:44:31 -0700743 """Get which side USB is connected to or 'off' if usb power is off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800744
Fang Dengafb88142013-05-30 17:44:31 -0700745 @return: A string, one of 'dut', 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800746 """
Fang Dengafb88142013-05-30 17:44:31 -0700747 if not self._usb_state:
748 if self.get('prtctl4_pwren') == 'off':
749 self._usb_state = 'off'
750 elif self.get('usb_mux_sel1').startswith('dut'):
751 self._usb_state = 'dut'
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800752 else:
Fang Dengafb88142013-05-30 17:44:31 -0700753 self._usb_state = 'host'
754 return self._usb_state