blob: a565449dfeda8d24c90a713565a51b133abd5a47 [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 Barnette69929a52013-03-15 13:22:11 -0700125 # TODO(jrbarnette): As of this writing, not all beaglebone
126 # servo hosts support the get_board() function. For now, we
127 # treat the problem hosts as an unsupported board. The try
128 # wrapper should be removed once all the hosts are updated.
129 try:
130 board = self._server.get_board()
131 self._power_state = (
132 power_state_controller.create_controller(self, board))
133 except xmlrpclib.Fault as e:
134 logging.error('Failed to create power state controller; '
135 'check hdctools version on %s.', servo_host)
136 logging.exception(e)
137 self._power_state = None
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800138
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800139 # a string, showing what interface (host or dut) the USB device is
140 # connected to.
141 self._usb_position = None
142 self.set('dut_hub_pwren', 'on')
143 self.set('usb_mux_oe1', 'on')
144 self.switch_usbkey('host')
145
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800146 # Commands on the servo host must be run by the superuser. Our account
147 # on Beaglebone is root, but locally we might be running as a
148 # different user. If so - `sudo ' will have to be added to the
149 # commands.
150 if self._is_localhost:
151 self._sudo_required = utils.system_output('id -u') != '0'
152 self._ssh_prefix = ''
153 else:
J. Richard Barnette91e6c6e2013-03-12 18:18:22 -0700154 common_options = ('-o BatchMode=yes '
155 '-o StrictHostKeyChecking=no '
156 '-o UserKnownHostsFile=/dev/null')
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800157 self._sudo_required = False
J. Richard Barnette91e6c6e2013-03-12 18:18:22 -0700158 self._ssh_prefix = 'ssh -a -x %s root@%s ' % (
159 common_options, servo_host)
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800160 self._scp_cmd_template = 'scp -r %s ' % common_options
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800161 self._scp_cmd_template += '%s ' + 'root@' + servo_host + ':%s'
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800162
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700163 def initialize_dut(self, cold_reset=False):
164 """Initializes a dut for testing purposes.
165
166 This sets various servo signals back to default values
167 appropriate for the target board. By default, if the DUT
168 is already on, it stays on. If the DUT is powered off
169 before initialization, its state afterward is unspecified.
170
171 If cold reset is requested, the DUT is guaranteed to be off
172 at the end of initialization, regardless of its initial
173 state.
174
175 Rationale: Basic initialization of servo sets the lid open,
176 when there is a lid. This operation won't affect powered on
177 units; however, setting the lid open may power on a unit
178 that's off, depending on factors outside the scope of this
179 function.
180
181 @param cold_reset If True, cold reset the device after
182 initialization.
183 """
184 self._server.hwinit()
185 if cold_reset:
186 self.cold_reset()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700187
188
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800189 def is_localhost(self):
190 """Is the servod hosted locally?
191
192 Returns:
193 True if local hosted; otherwise, False.
194 """
195 return self._is_localhost
196
197
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700198 def power_long_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700199 """Simulate a long power button press."""
J. Richard Barnettef12bff22012-07-18 14:41:06 -0700200 # After a long power press, the EC may ignore the next power
201 # button press (at least on Alex). To guarantee that this
202 # won't happen, we need to allow the EC one second to
203 # collect itself.
Chrome Bot9a1137d2011-07-19 14:35:00 -0700204 self.power_key(Servo.LONG_DELAY)
J. Richard Barnettef12bff22012-07-18 14:41:06 -0700205 time.sleep(1.0)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700206
207
208 def power_normal_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700209 """Simulate a normal power button press."""
210 self.power_key()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700211
212
213 def power_short_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700214 """Simulate a short power button press."""
215 self.power_key(Servo.SHORT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700216
217
Chrome Bot9a1137d2011-07-19 14:35:00 -0700218 def power_key(self, secs=NORMAL_TRANSITION_DELAY):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700219 """Simulate a power button press.
220
221 Args:
222 secs: Time in seconds to simulate the keypress.
223 """
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800224 self.set_get_all(['pwr_button:press',
225 'sleep:%.4f' % secs,
226 'pwr_button:release'])
Todd Broch31c82502011-08-29 08:14:39 -0700227 # TODO(tbroch) Different systems have different release times on the
228 # power button that this loop addresses. Longer term we may want to
229 # make this delay platform specific.
230 retry = 1
231 while True:
232 value = self.get('pwr_button')
233 if value == 'release' or retry > Servo.RELEASE_RETRY_MAX:
234 break
Todd Broch9753bd42012-03-21 10:15:08 -0700235 logging.info('Waiting for pwr_button to release, retry %d.', retry)
Todd Broch31c82502011-08-29 08:14:39 -0700236 retry += 1
237 time.sleep(Servo.SHORT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700238
239
240 def lid_open(self):
241 """Simulate opening the lid."""
Craig Harrison48997262011-06-27 14:31:10 -0700242 self.set_nocheck('lid_open', 'yes')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700243
244
245 def lid_close(self):
Craig Harrison48997262011-06-27 14:31:10 -0700246 """Simulate closing the lid.
247
248 Waits 6 seconds to ensure the device is fully asleep before returning.
249 """
250 self.set_nocheck('lid_open', 'no')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700251 time.sleep(Servo.SLEEP_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700252
253
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800254 def _press_keys(self, key):
255 """Simulate button presses.
256
257 Note, key presses will remain on indefinitely. See
258 _press_and_release_keys for release procedure.
259 """
Gediminas Ramanauskas9da80f22012-11-14 12:59:43 -0800260 (m1_a1, m1_a0, m2_a1, m2_a0) = self.KEY_MATRIX[self._key_matrix]['none']
Todd Broch9753bd42012-03-21 10:15:08 -0700261 self.set_nocheck('kbd_m2_a0', m2_a0)
262 self.set_nocheck('kbd_m2_a1', m2_a1)
263 self.set_nocheck('kbd_m1_a0', m1_a0)
264 self.set_nocheck('kbd_m1_a1', m1_a1)
Vic Yange262a3e2012-11-02 18:48:37 +0800265 self.set_nocheck('kbd_en', 'on')
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800266
Gediminas Ramanauskas9da80f22012-11-14 12:59:43 -0800267 (m1_a1, m1_a0, m2_a1, m2_a0) = self.KEY_MATRIX[self._key_matrix][key]
268 self.set_nocheck('kbd_m2_a0', m2_a0)
269 self.set_nocheck('kbd_m2_a1', m2_a1)
270 self.set_nocheck('kbd_m1_a0', m1_a0)
271 self.set_nocheck('kbd_m1_a1', m1_a1)
272
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800273
274 def _press_and_release_keys(self, key,
275 press_secs=SERVO_KEY_PRESS_DELAY):
276 """Simulate button presses and release."""
277 self._press_keys(key)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700278 time.sleep(press_secs)
279 self.set_nocheck('kbd_en', 'off')
280
281
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -0700282 def set_key_matrix(self, matrix=0):
283 """Set keyboard mapping"""
284 self._key_matrix = matrix
285
286
Chrome Bot9a1137d2011-07-19 14:35:00 -0700287 def ctrl_d(self):
288 """Simulate Ctrl-d simultaneous button presses."""
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700289 self._press_and_release_keys('ctrl_d')
Todd Broch9dfc3a82011-11-01 08:09:28 -0700290
291
Todd Broch9753bd42012-03-21 10:15:08 -0700292 def ctrl_enter(self):
293 """Simulate Ctrl-enter simultaneous button presses."""
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700294 self._press_and_release_keys('ctrl_enter')
Todd Broch9753bd42012-03-21 10:15:08 -0700295
296
Todd Broch9dfc3a82011-11-01 08:09:28 -0700297 def d_key(self):
298 """Simulate Enter key button press."""
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700299 self._press_and_release_keys('d')
Todd Broch9dfc3a82011-11-01 08:09:28 -0700300
301
302 def ctrl_key(self):
303 """Simulate Enter key button press."""
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700304 self._press_and_release_keys('ctrl')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700305
306
Chrome Bot9a1137d2011-07-19 14:35:00 -0700307 def enter_key(self):
308 """Simulate Enter key button press."""
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700309 self._press_and_release_keys('enter')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700310
311
Chrome Bot9a1137d2011-07-19 14:35:00 -0700312 def refresh_key(self):
313 """Simulate Refresh key (F3) button press."""
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700314 self._press_and_release_keys('refresh')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700315
316
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800317 def ctrl_refresh_key(self):
318 """Simulate Ctrl and Refresh (F3) simultaneous press.
319
320 This key combination is an alternative of Space key.
321 """
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700322 self._press_and_release_keys('ctrl_refresh')
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800323
324
Chrome Bot9a1137d2011-07-19 14:35:00 -0700325 def imaginary_key(self):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700326 """Simulate imaginary key button press.
327
328 Maps to a key that doesn't physically exist.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700329 """
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700330 self._press_and_release_keys('unused')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700331
332
Craig Harrison6b36b122011-06-28 17:58:43 -0700333 def enable_recovery_mode(self):
334 """Enable recovery mode on device."""
335 self.set('rec_mode', 'on')
336
337
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800338 def custom_recovery_mode(self):
339 """Custom key combination to enter recovery mode."""
340 self._press_keys('rec_mode')
341 self.power_normal_press()
342 time.sleep(self.SERVO_KEY_PRESS_DELAY)
343 self.set_nocheck('kbd_en', 'off')
344
345
Craig Harrison6b36b122011-06-28 17:58:43 -0700346 def disable_recovery_mode(self):
347 """Disable recovery mode on device."""
348 self.set('rec_mode', 'off')
349
350
351 def enable_development_mode(self):
352 """Enable development mode on device."""
353 self.set('dev_mode', 'on')
354
355
356 def disable_development_mode(self):
357 """Disable development mode on device."""
358 self.set('dev_mode', 'off')
359
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700360 def boot_devmode(self):
361 """Boot a dev-mode device that is powered off."""
Tom Wai-Hong Tam23869102012-01-17 15:41:30 +0800362 self.power_short_press()
Craig Harrison48997262011-06-27 14:31:10 -0700363 self.pass_devmode()
364
365
366 def pass_devmode(self):
367 """Pass through boot screens in dev-mode."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700368 time.sleep(Servo.BOOT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700369 self.ctrl_d()
Chrome Bot9a1137d2011-07-19 14:35:00 -0700370 time.sleep(Servo.BOOT_DELAY)
Craig Harrison48997262011-06-27 14:31:10 -0700371
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700372
Craig Harrison6b36b122011-06-28 17:58:43 -0700373 def cold_reset(self):
374 """Perform a cold reset of the EC.
375
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700376 This has the side effect of shutting off the device. The
377 device is guaranteed to be off at the end of this call.
Craig Harrison6b36b122011-06-28 17:58:43 -0700378 """
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800379 self.set_get_all(['cold_reset:on',
380 'sleep:%.4f' % self._DUT_RESET_DELAY,
381 'cold_reset:off'])
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700382 # After the reset, give the EC the time it needs to
383 # re-initialize.
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700384 time.sleep(self._EC_RESET_DELAY)
Craig Harrison6b36b122011-06-28 17:58:43 -0700385
386
387 def warm_reset(self):
388 """Perform a warm reset of the device.
389
390 Has the side effect of restarting the device.
391 """
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800392 self.set_get_all(['warm_reset:on',
393 'sleep:%.4f' % self._DUT_RESET_DELAY,
394 'warm_reset:off'])
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700395
396
Todd Brochefe72cb2012-07-11 19:58:53 -0700397 def _get_xmlrpclib_exception(self, xmlexc):
398 """Get meaningful exception string from xmlrpc.
399
400 Args:
401 xmlexc: xmlrpclib.Fault object
402
403 xmlrpclib.Fault.faultString has the following format:
404
405 <type 'exception type'>:'actual error message'
406
407 Parse and return the real exception from the servod side instead of the
408 less meaningful one like,
409 <Fault 1: "<type 'exceptions.AttributeError'>:'tca6416' object has no
410 attribute 'hw_driver'">
411
412 Returns:
413 string of underlying exception raised in servod.
414 """
415 return re.sub('^.*>:', '', xmlexc.faultString)
416
417
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700418 def get(self, gpio_name):
419 """Get the value of a gpio from Servod."""
420 assert gpio_name
Todd Brochefe72cb2012-07-11 19:58:53 -0700421 try:
422 return self._server.get(gpio_name)
423 except xmlrpclib.Fault as e:
424 err_msg = "Getting '%s' :: %s" % \
425 (gpio_name, self._get_xmlrpclib_exception(e))
426 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700427
428
429 def set(self, gpio_name, gpio_value):
430 """Set and check the value of a gpio using Servod."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700431 self.set_nocheck(gpio_name, gpio_value)
Todd Brochcf7c6652012-02-24 13:03:59 -0800432 retry_count = Servo.GET_RETRY_MAX
433 while gpio_value != self.get(gpio_name) and retry_count:
434 logging.warn("%s != %s, retry %d", gpio_name, gpio_value,
435 retry_count)
436 retry_count -= 1
437 time.sleep(Servo.SHORT_DELAY)
438 if not retry_count:
439 assert gpio_value == self.get(gpio_name), \
440 'Servo failed to set %s to %s' % (gpio_name, gpio_value)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700441
442
443 def set_nocheck(self, gpio_name, gpio_value):
444 """Set the value of a gpio using Servod."""
445 assert gpio_name and gpio_value
Todd Broch9753bd42012-03-21 10:15:08 -0700446 logging.info('Setting %s to %s', gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700447 try:
448 self._server.set(gpio_name, gpio_value)
449 except xmlrpclib.Fault as e:
450 err_msg = "Setting '%s' to '%s' :: %s" % \
451 (gpio_name, gpio_value, self._get_xmlrpclib_exception(e))
452 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700453
454
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800455 def set_get_all(self, controls):
456 """Set &| get one or more control values.
457
458 @param controls: list of strings, controls to set &| get.
459
460 @raise: error.TestError in case error occurs setting/getting values.
461 """
462 rv = []
463 try:
464 rv = self._server.set_get_all(controls)
465 except xmlrpclib.Fault as e:
466 # TODO(waihong): Remove the following backward compatibility when
467 # the new versions of hdctools are deployed.
468 if 'not supported' in str(e):
469 logging.warning('The servod is too old that set_get_all '
470 'not supported. Use set and get instead.')
471 for control in controls:
472 if ':' in control:
473 (name, value) = control.split(':')
474 if name == 'sleep':
475 time.sleep(float(value))
476 else:
477 self.set_nocheck(name, value)
478 rv.append(True)
479 else:
480 rv.append(self.get(name))
481 else:
482 err_msg = "Problem with '%s' :: %s" % \
483 (controls, self._get_xmlrpclib_exception(e))
484 raise error.TestFail(err_msg)
485 return rv
486
487
Jon Salzc88e5b62011-11-30 14:38:54 +0800488 # TODO(waihong) It may fail if multiple servo's are connected to the same
489 # host. Should look for a better way, like the USB serial name, to identify
490 # the USB device.
Simran Basi741b5d42012-05-18 11:27:15 -0700491 # TODO(sbasi) Remove this code from autoserv once firmware tests have been
492 # updated.
Jon Salzc88e5b62011-11-30 14:38:54 +0800493 def probe_host_usb_dev(self):
494 """Probe the USB disk device plugged-in the servo from the host side.
495
496 It tries to switch the USB mux to make the host unable to see the
497 USB disk and compares the result difference.
498
Jon Salzc88e5b62011-11-30 14:38:54 +0800499 Returns:
500 A string of USB disk path, like '/dev/sdb', or None if not existed.
501 """
502 cmd = 'ls /dev/sd[a-z]'
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800503 original_value = self.get_usbkey_direction()
Jon Salzc88e5b62011-11-30 14:38:54 +0800504
505 # Make the host unable to see the USB disk.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800506 if original_value != 'dut':
507 self.switch_usbkey('dut')
Jon Salzc88e5b62011-11-30 14:38:54 +0800508 time.sleep(self.USB_DETECTION_DELAY)
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800509 no_usb_set = set(self.system_output(cmd, ignore_status=True).split())
Jon Salzc88e5b62011-11-30 14:38:54 +0800510
511 # Make the host able to see the USB disk.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800512 self.switch_usbkey('host')
Jon Salzc88e5b62011-11-30 14:38:54 +0800513 time.sleep(self.USB_DETECTION_DELAY)
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800514 has_usb_set = set(self.system_output(cmd, ignore_status=True).split())
Jon Salzc88e5b62011-11-30 14:38:54 +0800515
516 # Back to its original value.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800517 if original_value != self.get_usbkey_direction():
518 self.switch_usbkey(original_value)
Jon Salzc88e5b62011-11-30 14:38:54 +0800519 time.sleep(self.USB_DETECTION_DELAY)
520
521 diff_set = has_usb_set - no_usb_set
522 if len(diff_set) == 1:
523 return diff_set.pop()
524 else:
525 return None
526
527
J. Richard Barnette69929a52013-03-15 13:22:11 -0700528 def recovery_supported(self):
529 """Return whether servo-based recovery should work.
530
531 Use of `image_to_servo_usb()` and `install_recovery_image()`
532 relies on DUT-board specific behaviors, and is not supported
533 for all types of board. Return whether these two operations
534 are expected to succeed for the current DUT.
535
536 @return `True` iff the recovery related methods are supported
537 for this servo and DUT.
538
539 """
540 return self._power_state is not None
541
542
Mike Truty49153d82012-08-21 22:27:30 -0500543 def image_to_servo_usb(self, image_path=None,
544 make_image_noninteractive=False):
545 """Install an image to the USB key plugged into the servo.
546
547 This method may copy any image to the servo USB key including a
548 recovery image or a test image. These images are frequently used
549 for test purposes such as restoring a corrupted image or conducting
550 an upgrade of ec/fw/kernel as part of a test of a specific image part.
551
552 Args:
553 image_path: Path on the host to the recovery image.
554 make_image_noninteractive: Make the recovery image noninteractive,
555 therefore the DUT will reboot
556 automatically after installation.
557 """
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700558 # We're about to start plugging/unplugging the USB key. We
559 # don't know the state of the DUT, or what it might choose
560 # to do to the device after hotplug. To avoid surprises,
561 # force the DUT to be off.
562 self._server.hwinit()
563 self._power_state.power_off()
Mike Truty49153d82012-08-21 22:27:30 -0500564
565 # Set up Servo's usb mux.
566 self.set('prtctl4_pwren', 'on')
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800567 self.switch_usbkey('host')
Mike Truty49153d82012-08-21 22:27:30 -0500568 if image_path:
Tom Wai-Hong Tam42f136d2012-10-26 11:11:23 +0800569 logging.info('Searching for usb device and copying image to it. '
570 'Please wait a few minutes...')
Mike Truty49153d82012-08-21 22:27:30 -0500571 if not self._server.download_image_to_usb(image_path):
572 logging.error('Failed to transfer requested image to USB. '
573 'Please take a look at Servo Logs.')
574 raise error.AutotestError('Download image to usb failed.')
575 if make_image_noninteractive:
576 logging.info('Making image noninteractive')
577 if not self._server.make_image_noninteractive():
578 logging.error('Failed to make image noninteractive. '
579 'Please take a look at Servo Logs.')
580
581
Simran Basi741b5d42012-05-18 11:27:15 -0700582 def install_recovery_image(self, image_path=None,
J. Richard Barnetteb6de7e32013-02-14 13:28:04 -0800583 make_image_noninteractive=False):
Jon Salzc88e5b62011-11-30 14:38:54 +0800584 """Install the recovery image specied by the path onto the DUT.
585
586 This method uses google recovery mode to install a recovery image
587 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 +0800588 board specified by the usb_dev. If no image path is specified
Jon Salzc88e5b62011-11-30 14:38:54 +0800589 we use the recovery image already on the usb image.
590
591 Args:
592 image_path: Path on the host to the recovery image.
Simran Basi741b5d42012-05-18 11:27:15 -0700593 make_image_noninteractive: Make the recovery image noninteractive,
594 therefore the DUT will reboot
595 automatically after installation.
Jon Salzc88e5b62011-11-30 14:38:54 +0800596 """
Mike Truty49153d82012-08-21 22:27:30 -0500597 self.image_to_servo_usb(image_path, make_image_noninteractive)
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700598 self._power_state.power_on(dev_mode=self._power_state.DEV_OFF,
599 rec_mode=self._power_state.REC_ON)
600 time.sleep(self._RECOVERY_INSERT_DELAY)
601 self.switch_usbkey('dut')
Jon Salzc88e5b62011-11-30 14:38:54 +0800602
603
Todd Brochf24d2782011-08-19 10:55:41 -0700604 def _connect_servod(self, servo_host, servo_port):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700605 """Connect to the Servod process with XMLRPC.
606
607 Args:
608 servo_port: Port the Servod process is listening on.
609 """
Todd Brochf24d2782011-08-19 10:55:41 -0700610 remote = 'http://%s:%s' % (servo_host, servo_port)
Todd Broch96d83aa2011-08-29 14:37:38 -0700611 self._server = xmlrpclib.ServerProxy(remote)
Todd Brochf24d2782011-08-19 10:55:41 -0700612 try:
613 self._server.echo("ping-test")
Todd Broch96d83aa2011-08-29 14:37:38 -0700614 except:
615 logging.error('Connection to servod failed')
Todd Brochf24d2782011-08-19 10:55:41 -0700616 raise
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800617
618
619 def _scp_image(self, image_path):
620 """Copy image to the servo host.
621
622 When programming a firmware image on the DUT, the image must be
623 located on the host to which the servo device is connected. Sometimes
624 servo is controlled by a remote host, in this case the image needs to
625 be transferred to the remote host.
626
627 @param image_path: a string, name of the firmware image file to be
628 transferred.
629 @return: a string, full path name of the copied file on the remote.
630 """
631
632 dest_path = os.path.join('/tmp', os.path.basename(image_path))
633 scp_cmd = self._scp_cmd_template % (image_path, dest_path)
634 utils.system(scp_cmd)
635 return dest_path
636
637
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800638 def system(self, command, timeout=None):
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800639 """Execute the passed in command on the servod host."""
640 if self._sudo_required:
641 command = 'sudo -n %s' % command
642 if self._ssh_prefix:
643 command = "%s '%s'" % (self._ssh_prefix, command)
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800644 logging.info('Will execute on servo host: %s', command)
645 utils.system(command, timeout=timeout)
646
647
648 def system_output(self, command, timeout=None,
649 ignore_status=False, args=()):
650 """Execute the passed in command on the servod host, return stdout.
651
652 @param command, a string, the command to execute
653 @param timeout, an int, max number of seconds to wait til command
654 execution completes
655 @ignore_status, a Boolean, if true - ignore command's nonzero exit
656 status, otherwise an exception will be thrown
657 @param args, a tuple of strings, each becoming a separate command line
658 parameter for the command
659 @return: command's stdout as a string.
660 """
661 if self._sudo_required:
662 command = 'sudo -n %s' % command
663 if self._ssh_prefix:
664 command = "%s '%s'" % (self._ssh_prefix, command)
665 logging.info('Will execute and collect output on servo host: %s %s',
666 command, ' '.join("'%s'" % x for x in args))
667 return utils.system_output(command, timeout=timeout,
668 ignore_status=ignore_status, args=args)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800669
670
671 def program_ec(self, board, image):
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800672 """Program EC on a given board using given image.
673
674 @param board: a string, type of the DUT board
675 @param image: a string, file name of the EC image to program on the
676 DUT
677 """
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800678 if not self.is_localhost():
679 image = self._scp_image(image)
680 programmer.program_ec(board, self, image)
681
682
683 def program_bootprom(self, board, image):
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800684 """Program bootprom on a given board using given image.
685
686 In case servo is controlled by a remote host, the image needs to be
687 transferred to the host.
688
689 If the device tree subdirectory is present along with the image, the
690 subdirectory is also copied to the remote host.
691
692 @param board: a string, type of the DUT board
693 @param image: a string, file name of the firmware image to program on
694 the DUT. The device tree subdirectory, if present, is on
695 the same level with the image file.
696 """
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800697 if not self.is_localhost():
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800698 dts_path = os.path.join(os.path.dirname(image), 'dts')
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800699 image = self._scp_image(image)
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800700 if os.path.isdir(dts_path):
701 self._scp_image(dts_path)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800702 programmer.program_bootprom(board, self, image)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800703
704 def switch_usbkey(self, side):
705 """Connect USB flash stick to either host or DUT.
706
707 This function switches the servo multiplexer to provide electrical
708 connection between the USB port J3 and either host or DUT side.
709
710 Switching is accompanied by powercycling of the USB stick, because it
711 sometimes gets wedged if the mux is switched while the stick power is
712 on.
713
714 @param side: a string, either 'dut' or 'host' - indicates which side
715 the USB flash device is required to be connected to.
716
717 @raise: error.TestError in case the parameter is neither 'dut' not
718 'host'
719 """
720
721 if self._usb_position == side:
722 return
723
724 if side == 'host':
725 mux_direction = 'servo_sees_usbkey'
726 elif side == 'dut':
727 mux_direction = 'dut_sees_usbkey'
728 else:
729 raise error.TestError('unknown USB mux setting: %s' % side)
730
731 self.set('prtctl4_pwren', 'off')
732 time.sleep(self.USB_POWEROFF_DELAY)
733 self.set('usb_mux_sel1', mux_direction)
734 time.sleep(self.USB_POWEROFF_DELAY)
735 self.set('prtctl4_pwren', 'on')
736
737 self._usb_position = side
738
739
740 def get_usbkey_direction(self):
741 """Get name of the side the USB device is connected to.
742
743 @return a string, either 'dut' or 'host'
744 """
745 if not self._usb_position:
746 if self.get('usb_mux_sel1').starstwith('dut'):
747 self._usb_position = 'dut'
748 else:
749 self._usb_position = 'host'
750 return self._usb_position