blob: dac3b2cffefa9e1325bf31b1a69fe89a68232245 [file] [log] [blame]
Simran Basia9f41032012-05-11 14:21:58 -07001# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
Todd Broche505b8d2011-03-21 18:19:54 -07002# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4"""Servo Server."""
Kevin Chengc49494e2016-07-25 12:13:38 -07005import datetime
6import fcntl
Simran Basia9f41032012-05-11 14:21:58 -07007import fnmatch
Todd Broche505b8d2011-03-21 18:19:54 -07008import imp
9import logging
Simran Basia9f41032012-05-11 14:21:58 -070010import os
Kevin Chengc49494e2016-07-25 12:13:38 -070011import random
Simran Basia9f41032012-05-11 14:21:58 -070012import shutil
Todd Broche505b8d2011-03-21 18:19:54 -070013import SimpleXMLRPCServer
Simran Basia9f41032012-05-11 14:21:58 -070014import subprocess
15import tempfile
Todd Broch7a91c252012-02-03 12:37:45 -080016import time
Simran Basia9f41032012-05-11 14:21:58 -070017import urllib
Todd Broche505b8d2011-03-21 18:19:54 -070018
19# TODO(tbroch) deprecate use of relative imports
Vic Yangbe6cf262012-09-10 10:40:56 +080020from drv.hw_driver import HwDriverError
Aaron.Chuang88eff332014-07-31 08:32:00 +080021import bbadc
Simran Basia9ad25e2013-04-23 11:57:00 -070022import bbi2c
Simran Basi5492bde2013-05-16 17:08:47 -070023import bbgpio
Simran Basi949309b2013-05-31 15:12:15 -070024import bbuart
Aseda Aboagyea4922212015-11-20 15:19:08 -080025import ec3po_interface
Todd Broche505b8d2011-03-21 18:19:54 -070026import ftdigpio
27import ftdii2c
Todd Brochdbb09982011-10-02 07:14:26 -070028import ftdi_common
Todd Broch47c43f42011-05-26 15:11:31 -070029import ftdiuart
Rong Changc6c8c022014-08-11 14:07:11 +080030import i2cbus
Yusuf Mohsinally29e30d22014-01-14 15:29:17 -080031import keyboard_handlers
Simran Basie750a342013-03-12 13:45:26 -070032import servo_interfaces
Kevin Cheng16304d12016-07-08 11:56:55 -070033import servo_postinit
Nick Sanders97bc4462016-01-04 15:37:31 -080034import stm32gpio
35import stm32i2c
36import stm32uart
Todd Broche505b8d2011-03-21 18:19:54 -070037
Aseda Aboagyea4922212015-11-20 15:19:08 -080038
Todd Broche505b8d2011-03-21 18:19:54 -070039MAX_I2C_CLOCK_HZ = 100000
40
Todd Brochdbb09982011-10-02 07:14:26 -070041
Todd Broche505b8d2011-03-21 18:19:54 -070042class ServodError(Exception):
43 """Exception class for servod."""
44
45class Servod(object):
46 """Main class for Servo debug/controller Daemon."""
Simran Basia9f41032012-05-11 14:21:58 -070047 _USB_DETECTION_DELAY = 10
Fang Deng90377712013-06-03 15:51:48 -070048 _USB_POWEROFF_DELAY = 2
Simran Basia9f41032012-05-11 14:21:58 -070049 _HTTP_PREFIX = "http://"
Fang Deng90377712013-06-03 15:51:48 -070050 _USB_J3 = "usb_mux_sel1"
51 _USB_J3_TO_SERVO = "servo_sees_usbkey"
52 _USB_J3_TO_DUT = "dut_sees_usbkey"
53 _USB_J3_PWR = "prtctl4_pwren"
54 _USB_J3_PWR_ON = "on"
55 _USB_J3_PWR_OFF = "off"
Kevin Chengc49494e2016-07-25 12:13:38 -070056 _USB_LOCK_FILE = "/var/lib/servod/lock_file"
57 # It takes about 16-17 seconds for the entire probe usb device method,
58 # let's wait double plus some buffer.
59 _MAX_USB_LOCK_WAIT = 40
Simran Basia9f41032012-05-11 14:21:58 -070060
Kevin Cheng4b4f0022016-09-09 02:37:07 -070061 # This is the key to get the main serial used in the _serialnames dict.
62 MAIN_SERIAL = "main"
63
Kevin Chengdc3befd2016-07-15 12:34:00 -070064 def init_servo_interfaces(self, vendor, product, serialname,
65 interfaces):
66 """Init the servo interfaces with the given interfaces.
67
68 We don't use the self._{vendor,product,serialname} attributes because we
69 want to allow other callers to initialize other interfaces that may not
70 be associated with the initialized attributes (e.g. a servo v4 servod object
71 that wants to also initialize a servo micro interface).
72
73 Args:
74 vendor: USB vendor id of FTDI device.
75 product: USB product id of FTDI device.
76 serialname: String of device serialname/number as defined in FTDI
77 eeprom.
78 interfaces: List of strings of interface types the server will
79 instantiate.
80
81 Raises:
82 ServodError if unable to locate init method for particular interface.
83 """
84 # Extend the interface list if we need to.
85 interfaces_len = len(interfaces)
86 interface_list_len = len(self._interface_list)
87 if interfaces_len > interface_list_len:
88 self._interface_list += [None] * (interfaces_len - interface_list_len)
89
90 shifted = 0
91 for i, interface in enumerate(interfaces):
92 is_ftdi_interface = False
93 if type(interface) is dict:
94 name = interface['name']
95 # Store interface index for those that care about it.
96 interface['index'] = i
97 elif type(interface) is str and interface != 'dummy':
98 name = interface
99 # It's a FTDI related interface.
100 interface = (i % ftdi_common.MAX_FTDI_INTERFACES_PER_DEVICE) + 1
101 is_ftdi_interface = True
102 elif type(interface) is str and interface == 'dummy':
103 # 'dummy' reserves the interface for future use. Typically the
104 # interface will be managed by external third-party tools like
105 # openOCD for JTAG or flashrom for SPI. In the case of servo V4,
106 # it serves as a placeholder for servo micro interfaces.
107 continue
108 else:
109 raise ServodError("Illegal interface type %s" % type(interface))
110
111 # servos with multiple FTDI are guaranteed to have contiguous USB PIDs
112 if is_ftdi_interface and i and \
113 ((i % ftdi_common.MAX_FTDI_INTERFACES_PER_DEVICE) == 0):
114 product += 1
115 self._logger.info("Changing to next FTDI part @ pid = 0x%04x",
116 product)
117
118 self._logger.info("Initializing interface %d to %s", i + 1, name)
119 try:
120 func = getattr(self, '_init_%s' % name)
121 except AttributeError:
122 raise ServodError("Unable to locate init for interface %s" % name)
123 result = func(vendor, product, serialname, interface)
124
125 if isinstance(result, tuple):
126 result_len = len(result)
127 shifted += result_len - 1
128 self._interface_list += [None] * result_len
129 for result_index, r in enumerate(result):
130 self._interface_list[i + result_index] = r
131 else:
132 self._interface_list[i + shifted] = result
133
J. Richard Barnettee2820552013-03-14 16:13:46 -0700134 def __init__(self, config, vendor, product, serialname=None,
Yusuf Mohsinally29e30d22014-01-14 15:29:17 -0800135 interfaces=None, board="", version=None, usbkm232=None):
Todd Broche505b8d2011-03-21 18:19:54 -0700136 """Servod constructor.
137
138 Args:
139 config: instance of SystemConfig containing all controls for
140 particular Servod invocation
141 vendor: usb vendor id of FTDI device
142 product: usb product id of FTDI device
Todd Brochad034442011-05-25 15:05:29 -0700143 serialname: string of device serialname/number as defined in FTDI eeprom.
Todd Brochdbb09982011-10-02 07:14:26 -0700144 interfaces: list of strings of interface types the server will instantiate
Simran Basia23c1392013-08-06 14:59:10 -0700145 version: String. Servo board version. Examples: servo_v1, servo_v2,
146 servo_v2_r0, servo_v3
Yusuf Mohsinally29e30d22014-01-14 15:29:17 -0800147 usbkm232: String. Optional. Path to USB-KM232 device which allow for
Kevin Chengdc3befd2016-07-15 12:34:00 -0700148 sending keyboard commands to DUTs that do not have built in
149 keyboards. Used in FAFT tests. Use 'atmega' for on board AVR MCU.
150 e.g. '/dev/ttyUSB0' or 'atmega'
Todd Brochdbb09982011-10-02 07:14:26 -0700151
152 Raises:
153 ServodError: if unable to locate init method for particular interface
Todd Broche505b8d2011-03-21 18:19:54 -0700154 """
155 self._logger = logging.getLogger("Servod")
156 self._logger.debug("")
157 self._vendor = vendor
158 self._product = product
Kevin Cheng4b4f0022016-09-09 02:37:07 -0700159 self._serialnames = {self.MAIN_SERIAL: serialname}
Todd Broche505b8d2011-03-21 18:19:54 -0700160 self._syscfg = config
Kevin Cheng9071ed92016-06-21 14:37:54 -0700161 # Hold the last image path so we can reduce downloads to the usb device.
162 self._image_path = None
Todd Broche505b8d2011-03-21 18:19:54 -0700163 # list of objects (Fi2c, Fgpio) to physical interfaces (gpio, i2c) that ftdi
164 # interfaces are mapped to
165 self._interface_list = []
166 # Dict of Dict to map control name, function name to to tuple (params, drv)
167 # Ex) _drv_dict[name]['get'] = (params, drv)
168 self._drv_dict = {}
J. Richard Barnettee2820552013-03-14 16:13:46 -0700169 self._board = board
Simran Basia23c1392013-08-06 14:59:10 -0700170 self._version = version
Yusuf Mohsinally29e30d22014-01-14 15:29:17 -0800171 self._usbkm232 = usbkm232
Kevin Chengc49494e2016-07-25 12:13:38 -0700172 # Seed the random generator with the serial to differentiate from other
173 # servod processes.
174 random.seed(serialname if serialname else time.time())
Todd Brochdbb09982011-10-02 07:14:26 -0700175 # Note, interface i is (i - 1) in list
176 if not interfaces:
Todd Brochb21d8042014-05-15 12:54:54 -0700177 try:
178 interfaces = servo_interfaces.INTERFACE_BOARDS[board][vendor][product]
179 except KeyError:
180 interfaces = servo_interfaces.INTERFACE_DEFAULTS[vendor][product]
Todd Brochdbb09982011-10-02 07:14:26 -0700181
Kevin Chengdc3befd2016-07-15 12:34:00 -0700182 self.init_servo_interfaces(vendor, product, serialname, interfaces)
Kevin Cheng16304d12016-07-08 11:56:55 -0700183 servo_postinit.post_init(self)
Danny Chan662b6022015-11-04 17:34:53 -0800184
Yusuf Mohsinally29e30d22014-01-14 15:29:17 -0800185 def _init_keyboard_handler(self, servo, board=''):
186 """Initialize the correct keyboard handler for board.
187
Kevin Chengdc3befd2016-07-15 12:34:00 -0700188 Args:
189 servo: servo object.
190 board: string, board name.
Yusuf Mohsinally29e30d22014-01-14 15:29:17 -0800191
Kevin Chengdc3befd2016-07-15 12:34:00 -0700192 Returns:
193 keyboard handler object.
Yusuf Mohsinally29e30d22014-01-14 15:29:17 -0800194 """
195 if board == 'parrot':
196 return keyboard_handlers.ParrotHandler(servo)
197 elif board == 'stout':
198 return keyboard_handlers.StoutHandler(servo)
PeggyChuang4f07d872015-08-07 12:11:38 +0800199 elif board in ('buddy', 'cranky', 'guado', 'jecht', 'mccloud', 'monroe',
Tom Wai-Hong Tamd64164c2015-04-29 07:59:45 +0800200 'ninja', 'nyan_kitty', 'panther', 'rikku', 'stumpy',
Jason Simmonse5cccd12015-08-25 16:05:25 -0700201 'sumo', 'tidus', 'tricky', 'veyron_mickey', 'veyron_rialto',
ren kuobf62ddd2016-06-24 11:55:43 +0800202 'veyron_tiger', 'zako'):
Yusuf Mohsinally29e30d22014-01-14 15:29:17 -0800203 if self._usbkm232 is None:
Tom Wai-Hong Tam4c7d6472016-03-08 06:55:50 +0800204 logging.info("No device path specified for usbkm232 handler. Use "
205 "the servo atmega chip to handle.")
206 self._usbkm232 = 'atmega'
Danny Chan662b6022015-11-04 17:34:53 -0800207 if self._usbkm232 == 'atmega':
208 # Use servo onboard keyboard emulator.
Nick Sanders78423782015-11-09 14:28:19 -0800209 self.set('atmega_rst', 'on')
Nick Sandersbc836282015-12-08 21:19:23 -0800210 self.set('at_hwb', 'off')
Nick Sanders78423782015-11-09 14:28:19 -0800211 self.set('atmega_rst', 'off')
Danny Chan662b6022015-11-04 17:34:53 -0800212 self._usbkm232 = self.get('atmega_pty')
213 self.set('atmega_baudrate', '9600')
214 self.set('atmega_bits', 'eight')
215 self.set('atmega_parity', 'none')
216 self.set('atmega_sbits', 'one')
217 self.set('usb_mux_sel4', 'on')
Nick Sandersbc836282015-12-08 21:19:23 -0800218 self.set('usb_mux_oe4', 'on')
Nick Sanders78423782015-11-09 14:28:19 -0800219 # Allow atmega bootup time.
220 time.sleep(1.0)
Danny Chan662b6022015-11-04 17:34:53 -0800221 self._logger.info('USBKM232: %s', self._usbkm232)
Yusuf Mohsinally29e30d22014-01-14 15:29:17 -0800222 return keyboard_handlers.USBkm232Handler(servo, self._usbkm232)
223 else:
Tom Wai-Hong Tamd64164c2015-04-29 07:59:45 +0800224 # The following boards don't use Chrome EC.
225 if board in ('alex', 'butterfly', 'lumpy', 'zgb'):
226 return keyboard_handlers.MatrixKeyboardHandler(servo)
227 return keyboard_handlers.ChromeECHandler(servo)
Yusuf Mohsinally29e30d22014-01-14 15:29:17 -0800228
Todd Broch3ec8df02012-11-20 10:53:03 -0800229 def __del__(self):
230 """Servod deconstructor."""
231 for interface in self._interface_list:
232 del(interface)
233
Kevin Chengdc3befd2016-07-15 12:34:00 -0700234 def _init_ftdi_dummy(self, vendor, product, serialname, interface):
Kevin Cheng042f4932016-07-19 10:46:00 -0700235 """Dummy interface for ftdi devices.
236
237 This is a dummy function specifically for ftdi devices to not initialize
238 anything but to help pad the interface list.
239
240 Returns:
241 None.
242 """
243 return None
244
Kevin Chengdc3befd2016-07-15 12:34:00 -0700245 def _init_ftdi_gpio(self, vendor, product, serialname, interface):
Todd Broche505b8d2011-03-21 18:19:54 -0700246 """Initialize gpio driver interface and open for use.
247
248 Args:
249 interface: interface number of FTDI device to use.
250
251 Returns:
252 Instance object of interface.
Todd Broch6de9dc62012-04-09 15:23:53 -0700253
254 Raises:
255 ServodError: If init fails
Todd Broche505b8d2011-03-21 18:19:54 -0700256 """
Kevin Chengdc3befd2016-07-15 12:34:00 -0700257 fobj = ftdigpio.Fgpio(vendor, product, interface, serialname)
Todd Broch6de9dc62012-04-09 15:23:53 -0700258 try:
259 fobj.open()
260 except ftdigpio.FgpioError as e:
261 raise ServodError('Opening gpio interface. %s ( %d )' % (e.msg, e.value))
262
Todd Broche505b8d2011-03-21 18:19:54 -0700263 return fobj
264
Kevin Chengdc3befd2016-07-15 12:34:00 -0700265 def _init_stm32_uart(self, vendor, product, serialname, interface):
Nick Sanders97bc4462016-01-04 15:37:31 -0800266 """Initialize stm32 uart interface and open for use
267
268 Note, the uart runs in a separate thread. Users wishing to
269 interact with it will query control for the pty's pathname and connect
270 with their favorite console program. For example:
271 cu -l /dev/pts/22
272
273 Args:
274 interface: dict of interface parameters.
275
276 Returns:
277 Instance object of interface
278
279 Raises:
280 ServodError: Raised on init failure.
281 """
282 self._logger.info("Suart: interface: %s" % interface)
Kevin Chengdc3befd2016-07-15 12:34:00 -0700283 sobj = stm32uart.Suart(vendor, product, interface['interface'],
284 serialname)
Nick Sanders97bc4462016-01-04 15:37:31 -0800285
286 try:
287 sobj.run()
288 except stm32uart.SuartError as e:
289 raise ServodError('Running uart interface. %s ( %d )' % (e.msg, e.value))
290
291 self._logger.info("%s" % sobj.get_pty())
292 return sobj
293
Kevin Chengdc3befd2016-07-15 12:34:00 -0700294 def _init_stm32_gpio(self, vendor, product, serialname, interface):
Nick Sanders97bc4462016-01-04 15:37:31 -0800295 """Initialize stm32 gpio interface.
296 Args:
297 interface: interface number of stm32 device to use.
298
299 Returns:
300 Instance object of interface
301
302 Raises:
303 SgpioError: Raised on init failure.
304 """
Kevin Cheng71a046f2016-06-13 16:37:58 -0700305 interface_number = interface
306 # Interface could be a dict.
307 if type(interface) is dict:
308 interface_number = interface['interface']
309 self._logger.info("Sgpio: interface: %s" % interface_number)
Kevin Chengdc3befd2016-07-15 12:34:00 -0700310 return stm32gpio.Sgpio(vendor, product, interface_number, serialname)
Nick Sanders97bc4462016-01-04 15:37:31 -0800311
Kevin Chengdc3befd2016-07-15 12:34:00 -0700312 def _init_stm32_i2c(self, vendor, product, serialname, interface):
Nick Sanders97bc4462016-01-04 15:37:31 -0800313 """Initialize stm32 USB to I2C bridge interface and open for use
314
315 Args:
316 interface: USB interface number of stm32 device to use
317
318 Returns:
319 Instance object of interface.
320
321 Raises:
322 Si2cError: Raised on init failure.
323 """
324 self._logger.info("Si2cBus: interface: %s" % interface)
Nick Sandersa3649712016-03-01 16:53:52 -0800325 port = interface.get('port', 0)
Kevin Chengdc3befd2016-07-15 12:34:00 -0700326 return stm32i2c.Si2cBus(vendor, product, interface['interface'],
327 port=port, serialname=serialname)
Nick Sanders97bc4462016-01-04 15:37:31 -0800328
Kevin Chengdc3befd2016-07-15 12:34:00 -0700329 def _init_bb_adc(self, vendor, product, serialname, interface):
Aaron.Chuang88eff332014-07-31 08:32:00 +0800330 """Initalize beaglebone ADC interface."""
331 return bbadc.BBadc()
332
Kevin Chengdc3befd2016-07-15 12:34:00 -0700333 def _init_bb_gpio(self, vendor, product, serialname, interface):
Simran Basie750a342013-03-12 13:45:26 -0700334 """Initalize beaglebone gpio interface."""
Simran Basi5492bde2013-05-16 17:08:47 -0700335 return bbgpio.BBgpio()
Simran Basie750a342013-03-12 13:45:26 -0700336
Kevin Chengdc3befd2016-07-15 12:34:00 -0700337 def _init_ftdi_i2c(self, vendor, product, serialname, interface):
Todd Broche505b8d2011-03-21 18:19:54 -0700338 """Initialize i2c interface and open for use.
339
340 Args:
341 interface: interface number of FTDI device to use
342
343 Returns:
344 Instance object of interface
Todd Broch6de9dc62012-04-09 15:23:53 -0700345
346 Raises:
347 ServodError: If init fails
Todd Broche505b8d2011-03-21 18:19:54 -0700348 """
Kevin Chengdc3befd2016-07-15 12:34:00 -0700349 fobj = ftdii2c.Fi2c(vendor, product, interface, serialname)
Todd Broch6de9dc62012-04-09 15:23:53 -0700350 try:
351 fobj.open()
352 except ftdii2c.Fi2cError as e:
353 raise ServodError('Opening i2c interface. %s ( %d )' % (e.msg, e.value))
354
Todd Broche505b8d2011-03-21 18:19:54 -0700355 # Set the frequency of operation of the i2c bus.
356 # TODO(tbroch) make configureable
357 fobj.setclock(MAX_I2C_CLOCK_HZ)
Todd Broch6de9dc62012-04-09 15:23:53 -0700358
Todd Broche505b8d2011-03-21 18:19:54 -0700359 return fobj
360
Simran Basie750a342013-03-12 13:45:26 -0700361 # TODO (sbasi) crbug.com/187489 - Implement bb_i2c.
362 def _init_bb_i2c(self, interface):
363 """Initalize beaglebone i2c interface."""
Simran Basia9ad25e2013-04-23 11:57:00 -0700364 return bbi2c.BBi2c(interface)
Simran Basie750a342013-03-12 13:45:26 -0700365
Kevin Chengdc3befd2016-07-15 12:34:00 -0700366 def _init_dev_i2c(self, vendor, product, serialname, interface):
Rong Changc6c8c022014-08-11 14:07:11 +0800367 """Initalize Linux i2c-dev interface."""
368 return i2cbus.I2CBus('/dev/i2c-%d' % interface['bus_num'])
369
Kevin Chengdc3befd2016-07-15 12:34:00 -0700370 def _init_ftdi_uart(self, vendor, product, serialname, interface):
Simran Basie750a342013-03-12 13:45:26 -0700371 """Initialize ftdi uart inteface and open for use
Todd Broch47c43f42011-05-26 15:11:31 -0700372
373 Note, the uart runs in a separate thread (pthreads). Users wishing to
374 interact with it will query control for the pty's pathname and connect
375 with there favorite console program. For example:
376 cu -l /dev/pts/22
377
378 Args:
379 interface: interface number of FTDI device to use
380
381 Returns:
382 Instance object of interface
Todd Broch6de9dc62012-04-09 15:23:53 -0700383
384 Raises:
385 ServodError: If init fails
Todd Broch47c43f42011-05-26 15:11:31 -0700386 """
Kevin Chengdc3befd2016-07-15 12:34:00 -0700387 fobj = ftdiuart.Fuart(vendor, product, interface, serialname)
Todd Broch6de9dc62012-04-09 15:23:53 -0700388 try:
389 fobj.run()
390 except ftdiuart.FuartError as e:
391 raise ServodError('Running uart interface. %s ( %d )' % (e.msg, e.value))
392
Todd Broch47c43f42011-05-26 15:11:31 -0700393 self._logger.info("%s" % fobj.get_pty())
394 return fobj
395
Simran Basie750a342013-03-12 13:45:26 -0700396 # TODO (sbasi) crbug.com/187492 - Implement bbuart.
Kevin Chengdc3befd2016-07-15 12:34:00 -0700397 def _init_bb_uart(self, vendor, product, serialname, interface):
Simran Basie750a342013-03-12 13:45:26 -0700398 """Initalize beaglebone uart interface."""
Simran Basi949309b2013-05-31 15:12:15 -0700399 logging.debug('UART INTERFACE: %s', interface)
400 return bbuart.BBuart(interface)
Simran Basie750a342013-03-12 13:45:26 -0700401
Kevin Chengdc3befd2016-07-15 12:34:00 -0700402 def _init_ftdi_gpiouart(self, vendor, product, serialname,
403 interface):
Todd Broch888da782011-10-07 14:29:09 -0700404 """Initialize special gpio + uart interface and open for use
405
406 Note, the uart runs in a separate thread (pthreads). Users wishing to
407 interact with it will query control for the pty's pathname and connect
408 with there favorite console program. For example:
409 cu -l /dev/pts/22
410
411 Args:
412 interface: interface number of FTDI device to use
413
414 Returns:
415 Instance objects of interface
Todd Broch6de9dc62012-04-09 15:23:53 -0700416
417 Raises:
418 ServodError: If init fails
Todd Broch888da782011-10-07 14:29:09 -0700419 """
Kevin Chengce7dafd2016-08-02 11:11:38 -0700420 fgpio = self._init_ftdi_gpio(vendor, product, serialname, interface)
Kevin Chengdc3befd2016-07-15 12:34:00 -0700421 fuart = ftdiuart.Fuart(vendor, product, interface, serialname, fgpio._fc)
Todd Broch6de9dc62012-04-09 15:23:53 -0700422 try:
423 fuart.run()
424 except ftdiuart.FuartError as e:
425 raise ServodError('Running uart interface. %s ( %d )' % (e.msg, e.value))
426
Todd Broch888da782011-10-07 14:29:09 -0700427 self._logger.info("uart pty: %s" % fuart.get_pty())
428 return fgpio, fuart
429
Kevin Chengdc3befd2016-07-15 12:34:00 -0700430 def _init_ec3po_uart(self, vendor, product, serialname, interface):
Aseda Aboagyea4922212015-11-20 15:19:08 -0800431 """Initialize EC-3PO console interpreter interface.
432
433 Args:
434 interface: A dictionary representing the interface.
435
436 Returns:
437 An EC3PO object representing the EC-3PO interface or None if there's no
438 interface for the USB PD UART.
439 """
Kevin Chengdc3befd2016-07-15 12:34:00 -0700440 vid = vendor
441 pid = product
Aseda Aboagyea4922212015-11-20 15:19:08 -0800442 # The current PID might be incremented if there are multiple FTDI.
443 # Therefore, try rewinding the PID back one if we don't find the base PID in
444 # the SERVO_ID_DEFAULTS
445 if (vid, pid) not in servo_interfaces.SERVO_ID_DEFAULTS:
446 self._logger.debug('VID:PID pair not found. Rewinding PID back one...')
447 pid -= 1
448 self._logger.debug('vid:0x%04x, pid:0x%04x', vid, pid)
449
Nick Sanders97bc4462016-01-04 15:37:31 -0800450 if 'raw_pty' in interface:
451 # We have specified an explicit target for this ec3po.
452 raw_uart_name = interface['raw_pty']
453 raw_ec_uart = self.get(raw_uart_name)
454
Aseda Aboagyea4922212015-11-20 15:19:08 -0800455 # Servo V2 / V3 should have the interface indicies in the same spot.
Nick Sanders97bc4462016-01-04 15:37:31 -0800456 elif ((vid, pid) in servo_interfaces.SERVO_V2_DEFAULTS or
Aseda Aboagyea4922212015-11-20 15:19:08 -0800457 (vid, pid) in servo_interfaces.SERVO_V3_DEFAULTS):
458 # Determine if it's a PD interface or just main EC console.
459 if interface['index'] == servo_interfaces.EC3PO_USBPD_INTERFACE_NUM:
460 try:
461 # Obtain the raw EC UART PTY and create the EC-3PO interface.
462 raw_ec_uart = self.get('raw_usbpd_uart_pty')
463 except NameError:
464 # This overlay doesn't have a USB PD MCU, so skip init.
465 self._logger.info('No PD MCU UART.')
466 return None
467 except AttributeError:
Aseda Aboagyea849d462016-05-04 17:08:16 -0700468 # This overlay has no get method for the interface so skip init. For
469 # servo v2, it's common for interfaces to be overridden such as
470 # reusing JTAG pins for the PD MCU UART instead. Therefore, print an
471 # error message indicating that the interface might be set
472 # incorrectly.
473 if (vid, pid) in servo_interfaces.SERVO_V2_DEFAULTS:
474 self._logger.warn('No interface for PD MCU UART.')
475 self._logger.warn('Usually, this happens because the interface is '
476 'set incorrectly. If you\'re overriding an '
477 'existing interface, be sure to update the '
478 'interface lists for your board at the end of '
479 'servo/servo_interfaces.py')
Aseda Aboagyea4922212015-11-20 15:19:08 -0800480 return None
481
482 elif interface['index'] == servo_interfaces.EC3PO_EC_INTERFACE_NUM:
483 raw_ec_uart = self.get('raw_ec_uart_pty')
484
485 # Servo V3, miniservo, Toad, Reston, Fruitpie, or Plankton
486 elif ((vid, pid) in servo_interfaces.MINISERVO_ID_DEFAULTS or
487 (vid, pid) in servo_interfaces.TOAD_ID_DEFAULTS or
488 (vid, pid) in servo_interfaces.RESTON_ID_DEFAULTS or
489 (vid, pid) in servo_interfaces.FRUITPIE_ID_DEFAULTS or
490 (vid, pid) in servo_interfaces.PLANKTON_ID_DEFAULTS):
491 raw_ec_uart = self.get('raw_ec_uart_pty')
Aseda Aboagyea4922212015-11-20 15:19:08 -0800492 else:
493 raise ServodError(('Unexpected EC-3PO interface!'
494 ' (0x%04x:0x%04x) %r') % (vid, pid, interface))
495
496 return ec3po_interface.EC3PO(raw_ec_uart)
497
Tom Wai-Hong Tam28f0a5f2012-08-21 12:49:57 +0800498 def _camel_case(self, string):
499 output = ''
500 for s in string.split('_'):
501 if output:
502 output += s.capitalize()
503 else:
504 output = s
505 return output
506
Todd Broche505b8d2011-03-21 18:19:54 -0700507 def _get_param_drv(self, control_name, is_get=True):
508 """Get access to driver for a given control.
509
510 Note, some controls have different parameter dictionaries for 'getting' the
511 control's value versus 'setting' it. Boolean is_get distinguishes which is
512 being requested.
513
514 Args:
515 control_name: string name of control
516 is_get: boolean to determine
517
518 Returns:
519 tuple (param, drv) where:
520 param: param dictionary for control
521 drv: instance object of driver for particular control
522
523 Raises:
524 ServodError: Error occurred while examining params dict
525 """
526 self._logger.debug("")
527 # if already setup just return tuple from driver dict
528 if control_name in self._drv_dict:
529 if is_get and ('get' in self._drv_dict[control_name]):
530 return self._drv_dict[control_name]['get']
531 if not is_get and ('set' in self._drv_dict[control_name]):
532 return self._drv_dict[control_name]['set']
533
534 params = self._syscfg.lookup_control_params(control_name, is_get)
535 if 'drv' not in params:
536 self._logger.error("Unable to determine driver for %s" % control_name)
537 raise ServodError("'drv' key not found in params dict")
538 if 'interface' not in params:
539 self._logger.error("Unable to determine interface for %s" %
540 control_name)
Todd Broche505b8d2011-03-21 18:19:54 -0700541 raise ServodError("'interface' key not found in params dict")
Simran Basi668be0e2013-08-07 11:54:50 -0700542
J. Richard Barnette275d9fd2014-02-11 14:38:54 -0800543 interface_id = params.get(
544 '%s_interface' % self._version, params['interface'])
545 if interface_id == 'servo':
546 interface = self
Simran Basi668be0e2013-08-07 11:54:50 -0700547 else:
J. Richard Barnette275d9fd2014-02-11 14:38:54 -0800548 index = int(interface_id) - 1
549 interface = self._interface_list[index]
Simran Basi668be0e2013-08-07 11:54:50 -0700550
Todd Broche505b8d2011-03-21 18:19:54 -0700551 servo_pkg = imp.load_module('servo', *imp.find_module('servo'))
552 drv_pkg = imp.load_module('drv',
553 *imp.find_module('drv', servo_pkg.__path__))
554 drv_name = params['drv']
555 drv_module = getattr(drv_pkg, drv_name)
Tom Wai-Hong Tam28f0a5f2012-08-21 12:49:57 +0800556 drv_class = getattr(drv_module, self._camel_case(drv_name))
Todd Broche505b8d2011-03-21 18:19:54 -0700557 drv = drv_class(interface, params)
558 if control_name not in self._drv_dict:
559 self._drv_dict[control_name] = {}
560 if is_get:
561 self._drv_dict[control_name]['get'] = (params, drv)
562 else:
563 self._drv_dict[control_name]['set'] = (params, drv)
564 return (params, drv)
565
566 def doc_all(self):
567 """Return all documenation for controls.
568
569 Returns:
570 string of <doc> text in config file (xml) and the params dictionary for
571 all controls.
572
573 For example:
574 warm_reset :: Reset the device warmly
575 ------------------------> {'interface': '1', 'map': 'onoff_i', ... }
576 """
577 return self._syscfg.display_config()
578
579 def doc(self, name):
580 """Retreive doc string in system config file for given control name.
581
582 Args:
583 name: name string of control to get doc string
584
585 Returns:
586 doc string of name
587
588 Raises:
589 NameError: if fails to locate control
590 """
591 self._logger.debug("name(%s)" % (name))
592 if self._syscfg.is_control(name):
593 return self._syscfg.get_control_docstring(name)
594 else:
595 raise NameError("No control %s" %name)
596
Fang Deng90377712013-06-03 15:51:48 -0700597 def _switch_usbkey(self, mux_direction):
598 """Connect USB flash stick to either servo or DUT.
599
600 This function switches 'usb_mux_sel1' to provide electrical
601 connection between the USB port J3 and either servo or DUT side.
602
603 Switching the usb mux is accompanied by powercycling
604 of the USB stick, because it sometimes gets wedged if the mux
605 is switched while the stick power is on.
606
607 Args:
608 mux_direction: "servo_sees_usbkey" or "dut_sees_usbkey".
609 """
610 self.set(self._USB_J3_PWR, self._USB_J3_PWR_OFF)
611 time.sleep(self._USB_POWEROFF_DELAY)
612 self.set(self._USB_J3, mux_direction)
613 time.sleep(self._USB_POWEROFF_DELAY)
614 self.set(self._USB_J3_PWR, self._USB_J3_PWR_ON)
615 if mux_direction == self._USB_J3_TO_SERVO:
616 time.sleep(self._USB_DETECTION_DELAY)
617
Simran Basia9f41032012-05-11 14:21:58 -0700618 def _get_usb_port_set(self):
619 """Gets a set of USB disks currently connected to the system
620
621 Returns:
622 A set of USB disk paths.
623 """
624 usb_set = fnmatch.filter(os.listdir("/dev/"), "sd[a-z]")
625 return set(["/dev/" + dev for dev in usb_set])
626
Kevin Chengc49494e2016-07-25 12:13:38 -0700627 def _block_other_servod_usb_probe(self):
628 """Block other servod processes by locking a file.
629
630 To enable multiple servods processes to safely probe_host_usb_dev, we use
631 a given lock file to signal other servod processes that we're probing
632 for a usb device.
633
634 If the lock file exists, we open it and try to lock it.
635 - If another servod processes has locked it already, we'll sleep a random
636 amount of time and try again, we'll keep doing that until
637 _MAX_USB_LOCK_WAIT amount of time has passed.
638
639 - If we're able to lock the file, we'll return the file descriptor for the
640 _unblock_other_servod_usb_probe() method to unlock and close the file
641 descriptor.
642
643 This blocking behavior is only enabled if the lock file exists, if it
644 doesn't, then we pretend the block was successful.
645
646 Returns:
647 A tuple of (lock_file_descriptor, lock_succeeded).
648 - lock_file_descriptor is the file descriptor of the open lock file.
649 We'll keep it open until we're done probing for the usb device.
650 - lock_succeeded is a boolean to enable us to proceed probing for a
651 device if the lock file does not exist. We need to be able to tell
652 the difference between failing to lock a file and no lock file existing.
653 """
654 if os.path.exists(self._USB_LOCK_FILE):
655 start_time = datetime.datetime.now()
656 while True:
657 try:
658 lock_file = open(self._USB_LOCK_FILE)
659 fcntl.flock(lock_file, fcntl.LOCK_EX | fcntl.LOCK_NB)
660 return lock_file, True
661 except IOError:
662 lock_file.close()
663 current_time = datetime.datetime.now()
664 current_wait_time = (current_time - start_time).total_seconds()
665 if current_wait_time > self._MAX_USB_LOCK_WAIT:
666 return None, False
667 # Sleep random amount.
668 sleep_time = time.sleep(random.random())
669 return None, True
670
671 def _unblock_other_servod_usb_probe(self, lock_file):
672 """Unblock other servod processes from probing usb devices.
673
674 Args:
675 lock_file: File descriptor of lock file to unlock and close.
676 """
677 if lock_file:
678 fcntl.flock(lock_file, fcntl.LOCK_UN)
679 lock_file.close()
680
681 def probe_host_usb_dev(self):
Simran Basia9f41032012-05-11 14:21:58 -0700682 """Probe the USB disk device plugged in the servo from the host side.
683
684 Method can fail by:
685 1) Having multiple servos connected and returning incorrect /dev/sdX of
Kevin Chengc49494e2016-07-25 12:13:38 -0700686 another servo unless _USB_LOCK_FILE exists on the servo host. If that
687 file exists, then it is safe to probe for usb devices among multiple
688 servod instances.
Simran Basia9f41032012-05-11 14:21:58 -0700689 2) Finding multiple /dev/sdX and returning None.
690
691 Returns:
Kevin Chengc49494e2016-07-25 12:13:38 -0700692 USB disk path if one and only one USB disk path is found, otherwise an
693 empty string.
Simran Basia9f41032012-05-11 14:21:58 -0700694 """
Kevin Chengc49494e2016-07-25 12:13:38 -0700695 lock_file, block_success = self._block_other_servod_usb_probe()
696 if not block_success:
697 return ''
698
Fang Deng90377712013-06-03 15:51:48 -0700699 original_value = self.get(self._USB_J3)
700 original_usb_power = self.get(self._USB_J3_PWR)
Simran Basia9f41032012-05-11 14:21:58 -0700701 # Make the host unable to see the USB disk.
Fang Deng90377712013-06-03 15:51:48 -0700702 if (original_usb_power == self._USB_J3_PWR_ON and
703 original_value != self._USB_J3_TO_DUT):
704 self._switch_usbkey(self._USB_J3_TO_DUT)
Simran Basia9f41032012-05-11 14:21:58 -0700705 no_usb_set = self._get_usb_port_set()
Simran Basia9f41032012-05-11 14:21:58 -0700706
Fang Deng90377712013-06-03 15:51:48 -0700707 # Make the host able to see the USB disk.
708 self._switch_usbkey(self._USB_J3_TO_SERVO)
Simran Basia9f41032012-05-11 14:21:58 -0700709 has_usb_set = self._get_usb_port_set()
Fang Deng90377712013-06-03 15:51:48 -0700710
Simran Basia9f41032012-05-11 14:21:58 -0700711 # Back to its original value.
Fang Deng90377712013-06-03 15:51:48 -0700712 if original_value != self._USB_J3_TO_SERVO:
713 self._switch_usbkey(original_value)
714 if original_usb_power != self._USB_J3_PWR_ON:
715 self.set(self._USB_J3_PWR, self._USB_J3_PWR_OFF)
716 time.sleep(self._USB_POWEROFF_DELAY)
717
Kevin Chengc49494e2016-07-25 12:13:38 -0700718 self._unblock_other_servod_usb_probe(lock_file)
719
Simran Basia9f41032012-05-11 14:21:58 -0700720 # Subtract the two sets to find the usb device.
721 diff_set = has_usb_set - no_usb_set
722 if len(diff_set) == 1:
723 return diff_set.pop()
724 else:
Kevin Chengc49494e2016-07-25 12:13:38 -0700725 return ''
Simran Basia9f41032012-05-11 14:21:58 -0700726
727 def download_image_to_usb(self, image_path):
728 """Download image and save to the USB device found by probe_host_usb_dev.
729 If the image_path is a URL, it will download this url to the USB path;
730 otherwise it will simply copy the image_path's contents to the USB path.
731
732 Args:
733 image_path: path or url to the recovery image.
734
735 Returns:
736 True|False: True if process completed successfully, False if error
737 occurred.
738 Can't return None because XMLRPC doesn't allow it. PTAL at tbroch's
739 comment at the end of set().
740 """
741 self._logger.debug("image_path(%s)" % image_path)
742 self._logger.debug("Detecting USB stick device...")
Kevin Chengc49494e2016-07-25 12:13:38 -0700743 usb_dev = self.probe_host_usb_dev()
Simran Basia9f41032012-05-11 14:21:58 -0700744 if not usb_dev:
745 self._logger.error("No usb device connected to servo")
746 return False
747
Kevin Cheng9071ed92016-06-21 14:37:54 -0700748 # Let's check if we downloaded this last time and if so assume the image is
749 # still on the usb device and return True.
750 if self._image_path == image_path:
751 self._logger.debug("Image already on USB device, skipping transfer")
752 return True
753
Simran Basia9f41032012-05-11 14:21:58 -0700754 try:
755 if image_path.startswith(self._HTTP_PREFIX):
756 self._logger.debug("Image path is a URL, downloading image")
757 urllib.urlretrieve(image_path, usb_dev)
758 else:
759 shutil.copyfile(image_path, usb_dev)
760 except IOError as e:
Victor Dodonb7cddb82016-04-28 17:00:24 -0700761 self._logger.error("Failed to transfer image to USB device: %s ( %s ) ",
Simran Basia9f41032012-05-11 14:21:58 -0700762 e.strerror, e.errno)
763 return False
764 except urllib.ContentTooShortError:
765 self._logger.error("Failed to download URL: %s to USB device: %s",
766 image_path, usb_dev)
767 return False
768 except BaseException as e:
769 self._logger.error("Unexpected exception downloading %s to %s: %s",
770 image_path, usb_dev, str(e))
771 return False
J. Richard Barnettee4125af2013-02-26 18:31:56 -0800772 finally:
773 # We just plastered the partition table for a block device.
774 # Pass or fail, we mustn't go without telling the kernel about
775 # the change, or it will punish us with sporadic, hard-to-debug
776 # failures.
777 subprocess.call(["sync"])
778 subprocess.call(["blockdev", "--rereadpt", usb_dev])
Kevin Cheng9071ed92016-06-21 14:37:54 -0700779 self._image_path = image_path
Simran Basia9f41032012-05-11 14:21:58 -0700780 return True
781
782 def make_image_noninteractive(self):
783 """Makes the recovery image noninteractive.
784
785 A noninteractive image will reboot automatically after installation
786 instead of waiting for the USB device to be removed to initiate a system
787 reboot.
788
789 Mounts partition 1 of the image stored on usb_dev and creates a file
790 called "non_interactive" so that the image will become noninteractive.
791
792 Returns:
793 True|False: True if process completed successfully, False if error
794 occurred.
795 """
796 result = True
Kevin Chengc49494e2016-07-25 12:13:38 -0700797 usb_dev = self.probe_host_usb_dev()
Simran Basia9f41032012-05-11 14:21:58 -0700798 if not usb_dev:
799 self._logger.error("No usb device connected to servo")
800 return False
801 # Create TempDirectory
802 tmpdir = tempfile.mkdtemp()
803 if tmpdir:
804 # Mount drive to tmpdir.
805 partition_1 = "%s1" % usb_dev
806 rc = subprocess.call(["mount", partition_1, tmpdir])
807 if rc == 0:
808 # Create file 'non_interactive'
809 non_interactive_file = os.path.join(tmpdir, "non_interactive")
810 try:
811 open(non_interactive_file, "w").close()
812 except IOError as e:
813 self._logger.error("Failed to create file %s : %s ( %d )",
814 non_interactive_file, e.strerror, e.errno)
815 result = False
816 except BaseException as e:
817 self._logger.error("Unexpected Exception creating file %s : %s",
818 non_interactive_file, str(e))
819 result = False
820 # Unmount drive regardless if file creation worked or not.
821 rc = subprocess.call(["umount", partition_1])
822 if rc != 0:
823 self._logger.error("Failed to unmount USB Device")
824 result = False
825 else:
826 self._logger.error("Failed to mount USB Device")
827 result = False
828
829 # Delete tmpdir. May throw exception if 'umount' failed.
830 try:
831 os.rmdir(tmpdir)
832 except OSError as e:
833 self._logger.error("Failed to remove temp directory %s : %s",
834 tmpdir, str(e))
835 return False
836 except BaseException as e:
837 self._logger.error("Unexpected Exception removing tempdir %s : %s",
838 tmpdir, str(e))
839 return False
840 else:
841 self._logger.error("Failed to create temp directory.")
842 return False
843 return result
844
Todd Broch352b4b22013-03-22 09:48:40 -0700845 def set_get_all(self, cmds):
846 """Set &| get one or more control values.
847
848 Args:
849 cmds: list of control[:value] to get or set.
850
851 Returns:
852 rv: list of responses from calling get or set methods.
853 """
854 rv = []
855 for cmd in cmds:
856 if ':' in cmd:
857 (control, value) = cmd.split(':')
858 rv.append(self.set(control, value))
859 else:
860 rv.append(self.get(cmd))
861 return rv
862
Todd Broche505b8d2011-03-21 18:19:54 -0700863 def get(self, name):
864 """Get control value.
865
866 Args:
867 name: name string of control
868
869 Returns:
870 Response from calling drv get method. Value is reformatted based on
871 control's dictionary parameters
872
873 Raises:
874 HwDriverError: Error occurred while using drv
875 """
876 self._logger.debug("name(%s)" % (name))
Vadim Bendeburyc3a83cf2015-03-24 13:07:00 -0700877 if name == 'serialname':
Kevin Cheng4b4f0022016-09-09 02:37:07 -0700878 if self._serialnames[self.MAIN_SERIAL]:
879 return self._serialnames[self.MAIN_SERIAL]
Vadim Bendeburyc3a83cf2015-03-24 13:07:00 -0700880 return 'unknown'
Todd Broche505b8d2011-03-21 18:19:54 -0700881 (param, drv) = self._get_param_drv(name)
882 try:
883 val = drv.get()
884 rd_val = self._syscfg.reformat_val(param, val)
Todd Brochb042e7a2011-12-14 17:41:36 -0800885 self._logger.debug("%s = %s" % (name, rd_val))
Todd Broche505b8d2011-03-21 18:19:54 -0700886 return rd_val
Todd Brochfbc499d2011-06-16 16:09:58 -0700887 except AttributeError, error:
888 self._logger.error("Getting %s: %s" % (name, error))
889 raise
Vic Yangbe6cf262012-09-10 10:40:56 +0800890 except HwDriverError:
Todd Broche505b8d2011-03-21 18:19:54 -0700891 self._logger.error("Getting %s" % (name))
892 raise
Todd Brochd6061672012-05-11 15:52:47 -0700893
Todd Broche505b8d2011-03-21 18:19:54 -0700894 def get_all(self, verbose):
895 """Get all controls values.
896
897 Args:
898 verbose: Boolean on whether to return doc info as well
899
900 Returns:
901 string creating from trying to get all values of all controls. In case of
902 error attempting access to control, response is 'ERR'.
903 """
Vadim Bendeburyb07944c2013-01-16 10:47:10 -0800904 rsp = []
Todd Broche505b8d2011-03-21 18:19:54 -0700905 for name in self._syscfg.syscfg_dict['control']:
906 self._logger.debug("name = %s" %name)
907 try:
908 value = self.get(name)
909 except Exception:
910 value = "ERR"
911 pass
912 if verbose:
Vadim Bendeburyb07944c2013-01-16 10:47:10 -0800913 rsp.append("GET %s = %s :: %s" % (name, value, self.doc(name)))
Todd Broche505b8d2011-03-21 18:19:54 -0700914 else:
Vadim Bendeburyb07944c2013-01-16 10:47:10 -0800915 rsp.append("%s:%s" % (name, value))
916 return '\n'.join(sorted(rsp))
Todd Broche505b8d2011-03-21 18:19:54 -0700917
918 def set(self, name, wr_val_str):
919 """Set control.
920
921 Args:
922 name: name string of control
923 wr_val_str: value string to write. Can be integer, float or a
924 alpha-numerical that is mapped to a integer or float.
925
926 Raises:
927 HwDriverError: Error occurred while using driver
928 """
929 self._logger.debug("name(%s) wr_val(%s)" % (name, wr_val_str))
930 (params, drv) = self._get_param_drv(name, False)
931 wr_val = self._syscfg.resolve_val(params, wr_val_str)
932 try:
933 drv.set(wr_val)
Vic Yangbe6cf262012-09-10 10:40:56 +0800934 except HwDriverError:
Todd Broche505b8d2011-03-21 18:19:54 -0700935 self._logger.error("Setting %s -> %s" % (name, wr_val_str))
936 raise
937 # TODO(tbroch) Figure out why despite allow_none=True for both xmlrpc server
938 # & client I still have to return something to appease the
939 # marshall/unmarshall
940 return True
941
Todd Brochd6061672012-05-11 15:52:47 -0700942 def hwinit(self, verbose=False):
943 """Initialize all controls.
944
945 These values are part of the system config XML files of the form
946 init=<value>. This command should be used by clients wishing to return the
947 servo and DUT its connected to a known good/safe state.
948
Vadim Bendeburybb51dd42013-01-31 13:47:46 -0800949 Note that initialization errors are ignored (as in some cases they could
950 be caused by DUT firmware deficiencies). This might need to be fine tuned
951 later.
952
Todd Brochd6061672012-05-11 15:52:47 -0700953 Args:
954 verbose: boolean, if True prints info about control initialized.
955 Otherwise prints nothing.
Vadim Bendebury5934e4b2013-02-06 13:57:54 -0800956
957 Returns:
958 This function is called across RPC and as such is expected to return
959 something unless transferring 'none' across is allowed. Hence adding a
960 dummy return value to make things simpler.
Todd Brochd6061672012-05-11 15:52:47 -0700961 """
Todd Brochd9acf0a2012-12-05 13:43:06 -0800962 for control_name, value in self._syscfg.hwinit:
Todd Broch3ec8df02012-11-20 10:53:03 -0800963 try:
John Carey6fe2bbf2015-08-31 16:13:03 -0700964 # Workaround for bug chrome-os-partner:42349. Without this check, the
965 # gpio will briefly pulse low if we set it from high to high.
966 if self.get(control_name) != value:
Aseda Aboagyea849d462016-05-04 17:08:16 -0700967 self.set(control_name, value)
968 if verbose:
969 self._logger.info('Initialized %s to %s', control_name, value)
Todd Broch3ec8df02012-11-20 10:53:03 -0800970 except Exception as e:
Todd Broch3ec8df02012-11-20 10:53:03 -0800971 self._logger.error("Problem initializing %s -> %s :: %s",
972 control_name, value, str(e))
Nick Sandersbc836282015-12-08 21:19:23 -0800973
974 # Init keyboard after all the intefaces are up.
975 self._keyboard = self._init_keyboard_handler(self, self._board)
Vadim Bendebury5934e4b2013-02-06 13:57:54 -0800976 return True
Todd Broch3ec8df02012-11-20 10:53:03 -0800977
Todd Broche505b8d2011-03-21 18:19:54 -0700978 def echo(self, echo):
979 """Dummy echo function for testing/examples.
980
981 Args:
982 echo: string to echo back to client
983 """
984 self._logger.debug("echo(%s)" % (echo))
985 return "ECH0ING: %s" % (echo)
986
J. Richard Barnettee2820552013-03-14 16:13:46 -0700987 def get_board(self):
988 """Return the board specified at startup, if any."""
989 return self._board
990
Simran Basia23c1392013-08-06 14:59:10 -0700991 def get_version(self):
992 """Get servo board version."""
993 return self._version
994
Yusuf Mohsinally29e30d22014-01-14 15:29:17 -0800995 def power_long_press(self):
996 """Simulate a long power button press."""
997 # After a long power press, the EC may ignore the next power
998 # button press (at least on Alex). To guarantee that this
999 # won't happen, we need to allow the EC one second to
1000 # collect itself.
1001 self._keyboard.power_long_press()
1002 return True
1003
1004 def power_normal_press(self):
1005 """Simulate a normal power button press."""
1006 self._keyboard.power_normal_press()
1007 return True
1008
1009 def power_short_press(self):
1010 """Simulate a short power button press."""
1011 self._keyboard.power_short_press()
1012 return True
1013
1014 def power_key(self, secs=''):
1015 """Simulate a power button press.
1016
1017 Args:
1018 secs: Time in seconds to simulate the keypress.
1019 """
1020 self._keyboard.power_key(secs)
1021 return True
1022
1023 def ctrl_d(self, press_secs=''):
1024 """Simulate Ctrl-d simultaneous button presses."""
1025 self._keyboard.ctrl_d(press_secs)
1026 return True
1027
Victor Dodone539cea2016-03-29 18:50:17 -07001028 def ctrl_u(self, press_secs=''):
Yusuf Mohsinally29e30d22014-01-14 15:29:17 -08001029 """Simulate Ctrl-u simultaneous button presses."""
Victor Dodone539cea2016-03-29 18:50:17 -07001030 self._keyboard.ctrl_u(press_secs)
Yusuf Mohsinally29e30d22014-01-14 15:29:17 -08001031 return True
1032
1033 def ctrl_enter(self, press_secs=''):
1034 """Simulate Ctrl-enter simultaneous button presses."""
1035 self._keyboard.ctrl_enter(press_secs)
1036 return True
1037
1038 def d_key(self, press_secs=''):
1039 """Simulate Enter key button press."""
1040 self._keyboard.d_key(press_secs)
1041 return True
1042
1043 def ctrl_key(self, press_secs=''):
1044 """Simulate Enter key button press."""
1045 self._keyboard.ctrl_key(press_secs)
1046 return True
1047
1048 def enter_key(self, press_secs=''):
1049 """Simulate Enter key button press."""
1050 self._keyboard.enter_key(press_secs)
1051 return True
1052
1053 def refresh_key(self, press_secs=''):
1054 """Simulate Refresh key (F3) button press."""
1055 self._keyboard.refresh_key(press_secs)
1056 return True
1057
1058 def ctrl_refresh_key(self, press_secs=''):
1059 """Simulate Ctrl and Refresh (F3) simultaneous press.
1060
1061 This key combination is an alternative of Space key.
1062 """
1063 self._keyboard.ctrl_refresh_key(press_secs)
1064 return True
1065
1066 def imaginary_key(self, press_secs=''):
1067 """Simulate imaginary key button press.
1068
1069 Maps to a key that doesn't physically exist.
1070 """
1071 self._keyboard.imaginary_key(press_secs)
1072 return True
1073
Todd Brochdbb09982011-10-02 07:14:26 -07001074
Vincent Palatin3acbbe52016-07-19 17:40:12 +02001075 def sysrq_x(self, press_secs=''):
1076 """Simulate Alt VolumeUp X simultaneous press.
1077
1078 This key combination is the kernel system request (sysrq) x.
1079 """
1080 self._keyboard.sysrq_x(press_secs)
1081 return True
1082
1083
Kevin Cheng4b4f0022016-09-09 02:37:07 -07001084 def get_servo_serials(self):
1085 """Return all the serials associated with this process."""
1086 return self._serialnames
1087
1088
Todd Broche505b8d2011-03-21 18:19:54 -07001089def test():
1090 """Integration testing.
1091
1092 TODO(tbroch) Enhance integration test and add unittest (see mox)
1093 """
1094 logging.basicConfig(level=logging.DEBUG,
1095 format="%(asctime)s - %(name)s - " +
1096 "%(levelname)s - %(message)s")
1097 # configure server & listen
1098 servod_obj = Servod(1)
1099 # 4 == number of interfaces on a FT4232H device
1100 for i in xrange(4):
1101 if i == 1:
1102 # its an i2c interface ... see __init__ for details and TODO to make
1103 # this configureable
1104 servod_obj._interface_list[i].wr_rd(0x21, [0], 1)
1105 else:
1106 # its a gpio interface
1107 servod_obj._interface_list[i].wr_rd(0)
1108
1109 server = SimpleXMLRPCServer.SimpleXMLRPCServer(("localhost", 9999),
1110 allow_none=True)
1111 server.register_introspection_functions()
1112 server.register_multicall_functions()
1113 server.register_instance(servod_obj)
1114 logging.info("Listening on localhost port 9999")
1115 server.serve_forever()
1116
1117if __name__ == "__main__":
1118 test()
1119
1120 # simple client transaction would look like
1121 """
1122 remote_uri = 'http://localhost:9999'
1123 client = xmlrpclib.ServerProxy(remote_uri, verbose=False)
1124 send_str = "Hello_there"
1125 print "Sent " + send_str + ", Recv " + client.echo(send_str)
1126 """