blob: bca267fec99260cb8d24ad141149a9522fab88e8 [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
Todd Brochefe72cb2012-07-11 19:58:53 -07008import logging, os, re, select, subprocess, sys, time, xmlrpclib
Jon Salzc88e5b62011-11-30 14:38:54 +08009from autotest_lib.client.bin import utils as client_utils
Simran Basi741b5d42012-05-18 11:27:15 -070010from autotest_lib.client.common_lib import error
Jon Salzc88e5b62011-11-30 14:38:54 +080011from autotest_lib.server import utils
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070012
J. Richard Barnette384056b2012-04-16 11:04:46 -070013class Servo(object):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070014 """Manages control of a Servo board.
15
16 Servo is a board developed by hardware group to aide in the debug and
17 control of various partner devices. Servo's features include the simulation
18 of pressing the power button, closing the lid, and pressing Ctrl-d. This
19 class manages setting up and communicating with a servo demon (servod)
20 process. It provides both high-level functions for common servo tasks and
21 low-level functions for directly setting and reading gpios.
22 """
23
Chrome Bot9a1137d2011-07-19 14:35:00 -070024 # Power button press delays in seconds.
J. Richard Barnetted2e4cbd2012-06-29 12:18:40 -070025 #
J. Richard Barnette5383f072012-07-26 17:35:40 -070026 # The EC specification says that 8.0 seconds should be enough
27 # for the long power press. However, some platforms need a bit
28 # more time. Empirical testing has found these requirements:
29 # Alex: 8.2 seconds
30 # ZGB: 8.5 seconds
31 # The actual value is set to the largest known necessary value.
32 #
33 # TODO(jrbarnette) Being generous is the right thing to do for
34 # existing platforms, but if this code is to be used for
35 # qualification of new hardware, we should be less generous.
36 LONG_DELAY = 8.5
Chrome Bot9a1137d2011-07-19 14:35:00 -070037 SHORT_DELAY = 0.1
38 NORMAL_TRANSITION_DELAY = 1.2
J. Richard Barnette5383f072012-07-26 17:35:40 -070039
Todd Broch31c82502011-08-29 08:14:39 -070040 # Maximum number of times to re-read power button on release.
41 RELEASE_RETRY_MAX = 5
Todd Brochcf7c6652012-02-24 13:03:59 -080042 GET_RETRY_MAX = 10
Chrome Bot9a1137d2011-07-19 14:35:00 -070043
J. Richard Barnette5383f072012-07-26 17:35:40 -070044 # Delays to deal with DUT state transitions.
Chrome Bot9a1137d2011-07-19 14:35:00 -070045 SLEEP_DELAY = 6
46 BOOT_DELAY = 10
J. Richard Barnette8bd49842012-07-19 14:21:15 -070047 RECOVERY_BOOT_DELAY = 10
J. Richard Barnettec5a77ad2012-04-25 08:19:00 -070048 RECOVERY_INSTALL_DELAY = 540
Chrome Bot9a1137d2011-07-19 14:35:00 -070049
J. Richard Barnetteb6133972012-07-19 17:13:55 -070050 # Time required for the EC to be working after cold reset.
51 # Five seconds is at least twice as big as necessary for Alex,
52 # and is presumably good enough for all future systems.
53 _EC_RESET_DELAY = 5.0
54
Chrome Bot9a1137d2011-07-19 14:35:00 -070055 # Servo-specific delays.
56 MAX_SERVO_STARTUP_DELAY = 10
57 SERVO_SEND_SIGNAL_DELAY = 0.5
Vic Yang4a1ef382012-08-21 17:03:19 +080058 SERVO_KEY_PRESS_DELAY = 0.1
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070059
Jon Salzc88e5b62011-11-30 14:38:54 +080060 # Time between an usb disk plugged-in and detected in the system.
61 USB_DETECTION_DELAY = 10
62
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -070063 KEY_MATRIX_ALT_0 = {
Todd Broch9753bd42012-03-21 10:15:08 -070064 'm1': {'ctrl_r': ['0', '0'], 'd': ['0', '1'],
65 'enter': ['1', '0'], 'none': ['1', '1']},
66 'm2': {'ctrl': ['0', '0'], 'refresh': ['0', '1'],
67 'unused': ['1', '0'], 'none': ['1', '1']}
68 }
Chris Masone6a0680f2012-03-02 08:40:00 -080069
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -070070 KEY_MATRIX_ALT_1 = {
71 'm1': {'d': ['0', '0'], 'enter': ['0', '1'],
72 'ctrl_r': ['1', '0'], 'none': ['1', '1']},
73 'm2': {'unused': ['0', '0'], 'refresh': ['0', '1'],
74 'ctrl': ['1', '0'], 'none': ['1', '1']}
75 }
76
77 KEY_MATRIX = [KEY_MATRIX_ALT_0, KEY_MATRIX_ALT_1]
J. Richard Barnette67ccb872012-04-19 16:34:56 -070078
Chris Masone6a0680f2012-03-02 08:40:00 -080079 @staticmethod
J. Richard Barnette67ccb872012-04-19 16:34:56 -070080 def _make_servo_hostname(hostname):
81 host_parts = hostname.split('.')
82 host_parts[0] = host_parts[0] + '-servo'
83 return '.'.join(host_parts)
Chris Masone6a0680f2012-03-02 08:40:00 -080084
J. Richard Barnette67ccb872012-04-19 16:34:56 -070085 @staticmethod
86 def get_lab_servo(target_hostname):
87 """Instantiate a Servo for |target_hostname| in the lab.
Chris Masone6a0680f2012-03-02 08:40:00 -080088
J. Richard Barnette67ccb872012-04-19 16:34:56 -070089 Assuming that |target_hostname| is a device in the CrOS test
90 lab, create and return a Servo object pointed at the servo
91 attached to that DUT. The servo in the test lab is assumed
92 to already have servod up and running on it.
93
94 @param target_hostname: device whose servo we want to target.
Chris Masone6a0680f2012-03-02 08:40:00 -080095 @return an appropriately configured Servo
96 """
J. Richard Barnette67ccb872012-04-19 16:34:56 -070097 servo_host = Servo._make_servo_hostname(target_hostname)
98 if utils.host_is_in_lab_zone(servo_host):
99 try:
100 return Servo(servo_host=servo_host)
101 except:
102 # TODO(jrbarnette): Long-term, if we can't get to
103 # a servo in the lab, we want to fail, so we should
104 # pass any exceptions along. Short-term, we're not
105 # ready to rely on servo, so we ignore failures.
106 pass
107 return None
Chris Masone6a0680f2012-03-02 08:40:00 -0800108
109
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700110 def __init__(self, servo_host='localhost', servo_port=9999):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700111 """Sets up the servo communication infrastructure.
112
J. Richard Barnette55fb8062012-05-23 10:29:31 -0700113 @param servo_host Name of the host where the servod process
114 is running.
115 @param servo_port Port the servod process is listening on.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700116 """
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -0700117 self._key_matrix = 0
J. Richard Barnette384056b2012-04-16 11:04:46 -0700118 self._server = None
Todd Brochf24d2782011-08-19 10:55:41 -0700119 self._connect_servod(servo_host, servo_port)
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800120 self._is_localhost = (servo_host == 'localhost')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700121
122
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700123 def initialize_dut(self, cold_reset=False):
124 """Initializes a dut for testing purposes.
125
126 This sets various servo signals back to default values
127 appropriate for the target board. By default, if the DUT
128 is already on, it stays on. If the DUT is powered off
129 before initialization, its state afterward is unspecified.
130
131 If cold reset is requested, the DUT is guaranteed to be off
132 at the end of initialization, regardless of its initial
133 state.
134
135 Rationale: Basic initialization of servo sets the lid open,
136 when there is a lid. This operation won't affect powered on
137 units; however, setting the lid open may power on a unit
138 that's off, depending on factors outside the scope of this
139 function.
140
141 @param cold_reset If True, cold reset the device after
142 initialization.
143 """
144 self._server.hwinit()
145 if cold_reset:
146 self.cold_reset()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700147
148
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800149 def is_localhost(self):
150 """Is the servod hosted locally?
151
152 Returns:
153 True if local hosted; otherwise, False.
154 """
155 return self._is_localhost
156
157
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700158 def power_long_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700159 """Simulate a long power button press."""
J. Richard Barnettef12bff22012-07-18 14:41:06 -0700160 # After a long power press, the EC may ignore the next power
161 # button press (at least on Alex). To guarantee that this
162 # won't happen, we need to allow the EC one second to
163 # collect itself.
Chrome Bot9a1137d2011-07-19 14:35:00 -0700164 self.power_key(Servo.LONG_DELAY)
J. Richard Barnettef12bff22012-07-18 14:41:06 -0700165 time.sleep(1.0)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700166
167
168 def power_normal_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700169 """Simulate a normal power button press."""
170 self.power_key()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700171
172
173 def power_short_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700174 """Simulate a short power button press."""
175 self.power_key(Servo.SHORT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700176
177
Chrome Bot9a1137d2011-07-19 14:35:00 -0700178 def power_key(self, secs=NORMAL_TRANSITION_DELAY):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700179 """Simulate a power button press.
180
181 Args:
182 secs: Time in seconds to simulate the keypress.
183 """
Craig Harrison6b36b122011-06-28 17:58:43 -0700184 self.set_nocheck('pwr_button', 'press')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700185 time.sleep(secs)
Todd Broch31c82502011-08-29 08:14:39 -0700186 self.set_nocheck('pwr_button', 'release')
187 # TODO(tbroch) Different systems have different release times on the
188 # power button that this loop addresses. Longer term we may want to
189 # make this delay platform specific.
190 retry = 1
191 while True:
192 value = self.get('pwr_button')
193 if value == 'release' or retry > Servo.RELEASE_RETRY_MAX:
194 break
Todd Broch9753bd42012-03-21 10:15:08 -0700195 logging.info('Waiting for pwr_button to release, retry %d.', retry)
Todd Broch31c82502011-08-29 08:14:39 -0700196 retry += 1
197 time.sleep(Servo.SHORT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700198
199
200 def lid_open(self):
201 """Simulate opening the lid."""
Craig Harrison48997262011-06-27 14:31:10 -0700202 self.set_nocheck('lid_open', 'yes')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700203
204
205 def lid_close(self):
Craig Harrison48997262011-06-27 14:31:10 -0700206 """Simulate closing the lid.
207
208 Waits 6 seconds to ensure the device is fully asleep before returning.
209 """
210 self.set_nocheck('lid_open', 'no')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700211 time.sleep(Servo.SLEEP_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700212
213
Todd Broch9753bd42012-03-21 10:15:08 -0700214 def _press_and_release_keys(self, m1, m2,
Vic Yang4a1ef382012-08-21 17:03:19 +0800215 press_secs=SERVO_KEY_PRESS_DELAY):
Todd Broch9dfc3a82011-11-01 08:09:28 -0700216 """Simulate button presses."""
Todd Broch9753bd42012-03-21 10:15:08 -0700217 # set keys to none
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -0700218 (m2_a1, m2_a0) = self.KEY_MATRIX[self._key_matrix]['m2']['none']
219 (m1_a1, m1_a0) = self.KEY_MATRIX[self._key_matrix]['m1']['none']
Todd Broch9753bd42012-03-21 10:15:08 -0700220 self.set_nocheck('kbd_m2_a0', m2_a0)
221 self.set_nocheck('kbd_m2_a1', m2_a1)
222 self.set_nocheck('kbd_m1_a0', m1_a0)
223 self.set_nocheck('kbd_m1_a1', m1_a1)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700224
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -0700225 (m2_a1, m2_a0) = self.KEY_MATRIX[self._key_matrix]['m2'][m2]
226 (m1_a1, m1_a0) = self.KEY_MATRIX[self._key_matrix]['m1'][m1]
Todd Broch9dfc3a82011-11-01 08:09:28 -0700227 self.set_nocheck('kbd_en', 'on')
Todd Broch9753bd42012-03-21 10:15:08 -0700228 self.set_nocheck('kbd_m2_a0', m2_a0)
229 self.set_nocheck('kbd_m2_a1', m2_a1)
230 self.set_nocheck('kbd_m1_a0', m1_a0)
231 self.set_nocheck('kbd_m1_a1', m1_a1)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700232 time.sleep(press_secs)
233 self.set_nocheck('kbd_en', 'off')
234
235
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -0700236 def set_key_matrix(self, matrix=0):
237 """Set keyboard mapping"""
238 self._key_matrix = matrix
239
240
Chrome Bot9a1137d2011-07-19 14:35:00 -0700241 def ctrl_d(self):
242 """Simulate Ctrl-d simultaneous button presses."""
Todd Broch9dfc3a82011-11-01 08:09:28 -0700243 self._press_and_release_keys('d', 'ctrl')
244
245
Todd Broch9753bd42012-03-21 10:15:08 -0700246 def ctrl_enter(self):
247 """Simulate Ctrl-enter simultaneous button presses."""
248 self._press_and_release_keys('enter', 'ctrl')
249
250
Todd Broch9dfc3a82011-11-01 08:09:28 -0700251 def d_key(self):
252 """Simulate Enter key button press."""
253 self._press_and_release_keys('d', 'none')
254
255
256 def ctrl_key(self):
257 """Simulate Enter key button press."""
258 self._press_and_release_keys('none', 'ctrl')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700259
260
Chrome Bot9a1137d2011-07-19 14:35:00 -0700261 def enter_key(self):
262 """Simulate Enter key button press."""
Todd Broch9dfc3a82011-11-01 08:09:28 -0700263 self._press_and_release_keys('enter', 'none')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700264
265
Chrome Bot9a1137d2011-07-19 14:35:00 -0700266 def refresh_key(self):
267 """Simulate Refresh key (F3) button press."""
Todd Broch9dfc3a82011-11-01 08:09:28 -0700268 self._press_and_release_keys('none', 'refresh')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700269
270
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800271 def ctrl_refresh_key(self):
272 """Simulate Ctrl and Refresh (F3) simultaneous press.
273
274 This key combination is an alternative of Space key.
275 """
276 self._press_and_release_keys('ctrl_r', 'refresh')
277
278
Chrome Bot9a1137d2011-07-19 14:35:00 -0700279 def imaginary_key(self):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700280 """Simulate imaginary key button press.
281
282 Maps to a key that doesn't physically exist.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700283 """
Todd Broch9dfc3a82011-11-01 08:09:28 -0700284 self._press_and_release_keys('none', 'unused')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700285
286
Craig Harrison6b36b122011-06-28 17:58:43 -0700287 def enable_recovery_mode(self):
288 """Enable recovery mode on device."""
289 self.set('rec_mode', 'on')
290
291
292 def disable_recovery_mode(self):
293 """Disable recovery mode on device."""
294 self.set('rec_mode', 'off')
295
296
297 def enable_development_mode(self):
298 """Enable development mode on device."""
299 self.set('dev_mode', 'on')
300
301
302 def disable_development_mode(self):
303 """Disable development mode on device."""
304 self.set('dev_mode', 'off')
305
Chris Sosa8ee1d592011-08-14 16:50:31 -0700306 def enable_usb_hub(self, host=False):
Craig Harrison86b1a572011-08-12 11:26:52 -0700307 """Enable Servo's USB/ethernet hub.
308
Chris Sosa8ee1d592011-08-14 16:50:31 -0700309 This is equivalent to plugging in the USB devices attached to Servo to
310 the host (if |host| is True) or dut (if |host| is False).
311 For host=False, requires that the USB out on the servo board is
312 connected to a USB in port on the target device. Servo's USB ports are
313 labeled DUT_HUB_USB1 and DUT_HUB_USB2. Servo's ethernet port is also
314 connected to this hub. Servo's USB port DUT_HUB_IN is the output of the
315 hub.
Craig Harrison86b1a572011-08-12 11:26:52 -0700316 """
317 self.set('dut_hub_pwren', 'on')
Chris Sosa8ee1d592011-08-14 16:50:31 -0700318 if host:
Todd Broch9753bd42012-03-21 10:15:08 -0700319 self.set('usb_mux_oe1', 'on')
320 self.set('usb_mux_sel1', 'servo_sees_usbkey')
Chris Sosa8ee1d592011-08-14 16:50:31 -0700321 else:
Todd Broch9753bd42012-03-21 10:15:08 -0700322 self.set('dut_hub_sel', 'dut_sees_hub')
Chris Sosa8ee1d592011-08-14 16:50:31 -0700323
Craig Harrison86b1a572011-08-12 11:26:52 -0700324 self.set('dut_hub_on', 'yes')
325
326
327 def disable_usb_hub(self):
328 """Disable Servo's USB/ethernet hub.
329
330 This is equivalent to unplugging the USB devices attached to Servo.
331 """
332 self.set('dut_hub_on', 'no')
333
334
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700335 def boot_devmode(self):
336 """Boot a dev-mode device that is powered off."""
Tom Wai-Hong Tam23869102012-01-17 15:41:30 +0800337 self.power_short_press()
Craig Harrison48997262011-06-27 14:31:10 -0700338 self.pass_devmode()
339
340
341 def pass_devmode(self):
342 """Pass through boot screens in dev-mode."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700343 time.sleep(Servo.BOOT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700344 self.ctrl_d()
Chrome Bot9a1137d2011-07-19 14:35:00 -0700345 time.sleep(Servo.BOOT_DELAY)
Craig Harrison48997262011-06-27 14:31:10 -0700346
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700347
Craig Harrison6b36b122011-06-28 17:58:43 -0700348 def cold_reset(self):
349 """Perform a cold reset of the EC.
350
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700351 This has the side effect of shutting off the device. The
352 device is guaranteed to be off at the end of this call.
Craig Harrison6b36b122011-06-28 17:58:43 -0700353 """
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700354 # After the reset, give the EC the time it needs to
355 # re-initialize.
Craig Harrison6b36b122011-06-28 17:58:43 -0700356 self.set('cold_reset', 'on')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700357 time.sleep(Servo.SERVO_SEND_SIGNAL_DELAY)
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700358 self.set('cold_reset', 'off')
359 time.sleep(self._EC_RESET_DELAY)
Craig Harrison6b36b122011-06-28 17:58:43 -0700360
361
362 def warm_reset(self):
363 """Perform a warm reset of the device.
364
365 Has the side effect of restarting the device.
366 """
367 self.set('warm_reset', 'on')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700368 time.sleep(Servo.SERVO_SEND_SIGNAL_DELAY)
Craig Harrison6b36b122011-06-28 17:58:43 -0700369 self.set('warm_reset', 'off')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700370
371
Todd Brochefe72cb2012-07-11 19:58:53 -0700372 def _get_xmlrpclib_exception(self, xmlexc):
373 """Get meaningful exception string from xmlrpc.
374
375 Args:
376 xmlexc: xmlrpclib.Fault object
377
378 xmlrpclib.Fault.faultString has the following format:
379
380 <type 'exception type'>:'actual error message'
381
382 Parse and return the real exception from the servod side instead of the
383 less meaningful one like,
384 <Fault 1: "<type 'exceptions.AttributeError'>:'tca6416' object has no
385 attribute 'hw_driver'">
386
387 Returns:
388 string of underlying exception raised in servod.
389 """
390 return re.sub('^.*>:', '', xmlexc.faultString)
391
392
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700393 def get(self, gpio_name):
394 """Get the value of a gpio from Servod."""
395 assert gpio_name
Todd Brochefe72cb2012-07-11 19:58:53 -0700396 try:
397 return self._server.get(gpio_name)
398 except xmlrpclib.Fault as e:
399 err_msg = "Getting '%s' :: %s" % \
400 (gpio_name, self._get_xmlrpclib_exception(e))
401 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700402
403
404 def set(self, gpio_name, gpio_value):
405 """Set and check the value of a gpio using Servod."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700406 self.set_nocheck(gpio_name, gpio_value)
Todd Brochcf7c6652012-02-24 13:03:59 -0800407 retry_count = Servo.GET_RETRY_MAX
408 while gpio_value != self.get(gpio_name) and retry_count:
409 logging.warn("%s != %s, retry %d", gpio_name, gpio_value,
410 retry_count)
411 retry_count -= 1
412 time.sleep(Servo.SHORT_DELAY)
413 if not retry_count:
414 assert gpio_value == self.get(gpio_name), \
415 'Servo failed to set %s to %s' % (gpio_name, gpio_value)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700416
417
418 def set_nocheck(self, gpio_name, gpio_value):
419 """Set the value of a gpio using Servod."""
420 assert gpio_name and gpio_value
Todd Broch9753bd42012-03-21 10:15:08 -0700421 logging.info('Setting %s to %s', gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700422 try:
423 self._server.set(gpio_name, gpio_value)
424 except xmlrpclib.Fault as e:
425 err_msg = "Setting '%s' to '%s' :: %s" % \
426 (gpio_name, gpio_value, self._get_xmlrpclib_exception(e))
427 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700428
429
Jon Salzc88e5b62011-11-30 14:38:54 +0800430 # TODO(waihong) It may fail if multiple servo's are connected to the same
431 # host. Should look for a better way, like the USB serial name, to identify
432 # the USB device.
Simran Basi741b5d42012-05-18 11:27:15 -0700433 # TODO(sbasi) Remove this code from autoserv once firmware tests have been
434 # updated.
Jon Salzc88e5b62011-11-30 14:38:54 +0800435 def probe_host_usb_dev(self):
436 """Probe the USB disk device plugged-in the servo from the host side.
437
438 It tries to switch the USB mux to make the host unable to see the
439 USB disk and compares the result difference.
440
441 This only works if the servo is attached to the local host.
442
443 Returns:
444 A string of USB disk path, like '/dev/sdb', or None if not existed.
445 """
446 cmd = 'ls /dev/sd[a-z]'
447 original_value = self.get('usb_mux_sel1')
448
449 # Make the host unable to see the USB disk.
450 if original_value != 'dut_sees_usbkey':
451 self.set('usb_mux_sel1', 'dut_sees_usbkey')
452 time.sleep(self.USB_DETECTION_DELAY)
453 no_usb_set = set(utils.system_output(cmd, ignore_status=True).split())
454
455 # Make the host able to see the USB disk.
456 self.set('usb_mux_sel1', 'servo_sees_usbkey')
457 time.sleep(self.USB_DETECTION_DELAY)
458 has_usb_set = set(utils.system_output(cmd, ignore_status=True).split())
459
460 # Back to its original value.
461 if original_value != 'servo_sees_usbkey':
462 self.set('usb_mux_sel1', original_value)
463 time.sleep(self.USB_DETECTION_DELAY)
464
465 diff_set = has_usb_set - no_usb_set
466 if len(diff_set) == 1:
467 return diff_set.pop()
468 else:
469 return None
470
471
Mike Truty49153d82012-08-21 22:27:30 -0500472 def image_to_servo_usb(self, image_path=None,
473 make_image_noninteractive=False):
474 """Install an image to the USB key plugged into the servo.
475
476 This method may copy any image to the servo USB key including a
477 recovery image or a test image. These images are frequently used
478 for test purposes such as restoring a corrupted image or conducting
479 an upgrade of ec/fw/kernel as part of a test of a specific image part.
480
481 Args:
482 image_path: Path on the host to the recovery image.
483 make_image_noninteractive: Make the recovery image noninteractive,
484 therefore the DUT will reboot
485 automatically after installation.
486 """
487 # Turn the device off. This should happen before USB key detection, to
488 # prevent a recovery destined DUT from sensing the USB key due to the
489 # autodetection procedure.
490 self.initialize_dut(cold_reset=True)
491
492 # Set up Servo's usb mux.
493 self.set('prtctl4_pwren', 'on')
494 self.enable_usb_hub(host=True)
495 if image_path:
Tom Wai-Hong Tam42f136d2012-10-26 11:11:23 +0800496 logging.info('Searching for usb device and copying image to it. '
497 'Please wait a few minutes...')
Mike Truty49153d82012-08-21 22:27:30 -0500498 if not self._server.download_image_to_usb(image_path):
499 logging.error('Failed to transfer requested image to USB. '
500 'Please take a look at Servo Logs.')
501 raise error.AutotestError('Download image to usb failed.')
502 if make_image_noninteractive:
503 logging.info('Making image noninteractive')
504 if not self._server.make_image_noninteractive():
505 logging.error('Failed to make image noninteractive. '
506 'Please take a look at Servo Logs.')
507
508
Simran Basi741b5d42012-05-18 11:27:15 -0700509 def install_recovery_image(self, image_path=None,
510 wait_timeout=RECOVERY_INSTALL_DELAY,
511 make_image_noninteractive=False,
512 host=None):
Jon Salzc88e5b62011-11-30 14:38:54 +0800513 """Install the recovery image specied by the path onto the DUT.
514
515 This method uses google recovery mode to install a recovery image
516 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 +0800517 board specified by the usb_dev. If no image path is specified
Jon Salzc88e5b62011-11-30 14:38:54 +0800518 we use the recovery image already on the usb image.
519
520 Args:
521 image_path: Path on the host to the recovery image.
Gilad Arnold9df73de2012-03-14 09:35:08 -0700522 wait_timeout: How long to wait for completion; default is
523 determined by a constant.
Simran Basi741b5d42012-05-18 11:27:15 -0700524 make_image_noninteractive: Make the recovery image noninteractive,
525 therefore the DUT will reboot
526 automatically after installation.
527 host: Host object for the DUT that the installation process is
528 running on. If provided, will wait to see if the host is back
529 up after starting recovery mode.
Jon Salzc88e5b62011-11-30 14:38:54 +0800530 """
Mike Truty49153d82012-08-21 22:27:30 -0500531 self.image_to_servo_usb(image_path, make_image_noninteractive)
Jon Salzc88e5b62011-11-30 14:38:54 +0800532
533 # Boot in recovery mode.
534 try:
Jon Salzc88e5b62011-11-30 14:38:54 +0800535 self.enable_recovery_mode()
Tom Wai-Hong Tam23869102012-01-17 15:41:30 +0800536 self.power_short_press()
Jon Salzc88e5b62011-11-30 14:38:54 +0800537 time.sleep(Servo.RECOVERY_BOOT_DELAY)
Tom Wai-Hong Tam922ed052012-01-09 12:27:52 +0800538 self.set('usb_mux_sel1', 'dut_sees_usbkey')
Jon Salzc88e5b62011-11-30 14:38:54 +0800539 self.disable_recovery_mode()
540
Simran Basi741b5d42012-05-18 11:27:15 -0700541 if host:
Jon Salzc88e5b62011-11-30 14:38:54 +0800542 logging.info('Running the recovery process on the DUT. '
Simran Basi741b5d42012-05-18 11:27:15 -0700543 'Will wait up to %d seconds for recovery to '
544 'complete.', wait_timeout)
545 start_time = time.time()
546 # Wait for the host to come up.
547 if host.wait_up(timeout=wait_timeout):
548 logging.info('Recovery process completed successfully in '
549 '%d seconds.', time.time() - start_time)
550 else:
551 logger.error('Host failed to come back up in the allotted '
552 'time: %d seconds.', wait_timeout)
Jon Salzc88e5b62011-11-30 14:38:54 +0800553 logging.info('Removing the usb key from the DUT.')
554 self.disable_usb_hub()
Jon Salzc88e5b62011-11-30 14:38:54 +0800555 except:
556 # In case anything went wrong we want to make sure to do a clean
557 # reset.
558 self.disable_recovery_mode()
559 self.warm_reset()
560 raise
561
562
Todd Brochf24d2782011-08-19 10:55:41 -0700563 def _connect_servod(self, servo_host, servo_port):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700564 """Connect to the Servod process with XMLRPC.
565
566 Args:
567 servo_port: Port the Servod process is listening on.
568 """
Todd Brochf24d2782011-08-19 10:55:41 -0700569 remote = 'http://%s:%s' % (servo_host, servo_port)
Todd Broch96d83aa2011-08-29 14:37:38 -0700570 self._server = xmlrpclib.ServerProxy(remote)
Todd Brochf24d2782011-08-19 10:55:41 -0700571 try:
572 self._server.echo("ping-test")
Todd Broch96d83aa2011-08-29 14:37:38 -0700573 except:
574 logging.error('Connection to servod failed')
Todd Brochf24d2782011-08-19 10:55:41 -0700575 raise