blob: 0113cee3a0072dfec9420cc0888f1736bda7c9d6 [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
J. Richard Barnette41320ee2013-03-11 13:00:13 -070014from autotest_lib.server.cros.servo import power_state_controller
J. Richard Barnette75487572013-03-08 12:47:50 -080015from autotest_lib.server.cros.servo import programmer
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070016
J. Richard Barnette41320ee2013-03-11 13:00:13 -070017
J. Richard Barnette384056b2012-04-16 11:04:46 -070018class Servo(object):
J. Richard Barnette41320ee2013-03-11 13:00:13 -070019
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070020 """Manages control of a Servo board.
21
22 Servo is a board developed by hardware group to aide in the debug and
23 control of various partner devices. Servo's features include the simulation
24 of pressing the power button, closing the lid, and pressing Ctrl-d. This
25 class manages setting up and communicating with a servo demon (servod)
26 process. It provides both high-level functions for common servo tasks and
27 low-level functions for directly setting and reading gpios.
J. Richard Barnette41320ee2013-03-11 13:00:13 -070028
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070029 """
30
Chrome Bot9a1137d2011-07-19 14:35:00 -070031 # Power button press delays in seconds.
J. Richard Barnetted2e4cbd2012-06-29 12:18:40 -070032 #
J. Richard Barnette5383f072012-07-26 17:35:40 -070033 # The EC specification says that 8.0 seconds should be enough
34 # for the long power press. However, some platforms need a bit
35 # more time. Empirical testing has found these requirements:
36 # Alex: 8.2 seconds
37 # ZGB: 8.5 seconds
38 # The actual value is set to the largest known necessary value.
39 #
40 # TODO(jrbarnette) Being generous is the right thing to do for
41 # existing platforms, but if this code is to be used for
42 # qualification of new hardware, we should be less generous.
43 LONG_DELAY = 8.5
Chrome Bot9a1137d2011-07-19 14:35:00 -070044 SHORT_DELAY = 0.1
45 NORMAL_TRANSITION_DELAY = 1.2
J. Richard Barnette5383f072012-07-26 17:35:40 -070046
Todd Broch31c82502011-08-29 08:14:39 -070047 # Maximum number of times to re-read power button on release.
48 RELEASE_RETRY_MAX = 5
Todd Brochcf7c6652012-02-24 13:03:59 -080049 GET_RETRY_MAX = 10
Chrome Bot9a1137d2011-07-19 14:35:00 -070050
J. Richard Barnette5383f072012-07-26 17:35:40 -070051 # Delays to deal with DUT state transitions.
Chrome Bot9a1137d2011-07-19 14:35:00 -070052 SLEEP_DELAY = 6
53 BOOT_DELAY = 10
J. Richard Barnette41320ee2013-03-11 13:00:13 -070054
55 # Time in seconds to allow the firmware to initialize itself and
56 # present the "INSERT" screen in recovery mode before actually
57 # inserting a USB stick to boot from.
58 _RECOVERY_INSERT_DELAY = 10.0
59
60 # Minimum time in seconds to hold the "cold_reset" or
61 # "warm_reset" signals asserted.
62 _DUT_RESET_DELAY = 0.5
Chrome Bot9a1137d2011-07-19 14:35:00 -070063
J. Richard Barnetteb6133972012-07-19 17:13:55 -070064 # Time required for the EC to be working after cold reset.
65 # Five seconds is at least twice as big as necessary for Alex,
66 # and is presumably good enough for all future systems.
67 _EC_RESET_DELAY = 5.0
68
J. Richard Barnette41320ee2013-03-11 13:00:13 -070069 # Default minimum time interval between 'press' and 'release'
70 # keyboard events.
Vic Yang0aca1c22012-11-19 18:33:56 -080071 SERVO_KEY_PRESS_DELAY = 0.1
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070072
Jon Salzc88e5b62011-11-30 14:38:54 +080073 # Time between an usb disk plugged-in and detected in the system.
74 USB_DETECTION_DELAY = 10
Vadim Bendeburye7bd9362012-12-19 14:35:20 -080075 # Time to keep USB power off before and after USB mux direction is changed
76 USB_POWEROFF_DELAY = 2
Jon Salzc88e5b62011-11-30 14:38:54 +080077
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -070078 KEY_MATRIX_ALT_0 = {
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -070079 'ctrl_refresh': ['0', '0', '0', '1'],
80 'ctrl_d': ['0', '1', '0', '0'],
81 'd': ['0', '1', '1', '1'],
82 'ctrl_enter': ['1', '0', '0', '0'],
83 'enter': ['1', '0', '1', '1'],
84 'ctrl': ['1', '1', '0', '0'],
85 'refresh': ['1', '1', '0', '1'],
86 'unused': ['1', '1', '1', '0'],
87 'none': ['1', '1', '1', '1']}
Chris Masone6a0680f2012-03-02 08:40:00 -080088
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -070089 KEY_MATRIX_ALT_1 = {
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -070090 'ctrl_d': ['0', '0', '1', '0'],
91 'd': ['0', '0', '1', '1'],
92 'ctrl_enter': ['0', '1', '1', '0'],
93 'enter': ['0', '1', '1', '1'],
94 'ctrl_refresh': ['1', '0', '0', '1'],
95 'unused': ['1', '1', '0', '0'],
96 'refresh': ['1', '1', '0', '1'],
97 'ctrl': ['1', '1', '1', '0'],
98 'none': ['1', '1', '1', '1']}
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -070099
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800100 KEY_MATRIX_ALT_2 = {
101 'ctrl_d': ['0', '0', '0', '1'],
102 'd': ['0', '0', '1', '1'],
103 'unused': ['0', '1', '1', '1'],
104 'rec_mode': ['1', '0', '0', '0'],
105 'ctrl_enter': ['1', '0', '0', '1'],
106 'enter': ['1', '0', '1', '1'],
107 'ctrl': ['1', '1', '0', '1'],
108 'refresh': ['1', '1', '1', '0'],
109 'ctrl_refresh': ['1', '1', '1', '1'],
110 'none': ['1', '1', '1', '1']}
111
112 KEY_MATRIX = [KEY_MATRIX_ALT_0, KEY_MATRIX_ALT_1, KEY_MATRIX_ALT_2]
J. Richard Barnette67ccb872012-04-19 16:34:56 -0700113
J. Richard Barnetted5f807a2013-02-11 16:51:00 -0800114 def __init__(self, servo_host='localhost', servo_port=9999):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700115 """Sets up the servo communication infrastructure.
116
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800117 @param servo_host Name of the host where the servod process
118 is running.
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800119 @param servo_port Port the servod process is listening on.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700120 """
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -0700121 self._key_matrix = 0
J. Richard Barnette384056b2012-04-16 11:04:46 -0700122 self._server = None
Todd Brochf24d2782011-08-19 10:55:41 -0700123 self._connect_servod(servo_host, servo_port)
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800124 self._is_localhost = (servo_host == 'localhost')
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700125 self._power_state = power_state_controller.PowerStateController(self)
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800126
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800127 # a string, showing what interface (host or dut) the USB device is
128 # connected to.
129 self._usb_position = None
130 self.set('dut_hub_pwren', 'on')
131 self.set('usb_mux_oe1', 'on')
132 self.switch_usbkey('host')
133
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800134 # Commands on the servo host must be run by the superuser. Our account
135 # on Beaglebone is root, but locally we might be running as a
136 # different user. If so - `sudo ' will have to be added to the
137 # commands.
138 if self._is_localhost:
139 self._sudo_required = utils.system_output('id -u') != '0'
140 self._ssh_prefix = ''
141 else:
142 common_options = '-o PasswordAuthentication=no'
143 self._sudo_required = False
144 self._ssh_prefix = 'ssh %s root@%s ' % (common_options, servo_host)
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800145 self._scp_cmd_template = 'scp -r %s ' % common_options
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800146 self._scp_cmd_template += '%s ' + 'root@' + servo_host + ':%s'
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800147
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700148 def initialize_dut(self, cold_reset=False):
149 """Initializes a dut for testing purposes.
150
151 This sets various servo signals back to default values
152 appropriate for the target board. By default, if the DUT
153 is already on, it stays on. If the DUT is powered off
154 before initialization, its state afterward is unspecified.
155
156 If cold reset is requested, the DUT is guaranteed to be off
157 at the end of initialization, regardless of its initial
158 state.
159
160 Rationale: Basic initialization of servo sets the lid open,
161 when there is a lid. This operation won't affect powered on
162 units; however, setting the lid open may power on a unit
163 that's off, depending on factors outside the scope of this
164 function.
165
166 @param cold_reset If True, cold reset the device after
167 initialization.
168 """
169 self._server.hwinit()
170 if cold_reset:
171 self.cold_reset()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700172
173
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800174 def is_localhost(self):
175 """Is the servod hosted locally?
176
177 Returns:
178 True if local hosted; otherwise, False.
179 """
180 return self._is_localhost
181
182
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700183 def power_long_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700184 """Simulate a long power button press."""
J. Richard Barnettef12bff22012-07-18 14:41:06 -0700185 # After a long power press, the EC may ignore the next power
186 # button press (at least on Alex). To guarantee that this
187 # won't happen, we need to allow the EC one second to
188 # collect itself.
Chrome Bot9a1137d2011-07-19 14:35:00 -0700189 self.power_key(Servo.LONG_DELAY)
J. Richard Barnettef12bff22012-07-18 14:41:06 -0700190 time.sleep(1.0)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700191
192
193 def power_normal_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700194 """Simulate a normal power button press."""
195 self.power_key()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700196
197
198 def power_short_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700199 """Simulate a short power button press."""
200 self.power_key(Servo.SHORT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700201
202
Chrome Bot9a1137d2011-07-19 14:35:00 -0700203 def power_key(self, secs=NORMAL_TRANSITION_DELAY):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700204 """Simulate a power button press.
205
206 Args:
207 secs: Time in seconds to simulate the keypress.
208 """
Craig Harrison6b36b122011-06-28 17:58:43 -0700209 self.set_nocheck('pwr_button', 'press')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700210 time.sleep(secs)
Todd Broch31c82502011-08-29 08:14:39 -0700211 self.set_nocheck('pwr_button', 'release')
212 # TODO(tbroch) Different systems have different release times on the
213 # power button that this loop addresses. Longer term we may want to
214 # make this delay platform specific.
215 retry = 1
216 while True:
217 value = self.get('pwr_button')
218 if value == 'release' or retry > Servo.RELEASE_RETRY_MAX:
219 break
Todd Broch9753bd42012-03-21 10:15:08 -0700220 logging.info('Waiting for pwr_button to release, retry %d.', retry)
Todd Broch31c82502011-08-29 08:14:39 -0700221 retry += 1
222 time.sleep(Servo.SHORT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700223
224
225 def lid_open(self):
226 """Simulate opening the lid."""
Craig Harrison48997262011-06-27 14:31:10 -0700227 self.set_nocheck('lid_open', 'yes')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700228
229
230 def lid_close(self):
Craig Harrison48997262011-06-27 14:31:10 -0700231 """Simulate closing the lid.
232
233 Waits 6 seconds to ensure the device is fully asleep before returning.
234 """
235 self.set_nocheck('lid_open', 'no')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700236 time.sleep(Servo.SLEEP_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700237
238
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800239 def _press_keys(self, key):
240 """Simulate button presses.
241
242 Note, key presses will remain on indefinitely. See
243 _press_and_release_keys for release procedure.
244 """
Gediminas Ramanauskas9da80f22012-11-14 12:59:43 -0800245 (m1_a1, m1_a0, m2_a1, m2_a0) = self.KEY_MATRIX[self._key_matrix]['none']
Todd Broch9753bd42012-03-21 10:15:08 -0700246 self.set_nocheck('kbd_m2_a0', m2_a0)
247 self.set_nocheck('kbd_m2_a1', m2_a1)
248 self.set_nocheck('kbd_m1_a0', m1_a0)
249 self.set_nocheck('kbd_m1_a1', m1_a1)
Vic Yange262a3e2012-11-02 18:48:37 +0800250 self.set_nocheck('kbd_en', 'on')
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800251
Gediminas Ramanauskas9da80f22012-11-14 12:59:43 -0800252 (m1_a1, m1_a0, m2_a1, m2_a0) = self.KEY_MATRIX[self._key_matrix][key]
253 self.set_nocheck('kbd_m2_a0', m2_a0)
254 self.set_nocheck('kbd_m2_a1', m2_a1)
255 self.set_nocheck('kbd_m1_a0', m1_a0)
256 self.set_nocheck('kbd_m1_a1', m1_a1)
257
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800258
259 def _press_and_release_keys(self, key,
260 press_secs=SERVO_KEY_PRESS_DELAY):
261 """Simulate button presses and release."""
262 self._press_keys(key)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700263 time.sleep(press_secs)
264 self.set_nocheck('kbd_en', 'off')
265
266
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -0700267 def set_key_matrix(self, matrix=0):
268 """Set keyboard mapping"""
269 self._key_matrix = matrix
270
271
Chrome Bot9a1137d2011-07-19 14:35:00 -0700272 def ctrl_d(self):
273 """Simulate Ctrl-d simultaneous button presses."""
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700274 self._press_and_release_keys('ctrl_d')
Todd Broch9dfc3a82011-11-01 08:09:28 -0700275
276
Todd Broch9753bd42012-03-21 10:15:08 -0700277 def ctrl_enter(self):
278 """Simulate Ctrl-enter simultaneous button presses."""
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700279 self._press_and_release_keys('ctrl_enter')
Todd Broch9753bd42012-03-21 10:15:08 -0700280
281
Todd Broch9dfc3a82011-11-01 08:09:28 -0700282 def d_key(self):
283 """Simulate Enter key button press."""
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700284 self._press_and_release_keys('d')
Todd Broch9dfc3a82011-11-01 08:09:28 -0700285
286
287 def ctrl_key(self):
288 """Simulate Enter key button press."""
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700289 self._press_and_release_keys('ctrl')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700290
291
Chrome Bot9a1137d2011-07-19 14:35:00 -0700292 def enter_key(self):
293 """Simulate Enter key button press."""
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700294 self._press_and_release_keys('enter')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700295
296
Chrome Bot9a1137d2011-07-19 14:35:00 -0700297 def refresh_key(self):
298 """Simulate Refresh key (F3) button press."""
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700299 self._press_and_release_keys('refresh')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700300
301
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800302 def ctrl_refresh_key(self):
303 """Simulate Ctrl and Refresh (F3) simultaneous press.
304
305 This key combination is an alternative of Space key.
306 """
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700307 self._press_and_release_keys('ctrl_refresh')
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800308
309
Chrome Bot9a1137d2011-07-19 14:35:00 -0700310 def imaginary_key(self):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700311 """Simulate imaginary key button press.
312
313 Maps to a key that doesn't physically exist.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700314 """
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700315 self._press_and_release_keys('unused')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700316
317
Craig Harrison6b36b122011-06-28 17:58:43 -0700318 def enable_recovery_mode(self):
319 """Enable recovery mode on device."""
320 self.set('rec_mode', 'on')
321
322
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800323 def custom_recovery_mode(self):
324 """Custom key combination to enter recovery mode."""
325 self._press_keys('rec_mode')
326 self.power_normal_press()
327 time.sleep(self.SERVO_KEY_PRESS_DELAY)
328 self.set_nocheck('kbd_en', 'off')
329
330
Craig Harrison6b36b122011-06-28 17:58:43 -0700331 def disable_recovery_mode(self):
332 """Disable recovery mode on device."""
333 self.set('rec_mode', 'off')
334
335
336 def enable_development_mode(self):
337 """Enable development mode on device."""
338 self.set('dev_mode', 'on')
339
340
341 def disable_development_mode(self):
342 """Disable development mode on device."""
343 self.set('dev_mode', 'off')
344
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700345 def boot_devmode(self):
346 """Boot a dev-mode device that is powered off."""
Tom Wai-Hong Tam23869102012-01-17 15:41:30 +0800347 self.power_short_press()
Craig Harrison48997262011-06-27 14:31:10 -0700348 self.pass_devmode()
349
350
351 def pass_devmode(self):
352 """Pass through boot screens in dev-mode."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700353 time.sleep(Servo.BOOT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700354 self.ctrl_d()
Chrome Bot9a1137d2011-07-19 14:35:00 -0700355 time.sleep(Servo.BOOT_DELAY)
Craig Harrison48997262011-06-27 14:31:10 -0700356
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700357
Craig Harrison6b36b122011-06-28 17:58:43 -0700358 def cold_reset(self):
359 """Perform a cold reset of the EC.
360
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700361 This has the side effect of shutting off the device. The
362 device is guaranteed to be off at the end of this call.
Craig Harrison6b36b122011-06-28 17:58:43 -0700363 """
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700364 # After the reset, give the EC the time it needs to
365 # re-initialize.
Craig Harrison6b36b122011-06-28 17:58:43 -0700366 self.set('cold_reset', 'on')
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700367 time.sleep(self._DUT_RESET_DELAY)
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700368 self.set('cold_reset', 'off')
369 time.sleep(self._EC_RESET_DELAY)
Craig Harrison6b36b122011-06-28 17:58:43 -0700370
371
372 def warm_reset(self):
373 """Perform a warm reset of the device.
374
375 Has the side effect of restarting the device.
376 """
377 self.set('warm_reset', 'on')
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700378 time.sleep(self._DUT_RESET_DELAY)
Craig Harrison6b36b122011-06-28 17:58:43 -0700379 self.set('warm_reset', 'off')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700380
381
Todd Brochefe72cb2012-07-11 19:58:53 -0700382 def _get_xmlrpclib_exception(self, xmlexc):
383 """Get meaningful exception string from xmlrpc.
384
385 Args:
386 xmlexc: xmlrpclib.Fault object
387
388 xmlrpclib.Fault.faultString has the following format:
389
390 <type 'exception type'>:'actual error message'
391
392 Parse and return the real exception from the servod side instead of the
393 less meaningful one like,
394 <Fault 1: "<type 'exceptions.AttributeError'>:'tca6416' object has no
395 attribute 'hw_driver'">
396
397 Returns:
398 string of underlying exception raised in servod.
399 """
400 return re.sub('^.*>:', '', xmlexc.faultString)
401
402
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700403 def get(self, gpio_name):
404 """Get the value of a gpio from Servod."""
405 assert gpio_name
Todd Brochefe72cb2012-07-11 19:58:53 -0700406 try:
407 return self._server.get(gpio_name)
408 except xmlrpclib.Fault as e:
409 err_msg = "Getting '%s' :: %s" % \
410 (gpio_name, self._get_xmlrpclib_exception(e))
411 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700412
413
414 def set(self, gpio_name, gpio_value):
415 """Set and check the value of a gpio using Servod."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700416 self.set_nocheck(gpio_name, gpio_value)
Todd Brochcf7c6652012-02-24 13:03:59 -0800417 retry_count = Servo.GET_RETRY_MAX
418 while gpio_value != self.get(gpio_name) and retry_count:
419 logging.warn("%s != %s, retry %d", gpio_name, gpio_value,
420 retry_count)
421 retry_count -= 1
422 time.sleep(Servo.SHORT_DELAY)
423 if not retry_count:
424 assert gpio_value == self.get(gpio_name), \
425 'Servo failed to set %s to %s' % (gpio_name, gpio_value)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700426
427
428 def set_nocheck(self, gpio_name, gpio_value):
429 """Set the value of a gpio using Servod."""
430 assert gpio_name and gpio_value
Todd Broch9753bd42012-03-21 10:15:08 -0700431 logging.info('Setting %s to %s', gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700432 try:
433 self._server.set(gpio_name, gpio_value)
434 except xmlrpclib.Fault as e:
435 err_msg = "Setting '%s' to '%s' :: %s" % \
436 (gpio_name, gpio_value, self._get_xmlrpclib_exception(e))
437 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700438
439
Jon Salzc88e5b62011-11-30 14:38:54 +0800440 # TODO(waihong) It may fail if multiple servo's are connected to the same
441 # host. Should look for a better way, like the USB serial name, to identify
442 # the USB device.
Simran Basi741b5d42012-05-18 11:27:15 -0700443 # TODO(sbasi) Remove this code from autoserv once firmware tests have been
444 # updated.
Jon Salzc88e5b62011-11-30 14:38:54 +0800445 def probe_host_usb_dev(self):
446 """Probe the USB disk device plugged-in the servo from the host side.
447
448 It tries to switch the USB mux to make the host unable to see the
449 USB disk and compares the result difference.
450
Jon Salzc88e5b62011-11-30 14:38:54 +0800451 Returns:
452 A string of USB disk path, like '/dev/sdb', or None if not existed.
453 """
454 cmd = 'ls /dev/sd[a-z]'
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800455 original_value = self.get_usbkey_direction()
Jon Salzc88e5b62011-11-30 14:38:54 +0800456
457 # Make the host unable to see the USB disk.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800458 if original_value != 'dut':
459 self.switch_usbkey('dut')
Jon Salzc88e5b62011-11-30 14:38:54 +0800460 time.sleep(self.USB_DETECTION_DELAY)
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800461 no_usb_set = set(self.system_output(cmd, ignore_status=True).split())
Jon Salzc88e5b62011-11-30 14:38:54 +0800462
463 # Make the host able to see the USB disk.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800464 self.switch_usbkey('host')
Jon Salzc88e5b62011-11-30 14:38:54 +0800465 time.sleep(self.USB_DETECTION_DELAY)
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800466 has_usb_set = set(self.system_output(cmd, ignore_status=True).split())
Jon Salzc88e5b62011-11-30 14:38:54 +0800467
468 # Back to its original value.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800469 if original_value != self.get_usbkey_direction():
470 self.switch_usbkey(original_value)
Jon Salzc88e5b62011-11-30 14:38:54 +0800471 time.sleep(self.USB_DETECTION_DELAY)
472
473 diff_set = has_usb_set - no_usb_set
474 if len(diff_set) == 1:
475 return diff_set.pop()
476 else:
477 return None
478
479
Mike Truty49153d82012-08-21 22:27:30 -0500480 def image_to_servo_usb(self, image_path=None,
481 make_image_noninteractive=False):
482 """Install an image to the USB key plugged into the servo.
483
484 This method may copy any image to the servo USB key including a
485 recovery image or a test image. These images are frequently used
486 for test purposes such as restoring a corrupted image or conducting
487 an upgrade of ec/fw/kernel as part of a test of a specific image part.
488
489 Args:
490 image_path: Path on the host to the recovery image.
491 make_image_noninteractive: Make the recovery image noninteractive,
492 therefore the DUT will reboot
493 automatically after installation.
494 """
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700495 # We're about to start plugging/unplugging the USB key. We
496 # don't know the state of the DUT, or what it might choose
497 # to do to the device after hotplug. To avoid surprises,
498 # force the DUT to be off.
499 self._server.hwinit()
500 self._power_state.power_off()
Mike Truty49153d82012-08-21 22:27:30 -0500501
502 # Set up Servo's usb mux.
503 self.set('prtctl4_pwren', 'on')
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800504 self.switch_usbkey('host')
Mike Truty49153d82012-08-21 22:27:30 -0500505 if image_path:
Tom Wai-Hong Tam42f136d2012-10-26 11:11:23 +0800506 logging.info('Searching for usb device and copying image to it. '
507 'Please wait a few minutes...')
Mike Truty49153d82012-08-21 22:27:30 -0500508 if not self._server.download_image_to_usb(image_path):
509 logging.error('Failed to transfer requested image to USB. '
510 'Please take a look at Servo Logs.')
511 raise error.AutotestError('Download image to usb failed.')
512 if make_image_noninteractive:
513 logging.info('Making image noninteractive')
514 if not self._server.make_image_noninteractive():
515 logging.error('Failed to make image noninteractive. '
516 'Please take a look at Servo Logs.')
517
518
Simran Basi741b5d42012-05-18 11:27:15 -0700519 def install_recovery_image(self, image_path=None,
J. Richard Barnetteb6de7e32013-02-14 13:28:04 -0800520 make_image_noninteractive=False):
Jon Salzc88e5b62011-11-30 14:38:54 +0800521 """Install the recovery image specied by the path onto the DUT.
522
523 This method uses google recovery mode to install a recovery image
524 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 +0800525 board specified by the usb_dev. If no image path is specified
Jon Salzc88e5b62011-11-30 14:38:54 +0800526 we use the recovery image already on the usb image.
527
528 Args:
529 image_path: Path on the host to the recovery image.
Simran Basi741b5d42012-05-18 11:27:15 -0700530 make_image_noninteractive: Make the recovery image noninteractive,
531 therefore the DUT will reboot
532 automatically after installation.
Jon Salzc88e5b62011-11-30 14:38:54 +0800533 """
Mike Truty49153d82012-08-21 22:27:30 -0500534 self.image_to_servo_usb(image_path, make_image_noninteractive)
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700535 self._power_state.power_on(dev_mode=self._power_state.DEV_OFF,
536 rec_mode=self._power_state.REC_ON)
537 time.sleep(self._RECOVERY_INSERT_DELAY)
538 self.switch_usbkey('dut')
Jon Salzc88e5b62011-11-30 14:38:54 +0800539
540
Todd Brochf24d2782011-08-19 10:55:41 -0700541 def _connect_servod(self, servo_host, servo_port):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700542 """Connect to the Servod process with XMLRPC.
543
544 Args:
545 servo_port: Port the Servod process is listening on.
546 """
Todd Brochf24d2782011-08-19 10:55:41 -0700547 remote = 'http://%s:%s' % (servo_host, servo_port)
Todd Broch96d83aa2011-08-29 14:37:38 -0700548 self._server = xmlrpclib.ServerProxy(remote)
Todd Brochf24d2782011-08-19 10:55:41 -0700549 try:
550 self._server.echo("ping-test")
Todd Broch96d83aa2011-08-29 14:37:38 -0700551 except:
552 logging.error('Connection to servod failed')
Todd Brochf24d2782011-08-19 10:55:41 -0700553 raise
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800554
555
556 def _scp_image(self, image_path):
557 """Copy image to the servo host.
558
559 When programming a firmware image on the DUT, the image must be
560 located on the host to which the servo device is connected. Sometimes
561 servo is controlled by a remote host, in this case the image needs to
562 be transferred to the remote host.
563
564 @param image_path: a string, name of the firmware image file to be
565 transferred.
566 @return: a string, full path name of the copied file on the remote.
567 """
568
569 dest_path = os.path.join('/tmp', os.path.basename(image_path))
570 scp_cmd = self._scp_cmd_template % (image_path, dest_path)
571 utils.system(scp_cmd)
572 return dest_path
573
574
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800575 def system(self, command, timeout=None):
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800576 """Execute the passed in command on the servod host."""
577 if self._sudo_required:
578 command = 'sudo -n %s' % command
579 if self._ssh_prefix:
580 command = "%s '%s'" % (self._ssh_prefix, command)
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800581 logging.info('Will execute on servo host: %s', command)
582 utils.system(command, timeout=timeout)
583
584
585 def system_output(self, command, timeout=None,
586 ignore_status=False, args=()):
587 """Execute the passed in command on the servod host, return stdout.
588
589 @param command, a string, the command to execute
590 @param timeout, an int, max number of seconds to wait til command
591 execution completes
592 @ignore_status, a Boolean, if true - ignore command's nonzero exit
593 status, otherwise an exception will be thrown
594 @param args, a tuple of strings, each becoming a separate command line
595 parameter for the command
596 @return: command's stdout as a string.
597 """
598 if self._sudo_required:
599 command = 'sudo -n %s' % command
600 if self._ssh_prefix:
601 command = "%s '%s'" % (self._ssh_prefix, command)
602 logging.info('Will execute and collect output on servo host: %s %s',
603 command, ' '.join("'%s'" % x for x in args))
604 return utils.system_output(command, timeout=timeout,
605 ignore_status=ignore_status, args=args)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800606
607
608 def program_ec(self, board, image):
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800609 """Program EC on a given board using given image.
610
611 @param board: a string, type of the DUT board
612 @param image: a string, file name of the EC image to program on the
613 DUT
614 """
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800615 if not self.is_localhost():
616 image = self._scp_image(image)
617 programmer.program_ec(board, self, image)
618
619
620 def program_bootprom(self, board, image):
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800621 """Program bootprom on a given board using given image.
622
623 In case servo is controlled by a remote host, the image needs to be
624 transferred to the host.
625
626 If the device tree subdirectory is present along with the image, the
627 subdirectory is also copied to the remote host.
628
629 @param board: a string, type of the DUT board
630 @param image: a string, file name of the firmware image to program on
631 the DUT. The device tree subdirectory, if present, is on
632 the same level with the image file.
633 """
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800634 if not self.is_localhost():
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800635 dts_path = os.path.join(os.path.dirname(image), 'dts')
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800636 image = self._scp_image(image)
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800637 if os.path.isdir(dts_path):
638 self._scp_image(dts_path)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800639 programmer.program_bootprom(board, self, image)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800640
641 def switch_usbkey(self, side):
642 """Connect USB flash stick to either host or DUT.
643
644 This function switches the servo multiplexer to provide electrical
645 connection between the USB port J3 and either host or DUT side.
646
647 Switching is accompanied by powercycling of the USB stick, because it
648 sometimes gets wedged if the mux is switched while the stick power is
649 on.
650
651 @param side: a string, either 'dut' or 'host' - indicates which side
652 the USB flash device is required to be connected to.
653
654 @raise: error.TestError in case the parameter is neither 'dut' not
655 'host'
656 """
657
658 if self._usb_position == side:
659 return
660
661 if side == 'host':
662 mux_direction = 'servo_sees_usbkey'
663 elif side == 'dut':
664 mux_direction = 'dut_sees_usbkey'
665 else:
666 raise error.TestError('unknown USB mux setting: %s' % side)
667
668 self.set('prtctl4_pwren', 'off')
669 time.sleep(self.USB_POWEROFF_DELAY)
670 self.set('usb_mux_sel1', mux_direction)
671 time.sleep(self.USB_POWEROFF_DELAY)
672 self.set('prtctl4_pwren', 'on')
673
674 self._usb_position = side
675
676
677 def get_usbkey_direction(self):
678 """Get name of the side the USB device is connected to.
679
680 @return a string, either 'dut' or 'host'
681 """
682 if not self._usb_position:
683 if self.get('usb_mux_sel1').starstwith('dut'):
684 self._usb_position = 'dut'
685 else:
686 self._usb_position = 'host'
687 return self._usb_position