blob: c163ab428c8d76b918ecaa58ccb011abf945f013 [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 Yange6bdfeb2012-11-07 11:06:21 +080058 SERVO_KEY_PRESS_DELAY = 0.3
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 = {
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -070064 'ctrl_refresh': ['0', '0', '0', '1'],
65 'ctrl_d': ['0', '1', '0', '0'],
66 'd': ['0', '1', '1', '1'],
67 'ctrl_enter': ['1', '0', '0', '0'],
68 'enter': ['1', '0', '1', '1'],
69 'ctrl': ['1', '1', '0', '0'],
70 'refresh': ['1', '1', '0', '1'],
71 'unused': ['1', '1', '1', '0'],
72 'none': ['1', '1', '1', '1']}
Chris Masone6a0680f2012-03-02 08:40:00 -080073
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -070074 KEY_MATRIX_ALT_1 = {
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -070075 'ctrl_d': ['0', '0', '1', '0'],
76 'd': ['0', '0', '1', '1'],
77 'ctrl_enter': ['0', '1', '1', '0'],
78 'enter': ['0', '1', '1', '1'],
79 'ctrl_refresh': ['1', '0', '0', '1'],
80 'unused': ['1', '1', '0', '0'],
81 'refresh': ['1', '1', '0', '1'],
82 'ctrl': ['1', '1', '1', '0'],
83 'none': ['1', '1', '1', '1']}
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -070084
85 KEY_MATRIX = [KEY_MATRIX_ALT_0, KEY_MATRIX_ALT_1]
J. Richard Barnette67ccb872012-04-19 16:34:56 -070086
Chris Masone6a0680f2012-03-02 08:40:00 -080087 @staticmethod
J. Richard Barnette67ccb872012-04-19 16:34:56 -070088 def _make_servo_hostname(hostname):
89 host_parts = hostname.split('.')
90 host_parts[0] = host_parts[0] + '-servo'
91 return '.'.join(host_parts)
Chris Masone6a0680f2012-03-02 08:40:00 -080092
J. Richard Barnette67ccb872012-04-19 16:34:56 -070093 @staticmethod
94 def get_lab_servo(target_hostname):
95 """Instantiate a Servo for |target_hostname| in the lab.
Chris Masone6a0680f2012-03-02 08:40:00 -080096
J. Richard Barnette67ccb872012-04-19 16:34:56 -070097 Assuming that |target_hostname| is a device in the CrOS test
98 lab, create and return a Servo object pointed at the servo
99 attached to that DUT. The servo in the test lab is assumed
100 to already have servod up and running on it.
101
102 @param target_hostname: device whose servo we want to target.
Chris Masone6a0680f2012-03-02 08:40:00 -0800103 @return an appropriately configured Servo
104 """
J. Richard Barnette67ccb872012-04-19 16:34:56 -0700105 servo_host = Servo._make_servo_hostname(target_hostname)
106 if utils.host_is_in_lab_zone(servo_host):
107 try:
108 return Servo(servo_host=servo_host)
109 except:
110 # TODO(jrbarnette): Long-term, if we can't get to
111 # a servo in the lab, we want to fail, so we should
112 # pass any exceptions along. Short-term, we're not
113 # ready to rely on servo, so we ignore failures.
114 pass
115 return None
Chris Masone6a0680f2012-03-02 08:40:00 -0800116
117
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700118 def __init__(self, servo_host='localhost', servo_port=9999):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700119 """Sets up the servo communication infrastructure.
120
J. Richard Barnette55fb8062012-05-23 10:29:31 -0700121 @param servo_host Name of the host where the servod process
122 is running.
123 @param servo_port Port the servod process is listening on.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700124 """
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -0700125 self._key_matrix = 0
J. Richard Barnette384056b2012-04-16 11:04:46 -0700126 self._server = None
Todd Brochf24d2782011-08-19 10:55:41 -0700127 self._connect_servod(servo_host, servo_port)
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800128 self._is_localhost = (servo_host == 'localhost')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700129
130
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700131 def initialize_dut(self, cold_reset=False):
132 """Initializes a dut for testing purposes.
133
134 This sets various servo signals back to default values
135 appropriate for the target board. By default, if the DUT
136 is already on, it stays on. If the DUT is powered off
137 before initialization, its state afterward is unspecified.
138
139 If cold reset is requested, the DUT is guaranteed to be off
140 at the end of initialization, regardless of its initial
141 state.
142
143 Rationale: Basic initialization of servo sets the lid open,
144 when there is a lid. This operation won't affect powered on
145 units; however, setting the lid open may power on a unit
146 that's off, depending on factors outside the scope of this
147 function.
148
149 @param cold_reset If True, cold reset the device after
150 initialization.
151 """
152 self._server.hwinit()
153 if cold_reset:
154 self.cold_reset()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700155
156
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800157 def is_localhost(self):
158 """Is the servod hosted locally?
159
160 Returns:
161 True if local hosted; otherwise, False.
162 """
163 return self._is_localhost
164
165
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700166 def power_long_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700167 """Simulate a long power button press."""
J. Richard Barnettef12bff22012-07-18 14:41:06 -0700168 # After a long power press, the EC may ignore the next power
169 # button press (at least on Alex). To guarantee that this
170 # won't happen, we need to allow the EC one second to
171 # collect itself.
Chrome Bot9a1137d2011-07-19 14:35:00 -0700172 self.power_key(Servo.LONG_DELAY)
J. Richard Barnettef12bff22012-07-18 14:41:06 -0700173 time.sleep(1.0)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700174
175
176 def power_normal_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700177 """Simulate a normal power button press."""
178 self.power_key()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700179
180
181 def power_short_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700182 """Simulate a short power button press."""
183 self.power_key(Servo.SHORT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700184
185
Chrome Bot9a1137d2011-07-19 14:35:00 -0700186 def power_key(self, secs=NORMAL_TRANSITION_DELAY):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700187 """Simulate a power button press.
188
189 Args:
190 secs: Time in seconds to simulate the keypress.
191 """
Craig Harrison6b36b122011-06-28 17:58:43 -0700192 self.set_nocheck('pwr_button', 'press')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700193 time.sleep(secs)
Todd Broch31c82502011-08-29 08:14:39 -0700194 self.set_nocheck('pwr_button', 'release')
195 # TODO(tbroch) Different systems have different release times on the
196 # power button that this loop addresses. Longer term we may want to
197 # make this delay platform specific.
198 retry = 1
199 while True:
200 value = self.get('pwr_button')
201 if value == 'release' or retry > Servo.RELEASE_RETRY_MAX:
202 break
Todd Broch9753bd42012-03-21 10:15:08 -0700203 logging.info('Waiting for pwr_button to release, retry %d.', retry)
Todd Broch31c82502011-08-29 08:14:39 -0700204 retry += 1
205 time.sleep(Servo.SHORT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700206
207
208 def lid_open(self):
209 """Simulate opening the lid."""
Craig Harrison48997262011-06-27 14:31:10 -0700210 self.set_nocheck('lid_open', 'yes')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700211
212
213 def lid_close(self):
Craig Harrison48997262011-06-27 14:31:10 -0700214 """Simulate closing the lid.
215
216 Waits 6 seconds to ensure the device is fully asleep before returning.
217 """
218 self.set_nocheck('lid_open', 'no')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700219 time.sleep(Servo.SLEEP_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700220
221
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700222 def _press_and_release_keys(self, key,
Vic Yang4a1ef382012-08-21 17:03:19 +0800223 press_secs=SERVO_KEY_PRESS_DELAY):
Todd Broch9dfc3a82011-11-01 08:09:28 -0700224 """Simulate button presses."""
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700225 (m1_a1, m1_a0, m2_a1, m2_a0) = self.KEY_MATRIX[self._key_matrix][key]
Todd Broch9753bd42012-03-21 10:15:08 -0700226 self.set_nocheck('kbd_m2_a0', m2_a0)
227 self.set_nocheck('kbd_m2_a1', m2_a1)
228 self.set_nocheck('kbd_m1_a0', m1_a0)
229 self.set_nocheck('kbd_m1_a1', m1_a1)
Vic Yange262a3e2012-11-02 18:48:37 +0800230 self.set_nocheck('kbd_en', 'on')
Todd Broch9dfc3a82011-11-01 08:09:28 -0700231 time.sleep(press_secs)
232 self.set_nocheck('kbd_en', 'off')
233
234
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -0700235 def set_key_matrix(self, matrix=0):
236 """Set keyboard mapping"""
237 self._key_matrix = matrix
238
239
Chrome Bot9a1137d2011-07-19 14:35:00 -0700240 def ctrl_d(self):
241 """Simulate Ctrl-d simultaneous button presses."""
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700242 self._press_and_release_keys('ctrl_d')
Todd Broch9dfc3a82011-11-01 08:09:28 -0700243
244
Todd Broch9753bd42012-03-21 10:15:08 -0700245 def ctrl_enter(self):
246 """Simulate Ctrl-enter simultaneous button presses."""
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700247 self._press_and_release_keys('ctrl_enter')
Todd Broch9753bd42012-03-21 10:15:08 -0700248
249
Todd Broch9dfc3a82011-11-01 08:09:28 -0700250 def d_key(self):
251 """Simulate Enter key button press."""
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700252 self._press_and_release_keys('d')
Todd Broch9dfc3a82011-11-01 08:09:28 -0700253
254
255 def ctrl_key(self):
256 """Simulate Enter key button press."""
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700257 self._press_and_release_keys('ctrl')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700258
259
Chrome Bot9a1137d2011-07-19 14:35:00 -0700260 def enter_key(self):
261 """Simulate Enter key button press."""
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700262 self._press_and_release_keys('enter')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700263
264
Chrome Bot9a1137d2011-07-19 14:35:00 -0700265 def refresh_key(self):
266 """Simulate Refresh key (F3) button press."""
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700267 self._press_and_release_keys('refresh')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700268
269
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800270 def ctrl_refresh_key(self):
271 """Simulate Ctrl and Refresh (F3) simultaneous press.
272
273 This key combination is an alternative of Space key.
274 """
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700275 self._press_and_release_keys('ctrl_refresh')
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800276
277
Chrome Bot9a1137d2011-07-19 14:35:00 -0700278 def imaginary_key(self):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700279 """Simulate imaginary key button press.
280
281 Maps to a key that doesn't physically exist.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700282 """
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700283 self._press_and_release_keys('unused')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700284
285
Craig Harrison6b36b122011-06-28 17:58:43 -0700286 def enable_recovery_mode(self):
287 """Enable recovery mode on device."""
288 self.set('rec_mode', 'on')
289
290
291 def disable_recovery_mode(self):
292 """Disable recovery mode on device."""
293 self.set('rec_mode', 'off')
294
295
296 def enable_development_mode(self):
297 """Enable development mode on device."""
298 self.set('dev_mode', 'on')
299
300
301 def disable_development_mode(self):
302 """Disable development mode on device."""
303 self.set('dev_mode', 'off')
304
Chris Sosa8ee1d592011-08-14 16:50:31 -0700305 def enable_usb_hub(self, host=False):
Craig Harrison86b1a572011-08-12 11:26:52 -0700306 """Enable Servo's USB/ethernet hub.
307
Chris Sosa8ee1d592011-08-14 16:50:31 -0700308 This is equivalent to plugging in the USB devices attached to Servo to
309 the host (if |host| is True) or dut (if |host| is False).
310 For host=False, requires that the USB out on the servo board is
311 connected to a USB in port on the target device. Servo's USB ports are
312 labeled DUT_HUB_USB1 and DUT_HUB_USB2. Servo's ethernet port is also
313 connected to this hub. Servo's USB port DUT_HUB_IN is the output of the
314 hub.
Craig Harrison86b1a572011-08-12 11:26:52 -0700315 """
316 self.set('dut_hub_pwren', 'on')
Chris Sosa8ee1d592011-08-14 16:50:31 -0700317 if host:
Todd Broch9753bd42012-03-21 10:15:08 -0700318 self.set('usb_mux_oe1', 'on')
319 self.set('usb_mux_sel1', 'servo_sees_usbkey')
Chris Sosa8ee1d592011-08-14 16:50:31 -0700320 else:
Todd Broch9753bd42012-03-21 10:15:08 -0700321 self.set('dut_hub_sel', 'dut_sees_hub')
Chris Sosa8ee1d592011-08-14 16:50:31 -0700322
Craig Harrison86b1a572011-08-12 11:26:52 -0700323 self.set('dut_hub_on', 'yes')
324
325
326 def disable_usb_hub(self):
327 """Disable Servo's USB/ethernet hub.
328
329 This is equivalent to unplugging the USB devices attached to Servo.
330 """
331 self.set('dut_hub_on', 'no')
332
333
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700334 def boot_devmode(self):
335 """Boot a dev-mode device that is powered off."""
Tom Wai-Hong Tam23869102012-01-17 15:41:30 +0800336 self.power_short_press()
Craig Harrison48997262011-06-27 14:31:10 -0700337 self.pass_devmode()
338
339
340 def pass_devmode(self):
341 """Pass through boot screens in dev-mode."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700342 time.sleep(Servo.BOOT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700343 self.ctrl_d()
Chrome Bot9a1137d2011-07-19 14:35:00 -0700344 time.sleep(Servo.BOOT_DELAY)
Craig Harrison48997262011-06-27 14:31:10 -0700345
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700346
Craig Harrison6b36b122011-06-28 17:58:43 -0700347 def cold_reset(self):
348 """Perform a cold reset of the EC.
349
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700350 This has the side effect of shutting off the device. The
351 device is guaranteed to be off at the end of this call.
Craig Harrison6b36b122011-06-28 17:58:43 -0700352 """
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700353 # After the reset, give the EC the time it needs to
354 # re-initialize.
Craig Harrison6b36b122011-06-28 17:58:43 -0700355 self.set('cold_reset', 'on')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700356 time.sleep(Servo.SERVO_SEND_SIGNAL_DELAY)
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700357 self.set('cold_reset', 'off')
358 time.sleep(self._EC_RESET_DELAY)
Craig Harrison6b36b122011-06-28 17:58:43 -0700359
360
361 def warm_reset(self):
362 """Perform a warm reset of the device.
363
364 Has the side effect of restarting the device.
365 """
366 self.set('warm_reset', 'on')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700367 time.sleep(Servo.SERVO_SEND_SIGNAL_DELAY)
Craig Harrison6b36b122011-06-28 17:58:43 -0700368 self.set('warm_reset', 'off')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700369
370
Todd Brochefe72cb2012-07-11 19:58:53 -0700371 def _get_xmlrpclib_exception(self, xmlexc):
372 """Get meaningful exception string from xmlrpc.
373
374 Args:
375 xmlexc: xmlrpclib.Fault object
376
377 xmlrpclib.Fault.faultString has the following format:
378
379 <type 'exception type'>:'actual error message'
380
381 Parse and return the real exception from the servod side instead of the
382 less meaningful one like,
383 <Fault 1: "<type 'exceptions.AttributeError'>:'tca6416' object has no
384 attribute 'hw_driver'">
385
386 Returns:
387 string of underlying exception raised in servod.
388 """
389 return re.sub('^.*>:', '', xmlexc.faultString)
390
391
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700392 def get(self, gpio_name):
393 """Get the value of a gpio from Servod."""
394 assert gpio_name
Todd Brochefe72cb2012-07-11 19:58:53 -0700395 try:
396 return self._server.get(gpio_name)
397 except xmlrpclib.Fault as e:
398 err_msg = "Getting '%s' :: %s" % \
399 (gpio_name, self._get_xmlrpclib_exception(e))
400 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700401
402
403 def set(self, gpio_name, gpio_value):
404 """Set and check the value of a gpio using Servod."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700405 self.set_nocheck(gpio_name, gpio_value)
Todd Brochcf7c6652012-02-24 13:03:59 -0800406 retry_count = Servo.GET_RETRY_MAX
407 while gpio_value != self.get(gpio_name) and retry_count:
408 logging.warn("%s != %s, retry %d", gpio_name, gpio_value,
409 retry_count)
410 retry_count -= 1
411 time.sleep(Servo.SHORT_DELAY)
412 if not retry_count:
413 assert gpio_value == self.get(gpio_name), \
414 'Servo failed to set %s to %s' % (gpio_name, gpio_value)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700415
416
417 def set_nocheck(self, gpio_name, gpio_value):
418 """Set the value of a gpio using Servod."""
419 assert gpio_name and gpio_value
Todd Broch9753bd42012-03-21 10:15:08 -0700420 logging.info('Setting %s to %s', gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700421 try:
422 self._server.set(gpio_name, gpio_value)
423 except xmlrpclib.Fault as e:
424 err_msg = "Setting '%s' to '%s' :: %s" % \
425 (gpio_name, gpio_value, self._get_xmlrpclib_exception(e))
426 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700427
428
Jon Salzc88e5b62011-11-30 14:38:54 +0800429 # TODO(waihong) It may fail if multiple servo's are connected to the same
430 # host. Should look for a better way, like the USB serial name, to identify
431 # the USB device.
Simran Basi741b5d42012-05-18 11:27:15 -0700432 # TODO(sbasi) Remove this code from autoserv once firmware tests have been
433 # updated.
Jon Salzc88e5b62011-11-30 14:38:54 +0800434 def probe_host_usb_dev(self):
435 """Probe the USB disk device plugged-in the servo from the host side.
436
437 It tries to switch the USB mux to make the host unable to see the
438 USB disk and compares the result difference.
439
440 This only works if the servo is attached to the local host.
441
442 Returns:
443 A string of USB disk path, like '/dev/sdb', or None if not existed.
444 """
445 cmd = 'ls /dev/sd[a-z]'
446 original_value = self.get('usb_mux_sel1')
447
448 # Make the host unable to see the USB disk.
449 if original_value != 'dut_sees_usbkey':
450 self.set('usb_mux_sel1', 'dut_sees_usbkey')
451 time.sleep(self.USB_DETECTION_DELAY)
452 no_usb_set = set(utils.system_output(cmd, ignore_status=True).split())
453
454 # Make the host able to see the USB disk.
455 self.set('usb_mux_sel1', 'servo_sees_usbkey')
456 time.sleep(self.USB_DETECTION_DELAY)
457 has_usb_set = set(utils.system_output(cmd, ignore_status=True).split())
458
459 # Back to its original value.
460 if original_value != 'servo_sees_usbkey':
461 self.set('usb_mux_sel1', original_value)
462 time.sleep(self.USB_DETECTION_DELAY)
463
464 diff_set = has_usb_set - no_usb_set
465 if len(diff_set) == 1:
466 return diff_set.pop()
467 else:
468 return None
469
470
Mike Truty49153d82012-08-21 22:27:30 -0500471 def image_to_servo_usb(self, image_path=None,
472 make_image_noninteractive=False):
473 """Install an image to the USB key plugged into the servo.
474
475 This method may copy any image to the servo USB key including a
476 recovery image or a test image. These images are frequently used
477 for test purposes such as restoring a corrupted image or conducting
478 an upgrade of ec/fw/kernel as part of a test of a specific image part.
479
480 Args:
481 image_path: Path on the host to the recovery image.
482 make_image_noninteractive: Make the recovery image noninteractive,
483 therefore the DUT will reboot
484 automatically after installation.
485 """
486 # Turn the device off. This should happen before USB key detection, to
487 # prevent a recovery destined DUT from sensing the USB key due to the
488 # autodetection procedure.
489 self.initialize_dut(cold_reset=True)
490
491 # Set up Servo's usb mux.
492 self.set('prtctl4_pwren', 'on')
493 self.enable_usb_hub(host=True)
494 if image_path:
Tom Wai-Hong Tam42f136d2012-10-26 11:11:23 +0800495 logging.info('Searching for usb device and copying image to it. '
496 'Please wait a few minutes...')
Mike Truty49153d82012-08-21 22:27:30 -0500497 if not self._server.download_image_to_usb(image_path):
498 logging.error('Failed to transfer requested image to USB. '
499 'Please take a look at Servo Logs.')
500 raise error.AutotestError('Download image to usb failed.')
501 if make_image_noninteractive:
502 logging.info('Making image noninteractive')
503 if not self._server.make_image_noninteractive():
504 logging.error('Failed to make image noninteractive. '
505 'Please take a look at Servo Logs.')
506
507
Simran Basi741b5d42012-05-18 11:27:15 -0700508 def install_recovery_image(self, image_path=None,
509 wait_timeout=RECOVERY_INSTALL_DELAY,
510 make_image_noninteractive=False,
511 host=None):
Jon Salzc88e5b62011-11-30 14:38:54 +0800512 """Install the recovery image specied by the path onto the DUT.
513
514 This method uses google recovery mode to install a recovery image
515 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 +0800516 board specified by the usb_dev. If no image path is specified
Jon Salzc88e5b62011-11-30 14:38:54 +0800517 we use the recovery image already on the usb image.
518
519 Args:
520 image_path: Path on the host to the recovery image.
Gilad Arnold9df73de2012-03-14 09:35:08 -0700521 wait_timeout: How long to wait for completion; default is
522 determined by a constant.
Simran Basi741b5d42012-05-18 11:27:15 -0700523 make_image_noninteractive: Make the recovery image noninteractive,
524 therefore the DUT will reboot
525 automatically after installation.
526 host: Host object for the DUT that the installation process is
527 running on. If provided, will wait to see if the host is back
528 up after starting recovery mode.
Jon Salzc88e5b62011-11-30 14:38:54 +0800529 """
Mike Truty49153d82012-08-21 22:27:30 -0500530 self.image_to_servo_usb(image_path, make_image_noninteractive)
Jon Salzc88e5b62011-11-30 14:38:54 +0800531
532 # Boot in recovery mode.
533 try:
Jon Salzc88e5b62011-11-30 14:38:54 +0800534 self.enable_recovery_mode()
Tom Wai-Hong Tam23869102012-01-17 15:41:30 +0800535 self.power_short_press()
Jon Salzc88e5b62011-11-30 14:38:54 +0800536 time.sleep(Servo.RECOVERY_BOOT_DELAY)
Tom Wai-Hong Tam922ed052012-01-09 12:27:52 +0800537 self.set('usb_mux_sel1', 'dut_sees_usbkey')
Jon Salzc88e5b62011-11-30 14:38:54 +0800538 self.disable_recovery_mode()
539
Simran Basi741b5d42012-05-18 11:27:15 -0700540 if host:
Jon Salzc88e5b62011-11-30 14:38:54 +0800541 logging.info('Running the recovery process on the DUT. '
Simran Basi741b5d42012-05-18 11:27:15 -0700542 'Will wait up to %d seconds for recovery to '
543 'complete.', wait_timeout)
544 start_time = time.time()
545 # Wait for the host to come up.
546 if host.wait_up(timeout=wait_timeout):
547 logging.info('Recovery process completed successfully in '
548 '%d seconds.', time.time() - start_time)
549 else:
550 logger.error('Host failed to come back up in the allotted '
551 'time: %d seconds.', wait_timeout)
Jon Salzc88e5b62011-11-30 14:38:54 +0800552 logging.info('Removing the usb key from the DUT.')
553 self.disable_usb_hub()
Jon Salzc88e5b62011-11-30 14:38:54 +0800554 except:
555 # In case anything went wrong we want to make sure to do a clean
556 # reset.
557 self.disable_recovery_mode()
558 self.warm_reset()
559 raise
560
561
Todd Brochf24d2782011-08-19 10:55:41 -0700562 def _connect_servod(self, servo_host, servo_port):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700563 """Connect to the Servod process with XMLRPC.
564
565 Args:
566 servo_port: Port the Servod process is listening on.
567 """
Todd Brochf24d2782011-08-19 10:55:41 -0700568 remote = 'http://%s:%s' % (servo_host, servo_port)
Todd Broch96d83aa2011-08-29 14:37:38 -0700569 self._server = xmlrpclib.ServerProxy(remote)
Todd Brochf24d2782011-08-19 10:55:41 -0700570 try:
571 self._server.echo("ping-test")
Todd Broch96d83aa2011-08-29 14:37:38 -0700572 except:
573 logging.error('Connection to servod failed')
Todd Brochf24d2782011-08-19 10:55:41 -0700574 raise