blob: 454606afa0c2be88d35956eaf8b943fa6b86d205 [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
J. Richard Barnette41320ee2013-03-11 13:00:13 -070013from autotest_lib.server.cros.servo import power_state_controller
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -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 Barnette384056b2012-04-16 11:04:46 -070017class Servo(object):
J. Richard Barnette41320ee2013-03-11 13:00:13 -070018
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070019 """Manages control of a Servo board.
20
21 Servo is a board developed by hardware group to aide in the debug and
22 control of various partner devices. Servo's features include the simulation
23 of pressing the power button, closing the lid, and pressing Ctrl-d. This
24 class manages setting up and communicating with a servo demon (servod)
25 process. It provides both high-level functions for common servo tasks and
26 low-level functions for directly setting and reading gpios.
J. Richard Barnette41320ee2013-03-11 13:00:13 -070027
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070028 """
29
Chrome Bot9a1137d2011-07-19 14:35:00 -070030 # Power button press delays in seconds.
J. Richard Barnetted2e4cbd2012-06-29 12:18:40 -070031 #
J. Richard Barnette5383f072012-07-26 17:35:40 -070032 # The EC specification says that 8.0 seconds should be enough
33 # for the long power press. However, some platforms need a bit
34 # more time. Empirical testing has found these requirements:
35 # Alex: 8.2 seconds
36 # ZGB: 8.5 seconds
37 # The actual value is set to the largest known necessary value.
38 #
39 # TODO(jrbarnette) Being generous is the right thing to do for
40 # existing platforms, but if this code is to be used for
41 # qualification of new hardware, we should be less generous.
Chrome Bot9a1137d2011-07-19 14:35:00 -070042 SHORT_DELAY = 0.1
J. Richard Barnette5383f072012-07-26 17:35:40 -070043
Todd Broch31c82502011-08-29 08:14:39 -070044 # Maximum number of times to re-read power button on release.
Todd Brochcf7c6652012-02-24 13:03:59 -080045 GET_RETRY_MAX = 10
Chrome Bot9a1137d2011-07-19 14:35:00 -070046
J. Richard Barnette5383f072012-07-26 17:35:40 -070047 # Delays to deal with DUT state transitions.
Chrome Bot9a1137d2011-07-19 14:35:00 -070048 SLEEP_DELAY = 6
49 BOOT_DELAY = 10
J. Richard Barnette41320ee2013-03-11 13:00:13 -070050
J. Richard Barnette41320ee2013-03-11 13:00:13 -070051 # Default minimum time interval between 'press' and 'release'
52 # keyboard events.
Vic Yang0aca1c22012-11-19 18:33:56 -080053 SERVO_KEY_PRESS_DELAY = 0.1
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070054
Jon Salzc88e5b62011-11-30 14:38:54 +080055 # Time between an usb disk plugged-in and detected in the system.
56 USB_DETECTION_DELAY = 10
Vadim Bendeburye7bd9362012-12-19 14:35:20 -080057 # Time to keep USB power off before and after USB mux direction is changed
58 USB_POWEROFF_DELAY = 2
Jon Salzc88e5b62011-11-30 14:38:54 +080059
Simran Basib7850bb2013-07-24 12:33:42 -070060 # Time to wait before timing out on servo initialization.
61 INIT_TIMEOUT_SECS = 10
Simran Basi59331342013-07-12 12:06:29 -070062
J. Richard Barnette67ccb872012-04-19 16:34:56 -070063
Ricky Liang0dd379c2014-04-23 16:29:08 +080064 def __init__(self, servo_host, servo_serial=None):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070065 """Sets up the servo communication infrastructure.
66
Fang Deng5d518f42013-08-02 14:04:32 -070067 @param servo_host: A ServoHost object representing
68 the host running servod.
Ricky Liang0dd379c2014-04-23 16:29:08 +080069 @param servo_serial: Serial number of the servo board.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070070 """
Fang Deng5d518f42013-08-02 14:04:32 -070071 # TODO(fdeng): crbug.com/298379
72 # We should move servo_host object out of servo object
73 # to minimize the dependencies on the rest of Autotest.
74 self._servo_host = servo_host
Ricky Liang0dd379c2014-04-23 16:29:08 +080075 self._servo_serial = servo_serial
Fang Deng5d518f42013-08-02 14:04:32 -070076 self._server = servo_host.get_servod_server_proxy()
J. Richard Barnette75136b32013-03-26 13:38:44 -070077 self._power_state = (
J. Richard Barnette4b6af0d2014-06-05 09:57:20 -070078 power_state_controller.create_controller(self))
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -080079
Vadim Bendeburye7bd9362012-12-19 14:35:20 -080080 # a string, showing what interface (host or dut) the USB device is
81 # connected to.
Fang Dengafb88142013-05-30 17:44:31 -070082 self._usb_state = None
Vadim Bendeburye7bd9362012-12-19 14:35:20 -080083 self.set('dut_hub_pwren', 'on')
84 self.set('usb_mux_oe1', 'on')
Fang Dengafb88142013-05-30 17:44:31 -070085 self.switch_usbkey('off')
Vadim Bendeburye7bd9362012-12-19 14:35:20 -080086
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -080087 # Initialize firmware programmer
Yusuf Mohsinally6a837572014-03-10 14:35:45 -070088 if self.get_version() == "servo_v2":
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -080089 self._programmer = firmware_programmer.ProgrammerV2(self)
90 else:
Ilja H. Friedel04be2bd2014-05-07 21:29:59 -070091 logging.warning("No firmware programmer for servo version: %s",
Yusuf Mohsinally6a837572014-03-10 14:35:45 -070092 self.get_version())
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -080093
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -080094
Ricky Liang0dd379c2014-04-23 16:29:08 +080095 @property
96 def servo_serial(self):
97 """Returns the serial number of the servo board."""
98 return self._servo_serial
99
100
Tom Wai-Hong Tam22ee0e52013-04-03 13:36:39 +0800101 def get_power_state_controller(self):
J. Richard Barnette75136b32013-03-26 13:38:44 -0700102 """Return the power state controller for this Servo.
103
104 The power state controller provides board-independent
105 interfaces for reset, power-on, power-off operations.
106
107 """
108 return self._power_state
109
Fang Deng5d518f42013-08-02 14:04:32 -0700110
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700111 def initialize_dut(self, cold_reset=False):
112 """Initializes a dut for testing purposes.
113
114 This sets various servo signals back to default values
115 appropriate for the target board. By default, if the DUT
116 is already on, it stays on. If the DUT is powered off
117 before initialization, its state afterward is unspecified.
118
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700119 Rationale: Basic initialization of servo sets the lid open,
120 when there is a lid. This operation won't affect powered on
121 units; however, setting the lid open may power on a unit
J. Richard Barnette75136b32013-03-26 13:38:44 -0700122 that's off, depending on the board type and previous state
123 of the device.
124
J. Richard Barnette4b6af0d2014-06-05 09:57:20 -0700125 If `cold_reset` is a true value, the DUT and its EC will be
126 reset, and the DUT rebooted in normal mode.
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700127
128 @param cold_reset If True, cold reset the device after
129 initialization.
130 """
131 self._server.hwinit()
132 if cold_reset:
J. Richard Barnette4b6af0d2014-06-05 09:57:20 -0700133 self._power_state.reset()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700134
135
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800136 def is_localhost(self):
137 """Is the servod hosted locally?
138
139 Returns:
140 True if local hosted; otherwise, False.
141 """
Fang Deng5d518f42013-08-02 14:04:32 -0700142 return self._servo_host.is_localhost()
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800143
144
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700145 def power_long_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700146 """Simulate a long power button press."""
J. Richard Barnettef12bff22012-07-18 14:41:06 -0700147 # After a long power press, the EC may ignore the next power
148 # button press (at least on Alex). To guarantee that this
149 # won't happen, we need to allow the EC one second to
150 # collect itself.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800151 self._server.power_long_press()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700152
153
154 def power_normal_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700155 """Simulate a normal power button press."""
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800156 self._server.power_normal_press()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700157
158
159 def power_short_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700160 """Simulate a short power button press."""
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800161 self._server.power_short_press()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700162
163
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800164 def power_key(self, press_secs=''):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700165 """Simulate a power button press.
166
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800167 @param press_secs : Str. Time to press key.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700168 """
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800169 self._server.power_key(press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700170
171
172 def lid_open(self):
173 """Simulate opening the lid."""
Craig Harrison48997262011-06-27 14:31:10 -0700174 self.set_nocheck('lid_open', 'yes')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700175
176
177 def lid_close(self):
Craig Harrison48997262011-06-27 14:31:10 -0700178 """Simulate closing the lid.
179
180 Waits 6 seconds to ensure the device is fully asleep before returning.
181 """
182 self.set_nocheck('lid_open', 'no')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700183 time.sleep(Servo.SLEEP_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700184
185
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800186 def ctrl_d(self, press_secs=''):
187 """Simulate Ctrl-d simultaneous button presses.
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800188
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800189 @param press_secs : Str. Time to press key.
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800190 """
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800191 self._server.ctrl_d(press_secs)
Gediminas Ramanauskas9da80f22012-11-14 12:59:43 -0800192
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800193
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800194 def ctrl_u(self):
195 """Simulate Ctrl-u simultaneous button presses.
196
197 @param press_secs : Str. Time to press key.
198 """
199 self._server.ctrl_u()
Todd Broch9dfc3a82011-11-01 08:09:28 -0700200
201
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800202 def ctrl_enter(self, press_secs=''):
203 """Simulate Ctrl-enter simultaneous button presses.
204
205 @param press_secs : Str. Time to press key.
206 """
207 self._server.ctrl_enter(press_secs)
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -0700208
209
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800210 def d_key(self, press_secs=''):
211 """Simulate Enter key button press.
212
213 @param press_secs : Str. Time to press key.
214 """
215 self._server.d_key(press_secs)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700216
217
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800218 def ctrl_key(self, press_secs=''):
219 """Simulate Enter key button press.
220
221 @param press_secs : Str. Time to press key.
222 """
223 self._server.ctrl_key(press_secs)
Todd Broch9753bd42012-03-21 10:15:08 -0700224
225
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800226 def enter_key(self, press_secs=''):
227 """Simulate Enter key button press.
228
229 @param press_secs : Str. Time to press key.
230 """
231 self._server.enter_key(press_secs)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700232
233
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800234 def refresh_key(self, press_secs=''):
235 """Simulate Refresh key (F3) button press.
236
237 @param press_secs : Str. Time to press key.
238 """
239 self._server.refresh_key(press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700240
241
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800242 def ctrl_refresh_key(self, press_secs=''):
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800243 """Simulate Ctrl and Refresh (F3) simultaneous press.
244
245 This key combination is an alternative of Space key.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800246
247 @param press_secs : Str. Time to press key.
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800248 """
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800249 self._server.ctrl_refresh_key(press_secs)
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800250
251
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800252 def imaginary_key(self, press_secs=''):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700253 """Simulate imaginary key button press.
254
255 Maps to a key that doesn't physically exist.
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800256
257 @param press_secs : Str. Time to press key.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700258 """
Yusuf Mohsinally103441a2014-01-14 15:25:44 -0800259 self._server.imaginary_key(press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700260
261
Craig Harrison6b36b122011-06-28 17:58:43 -0700262 def enable_recovery_mode(self):
263 """Enable recovery mode on device."""
264 self.set('rec_mode', 'on')
265
266
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800267 def custom_recovery_mode(self):
268 """Custom key combination to enter recovery mode."""
269 self._press_keys('rec_mode')
270 self.power_normal_press()
271 time.sleep(self.SERVO_KEY_PRESS_DELAY)
272 self.set_nocheck('kbd_en', 'off')
273
274
Craig Harrison6b36b122011-06-28 17:58:43 -0700275 def disable_recovery_mode(self):
276 """Disable recovery mode on device."""
277 self.set('rec_mode', 'off')
278
279
280 def enable_development_mode(self):
281 """Enable development mode on device."""
282 self.set('dev_mode', 'on')
283
284
285 def disable_development_mode(self):
286 """Disable development mode on device."""
287 self.set('dev_mode', 'off')
288
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700289 def boot_devmode(self):
290 """Boot a dev-mode device that is powered off."""
Tom Wai-Hong Tam23869102012-01-17 15:41:30 +0800291 self.power_short_press()
Craig Harrison48997262011-06-27 14:31:10 -0700292 self.pass_devmode()
293
294
295 def pass_devmode(self):
296 """Pass through boot screens in dev-mode."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700297 time.sleep(Servo.BOOT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700298 self.ctrl_d()
Chrome Bot9a1137d2011-07-19 14:35:00 -0700299 time.sleep(Servo.BOOT_DELAY)
Craig Harrison48997262011-06-27 14:31:10 -0700300
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700301
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800302 def get_version(self):
303 """Get the version of servod board.
304
305 """
306 return self._server.get_version()
307
308
309 def get_board(self):
310 """Get the board connected to servod.
311
312 """
313 return self._server.get_board()
314
315
Todd Brochefe72cb2012-07-11 19:58:53 -0700316 def _get_xmlrpclib_exception(self, xmlexc):
317 """Get meaningful exception string from xmlrpc.
318
319 Args:
320 xmlexc: xmlrpclib.Fault object
321
322 xmlrpclib.Fault.faultString has the following format:
323
324 <type 'exception type'>:'actual error message'
325
326 Parse and return the real exception from the servod side instead of the
327 less meaningful one like,
328 <Fault 1: "<type 'exceptions.AttributeError'>:'tca6416' object has no
329 attribute 'hw_driver'">
330
331 Returns:
332 string of underlying exception raised in servod.
333 """
334 return re.sub('^.*>:', '', xmlexc.faultString)
335
336
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700337 def get(self, gpio_name):
338 """Get the value of a gpio from Servod."""
339 assert gpio_name
Todd Brochefe72cb2012-07-11 19:58:53 -0700340 try:
Simran Basi1cbc5f22013-07-17 14:23:58 -0700341 return self._server.get(gpio_name)
Todd Brochefe72cb2012-07-11 19:58:53 -0700342 except xmlrpclib.Fault as e:
343 err_msg = "Getting '%s' :: %s" % \
344 (gpio_name, self._get_xmlrpclib_exception(e))
345 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700346
347
348 def set(self, gpio_name, gpio_value):
349 """Set and check the value of a gpio using Servod."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700350 self.set_nocheck(gpio_name, gpio_value)
Todd Brochcf7c6652012-02-24 13:03:59 -0800351 retry_count = Servo.GET_RETRY_MAX
352 while gpio_value != self.get(gpio_name) and retry_count:
Ilja H. Friedel04be2bd2014-05-07 21:29:59 -0700353 logging.warning("%s != %s, retry %d", gpio_name, gpio_value,
Todd Brochcf7c6652012-02-24 13:03:59 -0800354 retry_count)
355 retry_count -= 1
356 time.sleep(Servo.SHORT_DELAY)
357 if not retry_count:
358 assert gpio_value == self.get(gpio_name), \
359 'Servo failed to set %s to %s' % (gpio_name, gpio_value)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700360
361
362 def set_nocheck(self, gpio_name, gpio_value):
363 """Set the value of a gpio using Servod."""
364 assert gpio_name and gpio_value
Todd Broch9753bd42012-03-21 10:15:08 -0700365 logging.info('Setting %s to %s', gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700366 try:
Simran Basi1cbc5f22013-07-17 14:23:58 -0700367 self._server.set(gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700368 except xmlrpclib.Fault as e:
369 err_msg = "Setting '%s' to '%s' :: %s" % \
370 (gpio_name, gpio_value, self._get_xmlrpclib_exception(e))
371 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700372
373
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800374 def set_get_all(self, controls):
375 """Set &| get one or more control values.
376
377 @param controls: list of strings, controls to set &| get.
378
379 @raise: error.TestError in case error occurs setting/getting values.
380 """
381 rv = []
382 try:
Vic Yangcad9acb2013-05-21 14:02:05 +0800383 logging.info('Set/get all: %s', str(controls))
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800384 rv = self._server.set_get_all(controls)
385 except xmlrpclib.Fault as e:
386 # TODO(waihong): Remove the following backward compatibility when
387 # the new versions of hdctools are deployed.
388 if 'not supported' in str(e):
389 logging.warning('The servod is too old that set_get_all '
390 'not supported. Use set and get instead.')
391 for control in controls:
392 if ':' in control:
393 (name, value) = control.split(':')
394 if name == 'sleep':
395 time.sleep(float(value))
396 else:
397 self.set_nocheck(name, value)
398 rv.append(True)
399 else:
400 rv.append(self.get(name))
401 else:
402 err_msg = "Problem with '%s' :: %s" % \
403 (controls, self._get_xmlrpclib_exception(e))
404 raise error.TestFail(err_msg)
405 return rv
406
407
Jon Salzc88e5b62011-11-30 14:38:54 +0800408 # TODO(waihong) It may fail if multiple servo's are connected to the same
409 # host. Should look for a better way, like the USB serial name, to identify
410 # the USB device.
Simran Basi741b5d42012-05-18 11:27:15 -0700411 # TODO(sbasi) Remove this code from autoserv once firmware tests have been
412 # updated.
Jon Salzc88e5b62011-11-30 14:38:54 +0800413 def probe_host_usb_dev(self):
414 """Probe the USB disk device plugged-in the servo from the host side.
415
416 It tries to switch the USB mux to make the host unable to see the
417 USB disk and compares the result difference.
418
Jon Salzc88e5b62011-11-30 14:38:54 +0800419 Returns:
420 A string of USB disk path, like '/dev/sdb', or None if not existed.
421 """
422 cmd = 'ls /dev/sd[a-z]'
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800423 original_value = self.get_usbkey_direction()
Jon Salzc88e5b62011-11-30 14:38:54 +0800424
425 # Make the host unable to see the USB disk.
Fang Dengafb88142013-05-30 17:44:31 -0700426 self.switch_usbkey('off')
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800427 no_usb_set = set(self.system_output(cmd, ignore_status=True).split())
Jon Salzc88e5b62011-11-30 14:38:54 +0800428
429 # Make the host able to see the USB disk.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800430 self.switch_usbkey('host')
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800431 has_usb_set = set(self.system_output(cmd, ignore_status=True).split())
Jon Salzc88e5b62011-11-30 14:38:54 +0800432
433 # Back to its original value.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800434 if original_value != self.get_usbkey_direction():
435 self.switch_usbkey(original_value)
Jon Salzc88e5b62011-11-30 14:38:54 +0800436
437 diff_set = has_usb_set - no_usb_set
438 if len(diff_set) == 1:
439 return diff_set.pop()
440 else:
441 return None
442
443
J. Richard Barnette69929a52013-03-15 13:22:11 -0700444 def recovery_supported(self):
445 """Return whether servo-based recovery should work.
446
447 Use of `image_to_servo_usb()` and `install_recovery_image()`
448 relies on DUT-board specific behaviors, and is not supported
449 for all types of board. Return whether these two operations
450 are expected to succeed for the current DUT.
451
452 @return `True` iff the recovery related methods are supported
453 for this servo and DUT.
454
455 """
J. Richard Barnette75136b32013-03-26 13:38:44 -0700456 return self._power_state.recovery_supported()
J. Richard Barnette69929a52013-03-15 13:22:11 -0700457
458
Mike Truty49153d82012-08-21 22:27:30 -0500459 def image_to_servo_usb(self, image_path=None,
460 make_image_noninteractive=False):
461 """Install an image to the USB key plugged into the servo.
462
463 This method may copy any image to the servo USB key including a
464 recovery image or a test image. These images are frequently used
465 for test purposes such as restoring a corrupted image or conducting
466 an upgrade of ec/fw/kernel as part of a test of a specific image part.
467
468 Args:
469 image_path: Path on the host to the recovery image.
470 make_image_noninteractive: Make the recovery image noninteractive,
471 therefore the DUT will reboot
472 automatically after installation.
473 """
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700474 # We're about to start plugging/unplugging the USB key. We
475 # don't know the state of the DUT, or what it might choose
476 # to do to the device after hotplug. To avoid surprises,
477 # force the DUT to be off.
478 self._server.hwinit()
479 self._power_state.power_off()
Mike Truty49153d82012-08-21 22:27:30 -0500480
481 # Set up Servo's usb mux.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800482 self.switch_usbkey('host')
Mike Truty49153d82012-08-21 22:27:30 -0500483 if image_path:
Tom Wai-Hong Tam42f136d2012-10-26 11:11:23 +0800484 logging.info('Searching for usb device and copying image to it. '
485 'Please wait a few minutes...')
Mike Truty49153d82012-08-21 22:27:30 -0500486 if not self._server.download_image_to_usb(image_path):
487 logging.error('Failed to transfer requested image to USB. '
488 'Please take a look at Servo Logs.')
489 raise error.AutotestError('Download image to usb failed.')
490 if make_image_noninteractive:
491 logging.info('Making image noninteractive')
492 if not self._server.make_image_noninteractive():
493 logging.error('Failed to make image noninteractive. '
494 'Please take a look at Servo Logs.')
495
496
Simran Basi741b5d42012-05-18 11:27:15 -0700497 def install_recovery_image(self, image_path=None,
J. Richard Barnetteb6de7e32013-02-14 13:28:04 -0800498 make_image_noninteractive=False):
Jon Salzc88e5b62011-11-30 14:38:54 +0800499 """Install the recovery image specied by the path onto the DUT.
500
501 This method uses google recovery mode to install a recovery image
502 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 +0800503 board specified by the usb_dev. If no image path is specified
Jon Salzc88e5b62011-11-30 14:38:54 +0800504 we use the recovery image already on the usb image.
505
506 Args:
507 image_path: Path on the host to the recovery image.
Simran Basi741b5d42012-05-18 11:27:15 -0700508 make_image_noninteractive: Make the recovery image noninteractive,
509 therefore the DUT will reboot
510 automatically after installation.
Jon Salzc88e5b62011-11-30 14:38:54 +0800511 """
Mike Truty49153d82012-08-21 22:27:30 -0500512 self.image_to_servo_usb(image_path, make_image_noninteractive)
J. Richard Barnette4b6af0d2014-06-05 09:57:20 -0700513 self._power_state.power_on(rec_mode=self._power_state.REC_ON)
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700514 self.switch_usbkey('dut')
Jon Salzc88e5b62011-11-30 14:38:54 +0800515
516
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800517 def _scp_image(self, image_path):
518 """Copy image to the servo host.
519
520 When programming a firmware image on the DUT, the image must be
521 located on the host to which the servo device is connected. Sometimes
522 servo is controlled by a remote host, in this case the image needs to
523 be transferred to the remote host.
524
525 @param image_path: a string, name of the firmware image file to be
526 transferred.
527 @return: a string, full path name of the copied file on the remote.
528 """
529
530 dest_path = os.path.join('/tmp', os.path.basename(image_path))
Fang Deng5d518f42013-08-02 14:04:32 -0700531 self._servo_host.send_file(image_path, dest_path)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800532 return dest_path
533
534
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800535 def system(self, command, timeout=None):
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800536 """Execute the passed in command on the servod host."""
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800537 logging.info('Will execute on servo host: %s', command)
Fang Deng5d518f42013-08-02 14:04:32 -0700538 self._servo_host.run(command, timeout=timeout)
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800539
540
541 def system_output(self, command, timeout=None,
542 ignore_status=False, args=()):
543 """Execute the passed in command on the servod host, return stdout.
544
545 @param command, a string, the command to execute
546 @param timeout, an int, max number of seconds to wait til command
547 execution completes
548 @ignore_status, a Boolean, if true - ignore command's nonzero exit
549 status, otherwise an exception will be thrown
550 @param args, a tuple of strings, each becoming a separate command line
551 parameter for the command
552 @return: command's stdout as a string.
553 """
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800554 logging.info('Will execute and collect output on servo host: %s %s',
555 command, ' '.join("'%s'" % x for x in args))
Fang Deng5d518f42013-08-02 14:04:32 -0700556 return self._servo_host.run(command, timeout=timeout,
557 ignore_status=ignore_status,
558 args=args).stdout.strip()
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800559
560
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800561 def program_bios(self, image):
562 """Program bios on DUT with given image.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800563
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800564 @param image: a string, file name of the BIOS image to program
565 on the DUT.
566
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800567 """
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800568 if not self.is_localhost():
569 image = self._scp_image(image)
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800570 self._programmer.program_bios(image)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800571
572
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800573 def program_ec(self, image):
574 """Program ec on DUT with given image.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800575
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800576 @param image: a string, file name of the EC image to program
577 on the DUT.
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800578
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800579 """
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800580 if not self.is_localhost():
581 image = self._scp_image(image)
Yusuf Mohsinally6c078ae2013-11-21 11:06:42 -0800582 self._programmer.program_ec(image)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800583
Fang Dengafb88142013-05-30 17:44:31 -0700584
585 def _switch_usbkey_power(self, power_state, detection_delay=False):
586 """Switch usbkey power.
587
588 This function switches usbkey power by setting the value of
589 'prtctl4_pwren'. If the power is already in the
590 requested state, this function simply returns.
591
592 @param power_state: A string, 'on' or 'off'.
593 @param detection_delay: A boolean value, if True, sleep
594 for |USB_DETECTION_DELAY| after switching
595 the power on.
596 """
597 self.set('prtctl4_pwren', power_state)
598 if power_state == 'off':
599 time.sleep(self.USB_POWEROFF_DELAY)
600 elif detection_delay:
601 time.sleep(self.USB_DETECTION_DELAY)
602
603
604 def switch_usbkey(self, usb_state):
605 """Connect USB flash stick to either host or DUT, or turn USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800606
607 This function switches the servo multiplexer to provide electrical
Fang Dengafb88142013-05-30 17:44:31 -0700608 connection between the USB port J3 and either host or DUT side. It
609 can also be used to turn the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800610
Fang Dengafb88142013-05-30 17:44:31 -0700611 Switching to 'dut' or 'host' is accompanied by powercycling
612 of the USB stick, because it sometimes gets wedged if the mux
613 is switched while the stick power is on.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800614
Fang Dengafb88142013-05-30 17:44:31 -0700615 @param usb_state: A string, one of 'dut', 'host', or 'off'.
616 'dut' and 'host' indicate which side the
617 USB flash device is required to be connected to.
618 'off' indicates turning the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800619
Fang Dengafb88142013-05-30 17:44:31 -0700620 @raise: error.TestError in case the parameter is not 'dut'
621 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800622 """
Fang Dengafb88142013-05-30 17:44:31 -0700623 if self.get_usbkey_direction() == usb_state:
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800624 return
625
Fang Dengafb88142013-05-30 17:44:31 -0700626 if usb_state == 'off':
627 self._switch_usbkey_power('off')
628 self._usb_state = usb_state
629 return
630 elif usb_state == 'host':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800631 mux_direction = 'servo_sees_usbkey'
Fang Dengafb88142013-05-30 17:44:31 -0700632 elif usb_state == 'dut':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800633 mux_direction = 'dut_sees_usbkey'
634 else:
Fang Dengafb88142013-05-30 17:44:31 -0700635 raise error.TestError('Unknown USB state request: %s' % usb_state)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800636
Fang Dengafb88142013-05-30 17:44:31 -0700637 self._switch_usbkey_power('off')
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800638 self.set('usb_mux_sel1', mux_direction)
639 time.sleep(self.USB_POWEROFF_DELAY)
Fang Dengafb88142013-05-30 17:44:31 -0700640 self._switch_usbkey_power('on', usb_state == 'host')
641 self._usb_state = usb_state
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800642
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800643
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800644 def get_usbkey_direction(self):
Fang Dengafb88142013-05-30 17:44:31 -0700645 """Get which side USB is connected to or 'off' if usb power is off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800646
Fang Dengafb88142013-05-30 17:44:31 -0700647 @return: A string, one of 'dut', 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800648 """
Fang Dengafb88142013-05-30 17:44:31 -0700649 if not self._usb_state:
650 if self.get('prtctl4_pwren') == 'off':
651 self._usb_state = 'off'
652 elif self.get('usb_mux_sel1').startswith('dut'):
653 self._usb_state = 'dut'
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800654 else:
Fang Dengafb88142013-05-30 17:44:31 -0700655 self._usb_state = 'host'
656 return self._usb_state