blob: 7f394cd5a976efd814f0872471dffa9d22e75bdf [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
Simran Basi59331342013-07-12 12:06:29 -070013from autotest_lib.client.common_lib.cros import retry
Jon Salzc88e5b62011-11-30 14:38:54 +080014from autotest_lib.server import utils
J. Richard Barnette41320ee2013-03-11 13:00:13 -070015from autotest_lib.server.cros.servo import power_state_controller
J. Richard Barnette75487572013-03-08 12:47:50 -080016from autotest_lib.server.cros.servo import programmer
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070017
J. Richard Barnette41320ee2013-03-11 13:00:13 -070018
J. Richard Barnette384056b2012-04-16 11:04:46 -070019class Servo(object):
J. Richard Barnette41320ee2013-03-11 13:00:13 -070020
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070021 """Manages control of a Servo board.
22
23 Servo is a board developed by hardware group to aide in the debug and
24 control of various partner devices. Servo's features include the simulation
25 of pressing the power button, closing the lid, and pressing Ctrl-d. This
26 class manages setting up and communicating with a servo demon (servod)
27 process. It provides both high-level functions for common servo tasks and
28 low-level functions for directly setting and reading gpios.
J. Richard Barnette41320ee2013-03-11 13:00:13 -070029
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070030 """
31
Chrome Bot9a1137d2011-07-19 14:35:00 -070032 # Power button press delays in seconds.
J. Richard Barnetted2e4cbd2012-06-29 12:18:40 -070033 #
J. Richard Barnette5383f072012-07-26 17:35:40 -070034 # The EC specification says that 8.0 seconds should be enough
35 # for the long power press. However, some platforms need a bit
36 # more time. Empirical testing has found these requirements:
37 # Alex: 8.2 seconds
38 # ZGB: 8.5 seconds
39 # The actual value is set to the largest known necessary value.
40 #
41 # TODO(jrbarnette) Being generous is the right thing to do for
42 # existing platforms, but if this code is to be used for
43 # qualification of new hardware, we should be less generous.
44 LONG_DELAY = 8.5
Chrome Bot9a1137d2011-07-19 14:35:00 -070045 SHORT_DELAY = 0.1
46 NORMAL_TRANSITION_DELAY = 1.2
J. Richard Barnette5383f072012-07-26 17:35:40 -070047
Todd Broch31c82502011-08-29 08:14:39 -070048 # Maximum number of times to re-read power button on release.
49 RELEASE_RETRY_MAX = 5
Todd Brochcf7c6652012-02-24 13:03:59 -080050 GET_RETRY_MAX = 10
Chrome Bot9a1137d2011-07-19 14:35:00 -070051
J. Richard Barnette5383f072012-07-26 17:35:40 -070052 # Delays to deal with DUT state transitions.
Chrome Bot9a1137d2011-07-19 14:35:00 -070053 SLEEP_DELAY = 6
54 BOOT_DELAY = 10
J. Richard Barnette41320ee2013-03-11 13:00:13 -070055
J. Richard Barnette41320ee2013-03-11 13:00:13 -070056 # Default minimum time interval between 'press' and 'release'
57 # keyboard events.
Vic Yang0aca1c22012-11-19 18:33:56 -080058 SERVO_KEY_PRESS_DELAY = 0.1
Craig Harrison2b6c6fc2011-06-23 10:34:02 -070059
Jon Salzc88e5b62011-11-30 14:38:54 +080060 # Time between an usb disk plugged-in and detected in the system.
61 USB_DETECTION_DELAY = 10
Vadim Bendeburye7bd9362012-12-19 14:35:20 -080062 # Time to keep USB power off before and after USB mux direction is changed
63 USB_POWEROFF_DELAY = 2
Jon Salzc88e5b62011-11-30 14:38:54 +080064
Simran Basi59331342013-07-12 12:06:29 -070065 # Time to wait before timing out on a dut-control call.
66 CALL_TIMEOUT_SECS = 60
67
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -070068 KEY_MATRIX_ALT_0 = {
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -070069 'ctrl_refresh': ['0', '0', '0', '1'],
70 'ctrl_d': ['0', '1', '0', '0'],
71 'd': ['0', '1', '1', '1'],
72 'ctrl_enter': ['1', '0', '0', '0'],
73 'enter': ['1', '0', '1', '1'],
74 'ctrl': ['1', '1', '0', '0'],
75 'refresh': ['1', '1', '0', '1'],
76 'unused': ['1', '1', '1', '0'],
77 'none': ['1', '1', '1', '1']}
Chris Masone6a0680f2012-03-02 08:40:00 -080078
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -070079 KEY_MATRIX_ALT_1 = {
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -070080 'ctrl_d': ['0', '0', '1', '0'],
81 'd': ['0', '0', '1', '1'],
82 'ctrl_enter': ['0', '1', '1', '0'],
83 'enter': ['0', '1', '1', '1'],
84 'ctrl_refresh': ['1', '0', '0', '1'],
85 'unused': ['1', '1', '0', '0'],
86 'refresh': ['1', '1', '0', '1'],
87 'ctrl': ['1', '1', '1', '0'],
88 'none': ['1', '1', '1', '1']}
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -070089
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -080090 KEY_MATRIX_ALT_2 = {
91 'ctrl_d': ['0', '0', '0', '1'],
92 'd': ['0', '0', '1', '1'],
93 'unused': ['0', '1', '1', '1'],
94 'rec_mode': ['1', '0', '0', '0'],
95 'ctrl_enter': ['1', '0', '0', '1'],
96 'enter': ['1', '0', '1', '1'],
97 'ctrl': ['1', '1', '0', '1'],
98 'refresh': ['1', '1', '1', '0'],
99 'ctrl_refresh': ['1', '1', '1', '1'],
100 'none': ['1', '1', '1', '1']}
101
102 KEY_MATRIX = [KEY_MATRIX_ALT_0, KEY_MATRIX_ALT_1, KEY_MATRIX_ALT_2]
J. Richard Barnette67ccb872012-04-19 16:34:56 -0700103
J. Richard Barnetted5f807a2013-02-11 16:51:00 -0800104 def __init__(self, servo_host='localhost', servo_port=9999):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700105 """Sets up the servo communication infrastructure.
106
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800107 @param servo_host Name of the host where the servod process
108 is running.
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800109 @param servo_port Port the servod process is listening on.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700110 """
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -0700111 self._key_matrix = 0
J. Richard Barnette384056b2012-04-16 11:04:46 -0700112 self._server = None
Todd Brochf24d2782011-08-19 10:55:41 -0700113 self._connect_servod(servo_host, servo_port)
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800114 self._is_localhost = (servo_host == 'localhost')
J. Richard Barnette69929a52013-03-15 13:22:11 -0700115 # TODO(jrbarnette): As of this writing, not all beaglebone
116 # servo hosts support the get_board() function. For now, we
117 # treat the problem hosts as an unsupported board. The try
118 # wrapper should be removed once all the hosts are updated.
J. Richard Barnette75136b32013-03-26 13:38:44 -0700119 board = None
J. Richard Barnette69929a52013-03-15 13:22:11 -0700120 try:
121 board = self._server.get_board()
J. Richard Barnette69929a52013-03-15 13:22:11 -0700122 except xmlrpclib.Fault as e:
123 logging.error('Failed to create power state controller; '
124 'check hdctools version on %s.', servo_host)
125 logging.exception(e)
J. Richard Barnette75136b32013-03-26 13:38:44 -0700126 self._power_state = (
127 power_state_controller.create_controller(self, board))
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800128
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800129 # a string, showing what interface (host or dut) the USB device is
130 # connected to.
Fang Dengafb88142013-05-30 17:44:31 -0700131 self._usb_state = None
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800132 self.set('dut_hub_pwren', 'on')
133 self.set('usb_mux_oe1', 'on')
Fang Dengafb88142013-05-30 17:44:31 -0700134 self.switch_usbkey('off')
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800135
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800136 # Commands on the servo host must be run by the superuser. Our account
137 # on Beaglebone is root, but locally we might be running as a
138 # different user. If so - `sudo ' will have to be added to the
139 # commands.
140 if self._is_localhost:
141 self._sudo_required = utils.system_output('id -u') != '0'
142 self._ssh_prefix = ''
143 else:
J. Richard Barnette91e6c6e2013-03-12 18:18:22 -0700144 common_options = ('-o BatchMode=yes '
145 '-o StrictHostKeyChecking=no '
146 '-o UserKnownHostsFile=/dev/null')
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800147 self._sudo_required = False
J. Richard Barnette91e6c6e2013-03-12 18:18:22 -0700148 self._ssh_prefix = 'ssh -a -x %s root@%s ' % (
149 common_options, servo_host)
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800150 self._scp_cmd_template = 'scp -r %s ' % common_options
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800151 self._scp_cmd_template += '%s ' + 'root@' + servo_host + ':%s'
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800152
Tom Wai-Hong Tam22ee0e52013-04-03 13:36:39 +0800153 def get_power_state_controller(self):
J. Richard Barnette75136b32013-03-26 13:38:44 -0700154 """Return the power state controller for this Servo.
155
156 The power state controller provides board-independent
157 interfaces for reset, power-on, power-off operations.
158
159 """
160 return self._power_state
161
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700162 def initialize_dut(self, cold_reset=False):
163 """Initializes a dut for testing purposes.
164
165 This sets various servo signals back to default values
166 appropriate for the target board. By default, if the DUT
167 is already on, it stays on. If the DUT is powered off
168 before initialization, its state afterward is unspecified.
169
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700170 Rationale: Basic initialization of servo sets the lid open,
171 when there is a lid. This operation won't affect powered on
172 units; however, setting the lid open may power on a unit
J. Richard Barnette75136b32013-03-26 13:38:44 -0700173 that's off, depending on the board type and previous state
174 of the device.
175
176 If `cold_reset` is a true value, cold reset will be applied
177 to the DUT. Cold reset will force the DUT to power off;
178 however, the final state of the DUT depends on the board type.
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700179
180 @param cold_reset If True, cold reset the device after
181 initialization.
182 """
183 self._server.hwinit()
184 if cold_reset:
J. Richard Barnette75136b32013-03-26 13:38:44 -0700185 self._power_state.cold_reset()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700186
187
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800188 def is_localhost(self):
189 """Is the servod hosted locally?
190
191 Returns:
192 True if local hosted; otherwise, False.
193 """
194 return self._is_localhost
195
196
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700197 def power_long_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700198 """Simulate a long power button press."""
J. Richard Barnettef12bff22012-07-18 14:41:06 -0700199 # After a long power press, the EC may ignore the next power
200 # button press (at least on Alex). To guarantee that this
201 # won't happen, we need to allow the EC one second to
202 # collect itself.
Chrome Bot9a1137d2011-07-19 14:35:00 -0700203 self.power_key(Servo.LONG_DELAY)
J. Richard Barnettef12bff22012-07-18 14:41:06 -0700204 time.sleep(1.0)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700205
206
207 def power_normal_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700208 """Simulate a normal power button press."""
209 self.power_key()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700210
211
212 def power_short_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700213 """Simulate a short power button press."""
214 self.power_key(Servo.SHORT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700215
216
Chrome Bot9a1137d2011-07-19 14:35:00 -0700217 def power_key(self, secs=NORMAL_TRANSITION_DELAY):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700218 """Simulate a power button press.
219
220 Args:
221 secs: Time in seconds to simulate the keypress.
222 """
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800223 self.set_get_all(['pwr_button:press',
224 'sleep:%.4f' % secs,
225 'pwr_button:release'])
Todd Broch31c82502011-08-29 08:14:39 -0700226 # TODO(tbroch) Different systems have different release times on the
227 # power button that this loop addresses. Longer term we may want to
228 # make this delay platform specific.
229 retry = 1
230 while True:
231 value = self.get('pwr_button')
232 if value == 'release' or retry > Servo.RELEASE_RETRY_MAX:
233 break
Todd Broch9753bd42012-03-21 10:15:08 -0700234 logging.info('Waiting for pwr_button to release, retry %d.', retry)
Todd Broch31c82502011-08-29 08:14:39 -0700235 retry += 1
236 time.sleep(Servo.SHORT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700237
238
239 def lid_open(self):
240 """Simulate opening the lid."""
Craig Harrison48997262011-06-27 14:31:10 -0700241 self.set_nocheck('lid_open', 'yes')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700242
243
244 def lid_close(self):
Craig Harrison48997262011-06-27 14:31:10 -0700245 """Simulate closing the lid.
246
247 Waits 6 seconds to ensure the device is fully asleep before returning.
248 """
249 self.set_nocheck('lid_open', 'no')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700250 time.sleep(Servo.SLEEP_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700251
252
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800253 def _press_keys(self, key):
254 """Simulate button presses.
255
256 Note, key presses will remain on indefinitely. See
257 _press_and_release_keys for release procedure.
258 """
Vic Yangcad9acb2013-05-21 14:02:05 +0800259 (m1_a1_n, m1_a0_n, m2_a1_n, m2_a0_n) = (
260 self.KEY_MATRIX[self._key_matrix]['none'])
Gediminas Ramanauskas9da80f22012-11-14 12:59:43 -0800261 (m1_a1, m1_a0, m2_a1, m2_a0) = self.KEY_MATRIX[self._key_matrix][key]
Vic Yangcad9acb2013-05-21 14:02:05 +0800262 self.set_get_all(['kbd_m2_a0:%s' % m2_a0_n,
263 'kbd_m2_a1:%s' % m2_a1_n,
264 'kbd_m1_a0:%s' % m1_a0_n,
265 'kbd_m1_a1:%s' % m1_a1_n,
266 'kbd_en:on',
267 'kbd_m2_a0:%s' % m2_a0,
268 'kbd_m2_a1:%s' % m2_a1,
269 'kbd_m1_a0:%s' % m1_a0,
270 'kbd_m1_a1:%s' % m1_a1])
Gediminas Ramanauskas9da80f22012-11-14 12:59:43 -0800271
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800272
Vic Yang701a59e2013-05-02 05:10:22 +0800273 def _press_and_release_keys(self, key, press_secs=None):
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800274 """Simulate button presses and release."""
Vic Yang701a59e2013-05-02 05:10:22 +0800275 if press_secs is None:
276 press_secs = self.SERVO_KEY_PRESS_DELAY
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800277 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
Vic Yang701a59e2013-05-02 05:10:22 +0800287 def ctrl_d(self, press_secs=None):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700288 """Simulate Ctrl-d simultaneous button presses."""
Vic Yang701a59e2013-05-02 05:10:22 +0800289 self._press_and_release_keys('ctrl_d', press_secs)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700290
291
Vic Yang701a59e2013-05-02 05:10:22 +0800292 def ctrl_enter(self, press_secs=None):
Todd Broch9753bd42012-03-21 10:15:08 -0700293 """Simulate Ctrl-enter simultaneous button presses."""
Vic Yang701a59e2013-05-02 05:10:22 +0800294 self._press_and_release_keys('ctrl_enter', press_secs)
Todd Broch9753bd42012-03-21 10:15:08 -0700295
296
Vic Yang701a59e2013-05-02 05:10:22 +0800297 def d_key(self, press_secs=None):
Todd Broch9dfc3a82011-11-01 08:09:28 -0700298 """Simulate Enter key button press."""
Vic Yang701a59e2013-05-02 05:10:22 +0800299 self._press_and_release_keys('d', press_secs)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700300
301
Vic Yang701a59e2013-05-02 05:10:22 +0800302 def ctrl_key(self, press_secs=None):
Todd Broch9dfc3a82011-11-01 08:09:28 -0700303 """Simulate Enter key button press."""
Vic Yang701a59e2013-05-02 05:10:22 +0800304 self._press_and_release_keys('ctrl', press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700305
306
Vic Yang701a59e2013-05-02 05:10:22 +0800307 def enter_key(self, press_secs=None):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700308 """Simulate Enter key button press."""
Vic Yang701a59e2013-05-02 05:10:22 +0800309 self._press_and_release_keys('enter', press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700310
311
Vic Yang701a59e2013-05-02 05:10:22 +0800312 def refresh_key(self, press_secs=None):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700313 """Simulate Refresh key (F3) button press."""
Vic Yang701a59e2013-05-02 05:10:22 +0800314 self._press_and_release_keys('refresh', press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700315
316
Vic Yang701a59e2013-05-02 05:10:22 +0800317 def ctrl_refresh_key(self, press_secs=None):
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800318 """Simulate Ctrl and Refresh (F3) simultaneous press.
319
320 This key combination is an alternative of Space key.
321 """
Vic Yang701a59e2013-05-02 05:10:22 +0800322 self._press_and_release_keys('ctrl_refresh', press_secs)
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800323
324
Vic Yang701a59e2013-05-02 05:10:22 +0800325 def imaginary_key(self, press_secs=None):
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 """
Vic Yang701a59e2013-05-02 05:10:22 +0800330 self._press_and_release_keys('unused', press_secs)
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
Todd Brochefe72cb2012-07-11 19:58:53 -0700373 def _get_xmlrpclib_exception(self, xmlexc):
374 """Get meaningful exception string from xmlrpc.
375
376 Args:
377 xmlexc: xmlrpclib.Fault object
378
379 xmlrpclib.Fault.faultString has the following format:
380
381 <type 'exception type'>:'actual error message'
382
383 Parse and return the real exception from the servod side instead of the
384 less meaningful one like,
385 <Fault 1: "<type 'exceptions.AttributeError'>:'tca6416' object has no
386 attribute 'hw_driver'">
387
388 Returns:
389 string of underlying exception raised in servod.
390 """
391 return re.sub('^.*>:', '', xmlexc.faultString)
392
393
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700394 def get(self, gpio_name):
395 """Get the value of a gpio from Servod."""
396 assert gpio_name
Todd Brochefe72cb2012-07-11 19:58:53 -0700397 try:
Simran Basi1cbc5f22013-07-17 14:23:58 -0700398 return self._server.get(gpio_name)
Todd Brochefe72cb2012-07-11 19:58:53 -0700399 except xmlrpclib.Fault as e:
400 err_msg = "Getting '%s' :: %s" % \
401 (gpio_name, self._get_xmlrpclib_exception(e))
402 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700403
404
405 def set(self, gpio_name, gpio_value):
406 """Set and check the value of a gpio using Servod."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700407 self.set_nocheck(gpio_name, gpio_value)
Todd Brochcf7c6652012-02-24 13:03:59 -0800408 retry_count = Servo.GET_RETRY_MAX
409 while gpio_value != self.get(gpio_name) and retry_count:
410 logging.warn("%s != %s, retry %d", gpio_name, gpio_value,
411 retry_count)
412 retry_count -= 1
413 time.sleep(Servo.SHORT_DELAY)
414 if not retry_count:
415 assert gpio_value == self.get(gpio_name), \
416 'Servo failed to set %s to %s' % (gpio_name, gpio_value)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700417
418
419 def set_nocheck(self, gpio_name, gpio_value):
420 """Set the value of a gpio using Servod."""
421 assert gpio_name and gpio_value
Todd Broch9753bd42012-03-21 10:15:08 -0700422 logging.info('Setting %s to %s', gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700423 try:
Simran Basi1cbc5f22013-07-17 14:23:58 -0700424 self._server.set(gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700425 except xmlrpclib.Fault as e:
426 err_msg = "Setting '%s' to '%s' :: %s" % \
427 (gpio_name, gpio_value, self._get_xmlrpclib_exception(e))
428 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700429
430
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800431 def set_get_all(self, controls):
432 """Set &| get one or more control values.
433
434 @param controls: list of strings, controls to set &| get.
435
436 @raise: error.TestError in case error occurs setting/getting values.
437 """
438 rv = []
439 try:
Vic Yangcad9acb2013-05-21 14:02:05 +0800440 logging.info('Set/get all: %s', str(controls))
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800441 rv = self._server.set_get_all(controls)
442 except xmlrpclib.Fault as e:
443 # TODO(waihong): Remove the following backward compatibility when
444 # the new versions of hdctools are deployed.
445 if 'not supported' in str(e):
446 logging.warning('The servod is too old that set_get_all '
447 'not supported. Use set and get instead.')
448 for control in controls:
449 if ':' in control:
450 (name, value) = control.split(':')
451 if name == 'sleep':
452 time.sleep(float(value))
453 else:
454 self.set_nocheck(name, value)
455 rv.append(True)
456 else:
457 rv.append(self.get(name))
458 else:
459 err_msg = "Problem with '%s' :: %s" % \
460 (controls, self._get_xmlrpclib_exception(e))
461 raise error.TestFail(err_msg)
462 return rv
463
464
Jon Salzc88e5b62011-11-30 14:38:54 +0800465 # TODO(waihong) It may fail if multiple servo's are connected to the same
466 # host. Should look for a better way, like the USB serial name, to identify
467 # the USB device.
Simran Basi741b5d42012-05-18 11:27:15 -0700468 # TODO(sbasi) Remove this code from autoserv once firmware tests have been
469 # updated.
Jon Salzc88e5b62011-11-30 14:38:54 +0800470 def probe_host_usb_dev(self):
471 """Probe the USB disk device plugged-in the servo from the host side.
472
473 It tries to switch the USB mux to make the host unable to see the
474 USB disk and compares the result difference.
475
Jon Salzc88e5b62011-11-30 14:38:54 +0800476 Returns:
477 A string of USB disk path, like '/dev/sdb', or None if not existed.
478 """
479 cmd = 'ls /dev/sd[a-z]'
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800480 original_value = self.get_usbkey_direction()
Jon Salzc88e5b62011-11-30 14:38:54 +0800481
482 # Make the host unable to see the USB disk.
Fang Dengafb88142013-05-30 17:44:31 -0700483 self.switch_usbkey('off')
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800484 no_usb_set = set(self.system_output(cmd, ignore_status=True).split())
Jon Salzc88e5b62011-11-30 14:38:54 +0800485
486 # Make the host able to see the USB disk.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800487 self.switch_usbkey('host')
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800488 has_usb_set = set(self.system_output(cmd, ignore_status=True).split())
Jon Salzc88e5b62011-11-30 14:38:54 +0800489
490 # Back to its original value.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800491 if original_value != self.get_usbkey_direction():
492 self.switch_usbkey(original_value)
Jon Salzc88e5b62011-11-30 14:38:54 +0800493
494 diff_set = has_usb_set - no_usb_set
495 if len(diff_set) == 1:
496 return diff_set.pop()
497 else:
498 return None
499
500
J. Richard Barnette69929a52013-03-15 13:22:11 -0700501 def recovery_supported(self):
502 """Return whether servo-based recovery should work.
503
504 Use of `image_to_servo_usb()` and `install_recovery_image()`
505 relies on DUT-board specific behaviors, and is not supported
506 for all types of board. Return whether these two operations
507 are expected to succeed for the current DUT.
508
509 @return `True` iff the recovery related methods are supported
510 for this servo and DUT.
511
512 """
J. Richard Barnette75136b32013-03-26 13:38:44 -0700513 return self._power_state.recovery_supported()
J. Richard Barnette69929a52013-03-15 13:22:11 -0700514
515
Mike Truty49153d82012-08-21 22:27:30 -0500516 def image_to_servo_usb(self, image_path=None,
517 make_image_noninteractive=False):
518 """Install an image to the USB key plugged into the servo.
519
520 This method may copy any image to the servo USB key including a
521 recovery image or a test image. These images are frequently used
522 for test purposes such as restoring a corrupted image or conducting
523 an upgrade of ec/fw/kernel as part of a test of a specific image part.
524
525 Args:
526 image_path: Path on the host to the recovery image.
527 make_image_noninteractive: Make the recovery image noninteractive,
528 therefore the DUT will reboot
529 automatically after installation.
530 """
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700531 # We're about to start plugging/unplugging the USB key. We
532 # don't know the state of the DUT, or what it might choose
533 # to do to the device after hotplug. To avoid surprises,
534 # force the DUT to be off.
535 self._server.hwinit()
536 self._power_state.power_off()
Mike Truty49153d82012-08-21 22:27:30 -0500537
538 # Set up Servo's usb mux.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800539 self.switch_usbkey('host')
Mike Truty49153d82012-08-21 22:27:30 -0500540 if image_path:
Tom Wai-Hong Tam42f136d2012-10-26 11:11:23 +0800541 logging.info('Searching for usb device and copying image to it. '
542 'Please wait a few minutes...')
Mike Truty49153d82012-08-21 22:27:30 -0500543 if not self._server.download_image_to_usb(image_path):
544 logging.error('Failed to transfer requested image to USB. '
545 'Please take a look at Servo Logs.')
546 raise error.AutotestError('Download image to usb failed.')
547 if make_image_noninteractive:
548 logging.info('Making image noninteractive')
549 if not self._server.make_image_noninteractive():
550 logging.error('Failed to make image noninteractive. '
551 'Please take a look at Servo Logs.')
552
553
Simran Basi741b5d42012-05-18 11:27:15 -0700554 def install_recovery_image(self, image_path=None,
J. Richard Barnetteb6de7e32013-02-14 13:28:04 -0800555 make_image_noninteractive=False):
Jon Salzc88e5b62011-11-30 14:38:54 +0800556 """Install the recovery image specied by the path onto the DUT.
557
558 This method uses google recovery mode to install a recovery image
559 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 +0800560 board specified by the usb_dev. If no image path is specified
Jon Salzc88e5b62011-11-30 14:38:54 +0800561 we use the recovery image already on the usb image.
562
563 Args:
564 image_path: Path on the host to the recovery image.
Simran Basi741b5d42012-05-18 11:27:15 -0700565 make_image_noninteractive: Make the recovery image noninteractive,
566 therefore the DUT will reboot
567 automatically after installation.
Jon Salzc88e5b62011-11-30 14:38:54 +0800568 """
Mike Truty49153d82012-08-21 22:27:30 -0500569 self.image_to_servo_usb(image_path, make_image_noninteractive)
J. Richard Barnette75136b32013-03-26 13:38:44 -0700570 self._power_state.power_on(rec_mode=power_state_controller.REC_ON)
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700571 self.switch_usbkey('dut')
Jon Salzc88e5b62011-11-30 14:38:54 +0800572
573
Todd Brochf24d2782011-08-19 10:55:41 -0700574 def _connect_servod(self, servo_host, servo_port):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700575 """Connect to the Servod process with XMLRPC.
576
577 Args:
578 servo_port: Port the Servod process is listening on.
579 """
Todd Brochf24d2782011-08-19 10:55:41 -0700580 remote = 'http://%s:%s' % (servo_host, servo_port)
Todd Broch96d83aa2011-08-29 14:37:38 -0700581 self._server = xmlrpclib.ServerProxy(remote)
Todd Brochf24d2782011-08-19 10:55:41 -0700582 try:
Simran Basi59331342013-07-12 12:06:29 -0700583 # We have noticed incidents where we are able to connect but the
584 # actual servo calls go on for 20-30 mins. Thus _connect_servod now
585 # grabs the pwr_button state and if it times out raises an error.
586 timeout, result = retry.timeout(
587 self._server.get, args=('pwr_button', ),
588 timeout_sec=Servo.CALL_TIMEOUT_SECS)
589 if timeout:
590 raise error.AutotestError('Timed out getting value for '
591 'pwr_button.')
592 except Exception as e:
593 # Make sure to catch any errors that can occur connecting to servod.
594 error_msg = 'Connection to servod failed. Failure: %s' % e
595 logging.error(error_msg)
596 raise error.AutotestError(error_msg)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800597
598
599 def _scp_image(self, image_path):
600 """Copy image to the servo host.
601
602 When programming a firmware image on the DUT, the image must be
603 located on the host to which the servo device is connected. Sometimes
604 servo is controlled by a remote host, in this case the image needs to
605 be transferred to the remote host.
606
607 @param image_path: a string, name of the firmware image file to be
608 transferred.
609 @return: a string, full path name of the copied file on the remote.
610 """
611
612 dest_path = os.path.join('/tmp', os.path.basename(image_path))
613 scp_cmd = self._scp_cmd_template % (image_path, dest_path)
614 utils.system(scp_cmd)
615 return dest_path
616
617
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800618 def system(self, command, timeout=None):
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800619 """Execute the passed in command on the servod host."""
620 if self._sudo_required:
621 command = 'sudo -n %s' % command
622 if self._ssh_prefix:
623 command = "%s '%s'" % (self._ssh_prefix, command)
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800624 logging.info('Will execute on servo host: %s', command)
625 utils.system(command, timeout=timeout)
626
627
628 def system_output(self, command, timeout=None,
629 ignore_status=False, args=()):
630 """Execute the passed in command on the servod host, return stdout.
631
632 @param command, a string, the command to execute
633 @param timeout, an int, max number of seconds to wait til command
634 execution completes
635 @ignore_status, a Boolean, if true - ignore command's nonzero exit
636 status, otherwise an exception will be thrown
637 @param args, a tuple of strings, each becoming a separate command line
638 parameter for the command
639 @return: command's stdout as a string.
640 """
641 if self._sudo_required:
642 command = 'sudo -n %s' % command
643 if self._ssh_prefix:
644 command = "%s '%s'" % (self._ssh_prefix, command)
645 logging.info('Will execute and collect output on servo host: %s %s',
646 command, ' '.join("'%s'" % x for x in args))
647 return utils.system_output(command, timeout=timeout,
648 ignore_status=ignore_status, args=args)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800649
650
651 def program_ec(self, board, image):
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800652 """Program EC on a given board using given image.
653
654 @param board: a string, type of the DUT board
655 @param image: a string, file name of the EC image to program on the
656 DUT
657 """
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800658 if not self.is_localhost():
659 image = self._scp_image(image)
660 programmer.program_ec(board, self, image)
661
662
663 def program_bootprom(self, board, image):
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800664 """Program bootprom on a given board using given image.
665
666 In case servo is controlled by a remote host, the image needs to be
667 transferred to the host.
668
669 If the device tree subdirectory is present along with the image, the
670 subdirectory is also copied to the remote host.
671
672 @param board: a string, type of the DUT board
673 @param image: a string, file name of the firmware image to program on
674 the DUT. The device tree subdirectory, if present, is on
675 the same level with the image file.
676 """
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800677 if not self.is_localhost():
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800678 dts_path = os.path.join(os.path.dirname(image), 'dts')
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800679 image = self._scp_image(image)
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800680 if os.path.isdir(dts_path):
681 self._scp_image(dts_path)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800682 programmer.program_bootprom(board, self, image)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800683
Fang Dengafb88142013-05-30 17:44:31 -0700684
685 def _switch_usbkey_power(self, power_state, detection_delay=False):
686 """Switch usbkey power.
687
688 This function switches usbkey power by setting the value of
689 'prtctl4_pwren'. If the power is already in the
690 requested state, this function simply returns.
691
692 @param power_state: A string, 'on' or 'off'.
693 @param detection_delay: A boolean value, if True, sleep
694 for |USB_DETECTION_DELAY| after switching
695 the power on.
696 """
697 self.set('prtctl4_pwren', power_state)
698 if power_state == 'off':
699 time.sleep(self.USB_POWEROFF_DELAY)
700 elif detection_delay:
701 time.sleep(self.USB_DETECTION_DELAY)
702
703
704 def switch_usbkey(self, usb_state):
705 """Connect USB flash stick to either host or DUT, or turn USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800706
707 This function switches the servo multiplexer to provide electrical
Fang Dengafb88142013-05-30 17:44:31 -0700708 connection between the USB port J3 and either host or DUT side. It
709 can also be used to turn the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800710
Fang Dengafb88142013-05-30 17:44:31 -0700711 Switching to 'dut' or 'host' is accompanied by powercycling
712 of the USB stick, because it sometimes gets wedged if the mux
713 is switched while the stick power is on.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800714
Fang Dengafb88142013-05-30 17:44:31 -0700715 @param usb_state: A string, one of 'dut', 'host', or 'off'.
716 'dut' and 'host' indicate which side the
717 USB flash device is required to be connected to.
718 'off' indicates turning the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800719
Fang Dengafb88142013-05-30 17:44:31 -0700720 @raise: error.TestError in case the parameter is not 'dut'
721 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800722 """
Fang Dengafb88142013-05-30 17:44:31 -0700723 if self.get_usbkey_direction() == usb_state:
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800724 return
725
Fang Dengafb88142013-05-30 17:44:31 -0700726 if usb_state == 'off':
727 self._switch_usbkey_power('off')
728 self._usb_state = usb_state
729 return
730 elif usb_state == 'host':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800731 mux_direction = 'servo_sees_usbkey'
Fang Dengafb88142013-05-30 17:44:31 -0700732 elif usb_state == 'dut':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800733 mux_direction = 'dut_sees_usbkey'
734 else:
Fang Dengafb88142013-05-30 17:44:31 -0700735 raise error.TestError('Unknown USB state request: %s' % usb_state)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800736
Fang Dengafb88142013-05-30 17:44:31 -0700737 self._switch_usbkey_power('off')
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800738 self.set('usb_mux_sel1', mux_direction)
739 time.sleep(self.USB_POWEROFF_DELAY)
Fang Dengafb88142013-05-30 17:44:31 -0700740 self._switch_usbkey_power('on', usb_state == 'host')
741 self._usb_state = usb_state
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800742
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800743
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800744 def get_usbkey_direction(self):
Fang Dengafb88142013-05-30 17:44:31 -0700745 """Get which side USB is connected to or 'off' if usb power is off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800746
Fang Dengafb88142013-05-30 17:44:31 -0700747 @return: A string, one of 'dut', 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800748 """
Fang Dengafb88142013-05-30 17:44:31 -0700749 if not self._usb_state:
750 if self.get('prtctl4_pwren') == 'off':
751 self._usb_state = 'off'
752 elif self.get('usb_mux_sel1').startswith('dut'):
753 self._usb_state = 'dut'
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800754 else:
Fang Dengafb88142013-05-30 17:44:31 -0700755 self._usb_state = 'host'
756 return self._usb_state