blob: 3d6126aa1cceca1f7751afd61966bf23dc7670dc [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
J. Richard Barnette41320ee2013-03-11 13:00:13 -070055 # Default minimum time interval between 'press' and 'release'
56 # keyboard events.
Vic Yang0aca1c22012-11-19 18:33:56 -080057 SERVO_KEY_PRESS_DELAY = 0.1
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070058
Jon Salzc88e5b62011-11-30 14:38:54 +080059 # Time between an usb disk plugged-in and detected in the system.
60 USB_DETECTION_DELAY = 10
Vadim Bendeburye7bd9362012-12-19 14:35:20 -080061 # Time to keep USB power off before and after USB mux direction is changed
62 USB_POWEROFF_DELAY = 2
Jon Salzc88e5b62011-11-30 14:38:54 +080063
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -070064 KEY_MATRIX_ALT_0 = {
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -070065 'ctrl_refresh': ['0', '0', '0', '1'],
66 'ctrl_d': ['0', '1', '0', '0'],
67 'd': ['0', '1', '1', '1'],
68 'ctrl_enter': ['1', '0', '0', '0'],
69 'enter': ['1', '0', '1', '1'],
70 'ctrl': ['1', '1', '0', '0'],
71 'refresh': ['1', '1', '0', '1'],
72 'unused': ['1', '1', '1', '0'],
73 'none': ['1', '1', '1', '1']}
Chris Masone6a0680f2012-03-02 08:40:00 -080074
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -070075 KEY_MATRIX_ALT_1 = {
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -070076 'ctrl_d': ['0', '0', '1', '0'],
77 'd': ['0', '0', '1', '1'],
78 'ctrl_enter': ['0', '1', '1', '0'],
79 'enter': ['0', '1', '1', '1'],
80 'ctrl_refresh': ['1', '0', '0', '1'],
81 'unused': ['1', '1', '0', '0'],
82 'refresh': ['1', '1', '0', '1'],
83 'ctrl': ['1', '1', '1', '0'],
84 'none': ['1', '1', '1', '1']}
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -070085
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -080086 KEY_MATRIX_ALT_2 = {
87 'ctrl_d': ['0', '0', '0', '1'],
88 'd': ['0', '0', '1', '1'],
89 'unused': ['0', '1', '1', '1'],
90 'rec_mode': ['1', '0', '0', '0'],
91 'ctrl_enter': ['1', '0', '0', '1'],
92 'enter': ['1', '0', '1', '1'],
93 'ctrl': ['1', '1', '0', '1'],
94 'refresh': ['1', '1', '1', '0'],
95 'ctrl_refresh': ['1', '1', '1', '1'],
96 'none': ['1', '1', '1', '1']}
97
98 KEY_MATRIX = [KEY_MATRIX_ALT_0, KEY_MATRIX_ALT_1, KEY_MATRIX_ALT_2]
J. Richard Barnette67ccb872012-04-19 16:34:56 -070099
J. Richard Barnetted5f807a2013-02-11 16:51:00 -0800100 def __init__(self, servo_host='localhost', servo_port=9999):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700101 """Sets up the servo communication infrastructure.
102
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800103 @param servo_host Name of the host where the servod process
104 is running.
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800105 @param servo_port Port the servod process is listening on.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700106 """
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -0700107 self._key_matrix = 0
J. Richard Barnette384056b2012-04-16 11:04:46 -0700108 self._server = None
Todd Brochf24d2782011-08-19 10:55:41 -0700109 self._connect_servod(servo_host, servo_port)
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800110 self._is_localhost = (servo_host == 'localhost')
J. Richard Barnette69929a52013-03-15 13:22:11 -0700111 # TODO(jrbarnette): As of this writing, not all beaglebone
112 # servo hosts support the get_board() function. For now, we
113 # treat the problem hosts as an unsupported board. The try
114 # wrapper should be removed once all the hosts are updated.
J. Richard Barnette75136b32013-03-26 13:38:44 -0700115 board = None
J. Richard Barnette69929a52013-03-15 13:22:11 -0700116 try:
117 board = self._server.get_board()
J. Richard Barnette69929a52013-03-15 13:22:11 -0700118 except xmlrpclib.Fault as e:
119 logging.error('Failed to create power state controller; '
120 'check hdctools version on %s.', servo_host)
121 logging.exception(e)
J. Richard Barnette75136b32013-03-26 13:38:44 -0700122 self._power_state = (
123 power_state_controller.create_controller(self, board))
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800124
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800125 # a string, showing what interface (host or dut) the USB device is
126 # connected to.
127 self._usb_position = None
128 self.set('dut_hub_pwren', 'on')
129 self.set('usb_mux_oe1', 'on')
130 self.switch_usbkey('host')
131
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800132 # Commands on the servo host must be run by the superuser. Our account
133 # on Beaglebone is root, but locally we might be running as a
134 # different user. If so - `sudo ' will have to be added to the
135 # commands.
136 if self._is_localhost:
137 self._sudo_required = utils.system_output('id -u') != '0'
138 self._ssh_prefix = ''
139 else:
J. Richard Barnette91e6c6e2013-03-12 18:18:22 -0700140 common_options = ('-o BatchMode=yes '
141 '-o StrictHostKeyChecking=no '
142 '-o UserKnownHostsFile=/dev/null')
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800143 self._sudo_required = False
J. Richard Barnette91e6c6e2013-03-12 18:18:22 -0700144 self._ssh_prefix = 'ssh -a -x %s root@%s ' % (
145 common_options, servo_host)
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800146 self._scp_cmd_template = 'scp -r %s ' % common_options
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800147 self._scp_cmd_template += '%s ' + 'root@' + servo_host + ':%s'
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800148
Tom Wai-Hong Tam22ee0e52013-04-03 13:36:39 +0800149 def get_power_state_controller(self):
J. Richard Barnette75136b32013-03-26 13:38:44 -0700150 """Return the power state controller for this Servo.
151
152 The power state controller provides board-independent
153 interfaces for reset, power-on, power-off operations.
154
155 """
156 return self._power_state
157
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700158 def initialize_dut(self, cold_reset=False):
159 """Initializes a dut for testing purposes.
160
161 This sets various servo signals back to default values
162 appropriate for the target board. By default, if the DUT
163 is already on, it stays on. If the DUT is powered off
164 before initialization, its state afterward is unspecified.
165
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700166 Rationale: Basic initialization of servo sets the lid open,
167 when there is a lid. This operation won't affect powered on
168 units; however, setting the lid open may power on a unit
J. Richard Barnette75136b32013-03-26 13:38:44 -0700169 that's off, depending on the board type and previous state
170 of the device.
171
172 If `cold_reset` is a true value, cold reset will be applied
173 to the DUT. Cold reset will force the DUT to power off;
174 however, the final state of the DUT depends on the board type.
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700175
176 @param cold_reset If True, cold reset the device after
177 initialization.
178 """
179 self._server.hwinit()
180 if cold_reset:
J. Richard Barnette75136b32013-03-26 13:38:44 -0700181 self._power_state.cold_reset()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700182
183
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800184 def is_localhost(self):
185 """Is the servod hosted locally?
186
187 Returns:
188 True if local hosted; otherwise, False.
189 """
190 return self._is_localhost
191
192
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700193 def power_long_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700194 """Simulate a long power button press."""
J. Richard Barnettef12bff22012-07-18 14:41:06 -0700195 # After a long power press, the EC may ignore the next power
196 # button press (at least on Alex). To guarantee that this
197 # won't happen, we need to allow the EC one second to
198 # collect itself.
Chrome Bot9a1137d2011-07-19 14:35:00 -0700199 self.power_key(Servo.LONG_DELAY)
J. Richard Barnettef12bff22012-07-18 14:41:06 -0700200 time.sleep(1.0)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700201
202
203 def power_normal_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700204 """Simulate a normal power button press."""
205 self.power_key()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700206
207
208 def power_short_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700209 """Simulate a short power button press."""
210 self.power_key(Servo.SHORT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700211
212
Chrome Bot9a1137d2011-07-19 14:35:00 -0700213 def power_key(self, secs=NORMAL_TRANSITION_DELAY):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700214 """Simulate a power button press.
215
216 Args:
217 secs: Time in seconds to simulate the keypress.
218 """
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800219 self.set_get_all(['pwr_button:press',
220 'sleep:%.4f' % secs,
221 'pwr_button:release'])
Todd Broch31c82502011-08-29 08:14:39 -0700222 # TODO(tbroch) Different systems have different release times on the
223 # power button that this loop addresses. Longer term we may want to
224 # make this delay platform specific.
225 retry = 1
226 while True:
227 value = self.get('pwr_button')
228 if value == 'release' or retry > Servo.RELEASE_RETRY_MAX:
229 break
Todd Broch9753bd42012-03-21 10:15:08 -0700230 logging.info('Waiting for pwr_button to release, retry %d.', retry)
Todd Broch31c82502011-08-29 08:14:39 -0700231 retry += 1
232 time.sleep(Servo.SHORT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700233
234
235 def lid_open(self):
236 """Simulate opening the lid."""
Craig Harrison48997262011-06-27 14:31:10 -0700237 self.set_nocheck('lid_open', 'yes')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700238
239
240 def lid_close(self):
Craig Harrison48997262011-06-27 14:31:10 -0700241 """Simulate closing the lid.
242
243 Waits 6 seconds to ensure the device is fully asleep before returning.
244 """
245 self.set_nocheck('lid_open', 'no')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700246 time.sleep(Servo.SLEEP_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700247
248
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800249 def _press_keys(self, key):
250 """Simulate button presses.
251
252 Note, key presses will remain on indefinitely. See
253 _press_and_release_keys for release procedure.
254 """
Gediminas Ramanauskas9da80f22012-11-14 12:59:43 -0800255 (m1_a1, m1_a0, m2_a1, m2_a0) = self.KEY_MATRIX[self._key_matrix]['none']
Todd Broch9753bd42012-03-21 10:15:08 -0700256 self.set_nocheck('kbd_m2_a0', m2_a0)
257 self.set_nocheck('kbd_m2_a1', m2_a1)
258 self.set_nocheck('kbd_m1_a0', m1_a0)
259 self.set_nocheck('kbd_m1_a1', m1_a1)
Vic Yange262a3e2012-11-02 18:48:37 +0800260 self.set_nocheck('kbd_en', 'on')
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800261
Gediminas Ramanauskas9da80f22012-11-14 12:59:43 -0800262 (m1_a1, m1_a0, m2_a1, m2_a0) = self.KEY_MATRIX[self._key_matrix][key]
263 self.set_nocheck('kbd_m2_a0', m2_a0)
264 self.set_nocheck('kbd_m2_a1', m2_a1)
265 self.set_nocheck('kbd_m1_a0', m1_a0)
266 self.set_nocheck('kbd_m1_a1', m1_a1)
267
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800268
269 def _press_and_release_keys(self, key,
270 press_secs=SERVO_KEY_PRESS_DELAY):
271 """Simulate button presses and release."""
272 self._press_keys(key)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700273 time.sleep(press_secs)
274 self.set_nocheck('kbd_en', 'off')
275
276
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -0700277 def set_key_matrix(self, matrix=0):
278 """Set keyboard mapping"""
279 self._key_matrix = matrix
280
281
Chrome Bot9a1137d2011-07-19 14:35:00 -0700282 def ctrl_d(self):
283 """Simulate Ctrl-d simultaneous button presses."""
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700284 self._press_and_release_keys('ctrl_d')
Todd Broch9dfc3a82011-11-01 08:09:28 -0700285
286
Todd Broch9753bd42012-03-21 10:15:08 -0700287 def ctrl_enter(self):
288 """Simulate Ctrl-enter simultaneous button presses."""
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700289 self._press_and_release_keys('ctrl_enter')
Todd Broch9753bd42012-03-21 10:15:08 -0700290
291
Todd Broch9dfc3a82011-11-01 08:09:28 -0700292 def d_key(self):
293 """Simulate Enter key button press."""
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700294 self._press_and_release_keys('d')
Todd Broch9dfc3a82011-11-01 08:09:28 -0700295
296
297 def ctrl_key(self):
298 """Simulate Enter key button press."""
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700299 self._press_and_release_keys('ctrl')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700300
301
Chrome Bot9a1137d2011-07-19 14:35:00 -0700302 def enter_key(self):
303 """Simulate Enter key button press."""
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700304 self._press_and_release_keys('enter')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700305
306
Chrome Bot9a1137d2011-07-19 14:35:00 -0700307 def refresh_key(self):
308 """Simulate Refresh key (F3) button press."""
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700309 self._press_and_release_keys('refresh')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700310
311
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800312 def ctrl_refresh_key(self):
313 """Simulate Ctrl and Refresh (F3) simultaneous press.
314
315 This key combination is an alternative of Space key.
316 """
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700317 self._press_and_release_keys('ctrl_refresh')
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800318
319
Chrome Bot9a1137d2011-07-19 14:35:00 -0700320 def imaginary_key(self):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700321 """Simulate imaginary key button press.
322
323 Maps to a key that doesn't physically exist.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700324 """
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -0700325 self._press_and_release_keys('unused')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700326
327
Craig Harrison6b36b122011-06-28 17:58:43 -0700328 def enable_recovery_mode(self):
329 """Enable recovery mode on device."""
330 self.set('rec_mode', 'on')
331
332
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800333 def custom_recovery_mode(self):
334 """Custom key combination to enter recovery mode."""
335 self._press_keys('rec_mode')
336 self.power_normal_press()
337 time.sleep(self.SERVO_KEY_PRESS_DELAY)
338 self.set_nocheck('kbd_en', 'off')
339
340
Craig Harrison6b36b122011-06-28 17:58:43 -0700341 def disable_recovery_mode(self):
342 """Disable recovery mode on device."""
343 self.set('rec_mode', 'off')
344
345
346 def enable_development_mode(self):
347 """Enable development mode on device."""
348 self.set('dev_mode', 'on')
349
350
351 def disable_development_mode(self):
352 """Disable development mode on device."""
353 self.set('dev_mode', 'off')
354
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700355 def boot_devmode(self):
356 """Boot a dev-mode device that is powered off."""
Tom Wai-Hong Tam23869102012-01-17 15:41:30 +0800357 self.power_short_press()
Craig Harrison48997262011-06-27 14:31:10 -0700358 self.pass_devmode()
359
360
361 def pass_devmode(self):
362 """Pass through boot screens in dev-mode."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700363 time.sleep(Servo.BOOT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700364 self.ctrl_d()
Chrome Bot9a1137d2011-07-19 14:35:00 -0700365 time.sleep(Servo.BOOT_DELAY)
Craig Harrison48997262011-06-27 14:31:10 -0700366
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700367
Todd Brochefe72cb2012-07-11 19:58:53 -0700368 def _get_xmlrpclib_exception(self, xmlexc):
369 """Get meaningful exception string from xmlrpc.
370
371 Args:
372 xmlexc: xmlrpclib.Fault object
373
374 xmlrpclib.Fault.faultString has the following format:
375
376 <type 'exception type'>:'actual error message'
377
378 Parse and return the real exception from the servod side instead of the
379 less meaningful one like,
380 <Fault 1: "<type 'exceptions.AttributeError'>:'tca6416' object has no
381 attribute 'hw_driver'">
382
383 Returns:
384 string of underlying exception raised in servod.
385 """
386 return re.sub('^.*>:', '', xmlexc.faultString)
387
388
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700389 def get(self, gpio_name):
390 """Get the value of a gpio from Servod."""
391 assert gpio_name
Todd Brochefe72cb2012-07-11 19:58:53 -0700392 try:
393 return self._server.get(gpio_name)
394 except xmlrpclib.Fault as e:
395 err_msg = "Getting '%s' :: %s" % \
396 (gpio_name, self._get_xmlrpclib_exception(e))
397 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700398
399
400 def set(self, gpio_name, gpio_value):
401 """Set and check the value of a gpio using Servod."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700402 self.set_nocheck(gpio_name, gpio_value)
Todd Brochcf7c6652012-02-24 13:03:59 -0800403 retry_count = Servo.GET_RETRY_MAX
404 while gpio_value != self.get(gpio_name) and retry_count:
405 logging.warn("%s != %s, retry %d", gpio_name, gpio_value,
406 retry_count)
407 retry_count -= 1
408 time.sleep(Servo.SHORT_DELAY)
409 if not retry_count:
410 assert gpio_value == self.get(gpio_name), \
411 'Servo failed to set %s to %s' % (gpio_name, gpio_value)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700412
413
414 def set_nocheck(self, gpio_name, gpio_value):
415 """Set the value of a gpio using Servod."""
416 assert gpio_name and gpio_value
Todd Broch9753bd42012-03-21 10:15:08 -0700417 logging.info('Setting %s to %s', gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700418 try:
419 self._server.set(gpio_name, gpio_value)
420 except xmlrpclib.Fault as e:
421 err_msg = "Setting '%s' to '%s' :: %s" % \
422 (gpio_name, gpio_value, self._get_xmlrpclib_exception(e))
423 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700424
425
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800426 def set_get_all(self, controls):
427 """Set &| get one or more control values.
428
429 @param controls: list of strings, controls to set &| get.
430
431 @raise: error.TestError in case error occurs setting/getting values.
432 """
433 rv = []
434 try:
435 rv = self._server.set_get_all(controls)
436 except xmlrpclib.Fault as e:
437 # TODO(waihong): Remove the following backward compatibility when
438 # the new versions of hdctools are deployed.
439 if 'not supported' in str(e):
440 logging.warning('The servod is too old that set_get_all '
441 'not supported. Use set and get instead.')
442 for control in controls:
443 if ':' in control:
444 (name, value) = control.split(':')
445 if name == 'sleep':
446 time.sleep(float(value))
447 else:
448 self.set_nocheck(name, value)
449 rv.append(True)
450 else:
451 rv.append(self.get(name))
452 else:
453 err_msg = "Problem with '%s' :: %s" % \
454 (controls, self._get_xmlrpclib_exception(e))
455 raise error.TestFail(err_msg)
456 return rv
457
458
Jon Salzc88e5b62011-11-30 14:38:54 +0800459 # TODO(waihong) It may fail if multiple servo's are connected to the same
460 # host. Should look for a better way, like the USB serial name, to identify
461 # the USB device.
Simran Basi741b5d42012-05-18 11:27:15 -0700462 # TODO(sbasi) Remove this code from autoserv once firmware tests have been
463 # updated.
Jon Salzc88e5b62011-11-30 14:38:54 +0800464 def probe_host_usb_dev(self):
465 """Probe the USB disk device plugged-in the servo from the host side.
466
467 It tries to switch the USB mux to make the host unable to see the
468 USB disk and compares the result difference.
469
Jon Salzc88e5b62011-11-30 14:38:54 +0800470 Returns:
471 A string of USB disk path, like '/dev/sdb', or None if not existed.
472 """
473 cmd = 'ls /dev/sd[a-z]'
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800474 original_value = self.get_usbkey_direction()
Jon Salzc88e5b62011-11-30 14:38:54 +0800475
476 # Make the host unable to see the USB disk.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800477 if original_value != 'dut':
478 self.switch_usbkey('dut')
Jon Salzc88e5b62011-11-30 14:38:54 +0800479 time.sleep(self.USB_DETECTION_DELAY)
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800480 no_usb_set = set(self.system_output(cmd, ignore_status=True).split())
Jon Salzc88e5b62011-11-30 14:38:54 +0800481
482 # Make the host able to see the USB disk.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800483 self.switch_usbkey('host')
Jon Salzc88e5b62011-11-30 14:38:54 +0800484 time.sleep(self.USB_DETECTION_DELAY)
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800485 has_usb_set = set(self.system_output(cmd, ignore_status=True).split())
Jon Salzc88e5b62011-11-30 14:38:54 +0800486
487 # Back to its original value.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800488 if original_value != self.get_usbkey_direction():
489 self.switch_usbkey(original_value)
Jon Salzc88e5b62011-11-30 14:38:54 +0800490 time.sleep(self.USB_DETECTION_DELAY)
491
492 diff_set = has_usb_set - no_usb_set
493 if len(diff_set) == 1:
494 return diff_set.pop()
495 else:
496 return None
497
498
J. Richard Barnette69929a52013-03-15 13:22:11 -0700499 def recovery_supported(self):
500 """Return whether servo-based recovery should work.
501
502 Use of `image_to_servo_usb()` and `install_recovery_image()`
503 relies on DUT-board specific behaviors, and is not supported
504 for all types of board. Return whether these two operations
505 are expected to succeed for the current DUT.
506
507 @return `True` iff the recovery related methods are supported
508 for this servo and DUT.
509
510 """
J. Richard Barnette75136b32013-03-26 13:38:44 -0700511 return self._power_state.recovery_supported()
J. Richard Barnette69929a52013-03-15 13:22:11 -0700512
513
Mike Truty49153d82012-08-21 22:27:30 -0500514 def image_to_servo_usb(self, image_path=None,
515 make_image_noninteractive=False):
516 """Install an image to the USB key plugged into the servo.
517
518 This method may copy any image to the servo USB key including a
519 recovery image or a test image. These images are frequently used
520 for test purposes such as restoring a corrupted image or conducting
521 an upgrade of ec/fw/kernel as part of a test of a specific image part.
522
523 Args:
524 image_path: Path on the host to the recovery image.
525 make_image_noninteractive: Make the recovery image noninteractive,
526 therefore the DUT will reboot
527 automatically after installation.
528 """
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700529 # We're about to start plugging/unplugging the USB key. We
530 # don't know the state of the DUT, or what it might choose
531 # to do to the device after hotplug. To avoid surprises,
532 # force the DUT to be off.
533 self._server.hwinit()
534 self._power_state.power_off()
Mike Truty49153d82012-08-21 22:27:30 -0500535
536 # Set up Servo's usb mux.
537 self.set('prtctl4_pwren', 'on')
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800538 self.switch_usbkey('host')
Mike Truty49153d82012-08-21 22:27:30 -0500539 if image_path:
Tom Wai-Hong Tam42f136d2012-10-26 11:11:23 +0800540 logging.info('Searching for usb device and copying image to it. '
541 'Please wait a few minutes...')
Mike Truty49153d82012-08-21 22:27:30 -0500542 if not self._server.download_image_to_usb(image_path):
543 logging.error('Failed to transfer requested image to USB. '
544 'Please take a look at Servo Logs.')
545 raise error.AutotestError('Download image to usb failed.')
546 if make_image_noninteractive:
547 logging.info('Making image noninteractive')
548 if not self._server.make_image_noninteractive():
549 logging.error('Failed to make image noninteractive. '
550 'Please take a look at Servo Logs.')
551
552
Simran Basi741b5d42012-05-18 11:27:15 -0700553 def install_recovery_image(self, image_path=None,
J. Richard Barnetteb6de7e32013-02-14 13:28:04 -0800554 make_image_noninteractive=False):
Jon Salzc88e5b62011-11-30 14:38:54 +0800555 """Install the recovery image specied by the path onto the DUT.
556
557 This method uses google recovery mode to install a recovery image
558 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 +0800559 board specified by the usb_dev. If no image path is specified
Jon Salzc88e5b62011-11-30 14:38:54 +0800560 we use the recovery image already on the usb image.
561
562 Args:
563 image_path: Path on the host to the recovery image.
Simran Basi741b5d42012-05-18 11:27:15 -0700564 make_image_noninteractive: Make the recovery image noninteractive,
565 therefore the DUT will reboot
566 automatically after installation.
Jon Salzc88e5b62011-11-30 14:38:54 +0800567 """
Mike Truty49153d82012-08-21 22:27:30 -0500568 self.image_to_servo_usb(image_path, make_image_noninteractive)
J. Richard Barnette75136b32013-03-26 13:38:44 -0700569 self._power_state.power_on(rec_mode=power_state_controller.REC_ON)
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700570 self.switch_usbkey('dut')
Jon Salzc88e5b62011-11-30 14:38:54 +0800571
572
Todd Brochf24d2782011-08-19 10:55:41 -0700573 def _connect_servod(self, servo_host, servo_port):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700574 """Connect to the Servod process with XMLRPC.
575
576 Args:
577 servo_port: Port the Servod process is listening on.
578 """
Todd Brochf24d2782011-08-19 10:55:41 -0700579 remote = 'http://%s:%s' % (servo_host, servo_port)
Todd Broch96d83aa2011-08-29 14:37:38 -0700580 self._server = xmlrpclib.ServerProxy(remote)
Todd Brochf24d2782011-08-19 10:55:41 -0700581 try:
582 self._server.echo("ping-test")
Todd Broch96d83aa2011-08-29 14:37:38 -0700583 except:
584 logging.error('Connection to servod failed')
Todd Brochf24d2782011-08-19 10:55:41 -0700585 raise
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800586
587
588 def _scp_image(self, image_path):
589 """Copy image to the servo host.
590
591 When programming a firmware image on the DUT, the image must be
592 located on the host to which the servo device is connected. Sometimes
593 servo is controlled by a remote host, in this case the image needs to
594 be transferred to the remote host.
595
596 @param image_path: a string, name of the firmware image file to be
597 transferred.
598 @return: a string, full path name of the copied file on the remote.
599 """
600
601 dest_path = os.path.join('/tmp', os.path.basename(image_path))
602 scp_cmd = self._scp_cmd_template % (image_path, dest_path)
603 utils.system(scp_cmd)
604 return dest_path
605
606
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800607 def system(self, command, timeout=None):
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800608 """Execute the passed in command on the servod host."""
609 if self._sudo_required:
610 command = 'sudo -n %s' % command
611 if self._ssh_prefix:
612 command = "%s '%s'" % (self._ssh_prefix, command)
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800613 logging.info('Will execute on servo host: %s', command)
614 utils.system(command, timeout=timeout)
615
616
617 def system_output(self, command, timeout=None,
618 ignore_status=False, args=()):
619 """Execute the passed in command on the servod host, return stdout.
620
621 @param command, a string, the command to execute
622 @param timeout, an int, max number of seconds to wait til command
623 execution completes
624 @ignore_status, a Boolean, if true - ignore command's nonzero exit
625 status, otherwise an exception will be thrown
626 @param args, a tuple of strings, each becoming a separate command line
627 parameter for the command
628 @return: command's stdout as a string.
629 """
630 if self._sudo_required:
631 command = 'sudo -n %s' % command
632 if self._ssh_prefix:
633 command = "%s '%s'" % (self._ssh_prefix, command)
634 logging.info('Will execute and collect output on servo host: %s %s',
635 command, ' '.join("'%s'" % x for x in args))
636 return utils.system_output(command, timeout=timeout,
637 ignore_status=ignore_status, args=args)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800638
639
640 def program_ec(self, board, image):
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800641 """Program EC on a given board using given image.
642
643 @param board: a string, type of the DUT board
644 @param image: a string, file name of the EC image to program on the
645 DUT
646 """
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800647 if not self.is_localhost():
648 image = self._scp_image(image)
649 programmer.program_ec(board, self, image)
650
651
652 def program_bootprom(self, board, image):
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800653 """Program bootprom on a given board using given image.
654
655 In case servo is controlled by a remote host, the image needs to be
656 transferred to the host.
657
658 If the device tree subdirectory is present along with the image, the
659 subdirectory is also copied to the remote host.
660
661 @param board: a string, type of the DUT board
662 @param image: a string, file name of the firmware image to program on
663 the DUT. The device tree subdirectory, if present, is on
664 the same level with the image file.
665 """
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800666 if not self.is_localhost():
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800667 dts_path = os.path.join(os.path.dirname(image), 'dts')
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800668 image = self._scp_image(image)
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800669 if os.path.isdir(dts_path):
670 self._scp_image(dts_path)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800671 programmer.program_bootprom(board, self, image)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800672
673 def switch_usbkey(self, side):
674 """Connect USB flash stick to either host or DUT.
675
676 This function switches the servo multiplexer to provide electrical
677 connection between the USB port J3 and either host or DUT side.
678
679 Switching is accompanied by powercycling of the USB stick, because it
680 sometimes gets wedged if the mux is switched while the stick power is
681 on.
682
683 @param side: a string, either 'dut' or 'host' - indicates which side
684 the USB flash device is required to be connected to.
685
686 @raise: error.TestError in case the parameter is neither 'dut' not
687 'host'
688 """
689
690 if self._usb_position == side:
691 return
692
693 if side == 'host':
694 mux_direction = 'servo_sees_usbkey'
695 elif side == 'dut':
696 mux_direction = 'dut_sees_usbkey'
697 else:
698 raise error.TestError('unknown USB mux setting: %s' % side)
699
700 self.set('prtctl4_pwren', 'off')
701 time.sleep(self.USB_POWEROFF_DELAY)
702 self.set('usb_mux_sel1', mux_direction)
703 time.sleep(self.USB_POWEROFF_DELAY)
704 self.set('prtctl4_pwren', 'on')
705
706 self._usb_position = side
707
708
709 def get_usbkey_direction(self):
710 """Get name of the side the USB device is connected to.
711
712 @return a string, either 'dut' or 'host'
713 """
714 if not self._usb_position:
715 if self.get('usb_mux_sel1').starstwith('dut'):
716 self._usb_position = 'dut'
717 else:
718 self._usb_position = 'host'
719 return self._usb_position