blob: 22d8d7bbe46195286550ef872c48a49d517f9a15 [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
Jon Salzc88e5b62011-11-30 14:38:54 +080013from autotest_lib.server import utils
J. Richard Barnette41320ee2013-03-11 13:00:13 -070014from autotest_lib.server.cros.servo import power_state_controller
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -080015from autotest_lib.server.cros.servo import firmware_programmer
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070016
J. Richard Barnette41320ee2013-03-11 13:00:13 -070017
J. Richard Barnette384056b2012-04-16 11:04:46 -070018class Servo(object):
J. Richard Barnette41320ee2013-03-11 13:00:13 -070019
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070020 """Manages control of a Servo board.
21
22 Servo is a board developed by hardware group to aide in the debug and
23 control of various partner devices. Servo's features include the simulation
24 of pressing the power button, closing the lid, and pressing Ctrl-d. This
25 class manages setting up and communicating with a servo demon (servod)
26 process. It provides both high-level functions for common servo tasks and
27 low-level functions for directly setting and reading gpios.
J. Richard Barnette41320ee2013-03-11 13:00:13 -070028
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070029 """
30
Chrome Bot9a1137d2011-07-19 14:35:00 -070031 # Power button press delays in seconds.
J. Richard Barnetted2e4cbd2012-06-29 12:18:40 -070032 #
J. Richard Barnette5383f072012-07-26 17:35:40 -070033 # The EC specification says that 8.0 seconds should be enough
34 # for the long power press. However, some platforms need a bit
35 # more time. Empirical testing has found these requirements:
36 # Alex: 8.2 seconds
37 # ZGB: 8.5 seconds
38 # The actual value is set to the largest known necessary value.
39 #
40 # TODO(jrbarnette) Being generous is the right thing to do for
41 # existing platforms, but if this code is to be used for
42 # qualification of new hardware, we should be less generous.
Chrome Bot9a1137d2011-07-19 14:35:00 -070043 SHORT_DELAY = 0.1
J. Richard Barnette5383f072012-07-26 17:35:40 -070044
Todd Broch31c82502011-08-29 08:14:39 -070045 # Maximum number of times to re-read power button on release.
Todd Brochcf7c6652012-02-24 13:03:59 -080046 GET_RETRY_MAX = 10
Chrome Bot9a1137d2011-07-19 14:35:00 -070047
J. Richard Barnette5383f072012-07-26 17:35:40 -070048 # Delays to deal with DUT state transitions.
Chrome Bot9a1137d2011-07-19 14:35:00 -070049 SLEEP_DELAY = 6
50 BOOT_DELAY = 10
J. Richard Barnette41320ee2013-03-11 13:00:13 -070051
J. Richard Barnette41320ee2013-03-11 13:00:13 -070052 # Default minimum time interval between 'press' and 'release'
53 # keyboard events.
Vic Yang0aca1c22012-11-19 18:33:56 -080054 SERVO_KEY_PRESS_DELAY = 0.1
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070055
Jon Salzc88e5b62011-11-30 14:38:54 +080056 # Time between an usb disk plugged-in and detected in the system.
57 USB_DETECTION_DELAY = 10
Vadim Bendeburye7bd9362012-12-19 14:35:20 -080058 # Time to keep USB power off before and after USB mux direction is changed
59 USB_POWEROFF_DELAY = 2
Jon Salzc88e5b62011-11-30 14:38:54 +080060
Simran Basib7850bb2013-07-24 12:33:42 -070061 # Time to wait before timing out on servo initialization.
62 INIT_TIMEOUT_SECS = 10
Simran Basi59331342013-07-12 12:06:29 -070063
J. Richard Barnette67ccb872012-04-19 16:34:56 -070064
Ricky Liang0dd379c2014-04-23 16:29:08 +080065 def __init__(self, servo_host, servo_serial=None):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070066 """Sets up the servo communication infrastructure.
67
Fang Deng5d518f42013-08-02 14:04:32 -070068 @param servo_host: A ServoHost object representing
69 the host running servod.
Ricky Liang0dd379c2014-04-23 16:29:08 +080070 @param servo_serial: Serial number of the servo board.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070071 """
Fang Deng5d518f42013-08-02 14:04:32 -070072 # TODO(fdeng): crbug.com/298379
73 # We should move servo_host object out of servo object
74 # to minimize the dependencies on the rest of Autotest.
75 self._servo_host = servo_host
Ricky Liang0dd379c2014-04-23 16:29:08 +080076 self._servo_serial = servo_serial
Fang Deng5d518f42013-08-02 14:04:32 -070077 self._server = servo_host.get_servod_server_proxy()
78 board = self._server.get_board()
J. Richard Barnette75136b32013-03-26 13:38:44 -070079 self._power_state = (
80 power_state_controller.create_controller(self, board))
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -080081
Vadim Bendeburye7bd9362012-12-19 14:35:20 -080082 # a string, showing what interface (host or dut) the USB device is
83 # connected to.
Fang Dengafb88142013-05-30 17:44:31 -070084 self._usb_state = None
Vadim Bendeburye7bd9362012-12-19 14:35:20 -080085 self.set('dut_hub_pwren', 'on')
86 self.set('usb_mux_oe1', 'on')
Fang Dengafb88142013-05-30 17:44:31 -070087 self.switch_usbkey('off')
Vadim Bendeburye7bd9362012-12-19 14:35:20 -080088
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -080089 # Initialize firmware programmer
Yusuf Mohsinally6a837572014-03-10 14:35:45 -070090 if self.get_version() == "servo_v2":
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -080091 self._programmer = firmware_programmer.ProgrammerV2(self)
92 else:
Yusuf Mohsinally6a837572014-03-10 14:35:45 -070093 logging.warn("No firmware programmer for servo version: %s",
94 self.get_version())
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -080095
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -080096
Ricky Liang0dd379c2014-04-23 16:29:08 +080097 @property
98 def servo_serial(self):
99 """Returns the serial number of the servo board."""
100 return self._servo_serial
101
102
Tom Wai-Hong Tam22ee0e52013-04-03 13:36:39 +0800103 def get_power_state_controller(self):
J. Richard Barnette75136b32013-03-26 13:38:44 -0700104 """Return the power state controller for this Servo.
105
106 The power state controller provides board-independent
107 interfaces for reset, power-on, power-off operations.
108
109 """
110 return self._power_state
111
Fang Deng5d518f42013-08-02 14:04:32 -0700112
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700113 def initialize_dut(self, cold_reset=False):
114 """Initializes a dut for testing purposes.
115
116 This sets various servo signals back to default values
117 appropriate for the target board. By default, if the DUT
118 is already on, it stays on. If the DUT is powered off
119 before initialization, its state afterward is unspecified.
120
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700121 Rationale: Basic initialization of servo sets the lid open,
122 when there is a lid. This operation won't affect powered on
123 units; however, setting the lid open may power on a unit
J. Richard Barnette75136b32013-03-26 13:38:44 -0700124 that's off, depending on the board type and previous state
125 of the device.
126
127 If `cold_reset` is a true value, cold reset will be applied
128 to the DUT. Cold reset will force the DUT to power off;
129 however, the final state of the DUT depends on the board type.
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700130
131 @param cold_reset If True, cold reset the device after
132 initialization.
133 """
134 self._server.hwinit()
135 if cold_reset:
J. Richard Barnette75136b32013-03-26 13:38:44 -0700136 self._power_state.cold_reset()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700137
138
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800139 def is_localhost(self):
140 """Is the servod hosted locally?
141
142 Returns:
143 True if local hosted; otherwise, False.
144 """
Fang Deng5d518f42013-08-02 14:04:32 -0700145 return self._servo_host.is_localhost()
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800146
147
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700148 def power_long_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700149 """Simulate a long power button press."""
J. Richard Barnettef12bff22012-07-18 14:41:06 -0700150 # After a long power press, the EC may ignore the next power
151 # button press (at least on Alex). To guarantee that this
152 # won't happen, we need to allow the EC one second to
153 # collect itself.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800154 self._server.power_long_press()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700155
156
157 def power_normal_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700158 """Simulate a normal power button press."""
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800159 self._server.power_normal_press()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700160
161
162 def power_short_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700163 """Simulate a short power button press."""
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800164 self._server.power_short_press()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700165
166
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800167 def power_key(self, press_secs=''):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700168 """Simulate a power button press.
169
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800170 @param press_secs : Str. Time to press key.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700171 """
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800172 self._server.power_key(press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700173
174
175 def lid_open(self):
176 """Simulate opening the lid."""
Craig Harrison48997262011-06-27 14:31:10 -0700177 self.set_nocheck('lid_open', 'yes')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700178
179
180 def lid_close(self):
Craig Harrison48997262011-06-27 14:31:10 -0700181 """Simulate closing the lid.
182
183 Waits 6 seconds to ensure the device is fully asleep before returning.
184 """
185 self.set_nocheck('lid_open', 'no')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700186 time.sleep(Servo.SLEEP_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700187
188
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800189 def ctrl_d(self, press_secs=''):
190 """Simulate Ctrl-d simultaneous button presses.
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800191
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800192 @param press_secs : Str. Time to press key.
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800193 """
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800194 self._server.ctrl_d(press_secs)
Gediminas Ramanauskas9da80f22012-11-14 12:59:43 -0800195
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800196
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800197 def ctrl_u(self):
198 """Simulate Ctrl-u simultaneous button presses.
199
200 @param press_secs : Str. Time to press key.
201 """
202 self._server.ctrl_u()
Todd Broch9dfc3a82011-11-01 08:09:28 -0700203
204
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800205 def ctrl_enter(self, press_secs=''):
206 """Simulate Ctrl-enter simultaneous button presses.
207
208 @param press_secs : Str. Time to press key.
209 """
210 self._server.ctrl_enter(press_secs)
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -0700211
212
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800213 def d_key(self, press_secs=''):
214 """Simulate Enter key button press.
215
216 @param press_secs : Str. Time to press key.
217 """
218 self._server.d_key(press_secs)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700219
220
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800221 def ctrl_key(self, press_secs=''):
222 """Simulate Enter key button press.
223
224 @param press_secs : Str. Time to press key.
225 """
226 self._server.ctrl_key(press_secs)
Todd Broch9753bd42012-03-21 10:15:08 -0700227
228
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800229 def enter_key(self, press_secs=''):
230 """Simulate Enter key button press.
231
232 @param press_secs : Str. Time to press key.
233 """
234 self._server.enter_key(press_secs)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700235
236
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800237 def refresh_key(self, press_secs=''):
238 """Simulate Refresh key (F3) button press.
239
240 @param press_secs : Str. Time to press key.
241 """
242 self._server.refresh_key(press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700243
244
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800245 def ctrl_refresh_key(self, press_secs=''):
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800246 """Simulate Ctrl and Refresh (F3) simultaneous press.
247
248 This key combination is an alternative of Space key.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800249
250 @param press_secs : Str. Time to press key.
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800251 """
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800252 self._server.ctrl_refresh_key(press_secs)
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800253
254
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800255 def imaginary_key(self, press_secs=''):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700256 """Simulate imaginary key button press.
257
258 Maps to a key that doesn't physically exist.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800259
260 @param press_secs : Str. Time to press key.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700261 """
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800262 self._server.imaginary_key(press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700263
264
Craig Harrison6b36b122011-06-28 17:58:43 -0700265 def enable_recovery_mode(self):
266 """Enable recovery mode on device."""
267 self.set('rec_mode', 'on')
268
269
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800270 def custom_recovery_mode(self):
271 """Custom key combination to enter recovery mode."""
272 self._press_keys('rec_mode')
273 self.power_normal_press()
274 time.sleep(self.SERVO_KEY_PRESS_DELAY)
275 self.set_nocheck('kbd_en', 'off')
276
277
Craig Harrison6b36b122011-06-28 17:58:43 -0700278 def disable_recovery_mode(self):
279 """Disable recovery mode on device."""
280 self.set('rec_mode', 'off')
281
282
283 def enable_development_mode(self):
284 """Enable development mode on device."""
285 self.set('dev_mode', 'on')
286
287
288 def disable_development_mode(self):
289 """Disable development mode on device."""
290 self.set('dev_mode', 'off')
291
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700292 def boot_devmode(self):
293 """Boot a dev-mode device that is powered off."""
Tom Wai-Hong Tam23869102012-01-17 15:41:30 +0800294 self.power_short_press()
Craig Harrison48997262011-06-27 14:31:10 -0700295 self.pass_devmode()
296
297
298 def pass_devmode(self):
299 """Pass through boot screens in dev-mode."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700300 time.sleep(Servo.BOOT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700301 self.ctrl_d()
Chrome Bot9a1137d2011-07-19 14:35:00 -0700302 time.sleep(Servo.BOOT_DELAY)
Craig Harrison48997262011-06-27 14:31:10 -0700303
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700304
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800305 def get_version(self):
306 """Get the version of servod board.
307
308 """
309 return self._server.get_version()
310
311
312 def get_board(self):
313 """Get the board connected to servod.
314
315 """
316 return self._server.get_board()
317
318
Todd Brochefe72cb2012-07-11 19:58:53 -0700319 def _get_xmlrpclib_exception(self, xmlexc):
320 """Get meaningful exception string from xmlrpc.
321
322 Args:
323 xmlexc: xmlrpclib.Fault object
324
325 xmlrpclib.Fault.faultString has the following format:
326
327 <type 'exception type'>:'actual error message'
328
329 Parse and return the real exception from the servod side instead of the
330 less meaningful one like,
331 <Fault 1: "<type 'exceptions.AttributeError'>:'tca6416' object has no
332 attribute 'hw_driver'">
333
334 Returns:
335 string of underlying exception raised in servod.
336 """
337 return re.sub('^.*>:', '', xmlexc.faultString)
338
339
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700340 def get(self, gpio_name):
341 """Get the value of a gpio from Servod."""
342 assert gpio_name
Todd Brochefe72cb2012-07-11 19:58:53 -0700343 try:
Simran Basi1cbc5f22013-07-17 14:23:58 -0700344 return self._server.get(gpio_name)
Todd Brochefe72cb2012-07-11 19:58:53 -0700345 except xmlrpclib.Fault as e:
346 err_msg = "Getting '%s' :: %s" % \
347 (gpio_name, self._get_xmlrpclib_exception(e))
348 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700349
350
351 def set(self, gpio_name, gpio_value):
352 """Set and check the value of a gpio using Servod."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700353 self.set_nocheck(gpio_name, gpio_value)
Todd Brochcf7c6652012-02-24 13:03:59 -0800354 retry_count = Servo.GET_RETRY_MAX
355 while gpio_value != self.get(gpio_name) and retry_count:
356 logging.warn("%s != %s, retry %d", gpio_name, gpio_value,
357 retry_count)
358 retry_count -= 1
359 time.sleep(Servo.SHORT_DELAY)
360 if not retry_count:
361 assert gpio_value == self.get(gpio_name), \
362 'Servo failed to set %s to %s' % (gpio_name, gpio_value)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700363
364
365 def set_nocheck(self, gpio_name, gpio_value):
366 """Set the value of a gpio using Servod."""
367 assert gpio_name and gpio_value
Todd Broch9753bd42012-03-21 10:15:08 -0700368 logging.info('Setting %s to %s', gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700369 try:
Simran Basi1cbc5f22013-07-17 14:23:58 -0700370 self._server.set(gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700371 except xmlrpclib.Fault as e:
372 err_msg = "Setting '%s' to '%s' :: %s" % \
373 (gpio_name, gpio_value, self._get_xmlrpclib_exception(e))
374 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700375
376
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800377 def set_get_all(self, controls):
378 """Set &| get one or more control values.
379
380 @param controls: list of strings, controls to set &| get.
381
382 @raise: error.TestError in case error occurs setting/getting values.
383 """
384 rv = []
385 try:
Vic Yangcad9acb2013-05-21 14:02:05 +0800386 logging.info('Set/get all: %s', str(controls))
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800387 rv = self._server.set_get_all(controls)
388 except xmlrpclib.Fault as e:
389 # TODO(waihong): Remove the following backward compatibility when
390 # the new versions of hdctools are deployed.
391 if 'not supported' in str(e):
392 logging.warning('The servod is too old that set_get_all '
393 'not supported. Use set and get instead.')
394 for control in controls:
395 if ':' in control:
396 (name, value) = control.split(':')
397 if name == 'sleep':
398 time.sleep(float(value))
399 else:
400 self.set_nocheck(name, value)
401 rv.append(True)
402 else:
403 rv.append(self.get(name))
404 else:
405 err_msg = "Problem with '%s' :: %s" % \
406 (controls, self._get_xmlrpclib_exception(e))
407 raise error.TestFail(err_msg)
408 return rv
409
410
Jon Salzc88e5b62011-11-30 14:38:54 +0800411 # TODO(waihong) It may fail if multiple servo's are connected to the same
412 # host. Should look for a better way, like the USB serial name, to identify
413 # the USB device.
Simran Basi741b5d42012-05-18 11:27:15 -0700414 # TODO(sbasi) Remove this code from autoserv once firmware tests have been
415 # updated.
Jon Salzc88e5b62011-11-30 14:38:54 +0800416 def probe_host_usb_dev(self):
417 """Probe the USB disk device plugged-in the servo from the host side.
418
419 It tries to switch the USB mux to make the host unable to see the
420 USB disk and compares the result difference.
421
Jon Salzc88e5b62011-11-30 14:38:54 +0800422 Returns:
423 A string of USB disk path, like '/dev/sdb', or None if not existed.
424 """
425 cmd = 'ls /dev/sd[a-z]'
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800426 original_value = self.get_usbkey_direction()
Jon Salzc88e5b62011-11-30 14:38:54 +0800427
428 # Make the host unable to see the USB disk.
Fang Dengafb88142013-05-30 17:44:31 -0700429 self.switch_usbkey('off')
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800430 no_usb_set = set(self.system_output(cmd, ignore_status=True).split())
Jon Salzc88e5b62011-11-30 14:38:54 +0800431
432 # Make the host able to see the USB disk.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800433 self.switch_usbkey('host')
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800434 has_usb_set = set(self.system_output(cmd, ignore_status=True).split())
Jon Salzc88e5b62011-11-30 14:38:54 +0800435
436 # Back to its original value.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800437 if original_value != self.get_usbkey_direction():
438 self.switch_usbkey(original_value)
Jon Salzc88e5b62011-11-30 14:38:54 +0800439
440 diff_set = has_usb_set - no_usb_set
441 if len(diff_set) == 1:
442 return diff_set.pop()
443 else:
444 return None
445
446
J. Richard Barnette69929a52013-03-15 13:22:11 -0700447 def recovery_supported(self):
448 """Return whether servo-based recovery should work.
449
450 Use of `image_to_servo_usb()` and `install_recovery_image()`
451 relies on DUT-board specific behaviors, and is not supported
452 for all types of board. Return whether these two operations
453 are expected to succeed for the current DUT.
454
455 @return `True` iff the recovery related methods are supported
456 for this servo and DUT.
457
458 """
J. Richard Barnette75136b32013-03-26 13:38:44 -0700459 return self._power_state.recovery_supported()
J. Richard Barnette69929a52013-03-15 13:22:11 -0700460
461
Mike Truty49153d82012-08-21 22:27:30 -0500462 def image_to_servo_usb(self, image_path=None,
463 make_image_noninteractive=False):
464 """Install an image to the USB key plugged into the servo.
465
466 This method may copy any image to the servo USB key including a
467 recovery image or a test image. These images are frequently used
468 for test purposes such as restoring a corrupted image or conducting
469 an upgrade of ec/fw/kernel as part of a test of a specific image part.
470
471 Args:
472 image_path: Path on the host to the recovery image.
473 make_image_noninteractive: Make the recovery image noninteractive,
474 therefore the DUT will reboot
475 automatically after installation.
476 """
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700477 # We're about to start plugging/unplugging the USB key. We
478 # don't know the state of the DUT, or what it might choose
479 # to do to the device after hotplug. To avoid surprises,
480 # force the DUT to be off.
481 self._server.hwinit()
482 self._power_state.power_off()
Mike Truty49153d82012-08-21 22:27:30 -0500483
484 # Set up Servo's usb mux.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800485 self.switch_usbkey('host')
Mike Truty49153d82012-08-21 22:27:30 -0500486 if image_path:
Tom Wai-Hong Tam42f136d2012-10-26 11:11:23 +0800487 logging.info('Searching for usb device and copying image to it. '
488 'Please wait a few minutes...')
Mike Truty49153d82012-08-21 22:27:30 -0500489 if not self._server.download_image_to_usb(image_path):
490 logging.error('Failed to transfer requested image to USB. '
491 'Please take a look at Servo Logs.')
492 raise error.AutotestError('Download image to usb failed.')
493 if make_image_noninteractive:
494 logging.info('Making image noninteractive')
495 if not self._server.make_image_noninteractive():
496 logging.error('Failed to make image noninteractive. '
497 'Please take a look at Servo Logs.')
498
499
Simran Basi741b5d42012-05-18 11:27:15 -0700500 def install_recovery_image(self, image_path=None,
J. Richard Barnetteb6de7e32013-02-14 13:28:04 -0800501 make_image_noninteractive=False):
Jon Salzc88e5b62011-11-30 14:38:54 +0800502 """Install the recovery image specied by the path onto the DUT.
503
504 This method uses google recovery mode to install a recovery image
505 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 +0800506 board specified by the usb_dev. If no image path is specified
Jon Salzc88e5b62011-11-30 14:38:54 +0800507 we use the recovery image already on the usb image.
508
509 Args:
510 image_path: Path on the host to the recovery image.
Simran Basi741b5d42012-05-18 11:27:15 -0700511 make_image_noninteractive: Make the recovery image noninteractive,
512 therefore the DUT will reboot
513 automatically after installation.
Jon Salzc88e5b62011-11-30 14:38:54 +0800514 """
Mike Truty49153d82012-08-21 22:27:30 -0500515 self.image_to_servo_usb(image_path, make_image_noninteractive)
J. Richard Barnette75136b32013-03-26 13:38:44 -0700516 self._power_state.power_on(rec_mode=power_state_controller.REC_ON)
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700517 self.switch_usbkey('dut')
Jon Salzc88e5b62011-11-30 14:38:54 +0800518
519
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800520 def _scp_image(self, image_path):
521 """Copy image to the servo host.
522
523 When programming a firmware image on the DUT, the image must be
524 located on the host to which the servo device is connected. Sometimes
525 servo is controlled by a remote host, in this case the image needs to
526 be transferred to the remote host.
527
528 @param image_path: a string, name of the firmware image file to be
529 transferred.
530 @return: a string, full path name of the copied file on the remote.
531 """
532
533 dest_path = os.path.join('/tmp', os.path.basename(image_path))
Fang Deng5d518f42013-08-02 14:04:32 -0700534 self._servo_host.send_file(image_path, dest_path)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800535 return dest_path
536
537
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800538 def system(self, command, timeout=None):
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800539 """Execute the passed in command on the servod host."""
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800540 logging.info('Will execute on servo host: %s', command)
Fang Deng5d518f42013-08-02 14:04:32 -0700541 self._servo_host.run(command, timeout=timeout)
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800542
543
544 def system_output(self, command, timeout=None,
545 ignore_status=False, args=()):
546 """Execute the passed in command on the servod host, return stdout.
547
548 @param command, a string, the command to execute
549 @param timeout, an int, max number of seconds to wait til command
550 execution completes
551 @ignore_status, a Boolean, if true - ignore command's nonzero exit
552 status, otherwise an exception will be thrown
553 @param args, a tuple of strings, each becoming a separate command line
554 parameter for the command
555 @return: command's stdout as a string.
556 """
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800557 logging.info('Will execute and collect output on servo host: %s %s',
558 command, ' '.join("'%s'" % x for x in args))
Fang Deng5d518f42013-08-02 14:04:32 -0700559 return self._servo_host.run(command, timeout=timeout,
560 ignore_status=ignore_status,
561 args=args).stdout.strip()
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800562
563
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800564 def program_bios(self, image):
565 """Program bios on DUT with given image.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800566
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800567 @param image: a string, file name of the BIOS image to program
568 on the DUT.
569
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800570 """
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800571 if not self.is_localhost():
572 image = self._scp_image(image)
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800573 self._programmer.program_bios(image)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800574
575
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800576 def program_ec(self, image):
577 """Program ec on DUT with given image.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800578
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800579 @param image: a string, file name of the EC image to program
580 on the DUT.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800581
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800582 """
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800583 if not self.is_localhost():
584 image = self._scp_image(image)
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800585 self._programmer.program_ec(image)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800586
Fang Dengafb88142013-05-30 17:44:31 -0700587
588 def _switch_usbkey_power(self, power_state, detection_delay=False):
589 """Switch usbkey power.
590
591 This function switches usbkey power by setting the value of
592 'prtctl4_pwren'. If the power is already in the
593 requested state, this function simply returns.
594
595 @param power_state: A string, 'on' or 'off'.
596 @param detection_delay: A boolean value, if True, sleep
597 for |USB_DETECTION_DELAY| after switching
598 the power on.
599 """
600 self.set('prtctl4_pwren', power_state)
601 if power_state == 'off':
602 time.sleep(self.USB_POWEROFF_DELAY)
603 elif detection_delay:
604 time.sleep(self.USB_DETECTION_DELAY)
605
606
607 def switch_usbkey(self, usb_state):
608 """Connect USB flash stick to either host or DUT, or turn USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800609
610 This function switches the servo multiplexer to provide electrical
Fang Dengafb88142013-05-30 17:44:31 -0700611 connection between the USB port J3 and either host or DUT side. It
612 can also be used to turn the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800613
Fang Dengafb88142013-05-30 17:44:31 -0700614 Switching to 'dut' or 'host' is accompanied by powercycling
615 of the USB stick, because it sometimes gets wedged if the mux
616 is switched while the stick power is on.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800617
Fang Dengafb88142013-05-30 17:44:31 -0700618 @param usb_state: A string, one of 'dut', 'host', or 'off'.
619 'dut' and 'host' indicate which side the
620 USB flash device is required to be connected to.
621 'off' indicates turning the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800622
Fang Dengafb88142013-05-30 17:44:31 -0700623 @raise: error.TestError in case the parameter is not 'dut'
624 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800625 """
Fang Dengafb88142013-05-30 17:44:31 -0700626 if self.get_usbkey_direction() == usb_state:
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800627 return
628
Fang Dengafb88142013-05-30 17:44:31 -0700629 if usb_state == 'off':
630 self._switch_usbkey_power('off')
631 self._usb_state = usb_state
632 return
633 elif usb_state == 'host':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800634 mux_direction = 'servo_sees_usbkey'
Fang Dengafb88142013-05-30 17:44:31 -0700635 elif usb_state == 'dut':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800636 mux_direction = 'dut_sees_usbkey'
637 else:
Fang Dengafb88142013-05-30 17:44:31 -0700638 raise error.TestError('Unknown USB state request: %s' % usb_state)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800639
Fang Dengafb88142013-05-30 17:44:31 -0700640 self._switch_usbkey_power('off')
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800641 self.set('usb_mux_sel1', mux_direction)
642 time.sleep(self.USB_POWEROFF_DELAY)
Fang Dengafb88142013-05-30 17:44:31 -0700643 self._switch_usbkey_power('on', usb_state == 'host')
644 self._usb_state = usb_state
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800645
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800646
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800647 def get_usbkey_direction(self):
Fang Dengafb88142013-05-30 17:44:31 -0700648 """Get which side USB is connected to or 'off' if usb power is off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800649
Fang Dengafb88142013-05-30 17:44:31 -0700650 @return: A string, one of 'dut', 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800651 """
Fang Dengafb88142013-05-30 17:44:31 -0700652 if not self._usb_state:
653 if self.get('prtctl4_pwren') == 'off':
654 self._usb_state = 'off'
655 elif self.get('usb_mux_sel1').startswith('dut'):
656 self._usb_state = 'dut'
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800657 else:
Fang Dengafb88142013-05-30 17:44:31 -0700658 self._usb_state = 'host'
659 return self._usb_state