blob: e47a8c0dd88f1c22df1abdb4599920ea9488ae22 [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
Chrome Bot9a1137d2011-07-19 14:35:00 -070051
J. Richard Barnetteb6133972012-07-19 17:13:55 -070052 # Time required for the EC to be working after cold reset.
53 # Five seconds is at least twice as big as necessary for Alex,
54 # and is presumably good enough for all future systems.
55 _EC_RESET_DELAY = 5.0
56
Chrome Bot9a1137d2011-07-19 14:35:00 -070057 # Servo-specific delays.
58 MAX_SERVO_STARTUP_DELAY = 10
59 SERVO_SEND_SIGNAL_DELAY = 0.5
Vic Yang0aca1c22012-11-19 18:33:56 -080060 SERVO_KEY_PRESS_DELAY = 0.1
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070061
Jon Salzc88e5b62011-11-30 14:38:54 +080062 # Time between an usb disk plugged-in and detected in the system.
63 USB_DETECTION_DELAY = 10
Vadim Bendeburye7bd9362012-12-19 14:35:20 -080064 # Time to keep USB power off before and after USB mux direction is changed
65 USB_POWEROFF_DELAY = 2
Jon Salzc88e5b62011-11-30 14:38:54 +080066
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -070067 KEY_MATRIX_ALT_0 = {
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -070068 'ctrl_refresh': ['0', '0', '0', '1'],
69 'ctrl_d': ['0', '1', '0', '0'],
70 'd': ['0', '1', '1', '1'],
71 'ctrl_enter': ['1', '0', '0', '0'],
72 'enter': ['1', '0', '1', '1'],
73 'ctrl': ['1', '1', '0', '0'],
74 'refresh': ['1', '1', '0', '1'],
75 'unused': ['1', '1', '1', '0'],
76 'none': ['1', '1', '1', '1']}
Chris Masone6a0680f2012-03-02 08:40:00 -080077
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -070078 KEY_MATRIX_ALT_1 = {
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -070079 'ctrl_d': ['0', '0', '1', '0'],
80 'd': ['0', '0', '1', '1'],
81 'ctrl_enter': ['0', '1', '1', '0'],
82 'enter': ['0', '1', '1', '1'],
83 'ctrl_refresh': ['1', '0', '0', '1'],
84 'unused': ['1', '1', '0', '0'],
85 'refresh': ['1', '1', '0', '1'],
86 'ctrl': ['1', '1', '1', '0'],
87 'none': ['1', '1', '1', '1']}
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -070088
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -080089 KEY_MATRIX_ALT_2 = {
90 'ctrl_d': ['0', '0', '0', '1'],
91 'd': ['0', '0', '1', '1'],
92 'unused': ['0', '1', '1', '1'],
93 'rec_mode': ['1', '0', '0', '0'],
94 'ctrl_enter': ['1', '0', '0', '1'],
95 'enter': ['1', '0', '1', '1'],
96 'ctrl': ['1', '1', '0', '1'],
97 'refresh': ['1', '1', '1', '0'],
98 'ctrl_refresh': ['1', '1', '1', '1'],
99 'none': ['1', '1', '1', '1']}
100
101 KEY_MATRIX = [KEY_MATRIX_ALT_0, KEY_MATRIX_ALT_1, KEY_MATRIX_ALT_2]
J. Richard Barnette67ccb872012-04-19 16:34:56 -0700102
J. Richard Barnetted5f807a2013-02-11 16:51:00 -0800103 def __init__(self, servo_host='localhost', servo_port=9999):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700104 """Sets up the servo communication infrastructure.
105
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800106 @param servo_host Name of the host where the servod process
107 is running.
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800108 @param servo_port Port the servod process is listening on.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700109 """
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -0700110 self._key_matrix = 0
J. Richard Barnette384056b2012-04-16 11:04:46 -0700111 self._server = None
Todd Brochf24d2782011-08-19 10:55:41 -0700112 self._connect_servod(servo_host, servo_port)
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800113 self._is_localhost = (servo_host == 'localhost')
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800114
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800115 # a string, showing what interface (host or dut) the USB device is
116 # connected to.
117 self._usb_position = None
118 self.set('dut_hub_pwren', 'on')
119 self.set('usb_mux_oe1', 'on')
120 self.switch_usbkey('host')
121
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800122 # Commands on the servo host must be run by the superuser. Our account
123 # on Beaglebone is root, but locally we might be running as a
124 # different user. If so - `sudo ' will have to be added to the
125 # commands.
126 if self._is_localhost:
127 self._sudo_required = utils.system_output('id -u') != '0'
128 self._ssh_prefix = ''
129 else:
130 common_options = '-o PasswordAuthentication=no'
131 self._sudo_required = False
132 self._ssh_prefix = 'ssh %s root@%s ' % (common_options, servo_host)
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800133 self._scp_cmd_template = 'scp -r %s ' % common_options
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800134 self._scp_cmd_template += '%s ' + 'root@' + servo_host + ':%s'
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800135
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700136 def initialize_dut(self, cold_reset=False):
137 """Initializes a dut for testing purposes.
138
139 This sets various servo signals back to default values
140 appropriate for the target board. By default, if the DUT
141 is already on, it stays on. If the DUT is powered off
142 before initialization, its state afterward is unspecified.
143
144 If cold reset is requested, the DUT is guaranteed to be off
145 at the end of initialization, regardless of its initial
146 state.
147
148 Rationale: Basic initialization of servo sets the lid open,
149 when there is a lid. This operation won't affect powered on
150 units; however, setting the lid open may power on a unit
151 that's off, depending on factors outside the scope of this
152 function.
153
154 @param cold_reset If True, cold reset the device after
155 initialization.
156 """
157 self._server.hwinit()
158 if cold_reset:
159 self.cold_reset()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700160
161
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800162 def is_localhost(self):
163 """Is the servod hosted locally?
164
165 Returns:
166 True if local hosted; otherwise, False.
167 """
168 return self._is_localhost
169
170
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700171 def power_long_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700172 """Simulate a long power button press."""
J. Richard Barnettef12bff22012-07-18 14:41:06 -0700173 # After a long power press, the EC may ignore the next power
174 # button press (at least on Alex). To guarantee that this
175 # won't happen, we need to allow the EC one second to
176 # collect itself.
Chrome Bot9a1137d2011-07-19 14:35:00 -0700177 self.power_key(Servo.LONG_DELAY)
J. Richard Barnettef12bff22012-07-18 14:41:06 -0700178 time.sleep(1.0)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700179
180
181 def power_normal_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700182 """Simulate a normal power button press."""
183 self.power_key()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700184
185
186 def power_short_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700187 """Simulate a short power button press."""
188 self.power_key(Servo.SHORT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700189
190
Chrome Bot9a1137d2011-07-19 14:35:00 -0700191 def power_key(self, secs=NORMAL_TRANSITION_DELAY):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700192 """Simulate a power button press.
193
194 Args:
195 secs: Time in seconds to simulate the keypress.
196 """
Craig Harrison6b36b122011-06-28 17:58:43 -0700197 self.set_nocheck('pwr_button', 'press')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700198 time.sleep(secs)
Todd Broch31c82502011-08-29 08:14:39 -0700199 self.set_nocheck('pwr_button', 'release')
200 # TODO(tbroch) Different systems have different release times on the
201 # power button that this loop addresses. Longer term we may want to
202 # make this delay platform specific.
203 retry = 1
204 while True:
205 value = self.get('pwr_button')
206 if value == 'release' or retry > Servo.RELEASE_RETRY_MAX:
207 break
Todd Broch9753bd42012-03-21 10:15:08 -0700208 logging.info('Waiting for pwr_button to release, retry %d.', retry)
Todd Broch31c82502011-08-29 08:14:39 -0700209 retry += 1
210 time.sleep(Servo.SHORT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700211
212
213 def lid_open(self):
214 """Simulate opening the lid."""
Craig Harrison48997262011-06-27 14:31:10 -0700215 self.set_nocheck('lid_open', 'yes')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700216
217
218 def lid_close(self):
Craig Harrison48997262011-06-27 14:31:10 -0700219 """Simulate closing the lid.
220
221 Waits 6 seconds to ensure the device is fully asleep before returning.
222 """
223 self.set_nocheck('lid_open', 'no')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700224 time.sleep(Servo.SLEEP_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700225
226
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800227 def _press_keys(self, key):
228 """Simulate button presses.
229
230 Note, key presses will remain on indefinitely. See
231 _press_and_release_keys for release procedure.
232 """
Gediminas Ramanauskas9da80f22012-11-14 12:59:43 -0800233 (m1_a1, m1_a0, m2_a1, m2_a0) = self.KEY_MATRIX[self._key_matrix]['none']
Todd Broch9753bd42012-03-21 10:15:08 -0700234 self.set_nocheck('kbd_m2_a0', m2_a0)
235 self.set_nocheck('kbd_m2_a1', m2_a1)
236 self.set_nocheck('kbd_m1_a0', m1_a0)
237 self.set_nocheck('kbd_m1_a1', m1_a1)
Vic Yange262a3e2012-11-02 18:48:37 +0800238 self.set_nocheck('kbd_en', 'on')
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800239
Gediminas Ramanauskas9da80f22012-11-14 12:59:43 -0800240 (m1_a1, m1_a0, m2_a1, m2_a0) = self.KEY_MATRIX[self._key_matrix][key]
241 self.set_nocheck('kbd_m2_a0', m2_a0)
242 self.set_nocheck('kbd_m2_a1', m2_a1)
243 self.set_nocheck('kbd_m1_a0', m1_a0)
244 self.set_nocheck('kbd_m1_a1', m1_a1)
245
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800246
247 def _press_and_release_keys(self, key,
248 press_secs=SERVO_KEY_PRESS_DELAY):
249 """Simulate button presses and release."""
250 self._press_keys(key)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700251 time.sleep(press_secs)
252 self.set_nocheck('kbd_en', 'off')
253
254
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -0700255 def set_key_matrix(self, matrix=0):
256 """Set keyboard mapping"""
257 self._key_matrix = matrix
258
259
Chrome Bot9a1137d2011-07-19 14:35:00 -0700260 def ctrl_d(self):
261 """Simulate Ctrl-d simultaneous button presses."""
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700262 self._press_and_release_keys('ctrl_d')
Todd Broch9dfc3a82011-11-01 08:09:28 -0700263
264
Todd Broch9753bd42012-03-21 10:15:08 -0700265 def ctrl_enter(self):
266 """Simulate Ctrl-enter simultaneous button presses."""
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700267 self._press_and_release_keys('ctrl_enter')
Todd Broch9753bd42012-03-21 10:15:08 -0700268
269
Todd Broch9dfc3a82011-11-01 08:09:28 -0700270 def d_key(self):
271 """Simulate Enter key button press."""
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700272 self._press_and_release_keys('d')
Todd Broch9dfc3a82011-11-01 08:09:28 -0700273
274
275 def ctrl_key(self):
276 """Simulate Enter key button press."""
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700277 self._press_and_release_keys('ctrl')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700278
279
Chrome Bot9a1137d2011-07-19 14:35:00 -0700280 def enter_key(self):
281 """Simulate Enter key button press."""
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700282 self._press_and_release_keys('enter')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700283
284
Chrome Bot9a1137d2011-07-19 14:35:00 -0700285 def refresh_key(self):
286 """Simulate Refresh key (F3) button press."""
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700287 self._press_and_release_keys('refresh')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700288
289
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800290 def ctrl_refresh_key(self):
291 """Simulate Ctrl and Refresh (F3) simultaneous press.
292
293 This key combination is an alternative of Space key.
294 """
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700295 self._press_and_release_keys('ctrl_refresh')
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800296
297
Chrome Bot9a1137d2011-07-19 14:35:00 -0700298 def imaginary_key(self):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700299 """Simulate imaginary key button press.
300
301 Maps to a key that doesn't physically exist.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700302 """
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700303 self._press_and_release_keys('unused')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700304
305
Craig Harrison6b36b122011-06-28 17:58:43 -0700306 def enable_recovery_mode(self):
307 """Enable recovery mode on device."""
308 self.set('rec_mode', 'on')
309
310
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800311 def custom_recovery_mode(self):
312 """Custom key combination to enter recovery mode."""
313 self._press_keys('rec_mode')
314 self.power_normal_press()
315 time.sleep(self.SERVO_KEY_PRESS_DELAY)
316 self.set_nocheck('kbd_en', 'off')
317
318
Craig Harrison6b36b122011-06-28 17:58:43 -0700319 def disable_recovery_mode(self):
320 """Disable recovery mode on device."""
321 self.set('rec_mode', 'off')
322
323
324 def enable_development_mode(self):
325 """Enable development mode on device."""
326 self.set('dev_mode', 'on')
327
328
329 def disable_development_mode(self):
330 """Disable development mode on device."""
331 self.set('dev_mode', 'off')
332
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700333 def boot_devmode(self):
334 """Boot a dev-mode device that is powered off."""
Tom Wai-Hong Tam23869102012-01-17 15:41:30 +0800335 self.power_short_press()
Craig Harrison48997262011-06-27 14:31:10 -0700336 self.pass_devmode()
337
338
339 def pass_devmode(self):
340 """Pass through boot screens in dev-mode."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700341 time.sleep(Servo.BOOT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700342 self.ctrl_d()
Chrome Bot9a1137d2011-07-19 14:35:00 -0700343 time.sleep(Servo.BOOT_DELAY)
Craig Harrison48997262011-06-27 14:31:10 -0700344
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700345
Craig Harrison6b36b122011-06-28 17:58:43 -0700346 def cold_reset(self):
347 """Perform a cold reset of the EC.
348
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700349 This has the side effect of shutting off the device. The
350 device is guaranteed to be off at the end of this call.
Craig Harrison6b36b122011-06-28 17:58:43 -0700351 """
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700352 # After the reset, give the EC the time it needs to
353 # re-initialize.
Craig Harrison6b36b122011-06-28 17:58:43 -0700354 self.set('cold_reset', 'on')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700355 time.sleep(Servo.SERVO_SEND_SIGNAL_DELAY)
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700356 self.set('cold_reset', 'off')
357 time.sleep(self._EC_RESET_DELAY)
Craig Harrison6b36b122011-06-28 17:58:43 -0700358
359
360 def warm_reset(self):
361 """Perform a warm reset of the device.
362
363 Has the side effect of restarting the device.
364 """
365 self.set('warm_reset', 'on')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700366 time.sleep(Servo.SERVO_SEND_SIGNAL_DELAY)
Craig Harrison6b36b122011-06-28 17:58:43 -0700367 self.set('warm_reset', 'off')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700368
369
Todd Brochefe72cb2012-07-11 19:58:53 -0700370 def _get_xmlrpclib_exception(self, xmlexc):
371 """Get meaningful exception string from xmlrpc.
372
373 Args:
374 xmlexc: xmlrpclib.Fault object
375
376 xmlrpclib.Fault.faultString has the following format:
377
378 <type 'exception type'>:'actual error message'
379
380 Parse and return the real exception from the servod side instead of the
381 less meaningful one like,
382 <Fault 1: "<type 'exceptions.AttributeError'>:'tca6416' object has no
383 attribute 'hw_driver'">
384
385 Returns:
386 string of underlying exception raised in servod.
387 """
388 return re.sub('^.*>:', '', xmlexc.faultString)
389
390
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700391 def get(self, gpio_name):
392 """Get the value of a gpio from Servod."""
393 assert gpio_name
Todd Brochefe72cb2012-07-11 19:58:53 -0700394 try:
395 return self._server.get(gpio_name)
396 except xmlrpclib.Fault as e:
397 err_msg = "Getting '%s' :: %s" % \
398 (gpio_name, self._get_xmlrpclib_exception(e))
399 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700400
401
402 def set(self, gpio_name, gpio_value):
403 """Set and check the value of a gpio using Servod."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700404 self.set_nocheck(gpio_name, gpio_value)
Todd Brochcf7c6652012-02-24 13:03:59 -0800405 retry_count = Servo.GET_RETRY_MAX
406 while gpio_value != self.get(gpio_name) and retry_count:
407 logging.warn("%s != %s, retry %d", gpio_name, gpio_value,
408 retry_count)
409 retry_count -= 1
410 time.sleep(Servo.SHORT_DELAY)
411 if not retry_count:
412 assert gpio_value == self.get(gpio_name), \
413 'Servo failed to set %s to %s' % (gpio_name, gpio_value)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700414
415
416 def set_nocheck(self, gpio_name, gpio_value):
417 """Set the value of a gpio using Servod."""
418 assert gpio_name and gpio_value
Todd Broch9753bd42012-03-21 10:15:08 -0700419 logging.info('Setting %s to %s', gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700420 try:
421 self._server.set(gpio_name, gpio_value)
422 except xmlrpclib.Fault as e:
423 err_msg = "Setting '%s' to '%s' :: %s" % \
424 (gpio_name, gpio_value, self._get_xmlrpclib_exception(e))
425 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700426
427
Jon Salzc88e5b62011-11-30 14:38:54 +0800428 # TODO(waihong) It may fail if multiple servo's are connected to the same
429 # host. Should look for a better way, like the USB serial name, to identify
430 # the USB device.
Simran Basi741b5d42012-05-18 11:27:15 -0700431 # TODO(sbasi) Remove this code from autoserv once firmware tests have been
432 # updated.
Jon Salzc88e5b62011-11-30 14:38:54 +0800433 def probe_host_usb_dev(self):
434 """Probe the USB disk device plugged-in the servo from the host side.
435
436 It tries to switch the USB mux to make the host unable to see the
437 USB disk and compares the result difference.
438
Jon Salzc88e5b62011-11-30 14:38:54 +0800439 Returns:
440 A string of USB disk path, like '/dev/sdb', or None if not existed.
441 """
442 cmd = 'ls /dev/sd[a-z]'
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800443 original_value = self.get_usbkey_direction()
Jon Salzc88e5b62011-11-30 14:38:54 +0800444
445 # Make the host unable to see the USB disk.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800446 if original_value != 'dut':
447 self.switch_usbkey('dut')
Jon Salzc88e5b62011-11-30 14:38:54 +0800448 time.sleep(self.USB_DETECTION_DELAY)
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800449 no_usb_set = set(self.system_output(cmd, ignore_status=True).split())
Jon Salzc88e5b62011-11-30 14:38:54 +0800450
451 # Make the host able to see the USB disk.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800452 self.switch_usbkey('host')
Jon Salzc88e5b62011-11-30 14:38:54 +0800453 time.sleep(self.USB_DETECTION_DELAY)
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800454 has_usb_set = set(self.system_output(cmd, ignore_status=True).split())
Jon Salzc88e5b62011-11-30 14:38:54 +0800455
456 # Back to its original value.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800457 if original_value != self.get_usbkey_direction():
458 self.switch_usbkey(original_value)
Jon Salzc88e5b62011-11-30 14:38:54 +0800459 time.sleep(self.USB_DETECTION_DELAY)
460
461 diff_set = has_usb_set - no_usb_set
462 if len(diff_set) == 1:
463 return diff_set.pop()
464 else:
465 return None
466
467
Mike Truty49153d82012-08-21 22:27:30 -0500468 def image_to_servo_usb(self, image_path=None,
469 make_image_noninteractive=False):
470 """Install an image to the USB key plugged into the servo.
471
472 This method may copy any image to the servo USB key including a
473 recovery image or a test image. These images are frequently used
474 for test purposes such as restoring a corrupted image or conducting
475 an upgrade of ec/fw/kernel as part of a test of a specific image part.
476
477 Args:
478 image_path: Path on the host to the recovery image.
479 make_image_noninteractive: Make the recovery image noninteractive,
480 therefore the DUT will reboot
481 automatically after installation.
482 """
483 # Turn the device off. This should happen before USB key detection, to
484 # prevent a recovery destined DUT from sensing the USB key due to the
485 # autodetection procedure.
486 self.initialize_dut(cold_reset=True)
487
488 # Set up Servo's usb mux.
489 self.set('prtctl4_pwren', 'on')
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800490 self.switch_usbkey('host')
Mike Truty49153d82012-08-21 22:27:30 -0500491 if image_path:
Tom Wai-Hong Tam42f136d2012-10-26 11:11:23 +0800492 logging.info('Searching for usb device and copying image to it. '
493 'Please wait a few minutes...')
Mike Truty49153d82012-08-21 22:27:30 -0500494 if not self._server.download_image_to_usb(image_path):
495 logging.error('Failed to transfer requested image to USB. '
496 'Please take a look at Servo Logs.')
497 raise error.AutotestError('Download image to usb failed.')
498 if make_image_noninteractive:
499 logging.info('Making image noninteractive')
500 if not self._server.make_image_noninteractive():
501 logging.error('Failed to make image noninteractive. '
502 'Please take a look at Servo Logs.')
503
504
Simran Basi741b5d42012-05-18 11:27:15 -0700505 def install_recovery_image(self, image_path=None,
J. Richard Barnetteb6de7e32013-02-14 13:28:04 -0800506 make_image_noninteractive=False):
Jon Salzc88e5b62011-11-30 14:38:54 +0800507 """Install the recovery image specied by the path onto the DUT.
508
509 This method uses google recovery mode to install a recovery image
510 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 +0800511 board specified by the usb_dev. If no image path is specified
Jon Salzc88e5b62011-11-30 14:38:54 +0800512 we use the recovery image already on the usb image.
513
514 Args:
515 image_path: Path on the host to the recovery image.
Simran Basi741b5d42012-05-18 11:27:15 -0700516 make_image_noninteractive: Make the recovery image noninteractive,
517 therefore the DUT will reboot
518 automatically after installation.
Jon Salzc88e5b62011-11-30 14:38:54 +0800519 """
Mike Truty49153d82012-08-21 22:27:30 -0500520 self.image_to_servo_usb(image_path, make_image_noninteractive)
Jon Salzc88e5b62011-11-30 14:38:54 +0800521
522 # Boot in recovery mode.
523 try:
Jon Salzc88e5b62011-11-30 14:38:54 +0800524 self.enable_recovery_mode()
Tom Wai-Hong Tam23869102012-01-17 15:41:30 +0800525 self.power_short_press()
Jon Salzc88e5b62011-11-30 14:38:54 +0800526 time.sleep(Servo.RECOVERY_BOOT_DELAY)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800527 self.switch_usbkey('dut')
Jon Salzc88e5b62011-11-30 14:38:54 +0800528 self.disable_recovery_mode()
Jon Salzc88e5b62011-11-30 14:38:54 +0800529 except:
530 # In case anything went wrong we want to make sure to do a clean
531 # reset.
532 self.disable_recovery_mode()
533 self.warm_reset()
534 raise
535
536
Todd Brochf24d2782011-08-19 10:55:41 -0700537 def _connect_servod(self, servo_host, servo_port):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700538 """Connect to the Servod process with XMLRPC.
539
540 Args:
541 servo_port: Port the Servod process is listening on.
542 """
Todd Brochf24d2782011-08-19 10:55:41 -0700543 remote = 'http://%s:%s' % (servo_host, servo_port)
Todd Broch96d83aa2011-08-29 14:37:38 -0700544 self._server = xmlrpclib.ServerProxy(remote)
Todd Brochf24d2782011-08-19 10:55:41 -0700545 try:
546 self._server.echo("ping-test")
Todd Broch96d83aa2011-08-29 14:37:38 -0700547 except:
548 logging.error('Connection to servod failed')
Todd Brochf24d2782011-08-19 10:55:41 -0700549 raise
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800550
551
552 def _scp_image(self, image_path):
553 """Copy image to the servo host.
554
555 When programming a firmware image on the DUT, the image must be
556 located on the host to which the servo device is connected. Sometimes
557 servo is controlled by a remote host, in this case the image needs to
558 be transferred to the remote host.
559
560 @param image_path: a string, name of the firmware image file to be
561 transferred.
562 @return: a string, full path name of the copied file on the remote.
563 """
564
565 dest_path = os.path.join('/tmp', os.path.basename(image_path))
566 scp_cmd = self._scp_cmd_template % (image_path, dest_path)
567 utils.system(scp_cmd)
568 return dest_path
569
570
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800571 def system(self, command, timeout=None):
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800572 """Execute the passed in command on the servod host."""
573 if self._sudo_required:
574 command = 'sudo -n %s' % command
575 if self._ssh_prefix:
576 command = "%s '%s'" % (self._ssh_prefix, command)
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800577 logging.info('Will execute on servo host: %s', command)
578 utils.system(command, timeout=timeout)
579
580
581 def system_output(self, command, timeout=None,
582 ignore_status=False, args=()):
583 """Execute the passed in command on the servod host, return stdout.
584
585 @param command, a string, the command to execute
586 @param timeout, an int, max number of seconds to wait til command
587 execution completes
588 @ignore_status, a Boolean, if true - ignore command's nonzero exit
589 status, otherwise an exception will be thrown
590 @param args, a tuple of strings, each becoming a separate command line
591 parameter for the command
592 @return: command's stdout as a string.
593 """
594 if self._sudo_required:
595 command = 'sudo -n %s' % command
596 if self._ssh_prefix:
597 command = "%s '%s'" % (self._ssh_prefix, command)
598 logging.info('Will execute and collect output on servo host: %s %s',
599 command, ' '.join("'%s'" % x for x in args))
600 return utils.system_output(command, timeout=timeout,
601 ignore_status=ignore_status, args=args)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800602
603
604 def program_ec(self, board, image):
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800605 """Program EC on a given board using given image.
606
607 @param board: a string, type of the DUT board
608 @param image: a string, file name of the EC image to program on the
609 DUT
610 """
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800611 if not self.is_localhost():
612 image = self._scp_image(image)
613 programmer.program_ec(board, self, image)
614
615
616 def program_bootprom(self, board, image):
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800617 """Program bootprom on a given board using given image.
618
619 In case servo is controlled by a remote host, the image needs to be
620 transferred to the host.
621
622 If the device tree subdirectory is present along with the image, the
623 subdirectory is also copied to the remote host.
624
625 @param board: a string, type of the DUT board
626 @param image: a string, file name of the firmware image to program on
627 the DUT. The device tree subdirectory, if present, is on
628 the same level with the image file.
629 """
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800630 if not self.is_localhost():
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800631 dts_path = os.path.join(os.path.dirname(image), 'dts')
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800632 image = self._scp_image(image)
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800633 if os.path.isdir(dts_path):
634 self._scp_image(dts_path)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800635 programmer.program_bootprom(board, self, image)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800636
637 def switch_usbkey(self, side):
638 """Connect USB flash stick to either host or DUT.
639
640 This function switches the servo multiplexer to provide electrical
641 connection between the USB port J3 and either host or DUT side.
642
643 Switching is accompanied by powercycling of the USB stick, because it
644 sometimes gets wedged if the mux is switched while the stick power is
645 on.
646
647 @param side: a string, either 'dut' or 'host' - indicates which side
648 the USB flash device is required to be connected to.
649
650 @raise: error.TestError in case the parameter is neither 'dut' not
651 'host'
652 """
653
654 if self._usb_position == side:
655 return
656
657 if side == 'host':
658 mux_direction = 'servo_sees_usbkey'
659 elif side == 'dut':
660 mux_direction = 'dut_sees_usbkey'
661 else:
662 raise error.TestError('unknown USB mux setting: %s' % side)
663
664 self.set('prtctl4_pwren', 'off')
665 time.sleep(self.USB_POWEROFF_DELAY)
666 self.set('usb_mux_sel1', mux_direction)
667 time.sleep(self.USB_POWEROFF_DELAY)
668 self.set('prtctl4_pwren', 'on')
669
670 self._usb_position = side
671
672
673 def get_usbkey_direction(self):
674 """Get name of the side the USB device is connected to.
675
676 @return a string, either 'dut' or 'host'
677 """
678 if not self._usb_position:
679 if self.get('usb_mux_sel1').starstwith('dut'):
680 self._usb_position = 'dut'
681 else:
682 self._usb_position = 'host'
683 return self._usb_position