blob: 83f6377f50af3a62e40f5b5b7ca0f5d10551a254 [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
J. Richard Barnetted5f807a2013-02-11 16:51:00 -0800104 def __init__(self, servo_host='localhost', servo_port=9999):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700105 """Sets up the servo communication infrastructure.
106
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800107 @param servo_host Name of the host where the servod process
108 is running.
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800109 @param servo_port Port the servod process is listening on.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700110 """
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -0700111 self._key_matrix = 0
J. Richard Barnette384056b2012-04-16 11:04:46 -0700112 self._server = None
Todd Brochf24d2782011-08-19 10:55:41 -0700113 self._connect_servod(servo_host, servo_port)
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800114 self._is_localhost = (servo_host == 'localhost')
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800115
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800116 # a string, showing what interface (host or dut) the USB device is
117 # connected to.
118 self._usb_position = None
119 self.set('dut_hub_pwren', 'on')
120 self.set('usb_mux_oe1', 'on')
121 self.switch_usbkey('host')
122
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800123 # Commands on the servo host must be run by the superuser. Our account
124 # on Beaglebone is root, but locally we might be running as a
125 # different user. If so - `sudo ' will have to be added to the
126 # commands.
127 if self._is_localhost:
128 self._sudo_required = utils.system_output('id -u') != '0'
129 self._ssh_prefix = ''
130 else:
131 common_options = '-o PasswordAuthentication=no'
132 self._sudo_required = False
133 self._ssh_prefix = 'ssh %s root@%s ' % (common_options, servo_host)
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800134 self._scp_cmd_template = 'scp -r %s ' % common_options
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800135 self._scp_cmd_template += '%s ' + 'root@' + servo_host + ':%s'
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800136
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700137 def initialize_dut(self, cold_reset=False):
138 """Initializes a dut for testing purposes.
139
140 This sets various servo signals back to default values
141 appropriate for the target board. By default, if the DUT
142 is already on, it stays on. If the DUT is powered off
143 before initialization, its state afterward is unspecified.
144
145 If cold reset is requested, the DUT is guaranteed to be off
146 at the end of initialization, regardless of its initial
147 state.
148
149 Rationale: Basic initialization of servo sets the lid open,
150 when there is a lid. This operation won't affect powered on
151 units; however, setting the lid open may power on a unit
152 that's off, depending on factors outside the scope of this
153 function.
154
155 @param cold_reset If True, cold reset the device after
156 initialization.
157 """
158 self._server.hwinit()
159 if cold_reset:
160 self.cold_reset()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700161
162
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800163 def is_localhost(self):
164 """Is the servod hosted locally?
165
166 Returns:
167 True if local hosted; otherwise, False.
168 """
169 return self._is_localhost
170
171
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700172 def power_long_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700173 """Simulate a long power button press."""
J. Richard Barnettef12bff22012-07-18 14:41:06 -0700174 # After a long power press, the EC may ignore the next power
175 # button press (at least on Alex). To guarantee that this
176 # won't happen, we need to allow the EC one second to
177 # collect itself.
Chrome Bot9a1137d2011-07-19 14:35:00 -0700178 self.power_key(Servo.LONG_DELAY)
J. Richard Barnettef12bff22012-07-18 14:41:06 -0700179 time.sleep(1.0)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700180
181
182 def power_normal_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700183 """Simulate a normal power button press."""
184 self.power_key()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700185
186
187 def power_short_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700188 """Simulate a short power button press."""
189 self.power_key(Servo.SHORT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700190
191
Chrome Bot9a1137d2011-07-19 14:35:00 -0700192 def power_key(self, secs=NORMAL_TRANSITION_DELAY):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700193 """Simulate a power button press.
194
195 Args:
196 secs: Time in seconds to simulate the keypress.
197 """
Craig Harrison6b36b122011-06-28 17:58:43 -0700198 self.set_nocheck('pwr_button', 'press')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700199 time.sleep(secs)
Todd Broch31c82502011-08-29 08:14:39 -0700200 self.set_nocheck('pwr_button', 'release')
201 # TODO(tbroch) Different systems have different release times on the
202 # power button that this loop addresses. Longer term we may want to
203 # make this delay platform specific.
204 retry = 1
205 while True:
206 value = self.get('pwr_button')
207 if value == 'release' or retry > Servo.RELEASE_RETRY_MAX:
208 break
Todd Broch9753bd42012-03-21 10:15:08 -0700209 logging.info('Waiting for pwr_button to release, retry %d.', retry)
Todd Broch31c82502011-08-29 08:14:39 -0700210 retry += 1
211 time.sleep(Servo.SHORT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700212
213
214 def lid_open(self):
215 """Simulate opening the lid."""
Craig Harrison48997262011-06-27 14:31:10 -0700216 self.set_nocheck('lid_open', 'yes')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700217
218
219 def lid_close(self):
Craig Harrison48997262011-06-27 14:31:10 -0700220 """Simulate closing the lid.
221
222 Waits 6 seconds to ensure the device is fully asleep before returning.
223 """
224 self.set_nocheck('lid_open', 'no')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700225 time.sleep(Servo.SLEEP_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700226
227
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800228 def _press_keys(self, key):
229 """Simulate button presses.
230
231 Note, key presses will remain on indefinitely. See
232 _press_and_release_keys for release procedure.
233 """
Gediminas Ramanauskas9da80f22012-11-14 12:59:43 -0800234 (m1_a1, m1_a0, m2_a1, m2_a0) = self.KEY_MATRIX[self._key_matrix]['none']
Todd Broch9753bd42012-03-21 10:15:08 -0700235 self.set_nocheck('kbd_m2_a0', m2_a0)
236 self.set_nocheck('kbd_m2_a1', m2_a1)
237 self.set_nocheck('kbd_m1_a0', m1_a0)
238 self.set_nocheck('kbd_m1_a1', m1_a1)
Vic Yange262a3e2012-11-02 18:48:37 +0800239 self.set_nocheck('kbd_en', 'on')
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800240
Gediminas Ramanauskas9da80f22012-11-14 12:59:43 -0800241 (m1_a1, m1_a0, m2_a1, m2_a0) = self.KEY_MATRIX[self._key_matrix][key]
242 self.set_nocheck('kbd_m2_a0', m2_a0)
243 self.set_nocheck('kbd_m2_a1', m2_a1)
244 self.set_nocheck('kbd_m1_a0', m1_a0)
245 self.set_nocheck('kbd_m1_a1', m1_a1)
246
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800247
248 def _press_and_release_keys(self, key,
249 press_secs=SERVO_KEY_PRESS_DELAY):
250 """Simulate button presses and release."""
251 self._press_keys(key)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700252 time.sleep(press_secs)
253 self.set_nocheck('kbd_en', 'off')
254
255
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -0700256 def set_key_matrix(self, matrix=0):
257 """Set keyboard mapping"""
258 self._key_matrix = matrix
259
260
Chrome Bot9a1137d2011-07-19 14:35:00 -0700261 def ctrl_d(self):
262 """Simulate Ctrl-d simultaneous button presses."""
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700263 self._press_and_release_keys('ctrl_d')
Todd Broch9dfc3a82011-11-01 08:09:28 -0700264
265
Todd Broch9753bd42012-03-21 10:15:08 -0700266 def ctrl_enter(self):
267 """Simulate Ctrl-enter simultaneous button presses."""
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700268 self._press_and_release_keys('ctrl_enter')
Todd Broch9753bd42012-03-21 10:15:08 -0700269
270
Todd Broch9dfc3a82011-11-01 08:09:28 -0700271 def d_key(self):
272 """Simulate Enter key button press."""
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700273 self._press_and_release_keys('d')
Todd Broch9dfc3a82011-11-01 08:09:28 -0700274
275
276 def ctrl_key(self):
277 """Simulate Enter key button press."""
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700278 self._press_and_release_keys('ctrl')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700279
280
Chrome Bot9a1137d2011-07-19 14:35:00 -0700281 def enter_key(self):
282 """Simulate Enter key button press."""
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700283 self._press_and_release_keys('enter')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700284
285
Chrome Bot9a1137d2011-07-19 14:35:00 -0700286 def refresh_key(self):
287 """Simulate Refresh key (F3) button press."""
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700288 self._press_and_release_keys('refresh')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700289
290
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800291 def ctrl_refresh_key(self):
292 """Simulate Ctrl and Refresh (F3) simultaneous press.
293
294 This key combination is an alternative of Space key.
295 """
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700296 self._press_and_release_keys('ctrl_refresh')
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800297
298
Chrome Bot9a1137d2011-07-19 14:35:00 -0700299 def imaginary_key(self):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700300 """Simulate imaginary key button press.
301
302 Maps to a key that doesn't physically exist.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700303 """
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700304 self._press_and_release_keys('unused')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700305
306
Craig Harrison6b36b122011-06-28 17:58:43 -0700307 def enable_recovery_mode(self):
308 """Enable recovery mode on device."""
309 self.set('rec_mode', 'on')
310
311
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800312 def custom_recovery_mode(self):
313 """Custom key combination to enter recovery mode."""
314 self._press_keys('rec_mode')
315 self.power_normal_press()
316 time.sleep(self.SERVO_KEY_PRESS_DELAY)
317 self.set_nocheck('kbd_en', 'off')
318
319
Craig Harrison6b36b122011-06-28 17:58:43 -0700320 def disable_recovery_mode(self):
321 """Disable recovery mode on device."""
322 self.set('rec_mode', 'off')
323
324
325 def enable_development_mode(self):
326 """Enable development mode on device."""
327 self.set('dev_mode', 'on')
328
329
330 def disable_development_mode(self):
331 """Disable development mode on device."""
332 self.set('dev_mode', 'off')
333
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700334 def boot_devmode(self):
335 """Boot a dev-mode device that is powered off."""
Tom Wai-Hong Tam23869102012-01-17 15:41:30 +0800336 self.power_short_press()
Craig Harrison48997262011-06-27 14:31:10 -0700337 self.pass_devmode()
338
339
340 def pass_devmode(self):
341 """Pass through boot screens in dev-mode."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700342 time.sleep(Servo.BOOT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700343 self.ctrl_d()
Chrome Bot9a1137d2011-07-19 14:35:00 -0700344 time.sleep(Servo.BOOT_DELAY)
Craig Harrison48997262011-06-27 14:31:10 -0700345
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700346
Craig Harrison6b36b122011-06-28 17:58:43 -0700347 def cold_reset(self):
348 """Perform a cold reset of the EC.
349
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700350 This has the side effect of shutting off the device. The
351 device is guaranteed to be off at the end of this call.
Craig Harrison6b36b122011-06-28 17:58:43 -0700352 """
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700353 # After the reset, give the EC the time it needs to
354 # re-initialize.
Craig Harrison6b36b122011-06-28 17:58:43 -0700355 self.set('cold_reset', 'on')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700356 time.sleep(Servo.SERVO_SEND_SIGNAL_DELAY)
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700357 self.set('cold_reset', 'off')
358 time.sleep(self._EC_RESET_DELAY)
Craig Harrison6b36b122011-06-28 17:58:43 -0700359
360
361 def warm_reset(self):
362 """Perform a warm reset of the device.
363
364 Has the side effect of restarting the device.
365 """
366 self.set('warm_reset', 'on')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700367 time.sleep(Servo.SERVO_SEND_SIGNAL_DELAY)
Craig Harrison6b36b122011-06-28 17:58:43 -0700368 self.set('warm_reset', 'off')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700369
370
Todd Brochefe72cb2012-07-11 19:58:53 -0700371 def _get_xmlrpclib_exception(self, xmlexc):
372 """Get meaningful exception string from xmlrpc.
373
374 Args:
375 xmlexc: xmlrpclib.Fault object
376
377 xmlrpclib.Fault.faultString has the following format:
378
379 <type 'exception type'>:'actual error message'
380
381 Parse and return the real exception from the servod side instead of the
382 less meaningful one like,
383 <Fault 1: "<type 'exceptions.AttributeError'>:'tca6416' object has no
384 attribute 'hw_driver'">
385
386 Returns:
387 string of underlying exception raised in servod.
388 """
389 return re.sub('^.*>:', '', xmlexc.faultString)
390
391
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700392 def get(self, gpio_name):
393 """Get the value of a gpio from Servod."""
394 assert gpio_name
Todd Brochefe72cb2012-07-11 19:58:53 -0700395 try:
396 return self._server.get(gpio_name)
397 except xmlrpclib.Fault as e:
398 err_msg = "Getting '%s' :: %s" % \
399 (gpio_name, self._get_xmlrpclib_exception(e))
400 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700401
402
403 def set(self, gpio_name, gpio_value):
404 """Set and check the value of a gpio using Servod."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700405 self.set_nocheck(gpio_name, gpio_value)
Todd Brochcf7c6652012-02-24 13:03:59 -0800406 retry_count = Servo.GET_RETRY_MAX
407 while gpio_value != self.get(gpio_name) and retry_count:
408 logging.warn("%s != %s, retry %d", gpio_name, gpio_value,
409 retry_count)
410 retry_count -= 1
411 time.sleep(Servo.SHORT_DELAY)
412 if not retry_count:
413 assert gpio_value == self.get(gpio_name), \
414 'Servo failed to set %s to %s' % (gpio_name, gpio_value)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700415
416
417 def set_nocheck(self, gpio_name, gpio_value):
418 """Set the value of a gpio using Servod."""
419 assert gpio_name and gpio_value
Todd Broch9753bd42012-03-21 10:15:08 -0700420 logging.info('Setting %s to %s', gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700421 try:
422 self._server.set(gpio_name, gpio_value)
423 except xmlrpclib.Fault as e:
424 err_msg = "Setting '%s' to '%s' :: %s" % \
425 (gpio_name, gpio_value, self._get_xmlrpclib_exception(e))
426 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700427
428
Jon Salzc88e5b62011-11-30 14:38:54 +0800429 # TODO(waihong) It may fail if multiple servo's are connected to the same
430 # host. Should look for a better way, like the USB serial name, to identify
431 # the USB device.
Simran Basi741b5d42012-05-18 11:27:15 -0700432 # TODO(sbasi) Remove this code from autoserv once firmware tests have been
433 # updated.
Jon Salzc88e5b62011-11-30 14:38:54 +0800434 def probe_host_usb_dev(self):
435 """Probe the USB disk device plugged-in the servo from the host side.
436
437 It tries to switch the USB mux to make the host unable to see the
438 USB disk and compares the result difference.
439
Jon Salzc88e5b62011-11-30 14:38:54 +0800440 Returns:
441 A string of USB disk path, like '/dev/sdb', or None if not existed.
442 """
443 cmd = 'ls /dev/sd[a-z]'
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800444 original_value = self.get_usbkey_direction()
Jon Salzc88e5b62011-11-30 14:38:54 +0800445
446 # Make the host unable to see the USB disk.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800447 if original_value != 'dut':
448 self.switch_usbkey('dut')
Jon Salzc88e5b62011-11-30 14:38:54 +0800449 time.sleep(self.USB_DETECTION_DELAY)
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800450 no_usb_set = set(self.system_output(cmd, ignore_status=True).split())
Jon Salzc88e5b62011-11-30 14:38:54 +0800451
452 # Make the host able to see the USB disk.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800453 self.switch_usbkey('host')
Jon Salzc88e5b62011-11-30 14:38:54 +0800454 time.sleep(self.USB_DETECTION_DELAY)
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800455 has_usb_set = set(self.system_output(cmd, ignore_status=True).split())
Jon Salzc88e5b62011-11-30 14:38:54 +0800456
457 # Back to its original value.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800458 if original_value != self.get_usbkey_direction():
459 self.switch_usbkey(original_value)
Jon Salzc88e5b62011-11-30 14:38:54 +0800460 time.sleep(self.USB_DETECTION_DELAY)
461
462 diff_set = has_usb_set - no_usb_set
463 if len(diff_set) == 1:
464 return diff_set.pop()
465 else:
466 return None
467
468
Mike Truty49153d82012-08-21 22:27:30 -0500469 def image_to_servo_usb(self, image_path=None,
470 make_image_noninteractive=False):
471 """Install an image to the USB key plugged into the servo.
472
473 This method may copy any image to the servo USB key including a
474 recovery image or a test image. These images are frequently used
475 for test purposes such as restoring a corrupted image or conducting
476 an upgrade of ec/fw/kernel as part of a test of a specific image part.
477
478 Args:
479 image_path: Path on the host to the recovery image.
480 make_image_noninteractive: Make the recovery image noninteractive,
481 therefore the DUT will reboot
482 automatically after installation.
483 """
484 # Turn the device off. This should happen before USB key detection, to
485 # prevent a recovery destined DUT from sensing the USB key due to the
486 # autodetection procedure.
487 self.initialize_dut(cold_reset=True)
488
489 # Set up Servo's usb mux.
490 self.set('prtctl4_pwren', 'on')
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800491 self.switch_usbkey('host')
Mike Truty49153d82012-08-21 22:27:30 -0500492 if image_path:
Tom Wai-Hong Tam42f136d2012-10-26 11:11:23 +0800493 logging.info('Searching for usb device and copying image to it. '
494 'Please wait a few minutes...')
Mike Truty49153d82012-08-21 22:27:30 -0500495 if not self._server.download_image_to_usb(image_path):
496 logging.error('Failed to transfer requested image to USB. '
497 'Please take a look at Servo Logs.')
498 raise error.AutotestError('Download image to usb failed.')
499 if make_image_noninteractive:
500 logging.info('Making image noninteractive')
501 if not self._server.make_image_noninteractive():
502 logging.error('Failed to make image noninteractive. '
503 'Please take a look at Servo Logs.')
504
505
Simran Basi741b5d42012-05-18 11:27:15 -0700506 def install_recovery_image(self, image_path=None,
507 wait_timeout=RECOVERY_INSTALL_DELAY,
508 make_image_noninteractive=False,
509 host=None):
Jon Salzc88e5b62011-11-30 14:38:54 +0800510 """Install the recovery image specied by the path onto the DUT.
511
512 This method uses google recovery mode to install a recovery image
513 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 +0800514 board specified by the usb_dev. If no image path is specified
Jon Salzc88e5b62011-11-30 14:38:54 +0800515 we use the recovery image already on the usb image.
516
517 Args:
518 image_path: Path on the host to the recovery image.
Gilad Arnold9df73de2012-03-14 09:35:08 -0700519 wait_timeout: How long to wait for completion; default is
520 determined by a constant.
Simran Basi741b5d42012-05-18 11:27:15 -0700521 make_image_noninteractive: Make the recovery image noninteractive,
522 therefore the DUT will reboot
523 automatically after installation.
524 host: Host object for the DUT that the installation process is
525 running on. If provided, will wait to see if the host is back
526 up after starting recovery mode.
Jon Salzc88e5b62011-11-30 14:38:54 +0800527 """
Mike Truty49153d82012-08-21 22:27:30 -0500528 self.image_to_servo_usb(image_path, make_image_noninteractive)
Jon Salzc88e5b62011-11-30 14:38:54 +0800529
530 # Boot in recovery mode.
531 try:
Jon Salzc88e5b62011-11-30 14:38:54 +0800532 self.enable_recovery_mode()
Tom Wai-Hong Tam23869102012-01-17 15:41:30 +0800533 self.power_short_press()
Jon Salzc88e5b62011-11-30 14:38:54 +0800534 time.sleep(Servo.RECOVERY_BOOT_DELAY)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800535 self.switch_usbkey('dut')
Jon Salzc88e5b62011-11-30 14:38:54 +0800536 self.disable_recovery_mode()
537
Simran Basi741b5d42012-05-18 11:27:15 -0700538 if host:
Jon Salzc88e5b62011-11-30 14:38:54 +0800539 logging.info('Running the recovery process on the DUT. '
Simran Basi741b5d42012-05-18 11:27:15 -0700540 'Will wait up to %d seconds for recovery to '
541 'complete.', wait_timeout)
542 start_time = time.time()
543 # Wait for the host to come up.
544 if host.wait_up(timeout=wait_timeout):
545 logging.info('Recovery process completed successfully in '
546 '%d seconds.', time.time() - start_time)
547 else:
Vic Yang3a7cf602012-11-07 17:28:39 +0800548 logging.error('Host failed to come back up in the allotted '
549 'time: %d seconds.', wait_timeout)
Jon Salzc88e5b62011-11-30 14:38:54 +0800550 logging.info('Removing the usb key from the DUT.')
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800551 self.switch_usbkey('host')
Jon Salzc88e5b62011-11-30 14:38:54 +0800552 except:
553 # In case anything went wrong we want to make sure to do a clean
554 # reset.
555 self.disable_recovery_mode()
556 self.warm_reset()
557 raise
558
559
Todd Brochf24d2782011-08-19 10:55:41 -0700560 def _connect_servod(self, servo_host, servo_port):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700561 """Connect to the Servod process with XMLRPC.
562
563 Args:
564 servo_port: Port the Servod process is listening on.
565 """
Todd Brochf24d2782011-08-19 10:55:41 -0700566 remote = 'http://%s:%s' % (servo_host, servo_port)
Todd Broch96d83aa2011-08-29 14:37:38 -0700567 self._server = xmlrpclib.ServerProxy(remote)
Todd Brochf24d2782011-08-19 10:55:41 -0700568 try:
569 self._server.echo("ping-test")
Todd Broch96d83aa2011-08-29 14:37:38 -0700570 except:
571 logging.error('Connection to servod failed')
Todd Brochf24d2782011-08-19 10:55:41 -0700572 raise
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800573
574
575 def _scp_image(self, image_path):
576 """Copy image to the servo host.
577
578 When programming a firmware image on the DUT, the image must be
579 located on the host to which the servo device is connected. Sometimes
580 servo is controlled by a remote host, in this case the image needs to
581 be transferred to the remote host.
582
583 @param image_path: a string, name of the firmware image file to be
584 transferred.
585 @return: a string, full path name of the copied file on the remote.
586 """
587
588 dest_path = os.path.join('/tmp', os.path.basename(image_path))
589 scp_cmd = self._scp_cmd_template % (image_path, dest_path)
590 utils.system(scp_cmd)
591 return dest_path
592
593
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800594 def system(self, command, timeout=None):
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800595 """Execute the passed in command on the servod host."""
596 if self._sudo_required:
597 command = 'sudo -n %s' % command
598 if self._ssh_prefix:
599 command = "%s '%s'" % (self._ssh_prefix, command)
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800600 logging.info('Will execute on servo host: %s', command)
601 utils.system(command, timeout=timeout)
602
603
604 def system_output(self, command, timeout=None,
605 ignore_status=False, args=()):
606 """Execute the passed in command on the servod host, return stdout.
607
608 @param command, a string, the command to execute
609 @param timeout, an int, max number of seconds to wait til command
610 execution completes
611 @ignore_status, a Boolean, if true - ignore command's nonzero exit
612 status, otherwise an exception will be thrown
613 @param args, a tuple of strings, each becoming a separate command line
614 parameter for the command
615 @return: command's stdout as a string.
616 """
617 if self._sudo_required:
618 command = 'sudo -n %s' % command
619 if self._ssh_prefix:
620 command = "%s '%s'" % (self._ssh_prefix, command)
621 logging.info('Will execute and collect output on servo host: %s %s',
622 command, ' '.join("'%s'" % x for x in args))
623 return utils.system_output(command, timeout=timeout,
624 ignore_status=ignore_status, args=args)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800625
626
627 def program_ec(self, board, image):
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800628 """Program EC on a given board using given image.
629
630 @param board: a string, type of the DUT board
631 @param image: a string, file name of the EC image to program on the
632 DUT
633 """
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800634 if not self.is_localhost():
635 image = self._scp_image(image)
636 programmer.program_ec(board, self, image)
637
638
639 def program_bootprom(self, board, image):
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800640 """Program bootprom on a given board using given image.
641
642 In case servo is controlled by a remote host, the image needs to be
643 transferred to the host.
644
645 If the device tree subdirectory is present along with the image, the
646 subdirectory is also copied to the remote host.
647
648 @param board: a string, type of the DUT board
649 @param image: a string, file name of the firmware image to program on
650 the DUT. The device tree subdirectory, if present, is on
651 the same level with the image file.
652 """
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800653 if not self.is_localhost():
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800654 dts_path = os.path.join(os.path.dirname(image), 'dts')
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800655 image = self._scp_image(image)
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800656 if os.path.isdir(dts_path):
657 self._scp_image(dts_path)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800658 programmer.program_bootprom(board, self, image)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800659
660 def switch_usbkey(self, side):
661 """Connect USB flash stick to either host or DUT.
662
663 This function switches the servo multiplexer to provide electrical
664 connection between the USB port J3 and either host or DUT side.
665
666 Switching is accompanied by powercycling of the USB stick, because it
667 sometimes gets wedged if the mux is switched while the stick power is
668 on.
669
670 @param side: a string, either 'dut' or 'host' - indicates which side
671 the USB flash device is required to be connected to.
672
673 @raise: error.TestError in case the parameter is neither 'dut' not
674 'host'
675 """
676
677 if self._usb_position == side:
678 return
679
680 if side == 'host':
681 mux_direction = 'servo_sees_usbkey'
682 elif side == 'dut':
683 mux_direction = 'dut_sees_usbkey'
684 else:
685 raise error.TestError('unknown USB mux setting: %s' % side)
686
687 self.set('prtctl4_pwren', 'off')
688 time.sleep(self.USB_POWEROFF_DELAY)
689 self.set('usb_mux_sel1', mux_direction)
690 time.sleep(self.USB_POWEROFF_DELAY)
691 self.set('prtctl4_pwren', 'on')
692
693 self._usb_position = side
694
695
696 def get_usbkey_direction(self):
697 """Get name of the side the USB device is connected to.
698
699 @return a string, either 'dut' or 'host'
700 """
701 if not self._usb_position:
702 if self.get('usb_mux_sel1').starstwith('dut'):
703 self._usb_position = 'dut'
704 else:
705 self._usb_position = 'host'
706 return self._usb_position