blob: 845316a5065bd15a2719c65a4960a1dc003138d5 [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 Broch5fd6bc02011-07-20 15:53:37 -07008import logging
Craig Harrison2b6c6fc2011-06-23 10:34:02 -07009import time
10import xmlrpclib
11import subprocess
12
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070013class Servo:
14 """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
24 _server = None
25 _servod = None
26
Chrome Bot9a1137d2011-07-19 14:35:00 -070027 # Power button press delays in seconds.
28 LONG_DELAY = 8
29 SHORT_DELAY = 0.1
30 NORMAL_TRANSITION_DELAY = 1.2
Todd Broch31c82502011-08-29 08:14:39 -070031 # Maximum number of times to re-read power button on release.
32 RELEASE_RETRY_MAX = 5
Chrome Bot9a1137d2011-07-19 14:35:00 -070033
34 # Delays to deal with computer transitions.
35 SLEEP_DELAY = 6
36 BOOT_DELAY = 10
Chris Sosa8ee1d592011-08-14 16:50:31 -070037 RECOVERY_INSTALL_DELAY = 300
Chrome Bot9a1137d2011-07-19 14:35:00 -070038
39 # Servo-specific delays.
40 MAX_SERVO_STARTUP_DELAY = 10
41 SERVO_SEND_SIGNAL_DELAY = 0.5
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070042
Todd Broch96d83aa2011-08-29 14:37:38 -070043 def __init__(self, servo_host=None, servo_port=None, xml_config='servo.xml',
Todd Brochf24d2782011-08-19 10:55:41 -070044 servo_vid=None, servo_pid=None, servo_serial=None,
45 cold_reset=False):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070046 """Sets up the servo communication infrastructure.
47
48 Args:
Todd Brochf24d2782011-08-19 10:55:41 -070049 servo_host: Host the servod process should listen on.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070050 servo_port: Port the servod process should listen on.
51 xml_config: Configuration XML file for servod.
Todd Broch5fd6bc02011-07-20 15:53:37 -070052 servo_vid: USB vendor id of servo.
53 servo_pid: USB product id of servo.
54 servo_serial: USB serial id in device descriptor to host to
55 distinguish and control multiple servos. Note servo's EEPROM must
56 be programmed to use this feature.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070057 cold_reset: If True, cold reset device and boot during init,
58 otherwise perform init with device running.
59 """
60 # launch servod
Todd Brochf24d2782011-08-19 10:55:41 -070061 self._servod = None
Todd Broch5fd6bc02011-07-20 15:53:37 -070062
Todd Broch96d83aa2011-08-29 14:37:38 -070063 if not servo_port:
64 servo_port = 9999
65 # TODO(tbroch) In case where servo h/w is not connected to the host
66 # running the autotest server, servod will need to be launched by
67 # another means (udev likely). For now we can assume servo_host ==
68 # localhost as one hueristic way of determining this.
69 if not servo_host or servo_host == 'localhost':
70 servo_host = 'localhost'
71 self._launch_servod(servo_host, servo_port, xml_config, servo_vid,
72 servo_pid, servo_serial)
73 else:
74 logging.info('servod should already be running on host = %s',
75 servo_host)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070076
Chrome Bot9a1137d2011-07-19 14:35:00 -070077 self._do_cold_reset = cold_reset
Todd Brochf24d2782011-08-19 10:55:41 -070078 self._connect_servod(servo_host, servo_port)
Chrome Bot9a1137d2011-07-19 14:35:00 -070079
80
81 def initialize_dut(self):
82 """Initializes a dut for testing purposes."""
83 if self._do_cold_reset:
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070084 self._init_seq_cold_reset_devmode()
85 else:
86 self._init_seq()
87
88
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070089 def power_long_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -070090 """Simulate a long power button press."""
91 self.power_key(Servo.LONG_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070092
93
94 def power_normal_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -070095 """Simulate a normal power button press."""
96 self.power_key()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070097
98
99 def power_short_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700100 """Simulate a short power button press."""
101 self.power_key(Servo.SHORT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700102
103
Chrome Bot9a1137d2011-07-19 14:35:00 -0700104 def power_key(self, secs=NORMAL_TRANSITION_DELAY):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700105 """Simulate a power button press.
106
107 Args:
108 secs: Time in seconds to simulate the keypress.
109 """
Craig Harrison6b36b122011-06-28 17:58:43 -0700110 self.set_nocheck('pwr_button', 'press')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700111 time.sleep(secs)
Todd Broch31c82502011-08-29 08:14:39 -0700112 self.set_nocheck('pwr_button', 'release')
113 # TODO(tbroch) Different systems have different release times on the
114 # power button that this loop addresses. Longer term we may want to
115 # make this delay platform specific.
116 retry = 1
117 while True:
118 value = self.get('pwr_button')
119 if value == 'release' or retry > Servo.RELEASE_RETRY_MAX:
120 break
121 logging.info('Waiting for pwr_button to release, retry %d.' % retry)
122 retry += 1
123 time.sleep(Servo.SHORT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700124
125
126 def lid_open(self):
127 """Simulate opening the lid."""
Craig Harrison48997262011-06-27 14:31:10 -0700128 self.set_nocheck('lid_open', 'yes')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700129
130
131 def lid_close(self):
Craig Harrison48997262011-06-27 14:31:10 -0700132 """Simulate closing the lid.
133
134 Waits 6 seconds to ensure the device is fully asleep before returning.
135 """
136 self.set_nocheck('lid_open', 'no')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700137 time.sleep(Servo.SLEEP_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700138
139
Todd Broch9dfc3a82011-11-01 08:09:28 -0700140 def _press_and_release_keys(self, m1, m2, press_secs=None):
141 """Simulate button presses."""
142 if press_secs is None:
143 press_secs = Servo.SERVO_SEND_SIGNAL_DELAY
144
145 self.set_nocheck('kbd_en', 'on')
146 self.set_nocheck('kbd_m1', m1)
147 self.set_nocheck('kbd_m2', m2)
148 time.sleep(press_secs)
149 self.set_nocheck('kbd_en', 'off')
150
151
Chrome Bot9a1137d2011-07-19 14:35:00 -0700152 def ctrl_d(self):
153 """Simulate Ctrl-d simultaneous button presses."""
Todd Broch9dfc3a82011-11-01 08:09:28 -0700154 self._press_and_release_keys('d', 'ctrl')
155
156
157 def d_key(self):
158 """Simulate Enter key button press."""
159 self._press_and_release_keys('d', 'none')
160
161
162 def ctrl_key(self):
163 """Simulate Enter key button press."""
164 self._press_and_release_keys('none', 'ctrl')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700165
166
Chrome Bot9a1137d2011-07-19 14:35:00 -0700167 def enter_key(self):
168 """Simulate Enter key button press."""
Todd Broch9dfc3a82011-11-01 08:09:28 -0700169 self._press_and_release_keys('enter', 'none')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700170
171
Chrome Bot9a1137d2011-07-19 14:35:00 -0700172 def refresh_key(self):
173 """Simulate Refresh key (F3) button press."""
Todd Broch9dfc3a82011-11-01 08:09:28 -0700174 self._press_and_release_keys('none', 'refresh')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700175
176
Chrome Bot9a1137d2011-07-19 14:35:00 -0700177 def imaginary_key(self):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700178 """Simulate imaginary key button press.
179
180 Maps to a key that doesn't physically exist.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700181 """
Todd Broch9dfc3a82011-11-01 08:09:28 -0700182 self._press_and_release_keys('none', 'unused')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700183
184
Craig Harrison6b36b122011-06-28 17:58:43 -0700185 def enable_recovery_mode(self):
186 """Enable recovery mode on device."""
187 self.set('rec_mode', 'on')
188
189
190 def disable_recovery_mode(self):
191 """Disable recovery mode on device."""
192 self.set('rec_mode', 'off')
193
194
195 def enable_development_mode(self):
196 """Enable development mode on device."""
197 self.set('dev_mode', 'on')
198
199
200 def disable_development_mode(self):
201 """Disable development mode on device."""
202 self.set('dev_mode', 'off')
203
Chris Sosa8ee1d592011-08-14 16:50:31 -0700204 def enable_usb_hub(self, host=False):
Craig Harrison86b1a572011-08-12 11:26:52 -0700205 """Enable Servo's USB/ethernet hub.
206
Chris Sosa8ee1d592011-08-14 16:50:31 -0700207 This is equivalent to plugging in the USB devices attached to Servo to
208 the host (if |host| is True) or dut (if |host| is False).
209 For host=False, requires that the USB out on the servo board is
210 connected to a USB in port on the target device. Servo's USB ports are
211 labeled DUT_HUB_USB1 and DUT_HUB_USB2. Servo's ethernet port is also
212 connected to this hub. Servo's USB port DUT_HUB_IN is the output of the
213 hub.
Craig Harrison86b1a572011-08-12 11:26:52 -0700214 """
215 self.set('dut_hub_pwren', 'on')
Chris Sosa8ee1d592011-08-14 16:50:31 -0700216 if host:
217 self.set('usb_mux_oe1', 'on')
218 self.set('usb_mux_sel1', 'servo_sees_usbkey')
219 else:
220 self.set('dut_hub_sel', 'dut_sees_hub')
221
Craig Harrison86b1a572011-08-12 11:26:52 -0700222 self.set('dut_hub_on', 'yes')
223
224
225 def disable_usb_hub(self):
226 """Disable Servo's USB/ethernet hub.
227
228 This is equivalent to unplugging the USB devices attached to Servo.
229 """
230 self.set('dut_hub_on', 'no')
231
232
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700233 def boot_devmode(self):
234 """Boot a dev-mode device that is powered off."""
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700235 self.power_normal_press()
Craig Harrison48997262011-06-27 14:31:10 -0700236 self.pass_devmode()
237
238
239 def pass_devmode(self):
240 """Pass through boot screens in dev-mode."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700241 time.sleep(Servo.BOOT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700242 self.ctrl_d()
Chrome Bot9a1137d2011-07-19 14:35:00 -0700243 time.sleep(Servo.BOOT_DELAY)
Craig Harrison48997262011-06-27 14:31:10 -0700244
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700245
Craig Harrison6b36b122011-06-28 17:58:43 -0700246 def cold_reset(self):
247 """Perform a cold reset of the EC.
248
Chris Sosa8ee1d592011-08-14 16:50:31 -0700249 Has the side effect of shutting off the device. Device is guaranteed
250 to be off at the end of this call.
Craig Harrison6b36b122011-06-28 17:58:43 -0700251 """
252 self.set('cold_reset', 'on')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700253 time.sleep(Servo.SERVO_SEND_SIGNAL_DELAY)
Craig Harrison6b36b122011-06-28 17:58:43 -0700254
255
256 def warm_reset(self):
257 """Perform a warm reset of the device.
258
259 Has the side effect of restarting the device.
260 """
261 self.set('warm_reset', 'on')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700262 time.sleep(Servo.SERVO_SEND_SIGNAL_DELAY)
Craig Harrison6b36b122011-06-28 17:58:43 -0700263 self.set('warm_reset', 'off')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700264
265
266 def get(self, gpio_name):
267 """Get the value of a gpio from Servod."""
268 assert gpio_name
269 return self._server.get(gpio_name)
270
271
272 def set(self, gpio_name, gpio_value):
273 """Set and check the value of a gpio using Servod."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700274 self.set_nocheck(gpio_name, gpio_value)
275 assert gpio_value == self.get(gpio_name), \
276 'Servo failed to set %s to %s' % (gpio_name, gpio_value)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700277
278
279 def set_nocheck(self, gpio_name, gpio_value):
280 """Set the value of a gpio using Servod."""
281 assert gpio_name and gpio_value
Chrome Bot9a1137d2011-07-19 14:35:00 -0700282 logging.info('Setting %s to %s' % (gpio_name, gpio_value))
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700283 self._server.set(gpio_name, gpio_value)
284
285
Craig Harrison6b36b122011-06-28 17:58:43 -0700286 def _init_seq_cold_reset_devmode(self):
287 """Cold reset, init device, and boot in dev-mode."""
288 self.cold_reset()
289 self._init_seq()
290 self.set('dev_mode', 'on')
291 self.boot_devmode()
292
293
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700294 def __del__(self):
295 """Kill the Servod process."""
Todd Brochf24d2782011-08-19 10:55:41 -0700296 if not self._servod:
297 return
298
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700299 # kill servod one way or another
300 try:
301 # won't work without superuser privileges
302 self._servod.terminate()
303 except:
304 # should work without superuser privileges
305 assert subprocess.call(['sudo', 'kill', str(self._servod.pid)])
306
307
Todd Brochf24d2782011-08-19 10:55:41 -0700308 def _launch_servod(self, servo_host, servo_port, xml_config, servo_vid,
309 servo_pid, servo_serial):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700310 """Launch the servod process.
311
312 Args:
Todd Brochf24d2782011-08-19 10:55:41 -0700313 servo_host: Host to start servod listening on.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700314 servo_port: Port to start servod listening on.
315 xml_config: XML configuration file for servod.
Todd Broch5fd6bc02011-07-20 15:53:37 -0700316 servo_vid: USB vendor id of servo.
317 servo_pid: USB product id of servo.
318 servo_serial: USB serial id in device descriptor to host to
319 distinguish and control multiple servos. Note servo's EEPROM must
320 be programmed to use this feature.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700321 """
Todd Broch96d83aa2011-08-29 14:37:38 -0700322 cmdlist = ['sudo', 'servod', '-c', str(xml_config)]
323 if servo_host is not None:
324 cmdlist.append('--host=%s' % str(servo_host))
325 if servo_port is not None:
326 cmdlist.append('--port=%s' % str(servo_port))
Todd Broch5fd6bc02011-07-20 15:53:37 -0700327 if servo_vid is not None:
Todd Brochf24d2782011-08-19 10:55:41 -0700328 cmdlist.append('--vendor=%s' % str(servo_vid))
Todd Broch5fd6bc02011-07-20 15:53:37 -0700329 if servo_pid is not None:
Todd Brochf24d2782011-08-19 10:55:41 -0700330 cmdlist.append('--product=%s' % str(servo_pid))
Todd Broch5fd6bc02011-07-20 15:53:37 -0700331 if servo_serial is not None:
Todd Brochf24d2782011-08-19 10:55:41 -0700332 cmdlist.append('--serialname=%s' % str(servo_serial))
Todd Broch5fd6bc02011-07-20 15:53:37 -0700333 logging.info('starting servod w/ cmd :: %s' % ' '.join(cmdlist))
334 self._servod = subprocess.Popen(cmdlist, 0, None, None, None,
335 subprocess.PIPE)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700336 # wait for servod to initialize
Chrome Bot9a1137d2011-07-19 14:35:00 -0700337 timeout = Servo.MAX_SERVO_STARTUP_DELAY
338 while ('Listening' not in self._servod.stderr.readline() and
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700339 self._servod.returncode is None and timeout > 0):
340 time.sleep(1)
341 timeout -= 1
342 assert self._servod.returncode is None and timeout
343
344
345 def _init_seq(self):
346 """Initiate starting state for servo."""
347 self.set('tx_dir', 'input')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700348 self.set('lid_open', 'yes')
349 self.set('rec_mode', 'off')
350
351
Todd Brochf24d2782011-08-19 10:55:41 -0700352 def _connect_servod(self, servo_host, servo_port):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700353 """Connect to the Servod process with XMLRPC.
354
355 Args:
356 servo_port: Port the Servod process is listening on.
357 """
Todd Brochf24d2782011-08-19 10:55:41 -0700358 remote = 'http://%s:%s' % (servo_host, servo_port)
Todd Broch96d83aa2011-08-29 14:37:38 -0700359 self._server = xmlrpclib.ServerProxy(remote)
Todd Brochf24d2782011-08-19 10:55:41 -0700360 try:
361 self._server.echo("ping-test")
Todd Broch96d83aa2011-08-29 14:37:38 -0700362 except:
363 logging.error('Connection to servod failed')
Todd Brochf24d2782011-08-19 10:55:41 -0700364 raise