blob: 9b8d1e2fa3dc3388d0de633c30571c88b4a2d2f2 [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
Vadim Bendeburye7bd9362012-12-19 14:35:20 -080065 # Time to keep USB power off before and after USB mux direction is changed
66 USB_POWEROFF_DELAY = 2
Jon Salzc88e5b62011-11-30 14:38:54 +080067
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -070068 KEY_MATRIX_ALT_0 = {
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -070069 'ctrl_refresh': ['0', '0', '0', '1'],
70 'ctrl_d': ['0', '1', '0', '0'],
71 'd': ['0', '1', '1', '1'],
72 'ctrl_enter': ['1', '0', '0', '0'],
73 'enter': ['1', '0', '1', '1'],
74 'ctrl': ['1', '1', '0', '0'],
75 'refresh': ['1', '1', '0', '1'],
76 'unused': ['1', '1', '1', '0'],
77 'none': ['1', '1', '1', '1']}
Chris Masone6a0680f2012-03-02 08:40:00 -080078
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -070079 KEY_MATRIX_ALT_1 = {
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -070080 'ctrl_d': ['0', '0', '1', '0'],
81 'd': ['0', '0', '1', '1'],
82 'ctrl_enter': ['0', '1', '1', '0'],
83 'enter': ['0', '1', '1', '1'],
84 'ctrl_refresh': ['1', '0', '0', '1'],
85 'unused': ['1', '1', '0', '0'],
86 'refresh': ['1', '1', '0', '1'],
87 'ctrl': ['1', '1', '1', '0'],
88 'none': ['1', '1', '1', '1']}
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -070089
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -080090 KEY_MATRIX_ALT_2 = {
91 'ctrl_d': ['0', '0', '0', '1'],
92 'd': ['0', '0', '1', '1'],
93 'unused': ['0', '1', '1', '1'],
94 'rec_mode': ['1', '0', '0', '0'],
95 'ctrl_enter': ['1', '0', '0', '1'],
96 'enter': ['1', '0', '1', '1'],
97 'ctrl': ['1', '1', '0', '1'],
98 'refresh': ['1', '1', '1', '0'],
99 'ctrl_refresh': ['1', '1', '1', '1'],
100 'none': ['1', '1', '1', '1']}
101
102 KEY_MATRIX = [KEY_MATRIX_ALT_0, KEY_MATRIX_ALT_1, KEY_MATRIX_ALT_2]
J. Richard Barnette67ccb872012-04-19 16:34:56 -0700103
Chris Masone6a0680f2012-03-02 08:40:00 -0800104 @staticmethod
J. Richard Barnette67ccb872012-04-19 16:34:56 -0700105 def _make_servo_hostname(hostname):
106 host_parts = hostname.split('.')
107 host_parts[0] = host_parts[0] + '-servo'
108 return '.'.join(host_parts)
Chris Masone6a0680f2012-03-02 08:40:00 -0800109
J. Richard Barnette67ccb872012-04-19 16:34:56 -0700110 @staticmethod
111 def get_lab_servo(target_hostname):
112 """Instantiate a Servo for |target_hostname| in the lab.
Chris Masone6a0680f2012-03-02 08:40:00 -0800113
J. Richard Barnette67ccb872012-04-19 16:34:56 -0700114 Assuming that |target_hostname| is a device in the CrOS test
115 lab, create and return a Servo object pointed at the servo
116 attached to that DUT. The servo in the test lab is assumed
117 to already have servod up and running on it.
118
119 @param target_hostname: device whose servo we want to target.
Chris Masone6a0680f2012-03-02 08:40:00 -0800120 @return an appropriately configured Servo
121 """
J. Richard Barnette67ccb872012-04-19 16:34:56 -0700122 servo_host = Servo._make_servo_hostname(target_hostname)
123 if utils.host_is_in_lab_zone(servo_host):
Vic Yang3a7cf602012-11-07 17:28:39 +0800124 try:
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800125 return Servo(servo_host=servo_host, target_host=target_hostname)
Vic Yang3a7cf602012-11-07 17:28:39 +0800126 except: # pylint: disable=W0702
127 # TODO(jrbarnette): Long-term, if we can't get to
128 # a servo in the lab, we want to fail, so we should
129 # pass any exceptions along. Short-term, we're not
130 # ready to rely on servo, so we ignore failures.
131 pass
J. Richard Barnette67ccb872012-04-19 16:34:56 -0700132 return None
Chris Masone6a0680f2012-03-02 08:40:00 -0800133
134
Tom Wai-Hong Tamc5c14ef2012-11-20 16:33:37 +0800135 def __init__(self, servo_host='localhost', target_host=None,
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800136 servo_port=9999):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700137 """Sets up the servo communication infrastructure.
138
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800139 @param servo_host Name of the host where the servod process
140 is running.
141 @param target_host Name of the target which is connected to servo
142 @param servo_port Port the servod process is listening on.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700143 """
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -0700144 self._key_matrix = 0
J. Richard Barnette384056b2012-04-16 11:04:46 -0700145 self._server = None
Todd Brochf24d2782011-08-19 10:55:41 -0700146 self._connect_servod(servo_host, servo_port)
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800147 self._is_localhost = (servo_host == 'localhost')
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800148 self._target_host = target_host
149
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800150 # a string, showing what interface (host or dut) the USB device is
151 # connected to.
152 self._usb_position = None
153 self.set('dut_hub_pwren', 'on')
154 self.set('usb_mux_oe1', 'on')
155 self.switch_usbkey('host')
156
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800157 # Commands on the servo host must be run by the superuser. Our account
158 # on Beaglebone is root, but locally we might be running as a
159 # different user. If so - `sudo ' will have to be added to the
160 # commands.
161 if self._is_localhost:
162 self._sudo_required = utils.system_output('id -u') != '0'
163 self._ssh_prefix = ''
164 else:
165 common_options = '-o PasswordAuthentication=no'
166 self._sudo_required = False
167 self._ssh_prefix = 'ssh %s root@%s ' % (common_options, servo_host)
168 self._scp_cmd_template = 'scp %s ' % common_options
169 self._scp_cmd_template += '%s ' + 'root@' + servo_host + ':%s'
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800170
171 def get_target_hostname(self):
172 """Retrieves target (DUT) hostname."""
173 return self._target_host
Chrome Bot9a1137d2011-07-19 14:35:00 -0700174
175
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700176 def initialize_dut(self, cold_reset=False):
177 """Initializes a dut for testing purposes.
178
179 This sets various servo signals back to default values
180 appropriate for the target board. By default, if the DUT
181 is already on, it stays on. If the DUT is powered off
182 before initialization, its state afterward is unspecified.
183
184 If cold reset is requested, the DUT is guaranteed to be off
185 at the end of initialization, regardless of its initial
186 state.
187
188 Rationale: Basic initialization of servo sets the lid open,
189 when there is a lid. This operation won't affect powered on
190 units; however, setting the lid open may power on a unit
191 that's off, depending on factors outside the scope of this
192 function.
193
194 @param cold_reset If True, cold reset the device after
195 initialization.
196 """
197 self._server.hwinit()
198 if cold_reset:
199 self.cold_reset()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700200
201
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800202 def is_localhost(self):
203 """Is the servod hosted locally?
204
205 Returns:
206 True if local hosted; otherwise, False.
207 """
208 return self._is_localhost
209
210
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700211 def power_long_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700212 """Simulate a long power button press."""
J. Richard Barnettef12bff22012-07-18 14:41:06 -0700213 # After a long power press, the EC may ignore the next power
214 # button press (at least on Alex). To guarantee that this
215 # won't happen, we need to allow the EC one second to
216 # collect itself.
Chrome Bot9a1137d2011-07-19 14:35:00 -0700217 self.power_key(Servo.LONG_DELAY)
J. Richard Barnettef12bff22012-07-18 14:41:06 -0700218 time.sleep(1.0)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700219
220
221 def power_normal_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700222 """Simulate a normal power button press."""
223 self.power_key()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700224
225
226 def power_short_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700227 """Simulate a short power button press."""
228 self.power_key(Servo.SHORT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700229
230
Chrome Bot9a1137d2011-07-19 14:35:00 -0700231 def power_key(self, secs=NORMAL_TRANSITION_DELAY):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700232 """Simulate a power button press.
233
234 Args:
235 secs: Time in seconds to simulate the keypress.
236 """
Craig Harrison6b36b122011-06-28 17:58:43 -0700237 self.set_nocheck('pwr_button', 'press')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700238 time.sleep(secs)
Todd Broch31c82502011-08-29 08:14:39 -0700239 self.set_nocheck('pwr_button', 'release')
240 # TODO(tbroch) Different systems have different release times on the
241 # power button that this loop addresses. Longer term we may want to
242 # make this delay platform specific.
243 retry = 1
244 while True:
245 value = self.get('pwr_button')
246 if value == 'release' or retry > Servo.RELEASE_RETRY_MAX:
247 break
Todd Broch9753bd42012-03-21 10:15:08 -0700248 logging.info('Waiting for pwr_button to release, retry %d.', retry)
Todd Broch31c82502011-08-29 08:14:39 -0700249 retry += 1
250 time.sleep(Servo.SHORT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700251
252
253 def lid_open(self):
254 """Simulate opening the lid."""
Craig Harrison48997262011-06-27 14:31:10 -0700255 self.set_nocheck('lid_open', 'yes')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700256
257
258 def lid_close(self):
Craig Harrison48997262011-06-27 14:31:10 -0700259 """Simulate closing the lid.
260
261 Waits 6 seconds to ensure the device is fully asleep before returning.
262 """
263 self.set_nocheck('lid_open', 'no')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700264 time.sleep(Servo.SLEEP_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700265
266
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800267 def _press_keys(self, key):
268 """Simulate button presses.
269
270 Note, key presses will remain on indefinitely. See
271 _press_and_release_keys for release procedure.
272 """
Gediminas Ramanauskas9da80f22012-11-14 12:59:43 -0800273 (m1_a1, m1_a0, m2_a1, m2_a0) = self.KEY_MATRIX[self._key_matrix]['none']
Todd Broch9753bd42012-03-21 10:15:08 -0700274 self.set_nocheck('kbd_m2_a0', m2_a0)
275 self.set_nocheck('kbd_m2_a1', m2_a1)
276 self.set_nocheck('kbd_m1_a0', m1_a0)
277 self.set_nocheck('kbd_m1_a1', m1_a1)
Vic Yange262a3e2012-11-02 18:48:37 +0800278 self.set_nocheck('kbd_en', 'on')
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800279
Gediminas Ramanauskas9da80f22012-11-14 12:59:43 -0800280 (m1_a1, m1_a0, m2_a1, m2_a0) = self.KEY_MATRIX[self._key_matrix][key]
281 self.set_nocheck('kbd_m2_a0', m2_a0)
282 self.set_nocheck('kbd_m2_a1', m2_a1)
283 self.set_nocheck('kbd_m1_a0', m1_a0)
284 self.set_nocheck('kbd_m1_a1', m1_a1)
285
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800286
287 def _press_and_release_keys(self, key,
288 press_secs=SERVO_KEY_PRESS_DELAY):
289 """Simulate button presses and release."""
290 self._press_keys(key)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700291 time.sleep(press_secs)
292 self.set_nocheck('kbd_en', 'off')
293
294
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -0700295 def set_key_matrix(self, matrix=0):
296 """Set keyboard mapping"""
297 self._key_matrix = matrix
298
299
Chrome Bot9a1137d2011-07-19 14:35:00 -0700300 def ctrl_d(self):
301 """Simulate Ctrl-d simultaneous button presses."""
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700302 self._press_and_release_keys('ctrl_d')
Todd Broch9dfc3a82011-11-01 08:09:28 -0700303
304
Todd Broch9753bd42012-03-21 10:15:08 -0700305 def ctrl_enter(self):
306 """Simulate Ctrl-enter simultaneous button presses."""
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700307 self._press_and_release_keys('ctrl_enter')
Todd Broch9753bd42012-03-21 10:15:08 -0700308
309
Todd Broch9dfc3a82011-11-01 08:09:28 -0700310 def d_key(self):
311 """Simulate Enter key button press."""
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700312 self._press_and_release_keys('d')
Todd Broch9dfc3a82011-11-01 08:09:28 -0700313
314
315 def ctrl_key(self):
316 """Simulate Enter key button press."""
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700317 self._press_and_release_keys('ctrl')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700318
319
Chrome Bot9a1137d2011-07-19 14:35:00 -0700320 def enter_key(self):
321 """Simulate Enter key button press."""
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700322 self._press_and_release_keys('enter')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700323
324
Chrome Bot9a1137d2011-07-19 14:35:00 -0700325 def refresh_key(self):
326 """Simulate Refresh key (F3) button press."""
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700327 self._press_and_release_keys('refresh')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700328
329
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800330 def ctrl_refresh_key(self):
331 """Simulate Ctrl and Refresh (F3) simultaneous press.
332
333 This key combination is an alternative of Space key.
334 """
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700335 self._press_and_release_keys('ctrl_refresh')
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800336
337
Chrome Bot9a1137d2011-07-19 14:35:00 -0700338 def imaginary_key(self):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700339 """Simulate imaginary key button press.
340
341 Maps to a key that doesn't physically exist.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700342 """
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700343 self._press_and_release_keys('unused')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700344
345
Craig Harrison6b36b122011-06-28 17:58:43 -0700346 def enable_recovery_mode(self):
347 """Enable recovery mode on device."""
348 self.set('rec_mode', 'on')
349
350
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800351 def custom_recovery_mode(self):
352 """Custom key combination to enter recovery mode."""
353 self._press_keys('rec_mode')
354 self.power_normal_press()
355 time.sleep(self.SERVO_KEY_PRESS_DELAY)
356 self.set_nocheck('kbd_en', 'off')
357
358
Craig Harrison6b36b122011-06-28 17:58:43 -0700359 def disable_recovery_mode(self):
360 """Disable recovery mode on device."""
361 self.set('rec_mode', 'off')
362
363
364 def enable_development_mode(self):
365 """Enable development mode on device."""
366 self.set('dev_mode', 'on')
367
368
369 def disable_development_mode(self):
370 """Disable development mode on device."""
371 self.set('dev_mode', 'off')
372
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700373 def boot_devmode(self):
374 """Boot a dev-mode device that is powered off."""
Tom Wai-Hong Tam23869102012-01-17 15:41:30 +0800375 self.power_short_press()
Craig Harrison48997262011-06-27 14:31:10 -0700376 self.pass_devmode()
377
378
379 def pass_devmode(self):
380 """Pass through boot screens in dev-mode."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700381 time.sleep(Servo.BOOT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700382 self.ctrl_d()
Chrome Bot9a1137d2011-07-19 14:35:00 -0700383 time.sleep(Servo.BOOT_DELAY)
Craig Harrison48997262011-06-27 14:31:10 -0700384
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700385
Craig Harrison6b36b122011-06-28 17:58:43 -0700386 def cold_reset(self):
387 """Perform a cold reset of the EC.
388
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700389 This has the side effect of shutting off the device. The
390 device is guaranteed to be off at the end of this call.
Craig Harrison6b36b122011-06-28 17:58:43 -0700391 """
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700392 # After the reset, give the EC the time it needs to
393 # re-initialize.
Craig Harrison6b36b122011-06-28 17:58:43 -0700394 self.set('cold_reset', 'on')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700395 time.sleep(Servo.SERVO_SEND_SIGNAL_DELAY)
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700396 self.set('cold_reset', 'off')
397 time.sleep(self._EC_RESET_DELAY)
Craig Harrison6b36b122011-06-28 17:58:43 -0700398
399
400 def warm_reset(self):
401 """Perform a warm reset of the device.
402
403 Has the side effect of restarting the device.
404 """
405 self.set('warm_reset', 'on')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700406 time.sleep(Servo.SERVO_SEND_SIGNAL_DELAY)
Craig Harrison6b36b122011-06-28 17:58:43 -0700407 self.set('warm_reset', 'off')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700408
409
Todd Brochefe72cb2012-07-11 19:58:53 -0700410 def _get_xmlrpclib_exception(self, xmlexc):
411 """Get meaningful exception string from xmlrpc.
412
413 Args:
414 xmlexc: xmlrpclib.Fault object
415
416 xmlrpclib.Fault.faultString has the following format:
417
418 <type 'exception type'>:'actual error message'
419
420 Parse and return the real exception from the servod side instead of the
421 less meaningful one like,
422 <Fault 1: "<type 'exceptions.AttributeError'>:'tca6416' object has no
423 attribute 'hw_driver'">
424
425 Returns:
426 string of underlying exception raised in servod.
427 """
428 return re.sub('^.*>:', '', xmlexc.faultString)
429
430
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700431 def get(self, gpio_name):
432 """Get the value of a gpio from Servod."""
433 assert gpio_name
Todd Brochefe72cb2012-07-11 19:58:53 -0700434 try:
435 return self._server.get(gpio_name)
436 except xmlrpclib.Fault as e:
437 err_msg = "Getting '%s' :: %s" % \
438 (gpio_name, self._get_xmlrpclib_exception(e))
439 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700440
441
442 def set(self, gpio_name, gpio_value):
443 """Set and check the value of a gpio using Servod."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700444 self.set_nocheck(gpio_name, gpio_value)
Todd Brochcf7c6652012-02-24 13:03:59 -0800445 retry_count = Servo.GET_RETRY_MAX
446 while gpio_value != self.get(gpio_name) and retry_count:
447 logging.warn("%s != %s, retry %d", gpio_name, gpio_value,
448 retry_count)
449 retry_count -= 1
450 time.sleep(Servo.SHORT_DELAY)
451 if not retry_count:
452 assert gpio_value == self.get(gpio_name), \
453 'Servo failed to set %s to %s' % (gpio_name, gpio_value)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700454
455
456 def set_nocheck(self, gpio_name, gpio_value):
457 """Set the value of a gpio using Servod."""
458 assert gpio_name and gpio_value
Todd Broch9753bd42012-03-21 10:15:08 -0700459 logging.info('Setting %s to %s', gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700460 try:
461 self._server.set(gpio_name, gpio_value)
462 except xmlrpclib.Fault as e:
463 err_msg = "Setting '%s' to '%s' :: %s" % \
464 (gpio_name, gpio_value, self._get_xmlrpclib_exception(e))
465 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700466
467
Jon Salzc88e5b62011-11-30 14:38:54 +0800468 # TODO(waihong) It may fail if multiple servo's are connected to the same
469 # host. Should look for a better way, like the USB serial name, to identify
470 # the USB device.
Simran Basi741b5d42012-05-18 11:27:15 -0700471 # TODO(sbasi) Remove this code from autoserv once firmware tests have been
472 # updated.
Jon Salzc88e5b62011-11-30 14:38:54 +0800473 def probe_host_usb_dev(self):
474 """Probe the USB disk device plugged-in the servo from the host side.
475
476 It tries to switch the USB mux to make the host unable to see the
477 USB disk and compares the result difference.
478
Jon Salzc88e5b62011-11-30 14:38:54 +0800479 Returns:
480 A string of USB disk path, like '/dev/sdb', or None if not existed.
481 """
482 cmd = 'ls /dev/sd[a-z]'
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800483 original_value = self.get_usbkey_direction()
Jon Salzc88e5b62011-11-30 14:38:54 +0800484
485 # Make the host unable to see the USB disk.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800486 if original_value != 'dut':
487 self.switch_usbkey('dut')
Jon Salzc88e5b62011-11-30 14:38:54 +0800488 time.sleep(self.USB_DETECTION_DELAY)
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800489 no_usb_set = set(self.system_output(cmd, ignore_status=True).split())
Jon Salzc88e5b62011-11-30 14:38:54 +0800490
491 # Make the host able to see the USB disk.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800492 self.switch_usbkey('host')
Jon Salzc88e5b62011-11-30 14:38:54 +0800493 time.sleep(self.USB_DETECTION_DELAY)
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800494 has_usb_set = set(self.system_output(cmd, ignore_status=True).split())
Jon Salzc88e5b62011-11-30 14:38:54 +0800495
496 # Back to its original value.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800497 if original_value != self.get_usbkey_direction():
498 self.switch_usbkey(original_value)
Jon Salzc88e5b62011-11-30 14:38:54 +0800499 time.sleep(self.USB_DETECTION_DELAY)
500
501 diff_set = has_usb_set - no_usb_set
502 if len(diff_set) == 1:
503 return diff_set.pop()
504 else:
505 return None
506
507
Mike Truty49153d82012-08-21 22:27:30 -0500508 def image_to_servo_usb(self, image_path=None,
509 make_image_noninteractive=False):
510 """Install an image to the USB key plugged into the servo.
511
512 This method may copy any image to the servo USB key including a
513 recovery image or a test image. These images are frequently used
514 for test purposes such as restoring a corrupted image or conducting
515 an upgrade of ec/fw/kernel as part of a test of a specific image part.
516
517 Args:
518 image_path: Path on the host to the recovery image.
519 make_image_noninteractive: Make the recovery image noninteractive,
520 therefore the DUT will reboot
521 automatically after installation.
522 """
523 # Turn the device off. This should happen before USB key detection, to
524 # prevent a recovery destined DUT from sensing the USB key due to the
525 # autodetection procedure.
526 self.initialize_dut(cold_reset=True)
527
528 # Set up Servo's usb mux.
529 self.set('prtctl4_pwren', 'on')
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800530 self.switch_usbkey('host')
Mike Truty49153d82012-08-21 22:27:30 -0500531 if image_path:
Tom Wai-Hong Tam42f136d2012-10-26 11:11:23 +0800532 logging.info('Searching for usb device and copying image to it. '
533 'Please wait a few minutes...')
Mike Truty49153d82012-08-21 22:27:30 -0500534 if not self._server.download_image_to_usb(image_path):
535 logging.error('Failed to transfer requested image to USB. '
536 'Please take a look at Servo Logs.')
537 raise error.AutotestError('Download image to usb failed.')
538 if make_image_noninteractive:
539 logging.info('Making image noninteractive')
540 if not self._server.make_image_noninteractive():
541 logging.error('Failed to make image noninteractive. '
542 'Please take a look at Servo Logs.')
543
544
Simran Basi741b5d42012-05-18 11:27:15 -0700545 def install_recovery_image(self, image_path=None,
546 wait_timeout=RECOVERY_INSTALL_DELAY,
547 make_image_noninteractive=False,
548 host=None):
Jon Salzc88e5b62011-11-30 14:38:54 +0800549 """Install the recovery image specied by the path onto the DUT.
550
551 This method uses google recovery mode to install a recovery image
552 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 +0800553 board specified by the usb_dev. If no image path is specified
Jon Salzc88e5b62011-11-30 14:38:54 +0800554 we use the recovery image already on the usb image.
555
556 Args:
557 image_path: Path on the host to the recovery image.
Gilad Arnold9df73de2012-03-14 09:35:08 -0700558 wait_timeout: How long to wait for completion; default is
559 determined by a constant.
Simran Basi741b5d42012-05-18 11:27:15 -0700560 make_image_noninteractive: Make the recovery image noninteractive,
561 therefore the DUT will reboot
562 automatically after installation.
563 host: Host object for the DUT that the installation process is
564 running on. If provided, will wait to see if the host is back
565 up after starting recovery mode.
Jon Salzc88e5b62011-11-30 14:38:54 +0800566 """
Mike Truty49153d82012-08-21 22:27:30 -0500567 self.image_to_servo_usb(image_path, make_image_noninteractive)
Jon Salzc88e5b62011-11-30 14:38:54 +0800568
569 # Boot in recovery mode.
570 try:
Jon Salzc88e5b62011-11-30 14:38:54 +0800571 self.enable_recovery_mode()
Tom Wai-Hong Tam23869102012-01-17 15:41:30 +0800572 self.power_short_press()
Jon Salzc88e5b62011-11-30 14:38:54 +0800573 time.sleep(Servo.RECOVERY_BOOT_DELAY)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800574 self.switch_usbkey('dut')
Jon Salzc88e5b62011-11-30 14:38:54 +0800575 self.disable_recovery_mode()
576
Simran Basi741b5d42012-05-18 11:27:15 -0700577 if host:
Jon Salzc88e5b62011-11-30 14:38:54 +0800578 logging.info('Running the recovery process on the DUT. '
Simran Basi741b5d42012-05-18 11:27:15 -0700579 'Will wait up to %d seconds for recovery to '
580 'complete.', wait_timeout)
581 start_time = time.time()
582 # Wait for the host to come up.
583 if host.wait_up(timeout=wait_timeout):
584 logging.info('Recovery process completed successfully in '
585 '%d seconds.', time.time() - start_time)
586 else:
Vic Yang3a7cf602012-11-07 17:28:39 +0800587 logging.error('Host failed to come back up in the allotted '
588 'time: %d seconds.', wait_timeout)
Jon Salzc88e5b62011-11-30 14:38:54 +0800589 logging.info('Removing the usb key from the DUT.')
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800590 self.switch_usbkey('host')
Jon Salzc88e5b62011-11-30 14:38:54 +0800591 except:
592 # In case anything went wrong we want to make sure to do a clean
593 # reset.
594 self.disable_recovery_mode()
595 self.warm_reset()
596 raise
597
598
Todd Brochf24d2782011-08-19 10:55:41 -0700599 def _connect_servod(self, servo_host, servo_port):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700600 """Connect to the Servod process with XMLRPC.
601
602 Args:
603 servo_port: Port the Servod process is listening on.
604 """
Todd Brochf24d2782011-08-19 10:55:41 -0700605 remote = 'http://%s:%s' % (servo_host, servo_port)
Todd Broch96d83aa2011-08-29 14:37:38 -0700606 self._server = xmlrpclib.ServerProxy(remote)
Todd Brochf24d2782011-08-19 10:55:41 -0700607 try:
608 self._server.echo("ping-test")
Todd Broch96d83aa2011-08-29 14:37:38 -0700609 except:
610 logging.error('Connection to servod failed')
Todd Brochf24d2782011-08-19 10:55:41 -0700611 raise
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800612
613
614 def _scp_image(self, image_path):
615 """Copy image to the servo host.
616
617 When programming a firmware image on the DUT, the image must be
618 located on the host to which the servo device is connected. Sometimes
619 servo is controlled by a remote host, in this case the image needs to
620 be transferred to the remote host.
621
622 @param image_path: a string, name of the firmware image file to be
623 transferred.
624 @return: a string, full path name of the copied file on the remote.
625 """
626
627 dest_path = os.path.join('/tmp', os.path.basename(image_path))
628 scp_cmd = self._scp_cmd_template % (image_path, dest_path)
629 utils.system(scp_cmd)
630 return dest_path
631
632
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800633 def system(self, command, timeout=None):
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800634 """Execute the passed in command on the servod host."""
635 if self._sudo_required:
636 command = 'sudo -n %s' % command
637 if self._ssh_prefix:
638 command = "%s '%s'" % (self._ssh_prefix, command)
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800639 logging.info('Will execute on servo host: %s', command)
640 utils.system(command, timeout=timeout)
641
642
643 def system_output(self, command, timeout=None,
644 ignore_status=False, args=()):
645 """Execute the passed in command on the servod host, return stdout.
646
647 @param command, a string, the command to execute
648 @param timeout, an int, max number of seconds to wait til command
649 execution completes
650 @ignore_status, a Boolean, if true - ignore command's nonzero exit
651 status, otherwise an exception will be thrown
652 @param args, a tuple of strings, each becoming a separate command line
653 parameter for the command
654 @return: command's stdout as a string.
655 """
656 if self._sudo_required:
657 command = 'sudo -n %s' % command
658 if self._ssh_prefix:
659 command = "%s '%s'" % (self._ssh_prefix, command)
660 logging.info('Will execute and collect output on servo host: %s %s',
661 command, ' '.join("'%s'" % x for x in args))
662 return utils.system_output(command, timeout=timeout,
663 ignore_status=ignore_status, args=args)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800664
665
666 def program_ec(self, board, image):
667 """Program EC on a given board using given image."""
668 if not self.is_localhost():
669 image = self._scp_image(image)
670 programmer.program_ec(board, self, image)
671
672
673 def program_bootprom(self, board, image):
674 """Program bootprom on a given board using given image."""
675 if not self.is_localhost():
676 image = self._scp_image(image)
677 programmer.program_bootprom(board, self, image)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800678
679 def switch_usbkey(self, side):
680 """Connect USB flash stick to either host or DUT.
681
682 This function switches the servo multiplexer to provide electrical
683 connection between the USB port J3 and either host or DUT side.
684
685 Switching is accompanied by powercycling of the USB stick, because it
686 sometimes gets wedged if the mux is switched while the stick power is
687 on.
688
689 @param side: a string, either 'dut' or 'host' - indicates which side
690 the USB flash device is required to be connected to.
691
692 @raise: error.TestError in case the parameter is neither 'dut' not
693 'host'
694 """
695
696 if self._usb_position == side:
697 return
698
699 if side == 'host':
700 mux_direction = 'servo_sees_usbkey'
701 elif side == 'dut':
702 mux_direction = 'dut_sees_usbkey'
703 else:
704 raise error.TestError('unknown USB mux setting: %s' % side)
705
706 self.set('prtctl4_pwren', 'off')
707 time.sleep(self.USB_POWEROFF_DELAY)
708 self.set('usb_mux_sel1', mux_direction)
709 time.sleep(self.USB_POWEROFF_DELAY)
710 self.set('prtctl4_pwren', 'on')
711
712 self._usb_position = side
713
714
715 def get_usbkey_direction(self):
716 """Get name of the side the USB device is connected to.
717
718 @return a string, either 'dut' or 'host'
719 """
720 if not self._usb_position:
721 if self.get('usb_mux_sel1').starstwith('dut'):
722 self._usb_position = 'dut'
723 else:
724 self._usb_position = 'host'
725 return self._usb_position