blob: 13d0cd6e816907a6f4add18c3b005a52505ca4ec [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
Simran Basib7850bb2013-07-24 12:33:42 -070064 # Time to wait before timing out on servo initialization.
65 INIT_TIMEOUT_SECS = 10
Simran Basi59331342013-07-12 12:06:29 -070066
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -070067 KEY_MATRIX_ALT_0 = {
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -070068 'ctrl_refresh': ['0', '0', '0', '1'],
69 'ctrl_d': ['0', '1', '0', '0'],
70 'd': ['0', '1', '1', '1'],
71 'ctrl_enter': ['1', '0', '0', '0'],
72 'enter': ['1', '0', '1', '1'],
73 'ctrl': ['1', '1', '0', '0'],
74 'refresh': ['1', '1', '0', '1'],
75 'unused': ['1', '1', '1', '0'],
76 'none': ['1', '1', '1', '1']}
Chris Masone6a0680f2012-03-02 08:40:00 -080077
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -070078 KEY_MATRIX_ALT_1 = {
Gediminas Ramanauskas515e7012012-09-18 17:01:10 -070079 'ctrl_d': ['0', '0', '1', '0'],
80 'd': ['0', '0', '1', '1'],
81 'ctrl_enter': ['0', '1', '1', '0'],
82 'enter': ['0', '1', '1', '1'],
83 'ctrl_refresh': ['1', '0', '0', '1'],
84 'unused': ['1', '1', '0', '0'],
85 'refresh': ['1', '1', '0', '1'],
86 'ctrl': ['1', '1', '1', '0'],
87 'none': ['1', '1', '1', '1']}
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -070088
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -080089 KEY_MATRIX_ALT_2 = {
90 'ctrl_d': ['0', '0', '0', '1'],
91 'd': ['0', '0', '1', '1'],
92 'unused': ['0', '1', '1', '1'],
93 'rec_mode': ['1', '0', '0', '0'],
94 'ctrl_enter': ['1', '0', '0', '1'],
95 'enter': ['1', '0', '1', '1'],
96 'ctrl': ['1', '1', '0', '1'],
97 'refresh': ['1', '1', '1', '0'],
98 'ctrl_refresh': ['1', '1', '1', '1'],
99 'none': ['1', '1', '1', '1']}
100
101 KEY_MATRIX = [KEY_MATRIX_ALT_0, KEY_MATRIX_ALT_1, KEY_MATRIX_ALT_2]
J. Richard Barnette67ccb872012-04-19 16:34:56 -0700102
Fang Deng5d518f42013-08-02 14:04:32 -0700103 def __init__(self, servo_host):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700104 """Sets up the servo communication infrastructure.
105
Fang Deng5d518f42013-08-02 14:04:32 -0700106 @param servo_host: A ServoHost object representing
107 the host running servod.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700108 """
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -0700109 self._key_matrix = 0
Fang Deng5d518f42013-08-02 14:04:32 -0700110 # TODO(fdeng): crbug.com/298379
111 # We should move servo_host object out of servo object
112 # to minimize the dependencies on the rest of Autotest.
113 self._servo_host = servo_host
114 self._server = servo_host.get_servod_server_proxy()
115 board = self._server.get_board()
J. Richard Barnette75136b32013-03-26 13:38:44 -0700116 self._power_state = (
117 power_state_controller.create_controller(self, board))
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800118
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800119 # a string, showing what interface (host or dut) the USB device is
120 # connected to.
Fang Dengafb88142013-05-30 17:44:31 -0700121 self._usb_state = None
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800122 self.set('dut_hub_pwren', 'on')
123 self.set('usb_mux_oe1', 'on')
Fang Dengafb88142013-05-30 17:44:31 -0700124 self.switch_usbkey('off')
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800125
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800126
Tom Wai-Hong Tam22ee0e52013-04-03 13:36:39 +0800127 def get_power_state_controller(self):
J. Richard Barnette75136b32013-03-26 13:38:44 -0700128 """Return the power state controller for this Servo.
129
130 The power state controller provides board-independent
131 interfaces for reset, power-on, power-off operations.
132
133 """
134 return self._power_state
135
Fang Deng5d518f42013-08-02 14:04:32 -0700136
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700137 def initialize_dut(self, cold_reset=False):
138 """Initializes a dut for testing purposes.
139
140 This sets various servo signals back to default values
141 appropriate for the target board. By default, if the DUT
142 is already on, it stays on. If the DUT is powered off
143 before initialization, its state afterward is unspecified.
144
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700145 Rationale: Basic initialization of servo sets the lid open,
146 when there is a lid. This operation won't affect powered on
147 units; however, setting the lid open may power on a unit
J. Richard Barnette75136b32013-03-26 13:38:44 -0700148 that's off, depending on the board type and previous state
149 of the device.
150
151 If `cold_reset` is a true value, cold reset will be applied
152 to the DUT. Cold reset will force the DUT to power off;
153 however, the final state of the DUT depends on the board type.
J. Richard Barnetteb6133972012-07-19 17:13:55 -0700154
155 @param cold_reset If True, cold reset the device after
156 initialization.
157 """
158 self._server.hwinit()
159 if cold_reset:
J. Richard Barnette75136b32013-03-26 13:38:44 -0700160 self._power_state.cold_reset()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700161
162
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800163 def is_localhost(self):
164 """Is the servod hosted locally?
165
166 Returns:
167 True if local hosted; otherwise, False.
168 """
Fang Deng5d518f42013-08-02 14:04:32 -0700169 return self._servo_host.is_localhost()
Tom Wai-Hong Tam1c86c7a2012-10-22 10:08:24 +0800170
171
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700172 def power_long_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700173 """Simulate a long power button press."""
J. Richard Barnettef12bff22012-07-18 14:41:06 -0700174 # After a long power press, the EC may ignore the next power
175 # button press (at least on Alex). To guarantee that this
176 # won't happen, we need to allow the EC one second to
177 # collect itself.
Chrome Bot9a1137d2011-07-19 14:35:00 -0700178 self.power_key(Servo.LONG_DELAY)
J. Richard Barnettef12bff22012-07-18 14:41:06 -0700179 time.sleep(1.0)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700180
181
182 def power_normal_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700183 """Simulate a normal power button press."""
184 self.power_key()
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700185
186
187 def power_short_press(self):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700188 """Simulate a short power button press."""
189 self.power_key(Servo.SHORT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700190
191
Chrome Bot9a1137d2011-07-19 14:35:00 -0700192 def power_key(self, secs=NORMAL_TRANSITION_DELAY):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700193 """Simulate a power button press.
194
195 Args:
196 secs: Time in seconds to simulate the keypress.
197 """
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800198 self.set_get_all(['pwr_button:press',
199 'sleep:%.4f' % secs,
200 'pwr_button:release'])
Todd Broch31c82502011-08-29 08:14:39 -0700201 # TODO(tbroch) Different systems have different release times on the
202 # power button that this loop addresses. Longer term we may want to
203 # make this delay platform specific.
204 retry = 1
205 while True:
206 value = self.get('pwr_button')
207 if value == 'release' or retry > Servo.RELEASE_RETRY_MAX:
208 break
Todd Broch9753bd42012-03-21 10:15:08 -0700209 logging.info('Waiting for pwr_button to release, retry %d.', retry)
Todd Broch31c82502011-08-29 08:14:39 -0700210 retry += 1
211 time.sleep(Servo.SHORT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700212
213
214 def lid_open(self):
215 """Simulate opening the lid."""
Craig Harrison48997262011-06-27 14:31:10 -0700216 self.set_nocheck('lid_open', 'yes')
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700217
218
219 def lid_close(self):
Craig Harrison48997262011-06-27 14:31:10 -0700220 """Simulate closing the lid.
221
222 Waits 6 seconds to ensure the device is fully asleep before returning.
223 """
224 self.set_nocheck('lid_open', 'no')
Chrome Bot9a1137d2011-07-19 14:35:00 -0700225 time.sleep(Servo.SLEEP_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700226
227
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800228 def _press_keys(self, key):
229 """Simulate button presses.
230
231 Note, key presses will remain on indefinitely. See
232 _press_and_release_keys for release procedure.
233 """
Vic Yangcad9acb2013-05-21 14:02:05 +0800234 (m1_a1_n, m1_a0_n, m2_a1_n, m2_a0_n) = (
235 self.KEY_MATRIX[self._key_matrix]['none'])
Gediminas Ramanauskas9da80f22012-11-14 12:59:43 -0800236 (m1_a1, m1_a0, m2_a1, m2_a0) = self.KEY_MATRIX[self._key_matrix][key]
Vic Yangcad9acb2013-05-21 14:02:05 +0800237 self.set_get_all(['kbd_m2_a0:%s' % m2_a0_n,
238 'kbd_m2_a1:%s' % m2_a1_n,
239 'kbd_m1_a0:%s' % m1_a0_n,
240 'kbd_m1_a1:%s' % m1_a1_n,
241 'kbd_en:on',
242 'kbd_m2_a0:%s' % m2_a0,
243 'kbd_m2_a1:%s' % m2_a1,
244 'kbd_m1_a0:%s' % m1_a0,
245 'kbd_m1_a1:%s' % m1_a1])
Gediminas Ramanauskas9da80f22012-11-14 12:59:43 -0800246
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800247
Vic Yang701a59e2013-05-02 05:10:22 +0800248 def _press_and_release_keys(self, key, press_secs=None):
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800249 """Simulate button presses and release."""
Vic Yang701a59e2013-05-02 05:10:22 +0800250 if press_secs is None:
251 press_secs = self.SERVO_KEY_PRESS_DELAY
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800252 self._press_keys(key)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700253 time.sleep(press_secs)
254 self.set_nocheck('kbd_en', 'off')
255
256
Gediminas Ramanauskas3297d4f2012-09-10 15:30:10 -0700257 def set_key_matrix(self, matrix=0):
258 """Set keyboard mapping"""
259 self._key_matrix = matrix
260
261
Vic Yang701a59e2013-05-02 05:10:22 +0800262 def ctrl_d(self, press_secs=None):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700263 """Simulate Ctrl-d simultaneous button presses."""
Vic Yang701a59e2013-05-02 05:10:22 +0800264 self._press_and_release_keys('ctrl_d', press_secs)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700265
266
Vic Yang701a59e2013-05-02 05:10:22 +0800267 def ctrl_enter(self, press_secs=None):
Todd Broch9753bd42012-03-21 10:15:08 -0700268 """Simulate Ctrl-enter simultaneous button presses."""
Vic Yang701a59e2013-05-02 05:10:22 +0800269 self._press_and_release_keys('ctrl_enter', press_secs)
Todd Broch9753bd42012-03-21 10:15:08 -0700270
271
Vic Yang701a59e2013-05-02 05:10:22 +0800272 def d_key(self, press_secs=None):
Todd Broch9dfc3a82011-11-01 08:09:28 -0700273 """Simulate Enter key button press."""
Vic Yang701a59e2013-05-02 05:10:22 +0800274 self._press_and_release_keys('d', press_secs)
Todd Broch9dfc3a82011-11-01 08:09:28 -0700275
276
Vic Yang701a59e2013-05-02 05:10:22 +0800277 def ctrl_key(self, press_secs=None):
Todd Broch9dfc3a82011-11-01 08:09:28 -0700278 """Simulate Enter key button press."""
Vic Yang701a59e2013-05-02 05:10:22 +0800279 self._press_and_release_keys('ctrl', press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700280
281
Vic Yang701a59e2013-05-02 05:10:22 +0800282 def enter_key(self, press_secs=None):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700283 """Simulate Enter key button press."""
Vic Yang701a59e2013-05-02 05:10:22 +0800284 self._press_and_release_keys('enter', press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700285
286
Vic Yang701a59e2013-05-02 05:10:22 +0800287 def refresh_key(self, press_secs=None):
Chrome Bot9a1137d2011-07-19 14:35:00 -0700288 """Simulate Refresh key (F3) button press."""
Vic Yang701a59e2013-05-02 05:10:22 +0800289 self._press_and_release_keys('refresh', press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700290
291
Vic Yang701a59e2013-05-02 05:10:22 +0800292 def ctrl_refresh_key(self, press_secs=None):
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800293 """Simulate Ctrl and Refresh (F3) simultaneous press.
294
295 This key combination is an alternative of Space key.
296 """
Vic Yang701a59e2013-05-02 05:10:22 +0800297 self._press_and_release_keys('ctrl_refresh', press_secs)
Tom Wai-Hong Tam9e61e662012-08-01 15:10:07 +0800298
299
Vic Yang701a59e2013-05-02 05:10:22 +0800300 def imaginary_key(self, press_secs=None):
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700301 """Simulate imaginary key button press.
302
303 Maps to a key that doesn't physically exist.
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700304 """
Vic Yang701a59e2013-05-02 05:10:22 +0800305 self._press_and_release_keys('unused', press_secs)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700306
307
Craig Harrison6b36b122011-06-28 17:58:43 -0700308 def enable_recovery_mode(self):
309 """Enable recovery mode on device."""
310 self.set('rec_mode', 'on')
311
312
Gediminas Ramanauskasba352ad2012-11-09 09:43:32 -0800313 def custom_recovery_mode(self):
314 """Custom key combination to enter recovery mode."""
315 self._press_keys('rec_mode')
316 self.power_normal_press()
317 time.sleep(self.SERVO_KEY_PRESS_DELAY)
318 self.set_nocheck('kbd_en', 'off')
319
320
Craig Harrison6b36b122011-06-28 17:58:43 -0700321 def disable_recovery_mode(self):
322 """Disable recovery mode on device."""
323 self.set('rec_mode', 'off')
324
325
326 def enable_development_mode(self):
327 """Enable development mode on device."""
328 self.set('dev_mode', 'on')
329
330
331 def disable_development_mode(self):
332 """Disable development mode on device."""
333 self.set('dev_mode', 'off')
334
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700335 def boot_devmode(self):
336 """Boot a dev-mode device that is powered off."""
Tom Wai-Hong Tam23869102012-01-17 15:41:30 +0800337 self.power_short_press()
Craig Harrison48997262011-06-27 14:31:10 -0700338 self.pass_devmode()
339
340
341 def pass_devmode(self):
342 """Pass through boot screens in dev-mode."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700343 time.sleep(Servo.BOOT_DELAY)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700344 self.ctrl_d()
Chrome Bot9a1137d2011-07-19 14:35:00 -0700345 time.sleep(Servo.BOOT_DELAY)
Craig Harrison48997262011-06-27 14:31:10 -0700346
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700347
Todd Brochefe72cb2012-07-11 19:58:53 -0700348 def _get_xmlrpclib_exception(self, xmlexc):
349 """Get meaningful exception string from xmlrpc.
350
351 Args:
352 xmlexc: xmlrpclib.Fault object
353
354 xmlrpclib.Fault.faultString has the following format:
355
356 <type 'exception type'>:'actual error message'
357
358 Parse and return the real exception from the servod side instead of the
359 less meaningful one like,
360 <Fault 1: "<type 'exceptions.AttributeError'>:'tca6416' object has no
361 attribute 'hw_driver'">
362
363 Returns:
364 string of underlying exception raised in servod.
365 """
366 return re.sub('^.*>:', '', xmlexc.faultString)
367
368
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700369 def get(self, gpio_name):
370 """Get the value of a gpio from Servod."""
371 assert gpio_name
Todd Brochefe72cb2012-07-11 19:58:53 -0700372 try:
Simran Basi1cbc5f22013-07-17 14:23:58 -0700373 return self._server.get(gpio_name)
Todd Brochefe72cb2012-07-11 19:58:53 -0700374 except xmlrpclib.Fault as e:
375 err_msg = "Getting '%s' :: %s" % \
376 (gpio_name, self._get_xmlrpclib_exception(e))
377 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700378
379
380 def set(self, gpio_name, gpio_value):
381 """Set and check the value of a gpio using Servod."""
Chrome Bot9a1137d2011-07-19 14:35:00 -0700382 self.set_nocheck(gpio_name, gpio_value)
Todd Brochcf7c6652012-02-24 13:03:59 -0800383 retry_count = Servo.GET_RETRY_MAX
384 while gpio_value != self.get(gpio_name) and retry_count:
385 logging.warn("%s != %s, retry %d", gpio_name, gpio_value,
386 retry_count)
387 retry_count -= 1
388 time.sleep(Servo.SHORT_DELAY)
389 if not retry_count:
390 assert gpio_value == self.get(gpio_name), \
391 'Servo failed to set %s to %s' % (gpio_name, gpio_value)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700392
393
394 def set_nocheck(self, gpio_name, gpio_value):
395 """Set the value of a gpio using Servod."""
396 assert gpio_name and gpio_value
Todd Broch9753bd42012-03-21 10:15:08 -0700397 logging.info('Setting %s to %s', gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700398 try:
Simran Basi1cbc5f22013-07-17 14:23:58 -0700399 self._server.set(gpio_name, gpio_value)
Todd Brochefe72cb2012-07-11 19:58:53 -0700400 except xmlrpclib.Fault as e:
401 err_msg = "Setting '%s' to '%s' :: %s" % \
402 (gpio_name, gpio_value, self._get_xmlrpclib_exception(e))
403 raise error.TestFail(err_msg)
Craig Harrison2b6c6fc2011-06-23 10:34:02 -0700404
405
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800406 def set_get_all(self, controls):
407 """Set &| get one or more control values.
408
409 @param controls: list of strings, controls to set &| get.
410
411 @raise: error.TestError in case error occurs setting/getting values.
412 """
413 rv = []
414 try:
Vic Yangcad9acb2013-05-21 14:02:05 +0800415 logging.info('Set/get all: %s', str(controls))
Tom Wai-Hong Tam92569bb2013-03-26 14:25:10 +0800416 rv = self._server.set_get_all(controls)
417 except xmlrpclib.Fault as e:
418 # TODO(waihong): Remove the following backward compatibility when
419 # the new versions of hdctools are deployed.
420 if 'not supported' in str(e):
421 logging.warning('The servod is too old that set_get_all '
422 'not supported. Use set and get instead.')
423 for control in controls:
424 if ':' in control:
425 (name, value) = control.split(':')
426 if name == 'sleep':
427 time.sleep(float(value))
428 else:
429 self.set_nocheck(name, value)
430 rv.append(True)
431 else:
432 rv.append(self.get(name))
433 else:
434 err_msg = "Problem with '%s' :: %s" % \
435 (controls, self._get_xmlrpclib_exception(e))
436 raise error.TestFail(err_msg)
437 return rv
438
439
Jon Salzc88e5b62011-11-30 14:38:54 +0800440 # TODO(waihong) It may fail if multiple servo's are connected to the same
441 # host. Should look for a better way, like the USB serial name, to identify
442 # the USB device.
Simran Basi741b5d42012-05-18 11:27:15 -0700443 # TODO(sbasi) Remove this code from autoserv once firmware tests have been
444 # updated.
Jon Salzc88e5b62011-11-30 14:38:54 +0800445 def probe_host_usb_dev(self):
446 """Probe the USB disk device plugged-in the servo from the host side.
447
448 It tries to switch the USB mux to make the host unable to see the
449 USB disk and compares the result difference.
450
Jon Salzc88e5b62011-11-30 14:38:54 +0800451 Returns:
452 A string of USB disk path, like '/dev/sdb', or None if not existed.
453 """
454 cmd = 'ls /dev/sd[a-z]'
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800455 original_value = self.get_usbkey_direction()
Jon Salzc88e5b62011-11-30 14:38:54 +0800456
457 # Make the host unable to see the USB disk.
Fang Dengafb88142013-05-30 17:44:31 -0700458 self.switch_usbkey('off')
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800459 no_usb_set = set(self.system_output(cmd, ignore_status=True).split())
Jon Salzc88e5b62011-11-30 14:38:54 +0800460
461 # Make the host able to see the USB disk.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800462 self.switch_usbkey('host')
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800463 has_usb_set = set(self.system_output(cmd, ignore_status=True).split())
Jon Salzc88e5b62011-11-30 14:38:54 +0800464
465 # Back to its original value.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800466 if original_value != self.get_usbkey_direction():
467 self.switch_usbkey(original_value)
Jon Salzc88e5b62011-11-30 14:38:54 +0800468
469 diff_set = has_usb_set - no_usb_set
470 if len(diff_set) == 1:
471 return diff_set.pop()
472 else:
473 return None
474
475
J. Richard Barnette69929a52013-03-15 13:22:11 -0700476 def recovery_supported(self):
477 """Return whether servo-based recovery should work.
478
479 Use of `image_to_servo_usb()` and `install_recovery_image()`
480 relies on DUT-board specific behaviors, and is not supported
481 for all types of board. Return whether these two operations
482 are expected to succeed for the current DUT.
483
484 @return `True` iff the recovery related methods are supported
485 for this servo and DUT.
486
487 """
J. Richard Barnette75136b32013-03-26 13:38:44 -0700488 return self._power_state.recovery_supported()
J. Richard Barnette69929a52013-03-15 13:22:11 -0700489
490
Mike Truty49153d82012-08-21 22:27:30 -0500491 def image_to_servo_usb(self, image_path=None,
492 make_image_noninteractive=False):
493 """Install an image to the USB key plugged into the servo.
494
495 This method may copy any image to the servo USB key including a
496 recovery image or a test image. These images are frequently used
497 for test purposes such as restoring a corrupted image or conducting
498 an upgrade of ec/fw/kernel as part of a test of a specific image part.
499
500 Args:
501 image_path: Path on the host to the recovery image.
502 make_image_noninteractive: Make the recovery image noninteractive,
503 therefore the DUT will reboot
504 automatically after installation.
505 """
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700506 # We're about to start plugging/unplugging the USB key. We
507 # don't know the state of the DUT, or what it might choose
508 # to do to the device after hotplug. To avoid surprises,
509 # force the DUT to be off.
510 self._server.hwinit()
511 self._power_state.power_off()
Mike Truty49153d82012-08-21 22:27:30 -0500512
513 # Set up Servo's usb mux.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800514 self.switch_usbkey('host')
Mike Truty49153d82012-08-21 22:27:30 -0500515 if image_path:
Tom Wai-Hong Tam42f136d2012-10-26 11:11:23 +0800516 logging.info('Searching for usb device and copying image to it. '
517 'Please wait a few minutes...')
Mike Truty49153d82012-08-21 22:27:30 -0500518 if not self._server.download_image_to_usb(image_path):
519 logging.error('Failed to transfer requested image to USB. '
520 'Please take a look at Servo Logs.')
521 raise error.AutotestError('Download image to usb failed.')
522 if make_image_noninteractive:
523 logging.info('Making image noninteractive')
524 if not self._server.make_image_noninteractive():
525 logging.error('Failed to make image noninteractive. '
526 'Please take a look at Servo Logs.')
527
528
Simran Basi741b5d42012-05-18 11:27:15 -0700529 def install_recovery_image(self, image_path=None,
J. Richard Barnetteb6de7e32013-02-14 13:28:04 -0800530 make_image_noninteractive=False):
Jon Salzc88e5b62011-11-30 14:38:54 +0800531 """Install the recovery image specied by the path onto the DUT.
532
533 This method uses google recovery mode to install a recovery image
534 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 +0800535 board specified by the usb_dev. If no image path is specified
Jon Salzc88e5b62011-11-30 14:38:54 +0800536 we use the recovery image already on the usb image.
537
538 Args:
539 image_path: Path on the host to the recovery image.
Simran Basi741b5d42012-05-18 11:27:15 -0700540 make_image_noninteractive: Make the recovery image noninteractive,
541 therefore the DUT will reboot
542 automatically after installation.
Jon Salzc88e5b62011-11-30 14:38:54 +0800543 """
Mike Truty49153d82012-08-21 22:27:30 -0500544 self.image_to_servo_usb(image_path, make_image_noninteractive)
J. Richard Barnette75136b32013-03-26 13:38:44 -0700545 self._power_state.power_on(rec_mode=power_state_controller.REC_ON)
J. Richard Barnette41320ee2013-03-11 13:00:13 -0700546 self.switch_usbkey('dut')
Jon Salzc88e5b62011-11-30 14:38:54 +0800547
548
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800549 def _scp_image(self, image_path):
550 """Copy image to the servo host.
551
552 When programming a firmware image on the DUT, the image must be
553 located on the host to which the servo device is connected. Sometimes
554 servo is controlled by a remote host, in this case the image needs to
555 be transferred to the remote host.
556
557 @param image_path: a string, name of the firmware image file to be
558 transferred.
559 @return: a string, full path name of the copied file on the remote.
560 """
561
562 dest_path = os.path.join('/tmp', os.path.basename(image_path))
Fang Deng5d518f42013-08-02 14:04:32 -0700563 self._servo_host.send_file(image_path, dest_path)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800564 return dest_path
565
566
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800567 def system(self, command, timeout=None):
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800568 """Execute the passed in command on the servod host."""
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800569 logging.info('Will execute on servo host: %s', command)
Fang Deng5d518f42013-08-02 14:04:32 -0700570 self._servo_host.run(command, timeout=timeout)
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800571
572
573 def system_output(self, command, timeout=None,
574 ignore_status=False, args=()):
575 """Execute the passed in command on the servod host, return stdout.
576
577 @param command, a string, the command to execute
578 @param timeout, an int, max number of seconds to wait til command
579 execution completes
580 @ignore_status, a Boolean, if true - ignore command's nonzero exit
581 status, otherwise an exception will be thrown
582 @param args, a tuple of strings, each becoming a separate command line
583 parameter for the command
584 @return: command's stdout as a string.
585 """
Vadim Bendebury89ec24e2012-12-17 12:54:18 -0800586 logging.info('Will execute and collect output on servo host: %s %s',
587 command, ' '.join("'%s'" % x for x in args))
Fang Deng5d518f42013-08-02 14:04:32 -0700588 return self._servo_host.run(command, timeout=timeout,
589 ignore_status=ignore_status,
590 args=args).stdout.strip()
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800591
592
593 def program_ec(self, board, image):
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800594 """Program EC on a given board using given image.
595
596 @param board: a string, type of the DUT board
597 @param image: a string, file name of the EC image to program on the
598 DUT
599 """
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800600 if not self.is_localhost():
601 image = self._scp_image(image)
602 programmer.program_ec(board, self, image)
603
604
605 def program_bootprom(self, board, image):
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800606 """Program bootprom on a given board using given image.
607
608 In case servo is controlled by a remote host, the image needs to be
609 transferred to the host.
610
611 If the device tree subdirectory is present along with the image, the
612 subdirectory is also copied to the remote host.
613
614 @param board: a string, type of the DUT board
615 @param image: a string, file name of the firmware image to program on
616 the DUT. The device tree subdirectory, if present, is on
617 the same level with the image file.
618 """
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800619 if not self.is_localhost():
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800620 dts_path = os.path.join(os.path.dirname(image), 'dts')
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800621 image = self._scp_image(image)
Vadim Bendebury1a7ec632013-02-17 16:22:09 -0800622 if os.path.isdir(dts_path):
623 self._scp_image(dts_path)
Vadim Bendeburyb80ba592012-12-07 15:02:34 -0800624 programmer.program_bootprom(board, self, image)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800625
Fang Dengafb88142013-05-30 17:44:31 -0700626
627 def _switch_usbkey_power(self, power_state, detection_delay=False):
628 """Switch usbkey power.
629
630 This function switches usbkey power by setting the value of
631 'prtctl4_pwren'. If the power is already in the
632 requested state, this function simply returns.
633
634 @param power_state: A string, 'on' or 'off'.
635 @param detection_delay: A boolean value, if True, sleep
636 for |USB_DETECTION_DELAY| after switching
637 the power on.
638 """
639 self.set('prtctl4_pwren', power_state)
640 if power_state == 'off':
641 time.sleep(self.USB_POWEROFF_DELAY)
642 elif detection_delay:
643 time.sleep(self.USB_DETECTION_DELAY)
644
645
646 def switch_usbkey(self, usb_state):
647 """Connect USB flash stick to either host or DUT, or turn USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800648
649 This function switches the servo multiplexer to provide electrical
Fang Dengafb88142013-05-30 17:44:31 -0700650 connection between the USB port J3 and either host or DUT side. It
651 can also be used to turn the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800652
Fang Dengafb88142013-05-30 17:44:31 -0700653 Switching to 'dut' or 'host' is accompanied by powercycling
654 of the USB stick, because it sometimes gets wedged if the mux
655 is switched while the stick power is on.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800656
Fang Dengafb88142013-05-30 17:44:31 -0700657 @param usb_state: A string, one of 'dut', 'host', or 'off'.
658 'dut' and 'host' indicate which side the
659 USB flash device is required to be connected to.
660 'off' indicates turning the USB port off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800661
Fang Dengafb88142013-05-30 17:44:31 -0700662 @raise: error.TestError in case the parameter is not 'dut'
663 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800664 """
Fang Dengafb88142013-05-30 17:44:31 -0700665 if self.get_usbkey_direction() == usb_state:
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800666 return
667
Fang Dengafb88142013-05-30 17:44:31 -0700668 if usb_state == 'off':
669 self._switch_usbkey_power('off')
670 self._usb_state = usb_state
671 return
672 elif usb_state == 'host':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800673 mux_direction = 'servo_sees_usbkey'
Fang Dengafb88142013-05-30 17:44:31 -0700674 elif usb_state == 'dut':
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800675 mux_direction = 'dut_sees_usbkey'
676 else:
Fang Dengafb88142013-05-30 17:44:31 -0700677 raise error.TestError('Unknown USB state request: %s' % usb_state)
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800678
Fang Dengafb88142013-05-30 17:44:31 -0700679 self._switch_usbkey_power('off')
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800680 self.set('usb_mux_sel1', mux_direction)
681 time.sleep(self.USB_POWEROFF_DELAY)
Fang Dengafb88142013-05-30 17:44:31 -0700682 self._switch_usbkey_power('on', usb_state == 'host')
683 self._usb_state = usb_state
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800684
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800685
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800686 def get_usbkey_direction(self):
Fang Dengafb88142013-05-30 17:44:31 -0700687 """Get which side USB is connected to or 'off' if usb power is off.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800688
Fang Dengafb88142013-05-30 17:44:31 -0700689 @return: A string, one of 'dut', 'host', or 'off'.
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800690 """
Fang Dengafb88142013-05-30 17:44:31 -0700691 if not self._usb_state:
692 if self.get('prtctl4_pwren') == 'off':
693 self._usb_state = 'off'
694 elif self.get('usb_mux_sel1').startswith('dut'):
695 self._usb_state = 'dut'
Vadim Bendeburye7bd9362012-12-19 14:35:20 -0800696 else:
Fang Dengafb88142013-05-30 17:44:31 -0700697 self._usb_state = 'host'
698 return self._usb_state