blob: 7b1965dd5d97778821166acfd093d8167408da55 [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
Vadim Bendeburyb80ba592012-12-07 15:02:34 -08008import os
9
Vic Yang3a7cf602012-11-07 17:28:39 +080010import logging, re, time, xmlrpclib
Vadim Bendeburyb80ba592012-12-07 15:02:34 -080011
Simran Basi741b5d42012-05-18 11:27:15 -070012from autotest_lib.client.common_lib import error
Jon Salzc88e5b62011-11-30 14:38:54 +080013from autotest_lib.server import utils
Vadim Bendeburyb80ba592012-12-07 15:02:34 -080014from autotest_lib.server.cros import programmer
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070015
J. Richard Barnette384056b2012-04-16 11:04:46 -070016class Servo(object):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070017 """Manages control of a Servo board.
18
19 Servo is a board developed by hardware group to aide in the debug and
20 control of various partner devices. Servo's features include the simulation
21 of pressing the power button, closing the lid, and pressing Ctrl-d. This
22 class manages setting up and communicating with a servo demon (servod)
23 process. It provides both high-level functions for common servo tasks and
24 low-level functions for directly setting and reading gpios.
25 """
26
Chrome Bot9a1137d2011-07-19 14:35:00 -070027 # Power button press delays in seconds.
J. Richard Barnetted2e4cbd2012-06-29 12:18:40 -070028 #
J. Richard Barnette5383f072012-07-26 17:35:40 -070029 # The EC specification says that 8.0 seconds should be enough
30 # for the long power press. However, some platforms need a bit
31 # more time. Empirical testing has found these requirements:
32 # Alex: 8.2 seconds
33 # ZGB: 8.5 seconds
34 # The actual value is set to the largest known necessary value.
35 #
36 # TODO(jrbarnette) Being generous is the right thing to do for
37 # existing platforms, but if this code is to be used for
38 # qualification of new hardware, we should be less generous.
39 LONG_DELAY = 8.5
Chrome Bot9a1137d2011-07-19 14:35:00 -070040 SHORT_DELAY = 0.1
41 NORMAL_TRANSITION_DELAY = 1.2
J. Richard Barnette5383f072012-07-26 17:35:40 -070042
Todd Broch31c82502011-08-29 08:14:39 -070043 # Maximum number of times to re-read power button on release.
44 RELEASE_RETRY_MAX = 5
Todd Brochcf7c6652012-02-24 13:03:59 -080045 GET_RETRY_MAX = 10
Chrome Bot9a1137d2011-07-19 14:35:00 -070046
J. Richard Barnette5383f072012-07-26 17:35:40 -070047 # Delays to deal with DUT state transitions.
Chrome Bot9a1137d2011-07-19 14:35:00 -070048 SLEEP_DELAY = 6
49 BOOT_DELAY = 10
J. Richard Barnette8bd49842012-07-19 14:21:15 -070050 RECOVERY_BOOT_DELAY = 10
J. Richard Barnettec5a77ad2012-04-25 08:19:00 -070051 RECOVERY_INSTALL_DELAY = 540
Chrome Bot9a1137d2011-07-19 14:35:00 -070052
J. Richard Barnetteb6133972012-07-19 17:13:55 -070053 # Time required for the EC to be working after cold reset.
54 # Five seconds is at least twice as big as necessary for Alex,
55 # and is presumably good enough for all future systems.
56 _EC_RESET_DELAY = 5.0
57
Chrome Bot9a1137d2011-07-19 14:35:00 -070058 # Servo-specific delays.
59 MAX_SERVO_STARTUP_DELAY = 10
60 SERVO_SEND_SIGNAL_DELAY = 0.5
Vic Yang0aca1c22012-11-19 18:33:56 -080061 SERVO_KEY_PRESS_DELAY = 0.1
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070062
Jon Salzc88e5b62011-11-30 14:38:54 +080063 # Time between an usb disk plugged-in and detected in the system.
64 USB_DETECTION_DELAY = 10
65
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -070066 KEY_MATRIX_ALT_0 = {
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -070067 'ctrl_refresh': ['0', '0', '0', '1'],
68 'ctrl_d': ['0', '1', '0', '0'],
69 'd': ['0', '1', '1', '1'],
70 'ctrl_enter': ['1', '0', '0', '0'],
71 'enter': ['1', '0', '1', '1'],
72 'ctrl': ['1', '1', '0', '0'],
73 'refresh': ['1', '1', '0', '1'],
74 'unused': ['1', '1', '1', '0'],
75 'none': ['1', '1', '1', '1']}
Chris Masone6a0680f2012-03-02 08:40:00 -080076
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -070077 KEY_MATRIX_ALT_1 = {
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -070078 'ctrl_d': ['0', '0', '1', '0'],
79 'd': ['0', '0', '1', '1'],
80 'ctrl_enter': ['0', '1', '1', '0'],
81 'enter': ['0', '1', '1', '1'],
82 'ctrl_refresh': ['1', '0', '0', '1'],
83 'unused': ['1', '1', '0', '0'],
84 'refresh': ['1', '1', '0', '1'],
85 'ctrl': ['1', '1', '1', '0'],
86 'none': ['1', '1', '1', '1']}
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -070087
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -080088 KEY_MATRIX_ALT_2 = {
89 'ctrl_d': ['0', '0', '0', '1'],
90 'd': ['0', '0', '1', '1'],
91 'unused': ['0', '1', '1', '1'],
92 'rec_mode': ['1', '0', '0', '0'],
93 'ctrl_enter': ['1', '0', '0', '1'],
94 'enter': ['1', '0', '1', '1'],
95 'ctrl': ['1', '1', '0', '1'],
96 'refresh': ['1', '1', '1', '0'],
97 'ctrl_refresh': ['1', '1', '1', '1'],
98 'none': ['1', '1', '1', '1']}
99
100 KEY_MATRIX = [KEY_MATRIX_ALT_0, KEY_MATRIX_ALT_1, KEY_MATRIX_ALT_2]
J. Richard Barnette67ccb872012-04-19 16:34:56 -0700101
Chris Masone6a0680f2012-03-02 08:40:00 -0800102 @staticmethod
J. Richard Barnette67ccb872012-04-19 16:34:56 -0700103 def _make_servo_hostname(hostname):
104 host_parts = hostname.split('.')
105 host_parts[0] = host_parts[0] + '-servo'
106 return '.'.join(host_parts)
Chris Masone6a0680f2012-03-02 08:40:00 -0800107
J. Richard Barnette67ccb872012-04-19 16:34:56 -0700108 @staticmethod
109 def get_lab_servo(target_hostname):
110 """Instantiate a Servo for |target_hostname| in the lab.
Chris Masone6a0680f2012-03-02 08:40:00 -0800111
J. Richard Barnette67ccb872012-04-19 16:34:56 -0700112 Assuming that |target_hostname| is a device in the CrOS test
113 lab, create and return a Servo object pointed at the servo
114 attached to that DUT. The servo in the test lab is assumed
115 to already have servod up and running on it.
116
117 @param target_hostname: device whose servo we want to target.
Chris Masone6a0680f2012-03-02 08:40:00 -0800118 @return an appropriately configured Servo
119 """
J. Richard Barnette67ccb872012-04-19 16:34:56 -0700120 servo_host = Servo._make_servo_hostname(target_hostname)
121 if utils.host_is_in_lab_zone(servo_host):
Vic Yang3a7cf602012-11-07 17:28:39 +0800122 try:
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800123 return Servo(servo_host=servo_host, target_host=target_hostname)
Vic Yang3a7cf602012-11-07 17:28:39 +0800124 except: # pylint: disable=W0702
125 # TODO(jrbarnette): Long-term, if we can't get to
126 # a servo in the lab, we want to fail, so we should
127 # pass any exceptions along. Short-term, we're not
128 # ready to rely on servo, so we ignore failures.
129 pass
J. Richard Barnette67ccb872012-04-19 16:34:56 -0700130 return None
Chris Masone6a0680f2012-03-02 08:40:00 -0800131
132
Tom Wai-Hong Tamc5c14ef2012-11-20 16:33:37 +0800133 def __init__(self, servo_host='localhost', target_host=None,
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800134 servo_port=9999):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700135 """Sets up the servo communication infrastructure.
136
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800137 @param servo_host Name of the host where the servod process
138 is running.
139 @param target_host Name of the target which is connected to servo
140 @param servo_port Port the servod process is listening on.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700141 """
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -0700142 self._key_matrix = 0
J. Richard Barnette384056b2012-04-16 11:04:46 -0700143 self._server = None
Todd Brochf24d2782011-08-19 10:55:41 -0700144 self._connect_servod(servo_host, servo_port)
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800145 self._is_localhost = (servo_host == 'localhost')
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800146 self._target_host = target_host
147
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800148 # Commands on the servo host must be run by the superuser. Our account
149 # on Beaglebone is root, but locally we might be running as a
150 # different user. If so - `sudo ' will have to be added to the
151 # commands.
152 if self._is_localhost:
153 self._sudo_required = utils.system_output('id -u') != '0'
154 self._ssh_prefix = ''
155 else:
156 common_options = '-o PasswordAuthentication=no'
157 self._sudo_required = False
158 self._ssh_prefix = 'ssh %s root@%s ' % (common_options, servo_host)
159 self._scp_cmd_template = 'scp %s ' % common_options
160 self._scp_cmd_template += '%s ' + 'root@' + servo_host + ':%s'
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800161
162 def get_target_hostname(self):
163 """Retrieves target (DUT) hostname."""
164 return self._target_host
Chrome Bot9a1137d2011-07-19 14:35:00 -0700165
166
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700167 def initialize_dut(self, cold_reset=False):
168 """Initializes a dut for testing purposes.
169
170 This sets various servo signals back to default values
171 appropriate for the target board. By default, if the DUT
172 is already on, it stays on. If the DUT is powered off
173 before initialization, its state afterward is unspecified.
174
175 If cold reset is requested, the DUT is guaranteed to be off
176 at the end of initialization, regardless of its initial
177 state.
178
179 Rationale: Basic initialization of servo sets the lid open,
180 when there is a lid. This operation won't affect powered on
181 units; however, setting the lid open may power on a unit
182 that's off, depending on factors outside the scope of this
183 function.
184
185 @param cold_reset If True, cold reset the device after
186 initialization.
187 """
188 self._server.hwinit()
189 if cold_reset:
190 self.cold_reset()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700191
192
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800193 def is_localhost(self):
194 """Is the servod hosted locally?
195
196 Returns:
197 True if local hosted; otherwise, False.
198 """
199 return self._is_localhost
200
201
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700202 def power_long_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700203 """Simulate a long power button press."""
J. Richard Barnettef12bff22012-07-18 14:41:06 -0700204 # After a long power press, the EC may ignore the next power
205 # button press (at least on Alex). To guarantee that this
206 # won't happen, we need to allow the EC one second to
207 # collect itself.
Chrome Bot9a1137d2011-07-19 14:35:00 -0700208 self.power_key(Servo.LONG_DELAY)
J. Richard Barnettef12bff22012-07-18 14:41:06 -0700209 time.sleep(1.0)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700210
211
212 def power_normal_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700213 """Simulate a normal power button press."""
214 self.power_key()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700215
216
217 def power_short_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700218 """Simulate a short power button press."""
219 self.power_key(Servo.SHORT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700220
221
Chrome Bot9a1137d2011-07-19 14:35:00 -0700222 def power_key(self, secs=NORMAL_TRANSITION_DELAY):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700223 """Simulate a power button press.
224
225 Args:
226 secs: Time in seconds to simulate the keypress.
227 """
Craig Harrison6b36b122011-06-28 17:58:43 -0700228 self.set_nocheck('pwr_button', 'press')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700229 time.sleep(secs)
Todd Broch31c82502011-08-29 08:14:39 -0700230 self.set_nocheck('pwr_button', 'release')
231 # TODO(tbroch) Different systems have different release times on the
232 # power button that this loop addresses. Longer term we may want to
233 # make this delay platform specific.
234 retry = 1
235 while True:
236 value = self.get('pwr_button')
237 if value == 'release' or retry > Servo.RELEASE_RETRY_MAX:
238 break
Todd Broch9753bd42012-03-21 10:15:08 -0700239 logging.info('Waiting for pwr_button to release, retry %d.', retry)
Todd Broch31c82502011-08-29 08:14:39 -0700240 retry += 1
241 time.sleep(Servo.SHORT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700242
243
244 def lid_open(self):
245 """Simulate opening the lid."""
Craig Harrison48997262011-06-27 14:31:10 -0700246 self.set_nocheck('lid_open', 'yes')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700247
248
249 def lid_close(self):
Craig Harrison48997262011-06-27 14:31:10 -0700250 """Simulate closing the lid.
251
252 Waits 6 seconds to ensure the device is fully asleep before returning.
253 """
254 self.set_nocheck('lid_open', 'no')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700255 time.sleep(Servo.SLEEP_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700256
257
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800258 def _press_keys(self, key):
259 """Simulate button presses.
260
261 Note, key presses will remain on indefinitely. See
262 _press_and_release_keys for release procedure.
263 """
Gediminas Ramanauskas9da80f22012-11-14 12:59:43 -0800264 (m1_a1, m1_a0, m2_a1, m2_a0) = self.KEY_MATRIX[self._key_matrix]['none']
Todd Broch9753bd42012-03-21 10:15:08 -0700265 self.set_nocheck('kbd_m2_a0', m2_a0)
266 self.set_nocheck('kbd_m2_a1', m2_a1)
267 self.set_nocheck('kbd_m1_a0', m1_a0)
268 self.set_nocheck('kbd_m1_a1', m1_a1)
Vic Yange262a3e2012-11-02 18:48:37 +0800269 self.set_nocheck('kbd_en', 'on')
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800270
Gediminas Ramanauskas9da80f22012-11-14 12:59:43 -0800271 (m1_a1, m1_a0, m2_a1, m2_a0) = self.KEY_MATRIX[self._key_matrix][key]
272 self.set_nocheck('kbd_m2_a0', m2_a0)
273 self.set_nocheck('kbd_m2_a1', m2_a1)
274 self.set_nocheck('kbd_m1_a0', m1_a0)
275 self.set_nocheck('kbd_m1_a1', m1_a1)
276
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800277
278 def _press_and_release_keys(self, key,
279 press_secs=SERVO_KEY_PRESS_DELAY):
280 """Simulate button presses and release."""
281 self._press_keys(key)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700282 time.sleep(press_secs)
283 self.set_nocheck('kbd_en', 'off')
284
285
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -0700286 def set_key_matrix(self, matrix=0):
287 """Set keyboard mapping"""
288 self._key_matrix = matrix
289
290
Chrome Bot9a1137d2011-07-19 14:35:00 -0700291 def ctrl_d(self):
292 """Simulate Ctrl-d simultaneous button presses."""
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700293 self._press_and_release_keys('ctrl_d')
Todd Broch9dfc3a82011-11-01 08:09:28 -0700294
295
Todd Broch9753bd42012-03-21 10:15:08 -0700296 def ctrl_enter(self):
297 """Simulate Ctrl-enter simultaneous button presses."""
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700298 self._press_and_release_keys('ctrl_enter')
Todd Broch9753bd42012-03-21 10:15:08 -0700299
300
Todd Broch9dfc3a82011-11-01 08:09:28 -0700301 def d_key(self):
302 """Simulate Enter key button press."""
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700303 self._press_and_release_keys('d')
Todd Broch9dfc3a82011-11-01 08:09:28 -0700304
305
306 def ctrl_key(self):
307 """Simulate Enter key button press."""
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700308 self._press_and_release_keys('ctrl')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700309
310
Chrome Bot9a1137d2011-07-19 14:35:00 -0700311 def enter_key(self):
312 """Simulate Enter key button press."""
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700313 self._press_and_release_keys('enter')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700314
315
Chrome Bot9a1137d2011-07-19 14:35:00 -0700316 def refresh_key(self):
317 """Simulate Refresh key (F3) button press."""
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700318 self._press_and_release_keys('refresh')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700319
320
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800321 def ctrl_refresh_key(self):
322 """Simulate Ctrl and Refresh (F3) simultaneous press.
323
324 This key combination is an alternative of Space key.
325 """
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700326 self._press_and_release_keys('ctrl_refresh')
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800327
328
Chrome Bot9a1137d2011-07-19 14:35:00 -0700329 def imaginary_key(self):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700330 """Simulate imaginary key button press.
331
332 Maps to a key that doesn't physically exist.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700333 """
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700334 self._press_and_release_keys('unused')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700335
336
Craig Harrison6b36b122011-06-28 17:58:43 -0700337 def enable_recovery_mode(self):
338 """Enable recovery mode on device."""
339 self.set('rec_mode', 'on')
340
341
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800342 def custom_recovery_mode(self):
343 """Custom key combination to enter recovery mode."""
344 self._press_keys('rec_mode')
345 self.power_normal_press()
346 time.sleep(self.SERVO_KEY_PRESS_DELAY)
347 self.set_nocheck('kbd_en', 'off')
348
349
Craig Harrison6b36b122011-06-28 17:58:43 -0700350 def disable_recovery_mode(self):
351 """Disable recovery mode on device."""
352 self.set('rec_mode', 'off')
353
354
355 def enable_development_mode(self):
356 """Enable development mode on device."""
357 self.set('dev_mode', 'on')
358
359
360 def disable_development_mode(self):
361 """Disable development mode on device."""
362 self.set('dev_mode', 'off')
363
Chris Sosa8ee1d592011-08-14 16:50:31 -0700364 def enable_usb_hub(self, host=False):
Craig Harrison86b1a572011-08-12 11:26:52 -0700365 """Enable Servo's USB/ethernet hub.
366
Chris Sosa8ee1d592011-08-14 16:50:31 -0700367 This is equivalent to plugging in the USB devices attached to Servo to
368 the host (if |host| is True) or dut (if |host| is False).
369 For host=False, requires that the USB out on the servo board is
370 connected to a USB in port on the target device. Servo's USB ports are
371 labeled DUT_HUB_USB1 and DUT_HUB_USB2. Servo's ethernet port is also
372 connected to this hub. Servo's USB port DUT_HUB_IN is the output of the
373 hub.
Craig Harrison86b1a572011-08-12 11:26:52 -0700374 """
375 self.set('dut_hub_pwren', 'on')
Chris Sosa8ee1d592011-08-14 16:50:31 -0700376 if host:
Todd Broch9753bd42012-03-21 10:15:08 -0700377 self.set('usb_mux_oe1', 'on')
378 self.set('usb_mux_sel1', 'servo_sees_usbkey')
Chris Sosa8ee1d592011-08-14 16:50:31 -0700379 else:
Todd Broch9753bd42012-03-21 10:15:08 -0700380 self.set('dut_hub_sel', 'dut_sees_hub')
Chris Sosa8ee1d592011-08-14 16:50:31 -0700381
Craig Harrison86b1a572011-08-12 11:26:52 -0700382 self.set('dut_hub_on', 'yes')
383
384
385 def disable_usb_hub(self):
386 """Disable Servo's USB/ethernet hub.
387
388 This is equivalent to unplugging the USB devices attached to Servo.
389 """
390 self.set('dut_hub_on', 'no')
391
392
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700393 def boot_devmode(self):
394 """Boot a dev-mode device that is powered off."""
Tom Wai-Hong Tam23869102012-01-17 15:41:30 +0800395 self.power_short_press()
Craig Harrison48997262011-06-27 14:31:10 -0700396 self.pass_devmode()
397
398
399 def pass_devmode(self):
400 """Pass through boot screens in dev-mode."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700401 time.sleep(Servo.BOOT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700402 self.ctrl_d()
Chrome Bot9a1137d2011-07-19 14:35:00 -0700403 time.sleep(Servo.BOOT_DELAY)
Craig Harrison48997262011-06-27 14:31:10 -0700404
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700405
Craig Harrison6b36b122011-06-28 17:58:43 -0700406 def cold_reset(self):
407 """Perform a cold reset of the EC.
408
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700409 This has the side effect of shutting off the device. The
410 device is guaranteed to be off at the end of this call.
Craig Harrison6b36b122011-06-28 17:58:43 -0700411 """
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700412 # After the reset, give the EC the time it needs to
413 # re-initialize.
Craig Harrison6b36b122011-06-28 17:58:43 -0700414 self.set('cold_reset', 'on')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700415 time.sleep(Servo.SERVO_SEND_SIGNAL_DELAY)
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700416 self.set('cold_reset', 'off')
417 time.sleep(self._EC_RESET_DELAY)
Craig Harrison6b36b122011-06-28 17:58:43 -0700418
419
420 def warm_reset(self):
421 """Perform a warm reset of the device.
422
423 Has the side effect of restarting the device.
424 """
425 self.set('warm_reset', 'on')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700426 time.sleep(Servo.SERVO_SEND_SIGNAL_DELAY)
Craig Harrison6b36b122011-06-28 17:58:43 -0700427 self.set('warm_reset', 'off')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700428
429
Todd Brochefe72cb2012-07-11 19:58:53 -0700430 def _get_xmlrpclib_exception(self, xmlexc):
431 """Get meaningful exception string from xmlrpc.
432
433 Args:
434 xmlexc: xmlrpclib.Fault object
435
436 xmlrpclib.Fault.faultString has the following format:
437
438 <type 'exception type'>:'actual error message'
439
440 Parse and return the real exception from the servod side instead of the
441 less meaningful one like,
442 <Fault 1: "<type 'exceptions.AttributeError'>:'tca6416' object has no
443 attribute 'hw_driver'">
444
445 Returns:
446 string of underlying exception raised in servod.
447 """
448 return re.sub('^.*>:', '', xmlexc.faultString)
449
450
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700451 def get(self, gpio_name):
452 """Get the value of a gpio from Servod."""
453 assert gpio_name
Todd Brochefe72cb2012-07-11 19:58:53 -0700454 try:
455 return self._server.get(gpio_name)
456 except xmlrpclib.Fault as e:
457 err_msg = "Getting '%s' :: %s" % \
458 (gpio_name, self._get_xmlrpclib_exception(e))
459 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700460
461
462 def set(self, gpio_name, gpio_value):
463 """Set and check the value of a gpio using Servod."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700464 self.set_nocheck(gpio_name, gpio_value)
Todd Brochcf7c6652012-02-24 13:03:59 -0800465 retry_count = Servo.GET_RETRY_MAX
466 while gpio_value != self.get(gpio_name) and retry_count:
467 logging.warn("%s != %s, retry %d", gpio_name, gpio_value,
468 retry_count)
469 retry_count -= 1
470 time.sleep(Servo.SHORT_DELAY)
471 if not retry_count:
472 assert gpio_value == self.get(gpio_name), \
473 'Servo failed to set %s to %s' % (gpio_name, gpio_value)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700474
475
476 def set_nocheck(self, gpio_name, gpio_value):
477 """Set the value of a gpio using Servod."""
478 assert gpio_name and gpio_value
Todd Broch9753bd42012-03-21 10:15:08 -0700479 logging.info('Setting %s to %s', gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700480 try:
481 self._server.set(gpio_name, gpio_value)
482 except xmlrpclib.Fault as e:
483 err_msg = "Setting '%s' to '%s' :: %s" % \
484 (gpio_name, gpio_value, self._get_xmlrpclib_exception(e))
485 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700486
487
Jon Salzc88e5b62011-11-30 14:38:54 +0800488 # TODO(waihong) It may fail if multiple servo's are connected to the same
489 # host. Should look for a better way, like the USB serial name, to identify
490 # the USB device.
Simran Basi741b5d42012-05-18 11:27:15 -0700491 # TODO(sbasi) Remove this code from autoserv once firmware tests have been
492 # updated.
Jon Salzc88e5b62011-11-30 14:38:54 +0800493 def probe_host_usb_dev(self):
494 """Probe the USB disk device plugged-in the servo from the host side.
495
496 It tries to switch the USB mux to make the host unable to see the
497 USB disk and compares the result difference.
498
Jon Salzc88e5b62011-11-30 14:38:54 +0800499 Returns:
500 A string of USB disk path, like '/dev/sdb', or None if not existed.
501 """
502 cmd = 'ls /dev/sd[a-z]'
503 original_value = self.get('usb_mux_sel1')
504
505 # Make the host unable to see the USB disk.
506 if original_value != 'dut_sees_usbkey':
507 self.set('usb_mux_sel1', 'dut_sees_usbkey')
508 time.sleep(self.USB_DETECTION_DELAY)
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800509 no_usb_set = set(self.system_output(cmd, ignore_status=True).split())
Jon Salzc88e5b62011-11-30 14:38:54 +0800510
511 # Make the host able to see the USB disk.
512 self.set('usb_mux_sel1', 'servo_sees_usbkey')
513 time.sleep(self.USB_DETECTION_DELAY)
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800514 has_usb_set = set(self.system_output(cmd, ignore_status=True).split())
Jon Salzc88e5b62011-11-30 14:38:54 +0800515
516 # Back to its original value.
517 if original_value != 'servo_sees_usbkey':
518 self.set('usb_mux_sel1', original_value)
519 time.sleep(self.USB_DETECTION_DELAY)
520
521 diff_set = has_usb_set - no_usb_set
522 if len(diff_set) == 1:
523 return diff_set.pop()
524 else:
525 return None
526
527
Mike Truty49153d82012-08-21 22:27:30 -0500528 def image_to_servo_usb(self, image_path=None,
529 make_image_noninteractive=False):
530 """Install an image to the USB key plugged into the servo.
531
532 This method may copy any image to the servo USB key including a
533 recovery image or a test image. These images are frequently used
534 for test purposes such as restoring a corrupted image or conducting
535 an upgrade of ec/fw/kernel as part of a test of a specific image part.
536
537 Args:
538 image_path: Path on the host to the recovery image.
539 make_image_noninteractive: Make the recovery image noninteractive,
540 therefore the DUT will reboot
541 automatically after installation.
542 """
543 # Turn the device off. This should happen before USB key detection, to
544 # prevent a recovery destined DUT from sensing the USB key due to the
545 # autodetection procedure.
546 self.initialize_dut(cold_reset=True)
547
548 # Set up Servo's usb mux.
549 self.set('prtctl4_pwren', 'on')
550 self.enable_usb_hub(host=True)
551 if image_path:
Tom Wai-Hong Tam42f136d2012-10-26 11:11:23 +0800552 logging.info('Searching for usb device and copying image to it. '
553 'Please wait a few minutes...')
Mike Truty49153d82012-08-21 22:27:30 -0500554 if not self._server.download_image_to_usb(image_path):
555 logging.error('Failed to transfer requested image to USB. '
556 'Please take a look at Servo Logs.')
557 raise error.AutotestError('Download image to usb failed.')
558 if make_image_noninteractive:
559 logging.info('Making image noninteractive')
560 if not self._server.make_image_noninteractive():
561 logging.error('Failed to make image noninteractive. '
562 'Please take a look at Servo Logs.')
563
564
Simran Basi741b5d42012-05-18 11:27:15 -0700565 def install_recovery_image(self, image_path=None,
566 wait_timeout=RECOVERY_INSTALL_DELAY,
567 make_image_noninteractive=False,
568 host=None):
Jon Salzc88e5b62011-11-30 14:38:54 +0800569 """Install the recovery image specied by the path onto the DUT.
570
571 This method uses google recovery mode to install a recovery image
572 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 +0800573 board specified by the usb_dev. If no image path is specified
Jon Salzc88e5b62011-11-30 14:38:54 +0800574 we use the recovery image already on the usb image.
575
576 Args:
577 image_path: Path on the host to the recovery image.
Gilad Arnold9df73de2012-03-14 09:35:08 -0700578 wait_timeout: How long to wait for completion; default is
579 determined by a constant.
Simran Basi741b5d42012-05-18 11:27:15 -0700580 make_image_noninteractive: Make the recovery image noninteractive,
581 therefore the DUT will reboot
582 automatically after installation.
583 host: Host object for the DUT that the installation process is
584 running on. If provided, will wait to see if the host is back
585 up after starting recovery mode.
Jon Salzc88e5b62011-11-30 14:38:54 +0800586 """
Mike Truty49153d82012-08-21 22:27:30 -0500587 self.image_to_servo_usb(image_path, make_image_noninteractive)
Jon Salzc88e5b62011-11-30 14:38:54 +0800588
589 # Boot in recovery mode.
590 try:
Jon Salzc88e5b62011-11-30 14:38:54 +0800591 self.enable_recovery_mode()
Tom Wai-Hong Tam23869102012-01-17 15:41:30 +0800592 self.power_short_press()
Jon Salzc88e5b62011-11-30 14:38:54 +0800593 time.sleep(Servo.RECOVERY_BOOT_DELAY)
Tom Wai-Hong Tam922ed052012-01-09 12:27:52 +0800594 self.set('usb_mux_sel1', 'dut_sees_usbkey')
Jon Salzc88e5b62011-11-30 14:38:54 +0800595 self.disable_recovery_mode()
596
Simran Basi741b5d42012-05-18 11:27:15 -0700597 if host:
Jon Salzc88e5b62011-11-30 14:38:54 +0800598 logging.info('Running the recovery process on the DUT. '
Simran Basi741b5d42012-05-18 11:27:15 -0700599 'Will wait up to %d seconds for recovery to '
600 'complete.', wait_timeout)
601 start_time = time.time()
602 # Wait for the host to come up.
603 if host.wait_up(timeout=wait_timeout):
604 logging.info('Recovery process completed successfully in '
605 '%d seconds.', time.time() - start_time)
606 else:
Vic Yang3a7cf602012-11-07 17:28:39 +0800607 logging.error('Host failed to come back up in the allotted '
608 'time: %d seconds.', wait_timeout)
Jon Salzc88e5b62011-11-30 14:38:54 +0800609 logging.info('Removing the usb key from the DUT.')
610 self.disable_usb_hub()
Jon Salzc88e5b62011-11-30 14:38:54 +0800611 except:
612 # In case anything went wrong we want to make sure to do a clean
613 # reset.
614 self.disable_recovery_mode()
615 self.warm_reset()
616 raise
617
618
Todd Brochf24d2782011-08-19 10:55:41 -0700619 def _connect_servod(self, servo_host, servo_port):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700620 """Connect to the Servod process with XMLRPC.
621
622 Args:
623 servo_port: Port the Servod process is listening on.
624 """
Todd Brochf24d2782011-08-19 10:55:41 -0700625 remote = 'http://%s:%s' % (servo_host, servo_port)
Todd Broch96d83aa2011-08-29 14:37:38 -0700626 self._server = xmlrpclib.ServerProxy(remote)
Todd Brochf24d2782011-08-19 10:55:41 -0700627 try:
628 self._server.echo("ping-test")
Todd Broch96d83aa2011-08-29 14:37:38 -0700629 except:
630 logging.error('Connection to servod failed')
Todd Brochf24d2782011-08-19 10:55:41 -0700631 raise
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800632
633
634 def _scp_image(self, image_path):
635 """Copy image to the servo host.
636
637 When programming a firmware image on the DUT, the image must be
638 located on the host to which the servo device is connected. Sometimes
639 servo is controlled by a remote host, in this case the image needs to
640 be transferred to the remote host.
641
642 @param image_path: a string, name of the firmware image file to be
643 transferred.
644 @return: a string, full path name of the copied file on the remote.
645 """
646
647 dest_path = os.path.join('/tmp', os.path.basename(image_path))
648 scp_cmd = self._scp_cmd_template % (image_path, dest_path)
649 utils.system(scp_cmd)
650 return dest_path
651
652
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800653 def system(self, command, timeout=None):
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800654 """Execute the passed in command on the servod host."""
655 if self._sudo_required:
656 command = 'sudo -n %s' % command
657 if self._ssh_prefix:
658 command = "%s '%s'" % (self._ssh_prefix, command)
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800659 logging.info('Will execute on servo host: %s', command)
660 utils.system(command, timeout=timeout)
661
662
663 def system_output(self, command, timeout=None,
664 ignore_status=False, args=()):
665 """Execute the passed in command on the servod host, return stdout.
666
667 @param command, a string, the command to execute
668 @param timeout, an int, max number of seconds to wait til command
669 execution completes
670 @ignore_status, a Boolean, if true - ignore command's nonzero exit
671 status, otherwise an exception will be thrown
672 @param args, a tuple of strings, each becoming a separate command line
673 parameter for the command
674 @return: command's stdout as a string.
675 """
676 if self._sudo_required:
677 command = 'sudo -n %s' % command
678 if self._ssh_prefix:
679 command = "%s '%s'" % (self._ssh_prefix, command)
680 logging.info('Will execute and collect output on servo host: %s %s',
681 command, ' '.join("'%s'" % x for x in args))
682 return utils.system_output(command, timeout=timeout,
683 ignore_status=ignore_status, args=args)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800684
685
686 def program_ec(self, board, image):
687 """Program EC on a given board using given image."""
688 if not self.is_localhost():
689 image = self._scp_image(image)
690 programmer.program_ec(board, self, image)
691
692
693 def program_bootprom(self, board, image):
694 """Program bootprom on a given board using given image."""
695 if not self.is_localhost():
696 image = self._scp_image(image)
697 programmer.program_bootprom(board, self, image)