blob: 6f30e4556e18e0d4023c35e5c41721f90aed63b9 [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
37
38 # Servo-specific delays.
39 MAX_SERVO_STARTUP_DELAY = 10
40 SERVO_SEND_SIGNAL_DELAY = 0.5
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070041
Todd Brochf24d2782011-08-19 10:55:41 -070042 def __init__(self, servo_host, servo_port, xml_config='servo.xml',
43 servo_vid=None, servo_pid=None, servo_serial=None,
44 cold_reset=False):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070045 """Sets up the servo communication infrastructure.
46
47 Args:
Todd Brochf24d2782011-08-19 10:55:41 -070048 servo_host: Host the servod process should listen on.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070049 servo_port: Port the servod process should listen on.
50 xml_config: Configuration XML file for servod.
Todd Broch5fd6bc02011-07-20 15:53:37 -070051 servo_vid: USB vendor id of servo.
52 servo_pid: USB product id of servo.
53 servo_serial: USB serial id in device descriptor to host to
54 distinguish and control multiple servos. Note servo's EEPROM must
55 be programmed to use this feature.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070056 cold_reset: If True, cold reset device and boot during init,
57 otherwise perform init with device running.
58 """
59 # launch servod
Todd Brochf24d2782011-08-19 10:55:41 -070060 self._servod = None
61 self._launch_servod(servo_host, servo_port, xml_config, servo_vid,
62 servo_pid, servo_serial)
Todd Broch5fd6bc02011-07-20 15:53:37 -070063
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070064
65 # connect to servod
Todd Brochf24d2782011-08-19 10:55:41 -070066 assert servo_host
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070067 assert servo_port
68
Chrome Bot9a1137d2011-07-19 14:35:00 -070069 self._do_cold_reset = cold_reset
70
Todd Brochf24d2782011-08-19 10:55:41 -070071 self._connect_servod(servo_host, servo_port)
Chrome Bot9a1137d2011-07-19 14:35:00 -070072
73
74 def initialize_dut(self):
75 """Initializes a dut for testing purposes."""
76 if self._do_cold_reset:
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070077 self._init_seq_cold_reset_devmode()
78 else:
79 self._init_seq()
80
81
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070082 def power_long_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -070083 """Simulate a long power button press."""
84 self.power_key(Servo.LONG_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070085
86
87 def power_normal_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -070088 """Simulate a normal power button press."""
89 self.power_key()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070090
91
92 def power_short_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -070093 """Simulate a short power button press."""
94 self.power_key(Servo.SHORT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070095
96
Chrome Bot9a1137d2011-07-19 14:35:00 -070097 def power_key(self, secs=NORMAL_TRANSITION_DELAY):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070098 """Simulate a power button press.
99
100 Args:
101 secs: Time in seconds to simulate the keypress.
102 """
Craig Harrison6b36b122011-06-28 17:58:43 -0700103 self.set_nocheck('pwr_button', 'press')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700104 time.sleep(secs)
Todd Broch31c82502011-08-29 08:14:39 -0700105 self.set_nocheck('pwr_button', 'release')
106 # TODO(tbroch) Different systems have different release times on the
107 # power button that this loop addresses. Longer term we may want to
108 # make this delay platform specific.
109 retry = 1
110 while True:
111 value = self.get('pwr_button')
112 if value == 'release' or retry > Servo.RELEASE_RETRY_MAX:
113 break
114 logging.info('Waiting for pwr_button to release, retry %d.' % retry)
115 retry += 1
116 time.sleep(Servo.SHORT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700117
118
119 def lid_open(self):
120 """Simulate opening the lid."""
Craig Harrison48997262011-06-27 14:31:10 -0700121 self.set_nocheck('lid_open', 'yes')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700122
123
124 def lid_close(self):
Craig Harrison48997262011-06-27 14:31:10 -0700125 """Simulate closing the lid.
126
127 Waits 6 seconds to ensure the device is fully asleep before returning.
128 """
129 self.set_nocheck('lid_open', 'no')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700130 time.sleep(Servo.SLEEP_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700131
132
Chrome Bot9a1137d2011-07-19 14:35:00 -0700133 def ctrl_d(self):
134 """Simulate Ctrl-d simultaneous button presses."""
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700135 self.set_nocheck('kbd_en', 'on')
136 self.set_nocheck('kbd_m1', 'r2_c2')
137 self.set_nocheck('kbd_m2', 'r1_c1')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700138 time.sleep(Servo.SERVO_SEND_SIGNAL_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700139 self.set_nocheck('kbd_en', 'off')
140
141
Chrome Bot9a1137d2011-07-19 14:35:00 -0700142 def enter_key(self):
143 """Simulate Enter key button press."""
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700144 self.set_nocheck('kbd_en', 'on')
145 self.set_nocheck('kbd_m1', 'r3_c2')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700146 time.sleep(Servo.SERVO_SEND_SIGNAL_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700147 self.set_nocheck('kbd_en', 'off')
148
149
Chrome Bot9a1137d2011-07-19 14:35:00 -0700150 def refresh_key(self):
151 """Simulate Refresh key (F3) button press."""
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700152 self.set_nocheck('kbd_en', 'on')
153 self.set_nocheck('kbd_m2', 'r2_c1')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700154 time.sleep(Servo.SERVO_SEND_SIGNAL_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700155 self.set_nocheck('kbd_en', 'off')
156
157
Chrome Bot9a1137d2011-07-19 14:35:00 -0700158 def imaginary_key(self):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700159 """Simulate imaginary key button press.
160
161 Maps to a key that doesn't physically exist.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700162 """
163 self.set_nocheck('kbd_en', 'on')
164 self.set_nocheck('kbd_m2', 'r3_c1')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700165 time.sleep(Servo.SERVO_SEND_SIGNAL_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700166 self.set_nocheck('kbd_en', 'off')
167
168
Craig Harrison6b36b122011-06-28 17:58:43 -0700169 def enable_recovery_mode(self):
170 """Enable recovery mode on device."""
171 self.set('rec_mode', 'on')
172
173
174 def disable_recovery_mode(self):
175 """Disable recovery mode on device."""
176 self.set('rec_mode', 'off')
177
178
179 def enable_development_mode(self):
180 """Enable development mode on device."""
181 self.set('dev_mode', 'on')
182
183
184 def disable_development_mode(self):
185 """Disable development mode on device."""
186 self.set('dev_mode', 'off')
187
188
Craig Harrison86b1a572011-08-12 11:26:52 -0700189 def enable_usb_hub(self):
190 """Enable Servo's USB/ethernet hub.
191
192 This is equivalent to plugging in the USB devices attached to Servo.
193 Requires that the USB out on the servo board is connected to a USB
194 in port on the target device. Servo's USB ports are labeled DUT_HUB_USB1
195 and DUT_HUB_USB2. Servo's ethernet port is also connected to this hub.
196 Servo's USB port DUT_HUB_IN is the output of the hub.
197 """
198 self.set('dut_hub_pwren', 'on')
199 self.set('dut_hub_sel', 'dut_sees_hub')
200 self.set('dut_hub_on', 'yes')
201
202
203 def disable_usb_hub(self):
204 """Disable Servo's USB/ethernet hub.
205
206 This is equivalent to unplugging the USB devices attached to Servo.
207 """
208 self.set('dut_hub_on', 'no')
209
210
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700211 def boot_devmode(self):
212 """Boot a dev-mode device that is powered off."""
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700213 self.power_normal_press()
Craig Harrison48997262011-06-27 14:31:10 -0700214 self.pass_devmode()
215
216
217 def pass_devmode(self):
218 """Pass through boot screens in dev-mode."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700219 time.sleep(Servo.BOOT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700220 self.ctrl_d()
Chrome Bot9a1137d2011-07-19 14:35:00 -0700221 time.sleep(Servo.BOOT_DELAY)
Craig Harrison48997262011-06-27 14:31:10 -0700222
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700223
Craig Harrison6b36b122011-06-28 17:58:43 -0700224 def cold_reset(self):
225 """Perform a cold reset of the EC.
226
227 Has the side effect of shutting off the device.
228 """
229 self.set('cold_reset', 'on')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700230 time.sleep(Servo.SERVO_SEND_SIGNAL_DELAY)
Craig Harrison6b36b122011-06-28 17:58:43 -0700231 self.set('cold_reset', 'off')
232
233
234 def warm_reset(self):
235 """Perform a warm reset of the device.
236
237 Has the side effect of restarting the device.
238 """
239 self.set('warm_reset', 'on')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700240 time.sleep(Servo.SERVO_SEND_SIGNAL_DELAY)
Craig Harrison6b36b122011-06-28 17:58:43 -0700241 self.set('warm_reset', 'off')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700242
243
244 def get(self, gpio_name):
245 """Get the value of a gpio from Servod."""
246 assert gpio_name
247 return self._server.get(gpio_name)
248
249
250 def set(self, gpio_name, gpio_value):
251 """Set and check the value of a gpio using Servod."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700252 self.set_nocheck(gpio_name, gpio_value)
253 assert gpio_value == self.get(gpio_name), \
254 'Servo failed to set %s to %s' % (gpio_name, gpio_value)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700255
256
257 def set_nocheck(self, gpio_name, gpio_value):
258 """Set the value of a gpio using Servod."""
259 assert gpio_name and gpio_value
Chrome Bot9a1137d2011-07-19 14:35:00 -0700260 logging.info('Setting %s to %s' % (gpio_name, gpio_value))
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700261 self._server.set(gpio_name, gpio_value)
262
263
Craig Harrison6b36b122011-06-28 17:58:43 -0700264 def _init_seq_cold_reset_devmode(self):
265 """Cold reset, init device, and boot in dev-mode."""
266 self.cold_reset()
267 self._init_seq()
268 self.set('dev_mode', 'on')
269 self.boot_devmode()
270
271
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700272 def __del__(self):
273 """Kill the Servod process."""
Todd Brochf24d2782011-08-19 10:55:41 -0700274 if not self._servod:
275 return
276
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700277 # kill servod one way or another
278 try:
279 # won't work without superuser privileges
280 self._servod.terminate()
281 except:
282 # should work without superuser privileges
283 assert subprocess.call(['sudo', 'kill', str(self._servod.pid)])
284
285
Todd Brochf24d2782011-08-19 10:55:41 -0700286 def _launch_servod(self, servo_host, servo_port, xml_config, servo_vid,
287 servo_pid, servo_serial):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700288 """Launch the servod process.
289
290 Args:
Todd Brochf24d2782011-08-19 10:55:41 -0700291 servo_host: Host to start servod listening on.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700292 servo_port: Port to start servod listening on.
293 xml_config: XML configuration file for servod.
Todd Broch5fd6bc02011-07-20 15:53:37 -0700294 servo_vid: USB vendor id of servo.
295 servo_pid: USB product id of servo.
296 servo_serial: USB serial id in device descriptor to host to
297 distinguish and control multiple servos. Note servo's EEPROM must
298 be programmed to use this feature.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700299 """
Todd Brochf24d2782011-08-19 10:55:41 -0700300 # TODO(tbroch) In case where servo h/w is not connected to the host
301 # running the autotest server, servod will need to be launched by
302 # another means (udev likely). For now we can assume servo_host ==
303 # localhost as one hueristic way of determining this.
304 if servo_host != 'localhost':
305 logging.warn('servod should already be running on host = %s',
306 servo_host)
307 return
308
309 cmdlist = ['sudo', 'servod', '-c', str(xml_config), '--host=' +
310 str(servo_host), '--port=' + str(servo_port)]
Todd Broch5fd6bc02011-07-20 15:53:37 -0700311 if servo_vid is not None:
Todd Brochf24d2782011-08-19 10:55:41 -0700312 cmdlist.append('--vendor=%s' % str(servo_vid))
Todd Broch5fd6bc02011-07-20 15:53:37 -0700313 if servo_pid is not None:
Todd Brochf24d2782011-08-19 10:55:41 -0700314 cmdlist.append('--product=%s' % str(servo_pid))
Todd Broch5fd6bc02011-07-20 15:53:37 -0700315 if servo_serial is not None:
Todd Brochf24d2782011-08-19 10:55:41 -0700316 cmdlist.append('--serialname=%s' % str(servo_serial))
Todd Broch5fd6bc02011-07-20 15:53:37 -0700317 logging.info('starting servod w/ cmd :: %s' % ' '.join(cmdlist))
318 self._servod = subprocess.Popen(cmdlist, 0, None, None, None,
319 subprocess.PIPE)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700320 # wait for servod to initialize
Chrome Bot9a1137d2011-07-19 14:35:00 -0700321 timeout = Servo.MAX_SERVO_STARTUP_DELAY
322 while ('Listening' not in self._servod.stderr.readline() and
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700323 self._servod.returncode is None and timeout > 0):
324 time.sleep(1)
325 timeout -= 1
326 assert self._servod.returncode is None and timeout
327
328
329 def _init_seq(self):
330 """Initiate starting state for servo."""
331 self.set('tx_dir', 'input')
Craig Harrison63e9c6a2011-08-10 17:13:57 -0700332 self.set_nocheck('servo_dut_tx', 'off')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700333 self.set('lid_open', 'yes')
334 self.set('rec_mode', 'off')
335
336
Todd Brochf24d2782011-08-19 10:55:41 -0700337 def _connect_servod(self, servo_host, servo_port):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700338 """Connect to the Servod process with XMLRPC.
339
340 Args:
341 servo_port: Port the Servod process is listening on.
342 """
Todd Brochf24d2782011-08-19 10:55:41 -0700343 remote = 'http://%s:%s' % (servo_host, servo_port)
344 try:
345 self._server = xmlrpclib.ServerProxy(remote)
346 except Exception:
347 logging.error('Unable to connect to servod')
348 raise
349 try:
350 self._server.echo("ping-test")
351 except Exception:
352 logging.error('Unable to ping servod')
353 raise