blob: 404d4177048c7d36aa92d448484081afe204097a [file] [log] [blame]
Craig Harrison2b6c6fc2011-06-23 10:34:02 -07001# Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
2# 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 Broch9753bd42012-03-21 10:15:08 -07008import logging, os, select, subprocess, sys, time, xmlrpclib
Jon Salzc88e5b62011-11-30 14:38:54 +08009from autotest_lib.client.bin import utils as client_utils
10from autotest_lib.server import utils
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070011
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070012class Servo:
13 """Manages control of a Servo board.
14
15 Servo is a board developed by hardware group to aide in the debug and
16 control of various partner devices. Servo's features include the simulation
17 of pressing the power button, closing the lid, and pressing Ctrl-d. This
18 class manages setting up and communicating with a servo demon (servod)
19 process. It provides both high-level functions for common servo tasks and
20 low-level functions for directly setting and reading gpios.
21 """
22
23 _server = None
24 _servod = None
25
Chrome Bot9a1137d2011-07-19 14:35:00 -070026 # Power button press delays in seconds.
27 LONG_DELAY = 8
28 SHORT_DELAY = 0.1
29 NORMAL_TRANSITION_DELAY = 1.2
Todd Broch31c82502011-08-29 08:14:39 -070030 # Maximum number of times to re-read power button on release.
31 RELEASE_RETRY_MAX = 5
Todd Brochcf7c6652012-02-24 13:03:59 -080032 GET_RETRY_MAX = 10
Chrome Bot9a1137d2011-07-19 14:35:00 -070033
34 # Delays to deal with computer transitions.
35 SLEEP_DELAY = 6
36 BOOT_DELAY = 10
Jon Salzc88e5b62011-11-30 14:38:54 +080037 RECOVERY_BOOT_DELAY = 30
38 RECOVERY_INSTALL_DELAY = 180
Chrome Bot9a1137d2011-07-19 14:35:00 -070039
40 # Servo-specific delays.
41 MAX_SERVO_STARTUP_DELAY = 10
42 SERVO_SEND_SIGNAL_DELAY = 0.5
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070043
Jon Salzc88e5b62011-11-30 14:38:54 +080044 # Time between an usb disk plugged-in and detected in the system.
45 USB_DETECTION_DELAY = 10
46
Todd Broch9753bd42012-03-21 10:15:08 -070047 KEY_MATRIX = {
48 'm1': {'ctrl_r': ['0', '0'], 'd': ['0', '1'],
49 'enter': ['1', '0'], 'none': ['1', '1']},
50 'm2': {'ctrl': ['0', '0'], 'refresh': ['0', '1'],
51 'unused': ['1', '0'], 'none': ['1', '1']}
52 }
Chris Masone6a0680f2012-03-02 08:40:00 -080053
54 @staticmethod
55 def create_simple(device_under_test_hostname):
56 """Instantiate a Servo for |device_under_test_hostname| in the lab.
57
58 Assuming that |device_under_test_hostname| is a device in the CrOS
59 test lab, create and return a Servo object pointed at the
60 servo attached to that DUT. The servo in the test lab is assumed to
61 already have servod up and running on it.
62
63 @param device_under_test_hostname: device whose servo we want to target.
64 @return an appropriately configured Servo
65 """
66 host_parts = device_under_test_hostname.split('.')
67 host_parts[0] = host_parts[0] + '-servo'
68 return Servo(servo_host='.'.join(host_parts))
69
70
71 def __init__(self, servo_host=None, servo_port=9999,
Todd Broch9753bd42012-03-21 10:15:08 -070072 xml_config=[], servo_vid=None, servo_pid=None,
Gilad Arnold9df73de2012-03-14 09:35:08 -070073 servo_serial=None, cold_reset=False, servo_interfaces=[]):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070074 """Sets up the servo communication infrastructure.
75
76 Args:
Todd Brochf24d2782011-08-19 10:55:41 -070077 servo_host: Host the servod process should listen on.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070078 servo_port: Port the servod process should listen on.
Tom Wai-Hong Tam5fc2c792011-11-03 13:05:39 +080079 xml_config: A list of configuration XML files for servod.
Todd Broch5fd6bc02011-07-20 15:53:37 -070080 servo_vid: USB vendor id of servo.
81 servo_pid: USB product id of servo.
82 servo_serial: USB serial id in device descriptor to host to
83 distinguish and control multiple servos. Note servo's EEPROM must
84 be programmed to use this feature.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070085 cold_reset: If True, cold reset device and boot during init,
86 otherwise perform init with device running.
87 """
88 # launch servod
Todd Brochf24d2782011-08-19 10:55:41 -070089 self._servod = None
Todd Broch5fd6bc02011-07-20 15:53:37 -070090
Todd Broch96d83aa2011-08-29 14:37:38 -070091 # TODO(tbroch) In case where servo h/w is not connected to the host
92 # running the autotest server, servod will need to be launched by
93 # another means (udev likely). For now we can assume servo_host ==
94 # localhost as one hueristic way of determining this.
95 if not servo_host or servo_host == 'localhost':
96 servo_host = 'localhost'
97 self._launch_servod(servo_host, servo_port, xml_config, servo_vid,
Gilad Arnold9df73de2012-03-14 09:35:08 -070098 servo_pid, servo_serial, servo_interfaces)
Todd Broch96d83aa2011-08-29 14:37:38 -070099 else:
100 logging.info('servod should already be running on host = %s',
101 servo_host)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700102
Chrome Bot9a1137d2011-07-19 14:35:00 -0700103 self._do_cold_reset = cold_reset
Todd Brochf24d2782011-08-19 10:55:41 -0700104 self._connect_servod(servo_host, servo_port)
Chrome Bot9a1137d2011-07-19 14:35:00 -0700105
106
107 def initialize_dut(self):
108 """Initializes a dut for testing purposes."""
109 if self._do_cold_reset:
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700110 self._init_seq_cold_reset_devmode()
111 else:
112 self._init_seq()
113
114
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700115 def power_long_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700116 """Simulate a long power button press."""
117 self.power_key(Servo.LONG_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700118
119
120 def power_normal_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700121 """Simulate a normal power button press."""
122 self.power_key()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700123
124
125 def power_short_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700126 """Simulate a short power button press."""
127 self.power_key(Servo.SHORT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700128
129
Chrome Bot9a1137d2011-07-19 14:35:00 -0700130 def power_key(self, secs=NORMAL_TRANSITION_DELAY):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700131 """Simulate a power button press.
132
133 Args:
134 secs: Time in seconds to simulate the keypress.
135 """
Craig Harrison6b36b122011-06-28 17:58:43 -0700136 self.set_nocheck('pwr_button', 'press')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700137 time.sleep(secs)
Todd Broch31c82502011-08-29 08:14:39 -0700138 self.set_nocheck('pwr_button', 'release')
139 # TODO(tbroch) Different systems have different release times on the
140 # power button that this loop addresses. Longer term we may want to
141 # make this delay platform specific.
142 retry = 1
143 while True:
144 value = self.get('pwr_button')
145 if value == 'release' or retry > Servo.RELEASE_RETRY_MAX:
146 break
Todd Broch9753bd42012-03-21 10:15:08 -0700147 logging.info('Waiting for pwr_button to release, retry %d.', retry)
Todd Broch31c82502011-08-29 08:14:39 -0700148 retry += 1
149 time.sleep(Servo.SHORT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700150
151
152 def lid_open(self):
153 """Simulate opening the lid."""
Craig Harrison48997262011-06-27 14:31:10 -0700154 self.set_nocheck('lid_open', 'yes')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700155
156
157 def lid_close(self):
Craig Harrison48997262011-06-27 14:31:10 -0700158 """Simulate closing the lid.
159
160 Waits 6 seconds to ensure the device is fully asleep before returning.
161 """
162 self.set_nocheck('lid_open', 'no')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700163 time.sleep(Servo.SLEEP_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700164
165
Todd Broch9753bd42012-03-21 10:15:08 -0700166 def _press_and_release_keys(self, m1, m2,
167 press_secs=SERVO_SEND_SIGNAL_DELAY):
Todd Broch9dfc3a82011-11-01 08:09:28 -0700168 """Simulate button presses."""
Todd Broch9753bd42012-03-21 10:15:08 -0700169 # set keys to none
170 (m2_a1, m2_a0) = self.KEY_MATRIX['m2']['none']
171 (m1_a1, m1_a0) = self.KEY_MATRIX['m1']['none']
172 self.set_nocheck('kbd_m2_a0', m2_a0)
173 self.set_nocheck('kbd_m2_a1', m2_a1)
174 self.set_nocheck('kbd_m1_a0', m1_a0)
175 self.set_nocheck('kbd_m1_a1', m1_a1)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700176
Todd Broch9753bd42012-03-21 10:15:08 -0700177 (m2_a1, m2_a0) = self.KEY_MATRIX['m2'][m2]
178 (m1_a1, m1_a0) = self.KEY_MATRIX['m1'][m1]
Todd Broch9dfc3a82011-11-01 08:09:28 -0700179 self.set_nocheck('kbd_en', 'on')
Todd Broch9753bd42012-03-21 10:15:08 -0700180 self.set_nocheck('kbd_m2_a0', m2_a0)
181 self.set_nocheck('kbd_m2_a1', m2_a1)
182 self.set_nocheck('kbd_m1_a0', m1_a0)
183 self.set_nocheck('kbd_m1_a1', m1_a1)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700184 time.sleep(press_secs)
185 self.set_nocheck('kbd_en', 'off')
186
187
Chrome Bot9a1137d2011-07-19 14:35:00 -0700188 def ctrl_d(self):
189 """Simulate Ctrl-d simultaneous button presses."""
Todd Broch9dfc3a82011-11-01 08:09:28 -0700190 self._press_and_release_keys('d', 'ctrl')
191
192
Todd Broch9753bd42012-03-21 10:15:08 -0700193 def ctrl_enter(self):
194 """Simulate Ctrl-enter simultaneous button presses."""
195 self._press_and_release_keys('enter', 'ctrl')
196
197
Todd Broch9dfc3a82011-11-01 08:09:28 -0700198 def d_key(self):
199 """Simulate Enter key button press."""
200 self._press_and_release_keys('d', 'none')
201
202
203 def ctrl_key(self):
204 """Simulate Enter key button press."""
205 self._press_and_release_keys('none', 'ctrl')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700206
207
Chrome Bot9a1137d2011-07-19 14:35:00 -0700208 def enter_key(self):
209 """Simulate Enter key button press."""
Todd Broch9dfc3a82011-11-01 08:09:28 -0700210 self._press_and_release_keys('enter', 'none')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700211
212
Chrome Bot9a1137d2011-07-19 14:35:00 -0700213 def refresh_key(self):
214 """Simulate Refresh key (F3) button press."""
Todd Broch9dfc3a82011-11-01 08:09:28 -0700215 self._press_and_release_keys('none', 'refresh')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700216
217
Chrome Bot9a1137d2011-07-19 14:35:00 -0700218 def imaginary_key(self):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700219 """Simulate imaginary key button press.
220
221 Maps to a key that doesn't physically exist.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700222 """
Todd Broch9dfc3a82011-11-01 08:09:28 -0700223 self._press_and_release_keys('none', 'unused')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700224
225
Craig Harrison6b36b122011-06-28 17:58:43 -0700226 def enable_recovery_mode(self):
227 """Enable recovery mode on device."""
228 self.set('rec_mode', 'on')
229
230
231 def disable_recovery_mode(self):
232 """Disable recovery mode on device."""
233 self.set('rec_mode', 'off')
234
235
236 def enable_development_mode(self):
237 """Enable development mode on device."""
238 self.set('dev_mode', 'on')
239
240
241 def disable_development_mode(self):
242 """Disable development mode on device."""
243 self.set('dev_mode', 'off')
244
Chris Sosa8ee1d592011-08-14 16:50:31 -0700245 def enable_usb_hub(self, host=False):
Craig Harrison86b1a572011-08-12 11:26:52 -0700246 """Enable Servo's USB/ethernet hub.
247
Chris Sosa8ee1d592011-08-14 16:50:31 -0700248 This is equivalent to plugging in the USB devices attached to Servo to
249 the host (if |host| is True) or dut (if |host| is False).
250 For host=False, requires that the USB out on the servo board is
251 connected to a USB in port on the target device. Servo's USB ports are
252 labeled DUT_HUB_USB1 and DUT_HUB_USB2. Servo's ethernet port is also
253 connected to this hub. Servo's USB port DUT_HUB_IN is the output of the
254 hub.
Craig Harrison86b1a572011-08-12 11:26:52 -0700255 """
256 self.set('dut_hub_pwren', 'on')
Chris Sosa8ee1d592011-08-14 16:50:31 -0700257 if host:
Todd Broch9753bd42012-03-21 10:15:08 -0700258 self.set('usb_mux_oe1', 'on')
259 self.set('usb_mux_sel1', 'servo_sees_usbkey')
Chris Sosa8ee1d592011-08-14 16:50:31 -0700260 else:
Todd Broch9753bd42012-03-21 10:15:08 -0700261 self.set('dut_hub_sel', 'dut_sees_hub')
Chris Sosa8ee1d592011-08-14 16:50:31 -0700262
Craig Harrison86b1a572011-08-12 11:26:52 -0700263 self.set('dut_hub_on', 'yes')
264
265
266 def disable_usb_hub(self):
267 """Disable Servo's USB/ethernet hub.
268
269 This is equivalent to unplugging the USB devices attached to Servo.
270 """
271 self.set('dut_hub_on', 'no')
272
273
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700274 def boot_devmode(self):
275 """Boot a dev-mode device that is powered off."""
Tom Wai-Hong Tam23869102012-01-17 15:41:30 +0800276 self.power_short_press()
Craig Harrison48997262011-06-27 14:31:10 -0700277 self.pass_devmode()
278
279
280 def pass_devmode(self):
281 """Pass through boot screens in dev-mode."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700282 time.sleep(Servo.BOOT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700283 self.ctrl_d()
Chrome Bot9a1137d2011-07-19 14:35:00 -0700284 time.sleep(Servo.BOOT_DELAY)
Craig Harrison48997262011-06-27 14:31:10 -0700285
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700286
Craig Harrison6b36b122011-06-28 17:58:43 -0700287 def cold_reset(self):
288 """Perform a cold reset of the EC.
289
Chris Sosa8ee1d592011-08-14 16:50:31 -0700290 Has the side effect of shutting off the device. Device is guaranteed
291 to be off at the end of this call.
Craig Harrison6b36b122011-06-28 17:58:43 -0700292 """
293 self.set('cold_reset', 'on')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700294 time.sleep(Servo.SERVO_SEND_SIGNAL_DELAY)
Craig Harrison6b36b122011-06-28 17:58:43 -0700295
296
297 def warm_reset(self):
298 """Perform a warm reset of the device.
299
300 Has the side effect of restarting the device.
301 """
302 self.set('warm_reset', 'on')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700303 time.sleep(Servo.SERVO_SEND_SIGNAL_DELAY)
Craig Harrison6b36b122011-06-28 17:58:43 -0700304 self.set('warm_reset', 'off')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700305
306
307 def get(self, gpio_name):
308 """Get the value of a gpio from Servod."""
309 assert gpio_name
310 return self._server.get(gpio_name)
311
312
313 def set(self, gpio_name, gpio_value):
314 """Set and check the value of a gpio using Servod."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700315 self.set_nocheck(gpio_name, gpio_value)
Todd Brochcf7c6652012-02-24 13:03:59 -0800316 retry_count = Servo.GET_RETRY_MAX
317 while gpio_value != self.get(gpio_name) and retry_count:
318 logging.warn("%s != %s, retry %d", gpio_name, gpio_value,
319 retry_count)
320 retry_count -= 1
321 time.sleep(Servo.SHORT_DELAY)
322 if not retry_count:
323 assert gpio_value == self.get(gpio_name), \
324 'Servo failed to set %s to %s' % (gpio_name, gpio_value)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700325
326
327 def set_nocheck(self, gpio_name, gpio_value):
328 """Set the value of a gpio using Servod."""
329 assert gpio_name and gpio_value
Todd Broch9753bd42012-03-21 10:15:08 -0700330 logging.info('Setting %s to %s', gpio_name, gpio_value)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700331 self._server.set(gpio_name, gpio_value)
332
333
Jon Salzc88e5b62011-11-30 14:38:54 +0800334 # TODO(waihong) It may fail if multiple servo's are connected to the same
335 # host. Should look for a better way, like the USB serial name, to identify
336 # the USB device.
337 def probe_host_usb_dev(self):
338 """Probe the USB disk device plugged-in the servo from the host side.
339
340 It tries to switch the USB mux to make the host unable to see the
341 USB disk and compares the result difference.
342
343 This only works if the servo is attached to the local host.
344
345 Returns:
346 A string of USB disk path, like '/dev/sdb', or None if not existed.
347 """
348 cmd = 'ls /dev/sd[a-z]'
349 original_value = self.get('usb_mux_sel1')
350
351 # Make the host unable to see the USB disk.
352 if original_value != 'dut_sees_usbkey':
353 self.set('usb_mux_sel1', 'dut_sees_usbkey')
354 time.sleep(self.USB_DETECTION_DELAY)
355 no_usb_set = set(utils.system_output(cmd, ignore_status=True).split())
356
357 # Make the host able to see the USB disk.
358 self.set('usb_mux_sel1', 'servo_sees_usbkey')
359 time.sleep(self.USB_DETECTION_DELAY)
360 has_usb_set = set(utils.system_output(cmd, ignore_status=True).split())
361
362 # Back to its original value.
363 if original_value != 'servo_sees_usbkey':
364 self.set('usb_mux_sel1', original_value)
365 time.sleep(self.USB_DETECTION_DELAY)
366
367 diff_set = has_usb_set - no_usb_set
368 if len(diff_set) == 1:
369 return diff_set.pop()
370 else:
371 return None
372
373
Tom Wai-Hong Tama07115e2012-01-09 12:27:01 +0800374 def install_recovery_image(self, image_path=None, usb_dev=None,
Gilad Arnold9df73de2012-03-14 09:35:08 -0700375 wait_for_completion=True,
376 wait_timeout=RECOVERY_INSTALL_DELAY):
Jon Salzc88e5b62011-11-30 14:38:54 +0800377 """Install the recovery image specied by the path onto the DUT.
378
379 This method uses google recovery mode to install a recovery image
380 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 +0800381 board specified by the usb_dev. If no image path is specified
Jon Salzc88e5b62011-11-30 14:38:54 +0800382 we use the recovery image already on the usb image.
383
384 Args:
385 image_path: Path on the host to the recovery image.
Tom Wai-Hong Tama07115e2012-01-09 12:27:01 +0800386 usb_dev: When servo_sees_usbkey is enabled, which dev
387 e.g. /dev/sdb will the usb key show up as.
388 If None, detects it automatically.
Jon Salzc88e5b62011-11-30 14:38:54 +0800389 wait_for_completion: Whether to wait for completion of the
390 factory install and disable the USB hub
391 before returning. Currently this is just
Gilad Arnold9df73de2012-03-14 09:35:08 -0700392 waiting for a predetermined timeout period.
393 wait_timeout: How long to wait for completion; default is
394 determined by a constant.
Jon Salzc88e5b62011-11-30 14:38:54 +0800395 """
Gilad Arnold9df73de2012-03-14 09:35:08 -0700396 # Turn the device off. This should happen before USB key detection, to
397 # prevent a recovery destined DUT from sensing the USB key due to the
398 # autodetection procedure.
399 self.power_long_press()
400
Jon Salzc88e5b62011-11-30 14:38:54 +0800401 # Set up Servo's usb mux.
402 self.set('prtctl4_pwren', 'on')
403 self.enable_usb_hub(host=True)
Tom Wai-Hong Tama07115e2012-01-09 12:27:01 +0800404 if image_path:
405 if not usb_dev:
Gilad Arnold9df73de2012-03-14 09:35:08 -0700406 logging.info('Detecting USB stick device...')
Tom Wai-Hong Tama07115e2012-01-09 12:27:01 +0800407 usb_dev = self.probe_host_usb_dev()
Gilad Arnold9df73de2012-03-14 09:35:08 -0700408 if not usb_dev:
Todd Broch9753bd42012-03-21 10:15:08 -0700409 raise Exception('USB device not found')
410 logging.info('Found %s', usb_dev)
Tom Wai-Hong Tama07115e2012-01-09 12:27:01 +0800411 logging.info('Installing image onto usb stick. '
412 'This takes a while...')
413 client_utils.poll_for_condition(
414 lambda: os.path.exists(usb_dev),
415 timeout=Servo.USB_DETECTION_DELAY,
416 desc="%s exists" % usb_dev)
Gilad Arnold9df73de2012-03-14 09:35:08 -0700417 utils.system('sudo dd if=%s of=%s bs=4M status=noxfer' %
418 (image_path, usb_dev))
Jon Salzc88e5b62011-11-30 14:38:54 +0800419
420 # Boot in recovery mode.
421 try:
Jon Salzc88e5b62011-11-30 14:38:54 +0800422 self.enable_recovery_mode()
Tom Wai-Hong Tam23869102012-01-17 15:41:30 +0800423 self.power_short_press()
Jon Salzc88e5b62011-11-30 14:38:54 +0800424 time.sleep(Servo.RECOVERY_BOOT_DELAY)
Tom Wai-Hong Tam922ed052012-01-09 12:27:52 +0800425 self.set('usb_mux_sel1', 'dut_sees_usbkey')
Jon Salzc88e5b62011-11-30 14:38:54 +0800426 self.disable_recovery_mode()
427
428 if wait_for_completion:
429 # Enable recovery installation.
430 logging.info('Running the recovery process on the DUT. '
431 'Waiting %d seconds for recovery to complete ...',
Gilad Arnold9df73de2012-03-14 09:35:08 -0700432 wait_timeout)
433 time.sleep(wait_timeout)
Jon Salzc88e5b62011-11-30 14:38:54 +0800434
435 # Go back into normal mode and reboot.
436 # Machine automatically reboots after the usb key is removed.
437 logging.info('Removing the usb key from the DUT.')
438 self.disable_usb_hub()
439 time.sleep(Servo.BOOT_DELAY)
440 except:
441 # In case anything went wrong we want to make sure to do a clean
442 # reset.
443 self.disable_recovery_mode()
444 self.warm_reset()
445 raise
446
447
Craig Harrison6b36b122011-06-28 17:58:43 -0700448 def _init_seq_cold_reset_devmode(self):
449 """Cold reset, init device, and boot in dev-mode."""
450 self.cold_reset()
451 self._init_seq()
452 self.set('dev_mode', 'on')
453 self.boot_devmode()
454
455
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700456 def __del__(self):
457 """Kill the Servod process."""
Todd Brochf24d2782011-08-19 10:55:41 -0700458 if not self._servod:
459 return
460
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700461 # kill servod one way or another
462 try:
463 # won't work without superuser privileges
464 self._servod.terminate()
465 except:
466 # should work without superuser privileges
467 assert subprocess.call(['sudo', 'kill', str(self._servod.pid)])
468
469
Todd Brochf24d2782011-08-19 10:55:41 -0700470 def _launch_servod(self, servo_host, servo_port, xml_config, servo_vid,
Gilad Arnold9df73de2012-03-14 09:35:08 -0700471 servo_pid, servo_serial, servo_interfaces):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700472 """Launch the servod process.
473
474 Args:
Todd Brochf24d2782011-08-19 10:55:41 -0700475 servo_host: Host to start servod listening on.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700476 servo_port: Port to start servod listening on.
Tom Wai-Hong Tam5fc2c792011-11-03 13:05:39 +0800477 xml_config: A list of XML configuration files for servod.
Todd Broch5fd6bc02011-07-20 15:53:37 -0700478 servo_vid: USB vendor id of servo.
479 servo_pid: USB product id of servo.
480 servo_serial: USB serial id in device descriptor to host to
481 distinguish and control multiple servos. Note servo's EEPROM must
482 be programmed to use this feature.
Gilad Arnold9df73de2012-03-14 09:35:08 -0700483 servo_interfaces: a list of servo interface names out of 'gpio',
484 'i2c', 'uart', 'gpiouart' and 'dummy'.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700485 """
Tom Wai-Hong Tam5fc2c792011-11-03 13:05:39 +0800486 cmdlist = ['sudo', 'servod']
487 for config in xml_config:
488 cmdlist += ['-c', str(config)]
Todd Broch96d83aa2011-08-29 14:37:38 -0700489 if servo_host is not None:
490 cmdlist.append('--host=%s' % str(servo_host))
491 if servo_port is not None:
492 cmdlist.append('--port=%s' % str(servo_port))
Todd Broch5fd6bc02011-07-20 15:53:37 -0700493 if servo_vid is not None:
Todd Brochf24d2782011-08-19 10:55:41 -0700494 cmdlist.append('--vendor=%s' % str(servo_vid))
Todd Broch5fd6bc02011-07-20 15:53:37 -0700495 if servo_pid is not None:
Todd Brochf24d2782011-08-19 10:55:41 -0700496 cmdlist.append('--product=%s' % str(servo_pid))
Todd Broch5fd6bc02011-07-20 15:53:37 -0700497 if servo_serial is not None:
Todd Brochf24d2782011-08-19 10:55:41 -0700498 cmdlist.append('--serialname=%s' % str(servo_serial))
Gilad Arnold9df73de2012-03-14 09:35:08 -0700499 if servo_interfaces:
500 cmdlist.append('--interfaces=%s' % ' '.join(servo_interfaces))
Todd Broch9753bd42012-03-21 10:15:08 -0700501 logging.info('starting servod w/ cmd :: %s', ' '.join(cmdlist))
Todd Broch5fd6bc02011-07-20 15:53:37 -0700502 self._servod = subprocess.Popen(cmdlist, 0, None, None, None,
503 subprocess.PIPE)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700504 # wait for servod to initialize
Chrome Bot9a1137d2011-07-19 14:35:00 -0700505 timeout = Servo.MAX_SERVO_STARTUP_DELAY
Todd Broch9753bd42012-03-21 10:15:08 -0700506 start_time = time.time()
507 listening = False
508 while (time.time() - start_time) < timeout and \
509 self._servod.returncode is None:
510 (rfds, _, _) = select.select([self._servod.stderr], [], [], 0)
511 if len(rfds) > 0:
512 if 'Listening' in rfds[0].readline():
513 listening = True
514 break
515
516 if not listening:
517 logging.fatal("Unable to successfully launch servod")
518 sys.exit(-1)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700519
520
521 def _init_seq(self):
522 """Initiate starting state for servo."""
Todd Broch9753bd42012-03-21 10:15:08 -0700523 # TODO(tbroch) This is only a servo V1 control. Need to add ability in
524 # servod to easily identify version so I can make this conditional not
525 # try and fail quietly
526 try:
527 self.set('tx_dir', 'input')
528 except:
529 logging.warning("Failed to set tx_dir. This is ok if not servo V1")
530
531
Todd Broch6ec29432011-12-19 14:32:02 -0800532 # TODO(tbroch) Investigate method to determine DUT's type so we can
533 # conditionally set lid if applicable
534 self.set_nocheck('lid_open', 'yes')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700535 self.set('rec_mode', 'off')
536
537
Todd Brochf24d2782011-08-19 10:55:41 -0700538 def _connect_servod(self, servo_host, servo_port):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700539 """Connect to the Servod process with XMLRPC.
540
541 Args:
542 servo_port: Port the Servod process is listening on.
543 """
Todd Brochf24d2782011-08-19 10:55:41 -0700544 remote = 'http://%s:%s' % (servo_host, servo_port)
Todd Broch96d83aa2011-08-29 14:37:38 -0700545 self._server = xmlrpclib.ServerProxy(remote)
Todd Brochf24d2782011-08-19 10:55:41 -0700546 try:
547 self._server.echo("ping-test")
Todd Broch96d83aa2011-08-29 14:37:38 -0700548 except:
549 logging.error('Connection to servod failed')
Todd Brochf24d2782011-08-19 10:55:41 -0700550 raise