blob: df1beb1600697067ea871cf1ca696cae5a74fe05 [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)
Chrome Bot9a1137d2011-07-19 14:35:00 -0700120
121
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700122 def initialize_dut(self, cold_reset=False):
123 """Initializes a dut for testing purposes.
124
125 This sets various servo signals back to default values
126 appropriate for the target board. By default, if the DUT
127 is already on, it stays on. If the DUT is powered off
128 before initialization, its state afterward is unspecified.
129
130 If cold reset is requested, the DUT is guaranteed to be off
131 at the end of initialization, regardless of its initial
132 state.
133
134 Rationale: Basic initialization of servo sets the lid open,
135 when there is a lid. This operation won't affect powered on
136 units; however, setting the lid open may power on a unit
137 that's off, depending on factors outside the scope of this
138 function.
139
140 @param cold_reset If True, cold reset the device after
141 initialization.
142 """
143 self._server.hwinit()
144 if cold_reset:
145 self.cold_reset()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700146
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.
Chrome Bot9a1137d2011-07-19 14:35:00 -0700154 self.power_key(Servo.LONG_DELAY)
J. Richard Barnettef12bff22012-07-18 14:41:06 -0700155 time.sleep(1.0)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700156
157
158 def power_normal_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700159 """Simulate a normal power button press."""
160 self.power_key()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700161
162
163 def power_short_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700164 """Simulate a short power button press."""
165 self.power_key(Servo.SHORT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700166
167
Chrome Bot9a1137d2011-07-19 14:35:00 -0700168 def power_key(self, secs=NORMAL_TRANSITION_DELAY):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700169 """Simulate a power button press.
170
171 Args:
172 secs: Time in seconds to simulate the keypress.
173 """
Craig Harrison6b36b122011-06-28 17:58:43 -0700174 self.set_nocheck('pwr_button', 'press')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700175 time.sleep(secs)
Todd Broch31c82502011-08-29 08:14:39 -0700176 self.set_nocheck('pwr_button', 'release')
177 # TODO(tbroch) Different systems have different release times on the
178 # power button that this loop addresses. Longer term we may want to
179 # make this delay platform specific.
180 retry = 1
181 while True:
182 value = self.get('pwr_button')
183 if value == 'release' or retry > Servo.RELEASE_RETRY_MAX:
184 break
Todd Broch9753bd42012-03-21 10:15:08 -0700185 logging.info('Waiting for pwr_button to release, retry %d.', retry)
Todd Broch31c82502011-08-29 08:14:39 -0700186 retry += 1
187 time.sleep(Servo.SHORT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700188
189
190 def lid_open(self):
191 """Simulate opening the lid."""
Craig Harrison48997262011-06-27 14:31:10 -0700192 self.set_nocheck('lid_open', 'yes')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700193
194
195 def lid_close(self):
Craig Harrison48997262011-06-27 14:31:10 -0700196 """Simulate closing the lid.
197
198 Waits 6 seconds to ensure the device is fully asleep before returning.
199 """
200 self.set_nocheck('lid_open', 'no')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700201 time.sleep(Servo.SLEEP_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700202
203
Todd Broch9753bd42012-03-21 10:15:08 -0700204 def _press_and_release_keys(self, m1, m2,
Vic Yang4a1ef382012-08-21 17:03:19 +0800205 press_secs=SERVO_KEY_PRESS_DELAY):
Todd Broch9dfc3a82011-11-01 08:09:28 -0700206 """Simulate button presses."""
Todd Broch9753bd42012-03-21 10:15:08 -0700207 # set keys to none
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -0700208 (m2_a1, m2_a0) = self.KEY_MATRIX[self._key_matrix]['m2']['none']
209 (m1_a1, m1_a0) = self.KEY_MATRIX[self._key_matrix]['m1']['none']
Todd Broch9753bd42012-03-21 10:15:08 -0700210 self.set_nocheck('kbd_m2_a0', m2_a0)
211 self.set_nocheck('kbd_m2_a1', m2_a1)
212 self.set_nocheck('kbd_m1_a0', m1_a0)
213 self.set_nocheck('kbd_m1_a1', m1_a1)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700214
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -0700215 (m2_a1, m2_a0) = self.KEY_MATRIX[self._key_matrix]['m2'][m2]
216 (m1_a1, m1_a0) = self.KEY_MATRIX[self._key_matrix]['m1'][m1]
Todd Broch9dfc3a82011-11-01 08:09:28 -0700217 self.set_nocheck('kbd_en', 'on')
Todd Broch9753bd42012-03-21 10:15:08 -0700218 self.set_nocheck('kbd_m2_a0', m2_a0)
219 self.set_nocheck('kbd_m2_a1', m2_a1)
220 self.set_nocheck('kbd_m1_a0', m1_a0)
221 self.set_nocheck('kbd_m1_a1', m1_a1)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700222 time.sleep(press_secs)
223 self.set_nocheck('kbd_en', 'off')
224
225
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -0700226 def set_key_matrix(self, matrix=0):
227 """Set keyboard mapping"""
228 self._key_matrix = matrix
229
230
Chrome Bot9a1137d2011-07-19 14:35:00 -0700231 def ctrl_d(self):
232 """Simulate Ctrl-d simultaneous button presses."""
Todd Broch9dfc3a82011-11-01 08:09:28 -0700233 self._press_and_release_keys('d', 'ctrl')
234
235
Todd Broch9753bd42012-03-21 10:15:08 -0700236 def ctrl_enter(self):
237 """Simulate Ctrl-enter simultaneous button presses."""
238 self._press_and_release_keys('enter', 'ctrl')
239
240
Todd Broch9dfc3a82011-11-01 08:09:28 -0700241 def d_key(self):
242 """Simulate Enter key button press."""
243 self._press_and_release_keys('d', 'none')
244
245
246 def ctrl_key(self):
247 """Simulate Enter key button press."""
248 self._press_and_release_keys('none', 'ctrl')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700249
250
Chrome Bot9a1137d2011-07-19 14:35:00 -0700251 def enter_key(self):
252 """Simulate Enter key button press."""
Todd Broch9dfc3a82011-11-01 08:09:28 -0700253 self._press_and_release_keys('enter', 'none')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700254
255
Chrome Bot9a1137d2011-07-19 14:35:00 -0700256 def refresh_key(self):
257 """Simulate Refresh key (F3) button press."""
Todd Broch9dfc3a82011-11-01 08:09:28 -0700258 self._press_and_release_keys('none', 'refresh')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700259
260
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800261 def ctrl_refresh_key(self):
262 """Simulate Ctrl and Refresh (F3) simultaneous press.
263
264 This key combination is an alternative of Space key.
265 """
266 self._press_and_release_keys('ctrl_r', 'refresh')
267
268
Chrome Bot9a1137d2011-07-19 14:35:00 -0700269 def imaginary_key(self):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700270 """Simulate imaginary key button press.
271
272 Maps to a key that doesn't physically exist.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700273 """
Todd Broch9dfc3a82011-11-01 08:09:28 -0700274 self._press_and_release_keys('none', 'unused')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700275
276
Craig Harrison6b36b122011-06-28 17:58:43 -0700277 def enable_recovery_mode(self):
278 """Enable recovery mode on device."""
279 self.set('rec_mode', 'on')
280
281
282 def disable_recovery_mode(self):
283 """Disable recovery mode on device."""
284 self.set('rec_mode', 'off')
285
286
287 def enable_development_mode(self):
288 """Enable development mode on device."""
289 self.set('dev_mode', 'on')
290
291
292 def disable_development_mode(self):
293 """Disable development mode on device."""
294 self.set('dev_mode', 'off')
295
Chris Sosa8ee1d592011-08-14 16:50:31 -0700296 def enable_usb_hub(self, host=False):
Craig Harrison86b1a572011-08-12 11:26:52 -0700297 """Enable Servo's USB/ethernet hub.
298
Chris Sosa8ee1d592011-08-14 16:50:31 -0700299 This is equivalent to plugging in the USB devices attached to Servo to
300 the host (if |host| is True) or dut (if |host| is False).
301 For host=False, requires that the USB out on the servo board is
302 connected to a USB in port on the target device. Servo's USB ports are
303 labeled DUT_HUB_USB1 and DUT_HUB_USB2. Servo's ethernet port is also
304 connected to this hub. Servo's USB port DUT_HUB_IN is the output of the
305 hub.
Craig Harrison86b1a572011-08-12 11:26:52 -0700306 """
307 self.set('dut_hub_pwren', 'on')
Chris Sosa8ee1d592011-08-14 16:50:31 -0700308 if host:
Todd Broch9753bd42012-03-21 10:15:08 -0700309 self.set('usb_mux_oe1', 'on')
310 self.set('usb_mux_sel1', 'servo_sees_usbkey')
Chris Sosa8ee1d592011-08-14 16:50:31 -0700311 else:
Todd Broch9753bd42012-03-21 10:15:08 -0700312 self.set('dut_hub_sel', 'dut_sees_hub')
Chris Sosa8ee1d592011-08-14 16:50:31 -0700313
Craig Harrison86b1a572011-08-12 11:26:52 -0700314 self.set('dut_hub_on', 'yes')
315
316
317 def disable_usb_hub(self):
318 """Disable Servo's USB/ethernet hub.
319
320 This is equivalent to unplugging the USB devices attached to Servo.
321 """
322 self.set('dut_hub_on', 'no')
323
324
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700325 def boot_devmode(self):
326 """Boot a dev-mode device that is powered off."""
Tom Wai-Hong Tam23869102012-01-17 15:41:30 +0800327 self.power_short_press()
Craig Harrison48997262011-06-27 14:31:10 -0700328 self.pass_devmode()
329
330
331 def pass_devmode(self):
332 """Pass through boot screens in dev-mode."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700333 time.sleep(Servo.BOOT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700334 self.ctrl_d()
Chrome Bot9a1137d2011-07-19 14:35:00 -0700335 time.sleep(Servo.BOOT_DELAY)
Craig Harrison48997262011-06-27 14:31:10 -0700336
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700337
Craig Harrison6b36b122011-06-28 17:58:43 -0700338 def cold_reset(self):
339 """Perform a cold reset of the EC.
340
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700341 This has the side effect of shutting off the device. The
342 device is guaranteed to be off at the end of this call.
Craig Harrison6b36b122011-06-28 17:58:43 -0700343 """
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700344 # After the reset, give the EC the time it needs to
345 # re-initialize.
Craig Harrison6b36b122011-06-28 17:58:43 -0700346 self.set('cold_reset', 'on')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700347 time.sleep(Servo.SERVO_SEND_SIGNAL_DELAY)
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700348 self.set('cold_reset', 'off')
349 time.sleep(self._EC_RESET_DELAY)
Craig Harrison6b36b122011-06-28 17:58:43 -0700350
351
352 def warm_reset(self):
353 """Perform a warm reset of the device.
354
355 Has the side effect of restarting the device.
356 """
357 self.set('warm_reset', 'on')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700358 time.sleep(Servo.SERVO_SEND_SIGNAL_DELAY)
Craig Harrison6b36b122011-06-28 17:58:43 -0700359 self.set('warm_reset', 'off')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700360
361
Todd Brochefe72cb2012-07-11 19:58:53 -0700362 def _get_xmlrpclib_exception(self, xmlexc):
363 """Get meaningful exception string from xmlrpc.
364
365 Args:
366 xmlexc: xmlrpclib.Fault object
367
368 xmlrpclib.Fault.faultString has the following format:
369
370 <type 'exception type'>:'actual error message'
371
372 Parse and return the real exception from the servod side instead of the
373 less meaningful one like,
374 <Fault 1: "<type 'exceptions.AttributeError'>:'tca6416' object has no
375 attribute 'hw_driver'">
376
377 Returns:
378 string of underlying exception raised in servod.
379 """
380 return re.sub('^.*>:', '', xmlexc.faultString)
381
382
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700383 def get(self, gpio_name):
384 """Get the value of a gpio from Servod."""
385 assert gpio_name
Todd Brochefe72cb2012-07-11 19:58:53 -0700386 try:
387 return self._server.get(gpio_name)
388 except xmlrpclib.Fault as e:
389 err_msg = "Getting '%s' :: %s" % \
390 (gpio_name, self._get_xmlrpclib_exception(e))
391 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700392
393
394 def set(self, gpio_name, gpio_value):
395 """Set and check the value of a gpio using Servod."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700396 self.set_nocheck(gpio_name, gpio_value)
Todd Brochcf7c6652012-02-24 13:03:59 -0800397 retry_count = Servo.GET_RETRY_MAX
398 while gpio_value != self.get(gpio_name) and retry_count:
399 logging.warn("%s != %s, retry %d", gpio_name, gpio_value,
400 retry_count)
401 retry_count -= 1
402 time.sleep(Servo.SHORT_DELAY)
403 if not retry_count:
404 assert gpio_value == self.get(gpio_name), \
405 'Servo failed to set %s to %s' % (gpio_name, gpio_value)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700406
407
408 def set_nocheck(self, gpio_name, gpio_value):
409 """Set the value of a gpio using Servod."""
410 assert gpio_name and gpio_value
Todd Broch9753bd42012-03-21 10:15:08 -0700411 logging.info('Setting %s to %s', gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700412 try:
413 self._server.set(gpio_name, gpio_value)
414 except xmlrpclib.Fault as e:
415 err_msg = "Setting '%s' to '%s' :: %s" % \
416 (gpio_name, gpio_value, self._get_xmlrpclib_exception(e))
417 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700418
419
Jon Salzc88e5b62011-11-30 14:38:54 +0800420 # TODO(waihong) It may fail if multiple servo's are connected to the same
421 # host. Should look for a better way, like the USB serial name, to identify
422 # the USB device.
Simran Basi741b5d42012-05-18 11:27:15 -0700423 # TODO(sbasi) Remove this code from autoserv once firmware tests have been
424 # updated.
Jon Salzc88e5b62011-11-30 14:38:54 +0800425 def probe_host_usb_dev(self):
426 """Probe the USB disk device plugged-in the servo from the host side.
427
428 It tries to switch the USB mux to make the host unable to see the
429 USB disk and compares the result difference.
430
431 This only works if the servo is attached to the local host.
432
433 Returns:
434 A string of USB disk path, like '/dev/sdb', or None if not existed.
435 """
436 cmd = 'ls /dev/sd[a-z]'
437 original_value = self.get('usb_mux_sel1')
438
439 # Make the host unable to see the USB disk.
440 if original_value != 'dut_sees_usbkey':
441 self.set('usb_mux_sel1', 'dut_sees_usbkey')
442 time.sleep(self.USB_DETECTION_DELAY)
443 no_usb_set = set(utils.system_output(cmd, ignore_status=True).split())
444
445 # Make the host able to see the USB disk.
446 self.set('usb_mux_sel1', 'servo_sees_usbkey')
447 time.sleep(self.USB_DETECTION_DELAY)
448 has_usb_set = set(utils.system_output(cmd, ignore_status=True).split())
449
450 # Back to its original value.
451 if original_value != 'servo_sees_usbkey':
452 self.set('usb_mux_sel1', original_value)
453 time.sleep(self.USB_DETECTION_DELAY)
454
455 diff_set = has_usb_set - no_usb_set
456 if len(diff_set) == 1:
457 return diff_set.pop()
458 else:
459 return None
460
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 """
477 # Turn the device off. This should happen before USB key detection, to
478 # prevent a recovery destined DUT from sensing the USB key due to the
479 # autodetection procedure.
480 self.initialize_dut(cold_reset=True)
481
482 # Set up Servo's usb mux.
483 self.set('prtctl4_pwren', 'on')
484 self.enable_usb_hub(host=True)
485 if image_path:
486 logging.info('Searching for usb device and copying image to it.')
487 if not self._server.download_image_to_usb(image_path):
488 logging.error('Failed to transfer requested image to USB. '
489 'Please take a look at Servo Logs.')
490 raise error.AutotestError('Download image to usb failed.')
491 if make_image_noninteractive:
492 logging.info('Making image noninteractive')
493 if not self._server.make_image_noninteractive():
494 logging.error('Failed to make image noninteractive. '
495 'Please take a look at Servo Logs.')
496
497
Simran Basi741b5d42012-05-18 11:27:15 -0700498 def install_recovery_image(self, image_path=None,
499 wait_timeout=RECOVERY_INSTALL_DELAY,
500 make_image_noninteractive=False,
501 host=None):
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.
Gilad Arnold9df73de2012-03-14 09:35:08 -0700511 wait_timeout: How long to wait for completion; default is
512 determined by a constant.
Simran Basi741b5d42012-05-18 11:27:15 -0700513 make_image_noninteractive: Make the recovery image noninteractive,
514 therefore the DUT will reboot
515 automatically after installation.
516 host: Host object for the DUT that the installation process is
517 running on. If provided, will wait to see if the host is back
518 up after starting recovery mode.
Jon Salzc88e5b62011-11-30 14:38:54 +0800519 """
Mike Truty49153d82012-08-21 22:27:30 -0500520 self.image_to_servo_usb(image_path, make_image_noninteractive)
Jon Salzc88e5b62011-11-30 14:38:54 +0800521
522 # Boot in recovery mode.
523 try:
Jon Salzc88e5b62011-11-30 14:38:54 +0800524 self.enable_recovery_mode()
Tom Wai-Hong Tam23869102012-01-17 15:41:30 +0800525 self.power_short_press()
Jon Salzc88e5b62011-11-30 14:38:54 +0800526 time.sleep(Servo.RECOVERY_BOOT_DELAY)
Tom Wai-Hong Tam922ed052012-01-09 12:27:52 +0800527 self.set('usb_mux_sel1', 'dut_sees_usbkey')
Jon Salzc88e5b62011-11-30 14:38:54 +0800528 self.disable_recovery_mode()
529
Simran Basi741b5d42012-05-18 11:27:15 -0700530 if host:
Jon Salzc88e5b62011-11-30 14:38:54 +0800531 logging.info('Running the recovery process on the DUT. '
Simran Basi741b5d42012-05-18 11:27:15 -0700532 'Will wait up to %d seconds for recovery to '
533 'complete.', wait_timeout)
534 start_time = time.time()
535 # Wait for the host to come up.
536 if host.wait_up(timeout=wait_timeout):
537 logging.info('Recovery process completed successfully in '
538 '%d seconds.', time.time() - start_time)
539 else:
540 logger.error('Host failed to come back up in the allotted '
541 'time: %d seconds.', wait_timeout)
Jon Salzc88e5b62011-11-30 14:38:54 +0800542 logging.info('Removing the usb key from the DUT.')
543 self.disable_usb_hub()
Jon Salzc88e5b62011-11-30 14:38:54 +0800544 except:
545 # In case anything went wrong we want to make sure to do a clean
546 # reset.
547 self.disable_recovery_mode()
548 self.warm_reset()
549 raise
550
551
Todd Brochf24d2782011-08-19 10:55:41 -0700552 def _connect_servod(self, servo_host, servo_port):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700553 """Connect to the Servod process with XMLRPC.
554
555 Args:
556 servo_port: Port the Servod process is listening on.
557 """
Todd Brochf24d2782011-08-19 10:55:41 -0700558 remote = 'http://%s:%s' % (servo_host, servo_port)
Todd Broch96d83aa2011-08-29 14:37:38 -0700559 self._server = xmlrpclib.ServerProxy(remote)
Todd Brochf24d2782011-08-19 10:55:41 -0700560 try:
561 self._server.echo("ping-test")
Todd Broch96d83aa2011-08-29 14:37:38 -0700562 except:
563 logging.error('Connection to servod failed')
Todd Brochf24d2782011-08-19 10:55:41 -0700564 raise