blob: 91da6590321d636340303b5a9a00f5ac89fb0302 [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.
Fang Dengafb88142013-05-30 17:44:31 -0700127 self._usb_state = None
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800128 self.set('dut_hub_pwren', 'on')
129 self.set('usb_mux_oe1', 'on')
Fang Dengafb88142013-05-30 17:44:31 -0700130 self.switch_usbkey('off')
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800131
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 """
Vic Yangcad9acb2013-05-21 14:02:05 +0800255 (m1_a1_n, m1_a0_n, m2_a1_n, m2_a0_n) = (
256 self.KEY_MATRIX[self._key_matrix]['none'])
Gediminas Ramanauskas9da80f22012-11-14 12:59:43 -0800257 (m1_a1, m1_a0, m2_a1, m2_a0) = self.KEY_MATRIX[self._key_matrix][key]
Vic Yangcad9acb2013-05-21 14:02:05 +0800258 self.set_get_all(['kbd_m2_a0:%s' % m2_a0_n,
259 'kbd_m2_a1:%s' % m2_a1_n,
260 'kbd_m1_a0:%s' % m1_a0_n,
261 'kbd_m1_a1:%s' % m1_a1_n,
262 'kbd_en:on',
263 'kbd_m2_a0:%s' % m2_a0,
264 'kbd_m2_a1:%s' % m2_a1,
265 'kbd_m1_a0:%s' % m1_a0,
266 'kbd_m1_a1:%s' % m1_a1])
Gediminas Ramanauskas9da80f22012-11-14 12:59:43 -0800267
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800268
Vic Yang701a59e2013-05-02 05:10:22 +0800269 def _press_and_release_keys(self, key, press_secs=None):
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800270 """Simulate button presses and release."""
Vic Yang701a59e2013-05-02 05:10:22 +0800271 if press_secs is None:
272 press_secs = self.SERVO_KEY_PRESS_DELAY
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800273 self._press_keys(key)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700274 time.sleep(press_secs)
275 self.set_nocheck('kbd_en', 'off')
276
277
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -0700278 def set_key_matrix(self, matrix=0):
279 """Set keyboard mapping"""
280 self._key_matrix = matrix
281
282
Vic Yang701a59e2013-05-02 05:10:22 +0800283 def ctrl_d(self, press_secs=None):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700284 """Simulate Ctrl-d simultaneous button presses."""
Vic Yang701a59e2013-05-02 05:10:22 +0800285 self._press_and_release_keys('ctrl_d', press_secs)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700286
287
Vic Yang701a59e2013-05-02 05:10:22 +0800288 def ctrl_enter(self, press_secs=None):
Todd Broch9753bd42012-03-21 10:15:08 -0700289 """Simulate Ctrl-enter simultaneous button presses."""
Vic Yang701a59e2013-05-02 05:10:22 +0800290 self._press_and_release_keys('ctrl_enter', press_secs)
Todd Broch9753bd42012-03-21 10:15:08 -0700291
292
Vic Yang701a59e2013-05-02 05:10:22 +0800293 def d_key(self, press_secs=None):
Todd Broch9dfc3a82011-11-01 08:09:28 -0700294 """Simulate Enter key button press."""
Vic Yang701a59e2013-05-02 05:10:22 +0800295 self._press_and_release_keys('d', press_secs)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700296
297
Vic Yang701a59e2013-05-02 05:10:22 +0800298 def ctrl_key(self, press_secs=None):
Todd Broch9dfc3a82011-11-01 08:09:28 -0700299 """Simulate Enter key button press."""
Vic Yang701a59e2013-05-02 05:10:22 +0800300 self._press_and_release_keys('ctrl', press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700301
302
Vic Yang701a59e2013-05-02 05:10:22 +0800303 def enter_key(self, press_secs=None):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700304 """Simulate Enter key button press."""
Vic Yang701a59e2013-05-02 05:10:22 +0800305 self._press_and_release_keys('enter', press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700306
307
Vic Yang701a59e2013-05-02 05:10:22 +0800308 def refresh_key(self, press_secs=None):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700309 """Simulate Refresh key (F3) button press."""
Vic Yang701a59e2013-05-02 05:10:22 +0800310 self._press_and_release_keys('refresh', press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700311
312
Vic Yang701a59e2013-05-02 05:10:22 +0800313 def ctrl_refresh_key(self, press_secs=None):
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800314 """Simulate Ctrl and Refresh (F3) simultaneous press.
315
316 This key combination is an alternative of Space key.
317 """
Vic Yang701a59e2013-05-02 05:10:22 +0800318 self._press_and_release_keys('ctrl_refresh', press_secs)
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800319
320
Vic Yang701a59e2013-05-02 05:10:22 +0800321 def imaginary_key(self, press_secs=None):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700322 """Simulate imaginary key button press.
323
324 Maps to a key that doesn't physically exist.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700325 """
Vic Yang701a59e2013-05-02 05:10:22 +0800326 self._press_and_release_keys('unused', press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700327
328
Craig Harrison6b36b122011-06-28 17:58:43 -0700329 def enable_recovery_mode(self):
330 """Enable recovery mode on device."""
331 self.set('rec_mode', 'on')
332
333
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800334 def custom_recovery_mode(self):
335 """Custom key combination to enter recovery mode."""
336 self._press_keys('rec_mode')
337 self.power_normal_press()
338 time.sleep(self.SERVO_KEY_PRESS_DELAY)
339 self.set_nocheck('kbd_en', 'off')
340
341
Craig Harrison6b36b122011-06-28 17:58:43 -0700342 def disable_recovery_mode(self):
343 """Disable recovery mode on device."""
344 self.set('rec_mode', 'off')
345
346
347 def enable_development_mode(self):
348 """Enable development mode on device."""
349 self.set('dev_mode', 'on')
350
351
352 def disable_development_mode(self):
353 """Disable development mode on device."""
354 self.set('dev_mode', 'off')
355
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700356 def boot_devmode(self):
357 """Boot a dev-mode device that is powered off."""
Tom Wai-Hong Tam23869102012-01-17 15:41:30 +0800358 self.power_short_press()
Craig Harrison48997262011-06-27 14:31:10 -0700359 self.pass_devmode()
360
361
362 def pass_devmode(self):
363 """Pass through boot screens in dev-mode."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700364 time.sleep(Servo.BOOT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700365 self.ctrl_d()
Chrome Bot9a1137d2011-07-19 14:35:00 -0700366 time.sleep(Servo.BOOT_DELAY)
Craig Harrison48997262011-06-27 14:31:10 -0700367
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700368
Todd Brochefe72cb2012-07-11 19:58:53 -0700369 def _get_xmlrpclib_exception(self, xmlexc):
370 """Get meaningful exception string from xmlrpc.
371
372 Args:
373 xmlexc: xmlrpclib.Fault object
374
375 xmlrpclib.Fault.faultString has the following format:
376
377 <type 'exception type'>:'actual error message'
378
379 Parse and return the real exception from the servod side instead of the
380 less meaningful one like,
381 <Fault 1: "<type 'exceptions.AttributeError'>:'tca6416' object has no
382 attribute 'hw_driver'">
383
384 Returns:
385 string of underlying exception raised in servod.
386 """
387 return re.sub('^.*>:', '', xmlexc.faultString)
388
389
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700390 def get(self, gpio_name):
391 """Get the value of a gpio from Servod."""
392 assert gpio_name
Todd Brochefe72cb2012-07-11 19:58:53 -0700393 try:
394 return self._server.get(gpio_name)
395 except xmlrpclib.Fault as e:
396 err_msg = "Getting '%s' :: %s" % \
397 (gpio_name, self._get_xmlrpclib_exception(e))
398 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700399
400
401 def set(self, gpio_name, gpio_value):
402 """Set and check the value of a gpio using Servod."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700403 self.set_nocheck(gpio_name, gpio_value)
Todd Brochcf7c6652012-02-24 13:03:59 -0800404 retry_count = Servo.GET_RETRY_MAX
405 while gpio_value != self.get(gpio_name) and retry_count:
406 logging.warn("%s != %s, retry %d", gpio_name, gpio_value,
407 retry_count)
408 retry_count -= 1
409 time.sleep(Servo.SHORT_DELAY)
410 if not retry_count:
411 assert gpio_value == self.get(gpio_name), \
412 'Servo failed to set %s to %s' % (gpio_name, gpio_value)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700413
414
415 def set_nocheck(self, gpio_name, gpio_value):
416 """Set the value of a gpio using Servod."""
417 assert gpio_name and gpio_value
Todd Broch9753bd42012-03-21 10:15:08 -0700418 logging.info('Setting %s to %s', gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700419 try:
420 self._server.set(gpio_name, gpio_value)
421 except xmlrpclib.Fault as e:
422 err_msg = "Setting '%s' to '%s' :: %s" % \
423 (gpio_name, gpio_value, self._get_xmlrpclib_exception(e))
424 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700425
426
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800427 def set_get_all(self, controls):
428 """Set &| get one or more control values.
429
430 @param controls: list of strings, controls to set &| get.
431
432 @raise: error.TestError in case error occurs setting/getting values.
433 """
434 rv = []
435 try:
Vic Yangcad9acb2013-05-21 14:02:05 +0800436 logging.info('Set/get all: %s', str(controls))
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800437 rv = self._server.set_get_all(controls)
438 except xmlrpclib.Fault as e:
439 # TODO(waihong): Remove the following backward compatibility when
440 # the new versions of hdctools are deployed.
441 if 'not supported' in str(e):
442 logging.warning('The servod is too old that set_get_all '
443 'not supported. Use set and get instead.')
444 for control in controls:
445 if ':' in control:
446 (name, value) = control.split(':')
447 if name == 'sleep':
448 time.sleep(float(value))
449 else:
450 self.set_nocheck(name, value)
451 rv.append(True)
452 else:
453 rv.append(self.get(name))
454 else:
455 err_msg = "Problem with '%s' :: %s" % \
456 (controls, self._get_xmlrpclib_exception(e))
457 raise error.TestFail(err_msg)
458 return rv
459
460
Jon Salzc88e5b62011-11-30 14:38:54 +0800461 # TODO(waihong) It may fail if multiple servo's are connected to the same
462 # host. Should look for a better way, like the USB serial name, to identify
463 # the USB device.
Simran Basi741b5d42012-05-18 11:27:15 -0700464 # TODO(sbasi) Remove this code from autoserv once firmware tests have been
465 # updated.
Jon Salzc88e5b62011-11-30 14:38:54 +0800466 def probe_host_usb_dev(self):
467 """Probe the USB disk device plugged-in the servo from the host side.
468
469 It tries to switch the USB mux to make the host unable to see the
470 USB disk and compares the result difference.
471
Jon Salzc88e5b62011-11-30 14:38:54 +0800472 Returns:
473 A string of USB disk path, like '/dev/sdb', or None if not existed.
474 """
475 cmd = 'ls /dev/sd[a-z]'
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800476 original_value = self.get_usbkey_direction()
Jon Salzc88e5b62011-11-30 14:38:54 +0800477
478 # Make the host unable to see the USB disk.
Fang Dengafb88142013-05-30 17:44:31 -0700479 self.switch_usbkey('off')
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')
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800484 has_usb_set = set(self.system_output(cmd, ignore_status=True).split())
Jon Salzc88e5b62011-11-30 14:38:54 +0800485
486 # Back to its original value.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800487 if original_value != self.get_usbkey_direction():
488 self.switch_usbkey(original_value)
Jon Salzc88e5b62011-11-30 14:38:54 +0800489
490 diff_set = has_usb_set - no_usb_set
491 if len(diff_set) == 1:
492 return diff_set.pop()
493 else:
494 return None
495
496
J. Richard Barnette69929a52013-03-15 13:22:11 -0700497 def recovery_supported(self):
498 """Return whether servo-based recovery should work.
499
500 Use of `image_to_servo_usb()` and `install_recovery_image()`
501 relies on DUT-board specific behaviors, and is not supported
502 for all types of board. Return whether these two operations
503 are expected to succeed for the current DUT.
504
505 @return `True` iff the recovery related methods are supported
506 for this servo and DUT.
507
508 """
J. Richard Barnette75136b32013-03-26 13:38:44 -0700509 return self._power_state.recovery_supported()
J. Richard Barnette69929a52013-03-15 13:22:11 -0700510
511
Mike Truty49153d82012-08-21 22:27:30 -0500512 def image_to_servo_usb(self, image_path=None,
513 make_image_noninteractive=False):
514 """Install an image to the USB key plugged into the servo.
515
516 This method may copy any image to the servo USB key including a
517 recovery image or a test image. These images are frequently used
518 for test purposes such as restoring a corrupted image or conducting
519 an upgrade of ec/fw/kernel as part of a test of a specific image part.
520
521 Args:
522 image_path: Path on the host to the recovery image.
523 make_image_noninteractive: Make the recovery image noninteractive,
524 therefore the DUT will reboot
525 automatically after installation.
526 """
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700527 # We're about to start plugging/unplugging the USB key. We
528 # don't know the state of the DUT, or what it might choose
529 # to do to the device after hotplug. To avoid surprises,
530 # force the DUT to be off.
531 self._server.hwinit()
532 self._power_state.power_off()
Mike Truty49153d82012-08-21 22:27:30 -0500533
534 # Set up Servo's usb mux.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800535 self.switch_usbkey('host')
Mike Truty49153d82012-08-21 22:27:30 -0500536 if image_path:
Tom Wai-Hong Tam42f136d2012-10-26 11:11:23 +0800537 logging.info('Searching for usb device and copying image to it. '
538 'Please wait a few minutes...')
Mike Truty49153d82012-08-21 22:27:30 -0500539 if not self._server.download_image_to_usb(image_path):
540 logging.error('Failed to transfer requested image to USB. '
541 'Please take a look at Servo Logs.')
542 raise error.AutotestError('Download image to usb failed.')
543 if make_image_noninteractive:
544 logging.info('Making image noninteractive')
545 if not self._server.make_image_noninteractive():
546 logging.error('Failed to make image noninteractive. '
547 'Please take a look at Servo Logs.')
548
549
Simran Basi741b5d42012-05-18 11:27:15 -0700550 def install_recovery_image(self, image_path=None,
J. Richard Barnetteb6de7e32013-02-14 13:28:04 -0800551 make_image_noninteractive=False):
Jon Salzc88e5b62011-11-30 14:38:54 +0800552 """Install the recovery image specied by the path onto the DUT.
553
554 This method uses google recovery mode to install a recovery image
555 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 +0800556 board specified by the usb_dev. If no image path is specified
Jon Salzc88e5b62011-11-30 14:38:54 +0800557 we use the recovery image already on the usb image.
558
559 Args:
560 image_path: Path on the host to the recovery image.
Simran Basi741b5d42012-05-18 11:27:15 -0700561 make_image_noninteractive: Make the recovery image noninteractive,
562 therefore the DUT will reboot
563 automatically after installation.
Jon Salzc88e5b62011-11-30 14:38:54 +0800564 """
Mike Truty49153d82012-08-21 22:27:30 -0500565 self.image_to_servo_usb(image_path, make_image_noninteractive)
J. Richard Barnette75136b32013-03-26 13:38:44 -0700566 self._power_state.power_on(rec_mode=power_state_controller.REC_ON)
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700567 self.switch_usbkey('dut')
Jon Salzc88e5b62011-11-30 14:38:54 +0800568
569
Todd Brochf24d2782011-08-19 10:55:41 -0700570 def _connect_servod(self, servo_host, servo_port):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700571 """Connect to the Servod process with XMLRPC.
572
573 Args:
574 servo_port: Port the Servod process is listening on.
575 """
Todd Brochf24d2782011-08-19 10:55:41 -0700576 remote = 'http://%s:%s' % (servo_host, servo_port)
Todd Broch96d83aa2011-08-29 14:37:38 -0700577 self._server = xmlrpclib.ServerProxy(remote)
Todd Brochf24d2782011-08-19 10:55:41 -0700578 try:
579 self._server.echo("ping-test")
Todd Broch96d83aa2011-08-29 14:37:38 -0700580 except:
581 logging.error('Connection to servod failed')
Todd Brochf24d2782011-08-19 10:55:41 -0700582 raise
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800583
584
585 def _scp_image(self, image_path):
586 """Copy image to the servo host.
587
588 When programming a firmware image on the DUT, the image must be
589 located on the host to which the servo device is connected. Sometimes
590 servo is controlled by a remote host, in this case the image needs to
591 be transferred to the remote host.
592
593 @param image_path: a string, name of the firmware image file to be
594 transferred.
595 @return: a string, full path name of the copied file on the remote.
596 """
597
598 dest_path = os.path.join('/tmp', os.path.basename(image_path))
599 scp_cmd = self._scp_cmd_template % (image_path, dest_path)
600 utils.system(scp_cmd)
601 return dest_path
602
603
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800604 def system(self, command, timeout=None):
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800605 """Execute the passed in command on the servod host."""
606 if self._sudo_required:
607 command = 'sudo -n %s' % command
608 if self._ssh_prefix:
609 command = "%s '%s'" % (self._ssh_prefix, command)
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800610 logging.info('Will execute on servo host: %s', command)
611 utils.system(command, timeout=timeout)
612
613
614 def system_output(self, command, timeout=None,
615 ignore_status=False, args=()):
616 """Execute the passed in command on the servod host, return stdout.
617
618 @param command, a string, the command to execute
619 @param timeout, an int, max number of seconds to wait til command
620 execution completes
621 @ignore_status, a Boolean, if true - ignore command's nonzero exit
622 status, otherwise an exception will be thrown
623 @param args, a tuple of strings, each becoming a separate command line
624 parameter for the command
625 @return: command's stdout as a string.
626 """
627 if self._sudo_required:
628 command = 'sudo -n %s' % command
629 if self._ssh_prefix:
630 command = "%s '%s'" % (self._ssh_prefix, command)
631 logging.info('Will execute and collect output on servo host: %s %s',
632 command, ' '.join("'%s'" % x for x in args))
633 return utils.system_output(command, timeout=timeout,
634 ignore_status=ignore_status, args=args)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800635
636
637 def program_ec(self, board, image):
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800638 """Program EC on a given board using given image.
639
640 @param board: a string, type of the DUT board
641 @param image: a string, file name of the EC image to program on the
642 DUT
643 """
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800644 if not self.is_localhost():
645 image = self._scp_image(image)
646 programmer.program_ec(board, self, image)
647
648
649 def program_bootprom(self, board, image):
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800650 """Program bootprom on a given board using given image.
651
652 In case servo is controlled by a remote host, the image needs to be
653 transferred to the host.
654
655 If the device tree subdirectory is present along with the image, the
656 subdirectory is also copied to the remote host.
657
658 @param board: a string, type of the DUT board
659 @param image: a string, file name of the firmware image to program on
660 the DUT. The device tree subdirectory, if present, is on
661 the same level with the image file.
662 """
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800663 if not self.is_localhost():
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800664 dts_path = os.path.join(os.path.dirname(image), 'dts')
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800665 image = self._scp_image(image)
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800666 if os.path.isdir(dts_path):
667 self._scp_image(dts_path)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800668 programmer.program_bootprom(board, self, image)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800669
Fang Dengafb88142013-05-30 17:44:31 -0700670
671 def _switch_usbkey_power(self, power_state, detection_delay=False):
672 """Switch usbkey power.
673
674 This function switches usbkey power by setting the value of
675 'prtctl4_pwren'. If the power is already in the
676 requested state, this function simply returns.
677
678 @param power_state: A string, 'on' or 'off'.
679 @param detection_delay: A boolean value, if True, sleep
680 for |USB_DETECTION_DELAY| after switching
681 the power on.
682 """
683 self.set('prtctl4_pwren', power_state)
684 if power_state == 'off':
685 time.sleep(self.USB_POWEROFF_DELAY)
686 elif detection_delay:
687 time.sleep(self.USB_DETECTION_DELAY)
688
689
690 def switch_usbkey(self, usb_state):
691 """Connect USB flash stick to either host or DUT, or turn USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800692
693 This function switches the servo multiplexer to provide electrical
Fang Dengafb88142013-05-30 17:44:31 -0700694 connection between the USB port J3 and either host or DUT side. It
695 can also be used to turn the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800696
Fang Dengafb88142013-05-30 17:44:31 -0700697 Switching to 'dut' or 'host' is accompanied by powercycling
698 of the USB stick, because it sometimes gets wedged if the mux
699 is switched while the stick power is on.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800700
Fang Dengafb88142013-05-30 17:44:31 -0700701 @param usb_state: A string, one of 'dut', 'host', or 'off'.
702 'dut' and 'host' indicate which side the
703 USB flash device is required to be connected to.
704 'off' indicates turning the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800705
Fang Dengafb88142013-05-30 17:44:31 -0700706 @raise: error.TestError in case the parameter is not 'dut'
707 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800708 """
Fang Dengafb88142013-05-30 17:44:31 -0700709 if self.get_usbkey_direction() == usb_state:
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800710 return
711
Fang Dengafb88142013-05-30 17:44:31 -0700712 if usb_state == 'off':
713 self._switch_usbkey_power('off')
714 self._usb_state = usb_state
715 return
716 elif usb_state == 'host':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800717 mux_direction = 'servo_sees_usbkey'
Fang Dengafb88142013-05-30 17:44:31 -0700718 elif usb_state == 'dut':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800719 mux_direction = 'dut_sees_usbkey'
720 else:
Fang Dengafb88142013-05-30 17:44:31 -0700721 raise error.TestError('Unknown USB state request: %s' % usb_state)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800722
Fang Dengafb88142013-05-30 17:44:31 -0700723 self._switch_usbkey_power('off')
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800724 self.set('usb_mux_sel1', mux_direction)
725 time.sleep(self.USB_POWEROFF_DELAY)
Fang Dengafb88142013-05-30 17:44:31 -0700726 self._switch_usbkey_power('on', usb_state == 'host')
727 self._usb_state = usb_state
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800728
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800729
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800730 def get_usbkey_direction(self):
Fang Dengafb88142013-05-30 17:44:31 -0700731 """Get which side USB is connected to or 'off' if usb power is off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800732
Fang Dengafb88142013-05-30 17:44:31 -0700733 @return: A string, one of 'dut', 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800734 """
Fang Dengafb88142013-05-30 17:44:31 -0700735 if not self._usb_state:
736 if self.get('prtctl4_pwren') == 'off':
737 self._usb_state = 'off'
738 elif self.get('usb_mux_sel1').startswith('dut'):
739 self._usb_state = 'dut'
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800740 else:
Fang Dengafb88142013-05-30 17:44:31 -0700741 self._usb_state = 'host'
742 return self._usb_state