blob: 4ba2d0c6a8f188ced17a520294d60dfa032a69c2 [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 Cheng5595b342016-09-29 15:51:01 -07005import contextlib
Kevin Chengc49494e2016-07-25 12:13:38 -07006import datetime
7import fcntl
Simran Basia9f41032012-05-11 14:21:58 -07008import fnmatch
Todd Broche505b8d2011-03-21 18:19:54 -07009import imp
10import logging
Simran Basia9f41032012-05-11 14:21:58 -070011import os
Kevin Chengc49494e2016-07-25 12:13:38 -070012import random
Simran Basia9f41032012-05-11 14:21:58 -070013import shutil
Todd Broche505b8d2011-03-21 18:19:54 -070014import SimpleXMLRPCServer
Simran Basia9f41032012-05-11 14:21:58 -070015import subprocess
16import tempfile
Todd Broch7a91c252012-02-03 12:37:45 -080017import time
Simran Basia9f41032012-05-11 14:21:58 -070018import urllib
Todd Broche505b8d2011-03-21 18:19:54 -070019
20# TODO(tbroch) deprecate use of relative imports
Vic Yangbe6cf262012-09-10 10:40:56 +080021from drv.hw_driver import HwDriverError
Aaron.Chuang88eff332014-07-31 08:32:00 +080022import bbadc
Simran Basia9ad25e2013-04-23 11:57:00 -070023import bbi2c
Simran Basi5492bde2013-05-16 17:08:47 -070024import bbgpio
Simran Basi949309b2013-05-31 15:12:15 -070025import bbuart
Aseda Aboagyea4922212015-11-20 15:19:08 -080026import ec3po_interface
Todd Broche505b8d2011-03-21 18:19:54 -070027import ftdigpio
28import ftdii2c
Todd Brochdbb09982011-10-02 07:14:26 -070029import ftdi_common
Todd Broch47c43f42011-05-26 15:11:31 -070030import ftdiuart
Rong Changc6c8c022014-08-11 14:07:11 +080031import i2cbus
Yusuf Mohsinally29e30d22014-01-14 15:29:17 -080032import keyboard_handlers
Simran Basie750a342013-03-12 13:45:26 -070033import servo_interfaces
Kevin Cheng16304d12016-07-08 11:56:55 -070034import servo_postinit
Nick Sanders97bc4462016-01-04 15:37:31 -080035import stm32gpio
36import stm32i2c
37import stm32uart
Todd Broche505b8d2011-03-21 18:19:54 -070038
Aseda Aboagyea4922212015-11-20 15:19:08 -080039
Todd Broche505b8d2011-03-21 18:19:54 -070040MAX_I2C_CLOCK_HZ = 100000
41
Kevin Cheng5595b342016-09-29 15:51:01 -070042# It takes about 16-17 seconds for the entire probe usb device method,
43# let's wait double plus some buffer.
44_MAX_USB_LOCK_WAIT = 40
Todd Brochdbb09982011-10-02 07:14:26 -070045
Todd Broche505b8d2011-03-21 18:19:54 -070046class ServodError(Exception):
47 """Exception class for servod."""
48
49class Servod(object):
50 """Main class for Servo debug/controller Daemon."""
Simran Basia9f41032012-05-11 14:21:58 -070051 _USB_DETECTION_DELAY = 10
Fang Deng90377712013-06-03 15:51:48 -070052 _USB_POWEROFF_DELAY = 2
Simran Basia9f41032012-05-11 14:21:58 -070053 _HTTP_PREFIX = "http://"
Fang Deng90377712013-06-03 15:51:48 -070054 _USB_J3 = "usb_mux_sel1"
55 _USB_J3_TO_SERVO = "servo_sees_usbkey"
56 _USB_J3_TO_DUT = "dut_sees_usbkey"
57 _USB_J3_PWR = "prtctl4_pwren"
58 _USB_J3_PWR_ON = "on"
59 _USB_J3_PWR_OFF = "off"
Kevin Chengc49494e2016-07-25 12:13:38 -070060 _USB_LOCK_FILE = "/var/lib/servod/lock_file"
Simran Basia9f41032012-05-11 14:21:58 -070061
Kevin Cheng4b4f0022016-09-09 02:37:07 -070062 # This is the key to get the main serial used in the _serialnames dict.
63 MAIN_SERIAL = "main"
Wai-Hong Tam4b235922016-10-07 12:26:22 -070064 MICRO_SERVO_SERIAL = "micro_servo"
Wai-Hong Tam85b4ced2016-10-07 14:15:38 -070065 CCD_SERIAL = "ccd"
Kevin Cheng4b4f0022016-09-09 02:37:07 -070066
Kevin Chengdc3befd2016-07-15 12:34:00 -070067 def init_servo_interfaces(self, vendor, product, serialname,
68 interfaces):
69 """Init the servo interfaces with the given interfaces.
70
71 We don't use the self._{vendor,product,serialname} attributes because we
72 want to allow other callers to initialize other interfaces that may not
73 be associated with the initialized attributes (e.g. a servo v4 servod object
74 that wants to also initialize a servo micro interface).
75
76 Args:
77 vendor: USB vendor id of FTDI device.
78 product: USB product id of FTDI device.
79 serialname: String of device serialname/number as defined in FTDI
80 eeprom.
81 interfaces: List of strings of interface types the server will
82 instantiate.
83
84 Raises:
85 ServodError if unable to locate init method for particular interface.
86 """
87 # Extend the interface list if we need to.
88 interfaces_len = len(interfaces)
89 interface_list_len = len(self._interface_list)
90 if interfaces_len > interface_list_len:
91 self._interface_list += [None] * (interfaces_len - interface_list_len)
92
93 shifted = 0
94 for i, interface in enumerate(interfaces):
95 is_ftdi_interface = False
96 if type(interface) is dict:
97 name = interface['name']
98 # Store interface index for those that care about it.
99 interface['index'] = i
100 elif type(interface) is str and interface != 'dummy':
101 name = interface
102 # It's a FTDI related interface.
103 interface = (i % ftdi_common.MAX_FTDI_INTERFACES_PER_DEVICE) + 1
104 is_ftdi_interface = True
105 elif type(interface) is str and interface == 'dummy':
106 # 'dummy' reserves the interface for future use. Typically the
107 # interface will be managed by external third-party tools like
108 # openOCD for JTAG or flashrom for SPI. In the case of servo V4,
109 # it serves as a placeholder for servo micro interfaces.
110 continue
111 else:
112 raise ServodError("Illegal interface type %s" % type(interface))
113
114 # servos with multiple FTDI are guaranteed to have contiguous USB PIDs
115 if is_ftdi_interface and i and \
116 ((i % ftdi_common.MAX_FTDI_INTERFACES_PER_DEVICE) == 0):
117 product += 1
118 self._logger.info("Changing to next FTDI part @ pid = 0x%04x",
119 product)
120
121 self._logger.info("Initializing interface %d to %s", i + 1, name)
122 try:
123 func = getattr(self, '_init_%s' % name)
124 except AttributeError:
125 raise ServodError("Unable to locate init for interface %s" % name)
126 result = func(vendor, product, serialname, interface)
127
128 if isinstance(result, tuple):
129 result_len = len(result)
Wai-Hong Tam9441b182016-11-01 11:05:09 -0700130 # More than one interface return. Extend the list.
131 self._interface_list += [None] * (result_len - 1)
Kevin Chengdc3befd2016-07-15 12:34:00 -0700132 for result_index, r in enumerate(result):
Wai-Hong Tam9441b182016-11-01 11:05:09 -0700133 self._interface_list[i + shifted + result_index] = r
134 # Shift the remaining interfaces.
135 shifted += result_len - 1
Kevin Chengdc3befd2016-07-15 12:34:00 -0700136 else:
137 self._interface_list[i + shifted] = result
138
J. Richard Barnettee2820552013-03-14 16:13:46 -0700139 def __init__(self, config, vendor, product, serialname=None,
Yusuf Mohsinally29e30d22014-01-14 15:29:17 -0800140 interfaces=None, board="", version=None, usbkm232=None):
Todd Broche505b8d2011-03-21 18:19:54 -0700141 """Servod constructor.
142
143 Args:
144 config: instance of SystemConfig containing all controls for
145 particular Servod invocation
146 vendor: usb vendor id of FTDI device
147 product: usb product id of FTDI device
Todd Brochad034442011-05-25 15:05:29 -0700148 serialname: string of device serialname/number as defined in FTDI eeprom.
Todd Brochdbb09982011-10-02 07:14:26 -0700149 interfaces: list of strings of interface types the server will instantiate
Simran Basia23c1392013-08-06 14:59:10 -0700150 version: String. Servo board version. Examples: servo_v1, servo_v2,
151 servo_v2_r0, servo_v3
Yusuf Mohsinally29e30d22014-01-14 15:29:17 -0800152 usbkm232: String. Optional. Path to USB-KM232 device which allow for
Kevin Chengdc3befd2016-07-15 12:34:00 -0700153 sending keyboard commands to DUTs that do not have built in
154 keyboards. Used in FAFT tests. Use 'atmega' for on board AVR MCU.
155 e.g. '/dev/ttyUSB0' or 'atmega'
Todd Brochdbb09982011-10-02 07:14:26 -0700156
157 Raises:
158 ServodError: if unable to locate init method for particular interface
Todd Broche505b8d2011-03-21 18:19:54 -0700159 """
160 self._logger = logging.getLogger("Servod")
161 self._logger.debug("")
162 self._vendor = vendor
163 self._product = product
Kevin Cheng4b4f0022016-09-09 02:37:07 -0700164 self._serialnames = {self.MAIN_SERIAL: serialname}
Todd Broche505b8d2011-03-21 18:19:54 -0700165 self._syscfg = config
Kevin Cheng9071ed92016-06-21 14:37:54 -0700166 # Hold the last image path so we can reduce downloads to the usb device.
167 self._image_path = None
Todd Broche505b8d2011-03-21 18:19:54 -0700168 # list of objects (Fi2c, Fgpio) to physical interfaces (gpio, i2c) that ftdi
169 # interfaces are mapped to
170 self._interface_list = []
171 # Dict of Dict to map control name, function name to to tuple (params, drv)
172 # Ex) _drv_dict[name]['get'] = (params, drv)
173 self._drv_dict = {}
J. Richard Barnettee2820552013-03-14 16:13:46 -0700174 self._board = board
Simran Basia23c1392013-08-06 14:59:10 -0700175 self._version = version
Yusuf Mohsinally29e30d22014-01-14 15:29:17 -0800176 self._usbkm232 = usbkm232
Kevin Chengc49494e2016-07-25 12:13:38 -0700177 # Seed the random generator with the serial to differentiate from other
178 # servod processes.
179 random.seed(serialname if serialname else time.time())
Todd Brochdbb09982011-10-02 07:14:26 -0700180 # Note, interface i is (i - 1) in list
181 if not interfaces:
Todd Brochb21d8042014-05-15 12:54:54 -0700182 try:
183 interfaces = servo_interfaces.INTERFACE_BOARDS[board][vendor][product]
184 except KeyError:
185 interfaces = servo_interfaces.INTERFACE_DEFAULTS[vendor][product]
Todd Brochdbb09982011-10-02 07:14:26 -0700186
Kevin Chengdc3befd2016-07-15 12:34:00 -0700187 self.init_servo_interfaces(vendor, product, serialname, interfaces)
Kevin Cheng16304d12016-07-08 11:56:55 -0700188 servo_postinit.post_init(self)
Danny Chan662b6022015-11-04 17:34:53 -0800189
Yusuf Mohsinally29e30d22014-01-14 15:29:17 -0800190 def _init_keyboard_handler(self, servo, board=''):
191 """Initialize the correct keyboard handler for board.
192
Kevin Chengdc3befd2016-07-15 12:34:00 -0700193 Args:
194 servo: servo object.
195 board: string, board name.
Yusuf Mohsinally29e30d22014-01-14 15:29:17 -0800196
Kevin Chengdc3befd2016-07-15 12:34:00 -0700197 Returns:
198 keyboard handler object.
Yusuf Mohsinally29e30d22014-01-14 15:29:17 -0800199 """
200 if board == 'parrot':
201 return keyboard_handlers.ParrotHandler(servo)
202 elif board == 'stout':
203 return keyboard_handlers.StoutHandler(servo)
PeggyChuang4f07d872015-08-07 12:11:38 +0800204 elif board in ('buddy', 'cranky', 'guado', 'jecht', 'mccloud', 'monroe',
Tom Wai-Hong Tamd64164c2015-04-29 07:59:45 +0800205 'ninja', 'nyan_kitty', 'panther', 'rikku', 'stumpy',
philipchenfc9eea12016-09-21 17:36:54 -0700206 'sumo', 'tidus', 'tricky', 'veyron_fievel', 'veyron_mickey',
207 'veyron_rialto', 'veyron_tiger', 'zako'):
Yusuf Mohsinally29e30d22014-01-14 15:29:17 -0800208 if self._usbkm232 is None:
Tom Wai-Hong Tam4c7d6472016-03-08 06:55:50 +0800209 logging.info("No device path specified for usbkm232 handler. Use "
210 "the servo atmega chip to handle.")
211 self._usbkm232 = 'atmega'
Danny Chan662b6022015-11-04 17:34:53 -0800212 if self._usbkm232 == 'atmega':
213 # Use servo onboard keyboard emulator.
Nick Sanders78423782015-11-09 14:28:19 -0800214 self.set('atmega_rst', 'on')
Nick Sandersbc836282015-12-08 21:19:23 -0800215 self.set('at_hwb', 'off')
Nick Sanders78423782015-11-09 14:28:19 -0800216 self.set('atmega_rst', 'off')
Danny Chan662b6022015-11-04 17:34:53 -0800217 self._usbkm232 = self.get('atmega_pty')
Kevin Cheng810fc782016-11-01 12:36:46 -0700218 # We don't need to set the atmega uart settings if we're a servo v4.
219 if self._version != 'servo_v4':
220 self.set('atmega_baudrate', '9600')
221 self.set('atmega_bits', 'eight')
222 self.set('atmega_parity', 'none')
223 self.set('atmega_sbits', 'one')
224 self.set('usb_mux_sel4', 'on')
225 self.set('usb_mux_oe4', 'on')
226 # Allow atmega bootup time.
227 time.sleep(1.0)
Danny Chan662b6022015-11-04 17:34:53 -0800228 self._logger.info('USBKM232: %s', self._usbkm232)
Yusuf Mohsinally29e30d22014-01-14 15:29:17 -0800229 return keyboard_handlers.USBkm232Handler(servo, self._usbkm232)
230 else:
Tom Wai-Hong Tamd64164c2015-04-29 07:59:45 +0800231 # The following boards don't use Chrome EC.
232 if board in ('alex', 'butterfly', 'lumpy', 'zgb'):
233 return keyboard_handlers.MatrixKeyboardHandler(servo)
234 return keyboard_handlers.ChromeECHandler(servo)
Yusuf Mohsinally29e30d22014-01-14 15:29:17 -0800235
Todd Broch3ec8df02012-11-20 10:53:03 -0800236 def __del__(self):
237 """Servod deconstructor."""
238 for interface in self._interface_list:
239 del(interface)
240
Kevin Chengdc3befd2016-07-15 12:34:00 -0700241 def _init_ftdi_dummy(self, vendor, product, serialname, interface):
Kevin Cheng042f4932016-07-19 10:46:00 -0700242 """Dummy interface for ftdi devices.
243
244 This is a dummy function specifically for ftdi devices to not initialize
245 anything but to help pad the interface list.
246
247 Returns:
248 None.
249 """
250 return None
251
Kevin Chengdc3befd2016-07-15 12:34:00 -0700252 def _init_ftdi_gpio(self, vendor, product, serialname, interface):
Todd Broche505b8d2011-03-21 18:19:54 -0700253 """Initialize gpio driver interface and open for use.
254
255 Args:
256 interface: interface number of FTDI device to use.
257
258 Returns:
259 Instance object of interface.
Todd Broch6de9dc62012-04-09 15:23:53 -0700260
261 Raises:
262 ServodError: If init fails
Todd Broche505b8d2011-03-21 18:19:54 -0700263 """
Kevin Chengdc3befd2016-07-15 12:34:00 -0700264 fobj = ftdigpio.Fgpio(vendor, product, interface, serialname)
Todd Broch6de9dc62012-04-09 15:23:53 -0700265 try:
266 fobj.open()
267 except ftdigpio.FgpioError as e:
268 raise ServodError('Opening gpio interface. %s ( %d )' % (e.msg, e.value))
269
Todd Broche505b8d2011-03-21 18:19:54 -0700270 return fobj
271
Kevin Chengdc3befd2016-07-15 12:34:00 -0700272 def _init_stm32_uart(self, vendor, product, serialname, interface):
Nick Sanders97bc4462016-01-04 15:37:31 -0800273 """Initialize stm32 uart interface and open for use
274
275 Note, the uart runs in a separate thread. Users wishing to
276 interact with it will query control for the pty's pathname and connect
277 with their favorite console program. For example:
278 cu -l /dev/pts/22
279
280 Args:
281 interface: dict of interface parameters.
282
283 Returns:
284 Instance object of interface
285
286 Raises:
287 ServodError: Raised on init failure.
288 """
289 self._logger.info("Suart: interface: %s" % interface)
Kevin Chengdc3befd2016-07-15 12:34:00 -0700290 sobj = stm32uart.Suart(vendor, product, interface['interface'],
291 serialname)
Nick Sanders97bc4462016-01-04 15:37:31 -0800292
293 try:
294 sobj.run()
295 except stm32uart.SuartError as e:
296 raise ServodError('Running uart interface. %s ( %d )' % (e.msg, e.value))
297
298 self._logger.info("%s" % sobj.get_pty())
299 return sobj
300
Kevin Chengdc3befd2016-07-15 12:34:00 -0700301 def _init_stm32_gpio(self, vendor, product, serialname, interface):
Nick Sanders97bc4462016-01-04 15:37:31 -0800302 """Initialize stm32 gpio interface.
303 Args:
304 interface: interface number of stm32 device to use.
305
306 Returns:
307 Instance object of interface
308
309 Raises:
310 SgpioError: Raised on init failure.
311 """
Kevin Cheng71a046f2016-06-13 16:37:58 -0700312 interface_number = interface
313 # Interface could be a dict.
314 if type(interface) is dict:
315 interface_number = interface['interface']
316 self._logger.info("Sgpio: interface: %s" % interface_number)
Kevin Chengdc3befd2016-07-15 12:34:00 -0700317 return stm32gpio.Sgpio(vendor, product, interface_number, serialname)
Nick Sanders97bc4462016-01-04 15:37:31 -0800318
Kevin Chengdc3befd2016-07-15 12:34:00 -0700319 def _init_stm32_i2c(self, vendor, product, serialname, interface):
Nick Sanders97bc4462016-01-04 15:37:31 -0800320 """Initialize stm32 USB to I2C bridge interface and open for use
321
322 Args:
323 interface: USB interface number of stm32 device to use
324
325 Returns:
326 Instance object of interface.
327
328 Raises:
329 Si2cError: Raised on init failure.
330 """
331 self._logger.info("Si2cBus: interface: %s" % interface)
Nick Sandersa3649712016-03-01 16:53:52 -0800332 port = interface.get('port', 0)
Kevin Chengdc3befd2016-07-15 12:34:00 -0700333 return stm32i2c.Si2cBus(vendor, product, interface['interface'],
334 port=port, serialname=serialname)
Nick Sanders97bc4462016-01-04 15:37:31 -0800335
Kevin Chengdc3befd2016-07-15 12:34:00 -0700336 def _init_bb_adc(self, vendor, product, serialname, interface):
Aaron.Chuang88eff332014-07-31 08:32:00 +0800337 """Initalize beaglebone ADC interface."""
338 return bbadc.BBadc()
339
Kevin Chengdc3befd2016-07-15 12:34:00 -0700340 def _init_bb_gpio(self, vendor, product, serialname, interface):
Simran Basie750a342013-03-12 13:45:26 -0700341 """Initalize beaglebone gpio interface."""
Simran Basi5492bde2013-05-16 17:08:47 -0700342 return bbgpio.BBgpio()
Simran Basie750a342013-03-12 13:45:26 -0700343
Kevin Chengdc3befd2016-07-15 12:34:00 -0700344 def _init_ftdi_i2c(self, vendor, product, serialname, interface):
Todd Broche505b8d2011-03-21 18:19:54 -0700345 """Initialize i2c interface and open for use.
346
347 Args:
348 interface: interface number of FTDI device to use
349
350 Returns:
351 Instance object of interface
Todd Broch6de9dc62012-04-09 15:23:53 -0700352
353 Raises:
354 ServodError: If init fails
Todd Broche505b8d2011-03-21 18:19:54 -0700355 """
Kevin Chengdc3befd2016-07-15 12:34:00 -0700356 fobj = ftdii2c.Fi2c(vendor, product, interface, serialname)
Todd Broch6de9dc62012-04-09 15:23:53 -0700357 try:
358 fobj.open()
359 except ftdii2c.Fi2cError as e:
360 raise ServodError('Opening i2c interface. %s ( %d )' % (e.msg, e.value))
361
Todd Broche505b8d2011-03-21 18:19:54 -0700362 # Set the frequency of operation of the i2c bus.
363 # TODO(tbroch) make configureable
364 fobj.setclock(MAX_I2C_CLOCK_HZ)
Todd Broch6de9dc62012-04-09 15:23:53 -0700365
Todd Broche505b8d2011-03-21 18:19:54 -0700366 return fobj
367
Simran Basie750a342013-03-12 13:45:26 -0700368 # TODO (sbasi) crbug.com/187489 - Implement bb_i2c.
369 def _init_bb_i2c(self, interface):
370 """Initalize beaglebone i2c interface."""
Simran Basia9ad25e2013-04-23 11:57:00 -0700371 return bbi2c.BBi2c(interface)
Simran Basie750a342013-03-12 13:45:26 -0700372
Kevin Chengdc3befd2016-07-15 12:34:00 -0700373 def _init_dev_i2c(self, vendor, product, serialname, interface):
Rong Changc6c8c022014-08-11 14:07:11 +0800374 """Initalize Linux i2c-dev interface."""
375 return i2cbus.I2CBus('/dev/i2c-%d' % interface['bus_num'])
376
Kevin Chengdc3befd2016-07-15 12:34:00 -0700377 def _init_ftdi_uart(self, vendor, product, serialname, interface):
Simran Basie750a342013-03-12 13:45:26 -0700378 """Initialize ftdi uart inteface and open for use
Todd Broch47c43f42011-05-26 15:11:31 -0700379
380 Note, the uart runs in a separate thread (pthreads). Users wishing to
381 interact with it will query control for the pty's pathname and connect
382 with there favorite console program. For example:
383 cu -l /dev/pts/22
384
385 Args:
386 interface: interface number of FTDI device to use
387
388 Returns:
389 Instance object of interface
Todd Broch6de9dc62012-04-09 15:23:53 -0700390
391 Raises:
392 ServodError: If init fails
Todd Broch47c43f42011-05-26 15:11:31 -0700393 """
Kevin Chengdc3befd2016-07-15 12:34:00 -0700394 fobj = ftdiuart.Fuart(vendor, product, interface, serialname)
Todd Broch6de9dc62012-04-09 15:23:53 -0700395 try:
396 fobj.run()
397 except ftdiuart.FuartError as e:
398 raise ServodError('Running uart interface. %s ( %d )' % (e.msg, e.value))
399
Todd Broch47c43f42011-05-26 15:11:31 -0700400 self._logger.info("%s" % fobj.get_pty())
401 return fobj
402
Simran Basie750a342013-03-12 13:45:26 -0700403 # TODO (sbasi) crbug.com/187492 - Implement bbuart.
Kevin Chengdc3befd2016-07-15 12:34:00 -0700404 def _init_bb_uart(self, vendor, product, serialname, interface):
Simran Basie750a342013-03-12 13:45:26 -0700405 """Initalize beaglebone uart interface."""
Simran Basi949309b2013-05-31 15:12:15 -0700406 logging.debug('UART INTERFACE: %s', interface)
407 return bbuart.BBuart(interface)
Simran Basie750a342013-03-12 13:45:26 -0700408
Kevin Chengdc3befd2016-07-15 12:34:00 -0700409 def _init_ftdi_gpiouart(self, vendor, product, serialname,
410 interface):
Todd Broch888da782011-10-07 14:29:09 -0700411 """Initialize special gpio + uart interface and open for use
412
413 Note, the uart runs in a separate thread (pthreads). Users wishing to
414 interact with it will query control for the pty's pathname and connect
415 with there favorite console program. For example:
416 cu -l /dev/pts/22
417
418 Args:
419 interface: interface number of FTDI device to use
420
421 Returns:
422 Instance objects of interface
Todd Broch6de9dc62012-04-09 15:23:53 -0700423
424 Raises:
425 ServodError: If init fails
Todd Broch888da782011-10-07 14:29:09 -0700426 """
Kevin Chengce7dafd2016-08-02 11:11:38 -0700427 fgpio = self._init_ftdi_gpio(vendor, product, serialname, interface)
Kevin Chengdc3befd2016-07-15 12:34:00 -0700428 fuart = ftdiuart.Fuart(vendor, product, interface, serialname, fgpio._fc)
Todd Broch6de9dc62012-04-09 15:23:53 -0700429 try:
430 fuart.run()
431 except ftdiuart.FuartError as e:
432 raise ServodError('Running uart interface. %s ( %d )' % (e.msg, e.value))
433
Todd Broch888da782011-10-07 14:29:09 -0700434 self._logger.info("uart pty: %s" % fuart.get_pty())
435 return fgpio, fuart
436
Kevin Chengdc3befd2016-07-15 12:34:00 -0700437 def _init_ec3po_uart(self, vendor, product, serialname, interface):
Aseda Aboagyea4922212015-11-20 15:19:08 -0800438 """Initialize EC-3PO console interpreter interface.
439
440 Args:
441 interface: A dictionary representing the interface.
442
443 Returns:
444 An EC3PO object representing the EC-3PO interface or None if there's no
445 interface for the USB PD UART.
446 """
Kevin Chengdc3befd2016-07-15 12:34:00 -0700447 vid = vendor
448 pid = product
Aseda Aboagyea4922212015-11-20 15:19:08 -0800449 # The current PID might be incremented if there are multiple FTDI.
450 # Therefore, try rewinding the PID back one if we don't find the base PID in
451 # the SERVO_ID_DEFAULTS
452 if (vid, pid) not in servo_interfaces.SERVO_ID_DEFAULTS:
453 self._logger.debug('VID:PID pair not found. Rewinding PID back one...')
454 pid -= 1
455 self._logger.debug('vid:0x%04x, pid:0x%04x', vid, pid)
456
Nick Sanders97bc4462016-01-04 15:37:31 -0800457 if 'raw_pty' in interface:
458 # We have specified an explicit target for this ec3po.
459 raw_uart_name = interface['raw_pty']
460 raw_ec_uart = self.get(raw_uart_name)
461
Aseda Aboagyea4922212015-11-20 15:19:08 -0800462 # Servo V2 / V3 should have the interface indicies in the same spot.
Nick Sanders97bc4462016-01-04 15:37:31 -0800463 elif ((vid, pid) in servo_interfaces.SERVO_V2_DEFAULTS or
Aseda Aboagyea4922212015-11-20 15:19:08 -0800464 (vid, pid) in servo_interfaces.SERVO_V3_DEFAULTS):
465 # Determine if it's a PD interface or just main EC console.
466 if interface['index'] == servo_interfaces.EC3PO_USBPD_INTERFACE_NUM:
467 try:
468 # Obtain the raw EC UART PTY and create the EC-3PO interface.
469 raw_ec_uart = self.get('raw_usbpd_uart_pty')
470 except NameError:
471 # This overlay doesn't have a USB PD MCU, so skip init.
472 self._logger.info('No PD MCU UART.')
473 return None
474 except AttributeError:
Aseda Aboagyea849d462016-05-04 17:08:16 -0700475 # This overlay has no get method for the interface so skip init. For
476 # servo v2, it's common for interfaces to be overridden such as
477 # reusing JTAG pins for the PD MCU UART instead. Therefore, print an
478 # error message indicating that the interface might be set
479 # incorrectly.
480 if (vid, pid) in servo_interfaces.SERVO_V2_DEFAULTS:
481 self._logger.warn('No interface for PD MCU UART.')
482 self._logger.warn('Usually, this happens because the interface is '
483 'set incorrectly. If you\'re overriding an '
484 'existing interface, be sure to update the '
485 'interface lists for your board at the end of '
486 'servo/servo_interfaces.py')
Aseda Aboagyea4922212015-11-20 15:19:08 -0800487 return None
488
489 elif interface['index'] == servo_interfaces.EC3PO_EC_INTERFACE_NUM:
490 raw_ec_uart = self.get('raw_ec_uart_pty')
491
492 # Servo V3, miniservo, Toad, Reston, Fruitpie, or Plankton
493 elif ((vid, pid) in servo_interfaces.MINISERVO_ID_DEFAULTS or
494 (vid, pid) in servo_interfaces.TOAD_ID_DEFAULTS or
495 (vid, pid) in servo_interfaces.RESTON_ID_DEFAULTS or
496 (vid, pid) in servo_interfaces.FRUITPIE_ID_DEFAULTS or
497 (vid, pid) in servo_interfaces.PLANKTON_ID_DEFAULTS):
498 raw_ec_uart = self.get('raw_ec_uart_pty')
Aseda Aboagyea4922212015-11-20 15:19:08 -0800499 else:
500 raise ServodError(('Unexpected EC-3PO interface!'
501 ' (0x%04x:0x%04x) %r') % (vid, pid, interface))
502
503 return ec3po_interface.EC3PO(raw_ec_uart)
504
Tom Wai-Hong Tam28f0a5f2012-08-21 12:49:57 +0800505 def _camel_case(self, string):
506 output = ''
507 for s in string.split('_'):
508 if output:
509 output += s.capitalize()
510 else:
511 output = s
512 return output
513
Todd Broche505b8d2011-03-21 18:19:54 -0700514 def _get_param_drv(self, control_name, is_get=True):
515 """Get access to driver for a given control.
516
517 Note, some controls have different parameter dictionaries for 'getting' the
518 control's value versus 'setting' it. Boolean is_get distinguishes which is
519 being requested.
520
521 Args:
522 control_name: string name of control
523 is_get: boolean to determine
524
525 Returns:
526 tuple (param, drv) where:
527 param: param dictionary for control
528 drv: instance object of driver for particular control
529
530 Raises:
531 ServodError: Error occurred while examining params dict
532 """
533 self._logger.debug("")
534 # if already setup just return tuple from driver dict
535 if control_name in self._drv_dict:
536 if is_get and ('get' in self._drv_dict[control_name]):
537 return self._drv_dict[control_name]['get']
538 if not is_get and ('set' in self._drv_dict[control_name]):
539 return self._drv_dict[control_name]['set']
540
541 params = self._syscfg.lookup_control_params(control_name, is_get)
542 if 'drv' not in params:
543 self._logger.error("Unable to determine driver for %s" % control_name)
544 raise ServodError("'drv' key not found in params dict")
545 if 'interface' not in params:
546 self._logger.error("Unable to determine interface for %s" %
547 control_name)
Todd Broche505b8d2011-03-21 18:19:54 -0700548 raise ServodError("'interface' key not found in params dict")
Simran Basi668be0e2013-08-07 11:54:50 -0700549
J. Richard Barnette275d9fd2014-02-11 14:38:54 -0800550 interface_id = params.get(
551 '%s_interface' % self._version, params['interface'])
552 if interface_id == 'servo':
553 interface = self
Simran Basi668be0e2013-08-07 11:54:50 -0700554 else:
J. Richard Barnette275d9fd2014-02-11 14:38:54 -0800555 index = int(interface_id) - 1
556 interface = self._interface_list[index]
Simran Basi668be0e2013-08-07 11:54:50 -0700557
Todd Broche505b8d2011-03-21 18:19:54 -0700558 servo_pkg = imp.load_module('servo', *imp.find_module('servo'))
559 drv_pkg = imp.load_module('drv',
560 *imp.find_module('drv', servo_pkg.__path__))
561 drv_name = params['drv']
562 drv_module = getattr(drv_pkg, drv_name)
Tom Wai-Hong Tam28f0a5f2012-08-21 12:49:57 +0800563 drv_class = getattr(drv_module, self._camel_case(drv_name))
Todd Broche505b8d2011-03-21 18:19:54 -0700564 drv = drv_class(interface, params)
565 if control_name not in self._drv_dict:
566 self._drv_dict[control_name] = {}
567 if is_get:
568 self._drv_dict[control_name]['get'] = (params, drv)
569 else:
570 self._drv_dict[control_name]['set'] = (params, drv)
571 return (params, drv)
572
573 def doc_all(self):
574 """Return all documenation for controls.
575
576 Returns:
577 string of <doc> text in config file (xml) and the params dictionary for
578 all controls.
579
580 For example:
581 warm_reset :: Reset the device warmly
582 ------------------------> {'interface': '1', 'map': 'onoff_i', ... }
583 """
584 return self._syscfg.display_config()
585
586 def doc(self, name):
587 """Retreive doc string in system config file for given control name.
588
589 Args:
590 name: name string of control to get doc string
591
592 Returns:
593 doc string of name
594
595 Raises:
596 NameError: if fails to locate control
597 """
598 self._logger.debug("name(%s)" % (name))
599 if self._syscfg.is_control(name):
600 return self._syscfg.get_control_docstring(name)
601 else:
602 raise NameError("No control %s" %name)
603
Fang Deng90377712013-06-03 15:51:48 -0700604 def _switch_usbkey(self, mux_direction):
605 """Connect USB flash stick to either servo or DUT.
606
607 This function switches 'usb_mux_sel1' to provide electrical
608 connection between the USB port J3 and either servo or DUT side.
609
610 Switching the usb mux is accompanied by powercycling
611 of the USB stick, because it sometimes gets wedged if the mux
612 is switched while the stick power is on.
613
614 Args:
615 mux_direction: "servo_sees_usbkey" or "dut_sees_usbkey".
616 """
617 self.set(self._USB_J3_PWR, self._USB_J3_PWR_OFF)
618 time.sleep(self._USB_POWEROFF_DELAY)
619 self.set(self._USB_J3, mux_direction)
620 time.sleep(self._USB_POWEROFF_DELAY)
621 self.set(self._USB_J3_PWR, self._USB_J3_PWR_ON)
622 if mux_direction == self._USB_J3_TO_SERVO:
623 time.sleep(self._USB_DETECTION_DELAY)
624
Simran Basia9f41032012-05-11 14:21:58 -0700625 def _get_usb_port_set(self):
626 """Gets a set of USB disks currently connected to the system
627
628 Returns:
629 A set of USB disk paths.
630 """
631 usb_set = fnmatch.filter(os.listdir("/dev/"), "sd[a-z]")
632 return set(["/dev/" + dev for dev in usb_set])
633
Kevin Cheng5595b342016-09-29 15:51:01 -0700634 @contextlib.contextmanager
635 def _block_other_servod(self, timeout=None):
Kevin Chengc49494e2016-07-25 12:13:38 -0700636 """Block other servod processes by locking a file.
637
638 To enable multiple servods processes to safely probe_host_usb_dev, we use
639 a given lock file to signal other servod processes that we're probing
Kevin Cheng5595b342016-09-29 15:51:01 -0700640 for a usb device. This will be a context manager that will return
641 if the block was successful or not.
Kevin Chengc49494e2016-07-25 12:13:38 -0700642
643 If the lock file exists, we open it and try to lock it.
644 - If another servod processes has locked it already, we'll sleep a random
645 amount of time and try again, we'll keep doing that until
Kevin Cheng5595b342016-09-29 15:51:01 -0700646 timeout amount of time has passed.
Kevin Chengc49494e2016-07-25 12:13:38 -0700647
Kevin Cheng5595b342016-09-29 15:51:01 -0700648 - If we're able to lock the file, we'll yield that the block was successful
649 and upon return, unlock the file and exit out.
Kevin Chengc49494e2016-07-25 12:13:38 -0700650
651 This blocking behavior is only enabled if the lock file exists, if it
652 doesn't, then we pretend the block was successful.
653
Kevin Cheng5595b342016-09-29 15:51:01 -0700654 Args:
655 timeout: Max waiting time for the block to succeed.
Kevin Chengc49494e2016-07-25 12:13:38 -0700656 """
Kevin Cheng5595b342016-09-29 15:51:01 -0700657 if not os.path.exists(self._USB_LOCK_FILE):
658 # No lock file so we'll pretend the block was a success.
659 yield True
660 else:
Kevin Chengc49494e2016-07-25 12:13:38 -0700661 start_time = datetime.datetime.now()
662 while True:
Kevin Cheng5595b342016-09-29 15:51:01 -0700663 with open(self._USB_LOCK_FILE) as lock_file:
664 try:
665 fcntl.flock(lock_file, fcntl.LOCK_EX | fcntl.LOCK_NB)
666 yield True
667 fcntl.flock(lock_file, fcntl.LOCK_UN)
668 break
669 except IOError:
670 current_time = datetime.datetime.now()
671 current_wait_time = (current_time - start_time).total_seconds()
672 if timeout and current_wait_time > timeout:
673 yield False
674 break
Kevin Chengc49494e2016-07-25 12:13:38 -0700675 # Sleep random amount.
676 sleep_time = time.sleep(random.random())
Kevin Chengc49494e2016-07-25 12:13:38 -0700677
Kevin Cheng8fcf06c2016-10-12 08:02:44 -0700678 def safe_switch_usbkey_power(self, power_state, timeout=0):
Kevin Cheng5595b342016-09-29 15:51:01 -0700679 """Toggle the usb power safely.
680
681 We'll make sure we're the only servod process toggling the usbkey power.
Kevin Chengc49494e2016-07-25 12:13:38 -0700682
683 Args:
Kevin Cheng5595b342016-09-29 15:51:01 -0700684 power_state: The setting to set for the usbkey power.
Kevin Cheng8fcf06c2016-10-12 08:02:44 -0700685 timeout: Timeout to wait for blocking other servod processes, default is
686 no timeout.
Kevin Chengc49494e2016-07-25 12:13:38 -0700687
Kevin Cheng5595b342016-09-29 15:51:01 -0700688 Returns:
689 An empty string to appease the xmlrpc gods.
690 """
691 with self._block_other_servod(timeout=timeout):
692 if power_state != self.get(self._USB_J3_PWR):
693 self.set(self._USB_J3_PWR, power_state)
694 return ''
695
Kevin Cheng8fcf06c2016-10-12 08:02:44 -0700696 def safe_switch_usbkey(self, mux_direction, timeout=0):
Kevin Cheng5595b342016-09-29 15:51:01 -0700697 """Toggle the usb direction safely.
698
699 We'll make sure we're the only servod process toggling the usbkey direction.
700
701 Args:
702 power_state: The setting to set for the usbkey power.
Kevin Cheng8fcf06c2016-10-12 08:02:44 -0700703 timeout: Timeout to wait for blocking other servod processes, default is
704 no timeout.
Kevin Cheng5595b342016-09-29 15:51:01 -0700705
706 Returns:
707 An empty string to appease the xmlrpc gods.
708 """
709 with self._block_other_servod(timeout=timeout):
710 self._switch_usbkey(mux_direction)
711 return ''
712
713 def probe_host_usb_dev(self, timeout=_MAX_USB_LOCK_WAIT):
Simran Basia9f41032012-05-11 14:21:58 -0700714 """Probe the USB disk device plugged in the servo from the host side.
715
716 Method can fail by:
717 1) Having multiple servos connected and returning incorrect /dev/sdX of
Kevin Chengc49494e2016-07-25 12:13:38 -0700718 another servo unless _USB_LOCK_FILE exists on the servo host. If that
719 file exists, then it is safe to probe for usb devices among multiple
720 servod instances.
Simran Basia9f41032012-05-11 14:21:58 -0700721 2) Finding multiple /dev/sdX and returning None.
722
Kevin Cheng5595b342016-09-29 15:51:01 -0700723 Args:
724 timeout: Timeout to wait for blocking other servod processes.
725
Simran Basia9f41032012-05-11 14:21:58 -0700726 Returns:
Kevin Chengc49494e2016-07-25 12:13:38 -0700727 USB disk path if one and only one USB disk path is found, otherwise an
728 empty string.
Simran Basia9f41032012-05-11 14:21:58 -0700729 """
Kevin Cheng5595b342016-09-29 15:51:01 -0700730 with self._block_other_servod(timeout=timeout) as block_success:
731 if not block_success:
732 return ''
Kevin Chengc49494e2016-07-25 12:13:38 -0700733
Kevin Cheng5595b342016-09-29 15:51:01 -0700734 original_value = self.get(self._USB_J3)
735 original_usb_power = self.get(self._USB_J3_PWR)
736 # Make the host unable to see the USB disk.
737 if (original_usb_power == self._USB_J3_PWR_ON and
738 original_value != self._USB_J3_TO_DUT):
739 self._switch_usbkey(self._USB_J3_TO_DUT)
740 no_usb_set = self._get_usb_port_set()
Simran Basia9f41032012-05-11 14:21:58 -0700741
Kevin Cheng5595b342016-09-29 15:51:01 -0700742 # Make the host able to see the USB disk.
743 self._switch_usbkey(self._USB_J3_TO_SERVO)
744 has_usb_set = self._get_usb_port_set()
Fang Deng90377712013-06-03 15:51:48 -0700745
Kevin Cheng5595b342016-09-29 15:51:01 -0700746 # Back to its original value.
747 if original_value != self._USB_J3_TO_SERVO:
748 self._switch_usbkey(original_value)
749 if original_usb_power != self._USB_J3_PWR_ON:
750 self.set(self._USB_J3_PWR, self._USB_J3_PWR_OFF)
751 time.sleep(self._USB_POWEROFF_DELAY)
Fang Deng90377712013-06-03 15:51:48 -0700752
Kevin Cheng5595b342016-09-29 15:51:01 -0700753 # Subtract the two sets to find the usb device.
754 diff_set = has_usb_set - no_usb_set
755 if len(diff_set) == 1:
756 return diff_set.pop()
757 else:
758 return ''
Simran Basia9f41032012-05-11 14:21:58 -0700759
Kevin Cheng85831332016-10-13 13:14:44 -0700760 def download_image_to_usb(self, image_path, probe_timeout=_MAX_USB_LOCK_WAIT):
Simran Basia9f41032012-05-11 14:21:58 -0700761 """Download image and save to the USB device found by probe_host_usb_dev.
762 If the image_path is a URL, it will download this url to the USB path;
763 otherwise it will simply copy the image_path's contents to the USB path.
764
765 Args:
766 image_path: path or url to the recovery image.
Kevin Cheng85831332016-10-13 13:14:44 -0700767 probe_timeout: timeout for the probe to take.
Simran Basia9f41032012-05-11 14:21:58 -0700768
769 Returns:
770 True|False: True if process completed successfully, False if error
771 occurred.
772 Can't return None because XMLRPC doesn't allow it. PTAL at tbroch's
773 comment at the end of set().
774 """
775 self._logger.debug("image_path(%s)" % image_path)
776 self._logger.debug("Detecting USB stick device...")
Kevin Cheng85831332016-10-13 13:14:44 -0700777 usb_dev = self.probe_host_usb_dev(timeout=probe_timeout)
Simran Basia9f41032012-05-11 14:21:58 -0700778 if not usb_dev:
779 self._logger.error("No usb device connected to servo")
780 return False
781
Kevin Cheng9071ed92016-06-21 14:37:54 -0700782 # Let's check if we downloaded this last time and if so assume the image is
783 # still on the usb device and return True.
784 if self._image_path == image_path:
785 self._logger.debug("Image already on USB device, skipping transfer")
786 return True
787
Simran Basia9f41032012-05-11 14:21:58 -0700788 try:
789 if image_path.startswith(self._HTTP_PREFIX):
790 self._logger.debug("Image path is a URL, downloading image")
791 urllib.urlretrieve(image_path, usb_dev)
792 else:
793 shutil.copyfile(image_path, usb_dev)
794 except IOError as e:
Victor Dodonb7cddb82016-04-28 17:00:24 -0700795 self._logger.error("Failed to transfer image to USB device: %s ( %s ) ",
Simran Basia9f41032012-05-11 14:21:58 -0700796 e.strerror, e.errno)
797 return False
798 except urllib.ContentTooShortError:
799 self._logger.error("Failed to download URL: %s to USB device: %s",
800 image_path, usb_dev)
801 return False
802 except BaseException as e:
803 self._logger.error("Unexpected exception downloading %s to %s: %s",
804 image_path, usb_dev, str(e))
805 return False
J. Richard Barnettee4125af2013-02-26 18:31:56 -0800806 finally:
807 # We just plastered the partition table for a block device.
808 # Pass or fail, we mustn't go without telling the kernel about
809 # the change, or it will punish us with sporadic, hard-to-debug
810 # failures.
811 subprocess.call(["sync"])
812 subprocess.call(["blockdev", "--rereadpt", usb_dev])
Kevin Cheng9071ed92016-06-21 14:37:54 -0700813 self._image_path = image_path
Simran Basia9f41032012-05-11 14:21:58 -0700814 return True
815
816 def make_image_noninteractive(self):
817 """Makes the recovery image noninteractive.
818
819 A noninteractive image will reboot automatically after installation
820 instead of waiting for the USB device to be removed to initiate a system
821 reboot.
822
823 Mounts partition 1 of the image stored on usb_dev and creates a file
824 called "non_interactive" so that the image will become noninteractive.
825
826 Returns:
827 True|False: True if process completed successfully, False if error
828 occurred.
829 """
830 result = True
Kevin Chengc49494e2016-07-25 12:13:38 -0700831 usb_dev = self.probe_host_usb_dev()
Simran Basia9f41032012-05-11 14:21:58 -0700832 if not usb_dev:
833 self._logger.error("No usb device connected to servo")
834 return False
835 # Create TempDirectory
836 tmpdir = tempfile.mkdtemp()
837 if tmpdir:
838 # Mount drive to tmpdir.
839 partition_1 = "%s1" % usb_dev
840 rc = subprocess.call(["mount", partition_1, tmpdir])
841 if rc == 0:
842 # Create file 'non_interactive'
843 non_interactive_file = os.path.join(tmpdir, "non_interactive")
844 try:
845 open(non_interactive_file, "w").close()
846 except IOError as e:
847 self._logger.error("Failed to create file %s : %s ( %d )",
848 non_interactive_file, e.strerror, e.errno)
849 result = False
850 except BaseException as e:
851 self._logger.error("Unexpected Exception creating file %s : %s",
852 non_interactive_file, str(e))
853 result = False
854 # Unmount drive regardless if file creation worked or not.
855 rc = subprocess.call(["umount", partition_1])
856 if rc != 0:
857 self._logger.error("Failed to unmount USB Device")
858 result = False
859 else:
860 self._logger.error("Failed to mount USB Device")
861 result = False
862
863 # Delete tmpdir. May throw exception if 'umount' failed.
864 try:
865 os.rmdir(tmpdir)
866 except OSError as e:
867 self._logger.error("Failed to remove temp directory %s : %s",
868 tmpdir, str(e))
869 return False
870 except BaseException as e:
871 self._logger.error("Unexpected Exception removing tempdir %s : %s",
872 tmpdir, str(e))
873 return False
874 else:
875 self._logger.error("Failed to create temp directory.")
876 return False
877 return result
878
Todd Broch352b4b22013-03-22 09:48:40 -0700879 def set_get_all(self, cmds):
880 """Set &| get one or more control values.
881
882 Args:
883 cmds: list of control[:value] to get or set.
884
885 Returns:
886 rv: list of responses from calling get or set methods.
887 """
888 rv = []
889 for cmd in cmds:
890 if ':' in cmd:
891 (control, value) = cmd.split(':')
892 rv.append(self.set(control, value))
893 else:
894 rv.append(self.get(cmd))
895 return rv
896
Todd Broche505b8d2011-03-21 18:19:54 -0700897 def get(self, name):
898 """Get control value.
899
900 Args:
901 name: name string of control
902
903 Returns:
904 Response from calling drv get method. Value is reformatted based on
905 control's dictionary parameters
906
907 Raises:
908 HwDriverError: Error occurred while using drv
909 """
910 self._logger.debug("name(%s)" % (name))
Vadim Bendeburyc3a83cf2015-03-24 13:07:00 -0700911 if name == 'serialname':
Kevin Cheng4b4f0022016-09-09 02:37:07 -0700912 if self._serialnames[self.MAIN_SERIAL]:
913 return self._serialnames[self.MAIN_SERIAL]
Vadim Bendeburyc3a83cf2015-03-24 13:07:00 -0700914 return 'unknown'
Todd Broche505b8d2011-03-21 18:19:54 -0700915 (param, drv) = self._get_param_drv(name)
916 try:
917 val = drv.get()
918 rd_val = self._syscfg.reformat_val(param, val)
Todd Brochb042e7a2011-12-14 17:41:36 -0800919 self._logger.debug("%s = %s" % (name, rd_val))
Todd Broche505b8d2011-03-21 18:19:54 -0700920 return rd_val
Todd Brochfbc499d2011-06-16 16:09:58 -0700921 except AttributeError, error:
922 self._logger.error("Getting %s: %s" % (name, error))
923 raise
Vic Yangbe6cf262012-09-10 10:40:56 +0800924 except HwDriverError:
Todd Broche505b8d2011-03-21 18:19:54 -0700925 self._logger.error("Getting %s" % (name))
926 raise
Todd Brochd6061672012-05-11 15:52:47 -0700927
Todd Broche505b8d2011-03-21 18:19:54 -0700928 def get_all(self, verbose):
929 """Get all controls values.
930
931 Args:
932 verbose: Boolean on whether to return doc info as well
933
934 Returns:
935 string creating from trying to get all values of all controls. In case of
936 error attempting access to control, response is 'ERR'.
937 """
Vadim Bendeburyb07944c2013-01-16 10:47:10 -0800938 rsp = []
Todd Broche505b8d2011-03-21 18:19:54 -0700939 for name in self._syscfg.syscfg_dict['control']:
940 self._logger.debug("name = %s" %name)
941 try:
942 value = self.get(name)
943 except Exception:
944 value = "ERR"
945 pass
946 if verbose:
Vadim Bendeburyb07944c2013-01-16 10:47:10 -0800947 rsp.append("GET %s = %s :: %s" % (name, value, self.doc(name)))
Todd Broche505b8d2011-03-21 18:19:54 -0700948 else:
Vadim Bendeburyb07944c2013-01-16 10:47:10 -0800949 rsp.append("%s:%s" % (name, value))
950 return '\n'.join(sorted(rsp))
Todd Broche505b8d2011-03-21 18:19:54 -0700951
952 def set(self, name, wr_val_str):
953 """Set control.
954
955 Args:
956 name: name string of control
957 wr_val_str: value string to write. Can be integer, float or a
958 alpha-numerical that is mapped to a integer or float.
959
960 Raises:
961 HwDriverError: Error occurred while using driver
962 """
963 self._logger.debug("name(%s) wr_val(%s)" % (name, wr_val_str))
964 (params, drv) = self._get_param_drv(name, False)
965 wr_val = self._syscfg.resolve_val(params, wr_val_str)
966 try:
967 drv.set(wr_val)
Vic Yangbe6cf262012-09-10 10:40:56 +0800968 except HwDriverError:
Todd Broche505b8d2011-03-21 18:19:54 -0700969 self._logger.error("Setting %s -> %s" % (name, wr_val_str))
970 raise
971 # TODO(tbroch) Figure out why despite allow_none=True for both xmlrpc server
972 # & client I still have to return something to appease the
973 # marshall/unmarshall
974 return True
975
Todd Brochd6061672012-05-11 15:52:47 -0700976 def hwinit(self, verbose=False):
977 """Initialize all controls.
978
979 These values are part of the system config XML files of the form
980 init=<value>. This command should be used by clients wishing to return the
981 servo and DUT its connected to a known good/safe state.
982
Vadim Bendeburybb51dd42013-01-31 13:47:46 -0800983 Note that initialization errors are ignored (as in some cases they could
984 be caused by DUT firmware deficiencies). This might need to be fine tuned
985 later.
986
Todd Brochd6061672012-05-11 15:52:47 -0700987 Args:
988 verbose: boolean, if True prints info about control initialized.
989 Otherwise prints nothing.
Vadim Bendebury5934e4b2013-02-06 13:57:54 -0800990
991 Returns:
992 This function is called across RPC and as such is expected to return
993 something unless transferring 'none' across is allowed. Hence adding a
994 dummy return value to make things simpler.
Todd Brochd6061672012-05-11 15:52:47 -0700995 """
Todd Brochd9acf0a2012-12-05 13:43:06 -0800996 for control_name, value in self._syscfg.hwinit:
Todd Broch3ec8df02012-11-20 10:53:03 -0800997 try:
John Carey6fe2bbf2015-08-31 16:13:03 -0700998 # Workaround for bug chrome-os-partner:42349. Without this check, the
999 # gpio will briefly pulse low if we set it from high to high.
1000 if self.get(control_name) != value:
Aseda Aboagyea849d462016-05-04 17:08:16 -07001001 self.set(control_name, value)
1002 if verbose:
1003 self._logger.info('Initialized %s to %s', control_name, value)
Todd Broch3ec8df02012-11-20 10:53:03 -08001004 except Exception as e:
Todd Broch3ec8df02012-11-20 10:53:03 -08001005 self._logger.error("Problem initializing %s -> %s :: %s",
1006 control_name, value, str(e))
Nick Sandersbc836282015-12-08 21:19:23 -08001007
1008 # Init keyboard after all the intefaces are up.
1009 self._keyboard = self._init_keyboard_handler(self, self._board)
Vadim Bendebury5934e4b2013-02-06 13:57:54 -08001010 return True
Todd Broch3ec8df02012-11-20 10:53:03 -08001011
Todd Broche505b8d2011-03-21 18:19:54 -07001012 def echo(self, echo):
1013 """Dummy echo function for testing/examples.
1014
1015 Args:
1016 echo: string to echo back to client
1017 """
1018 self._logger.debug("echo(%s)" % (echo))
1019 return "ECH0ING: %s" % (echo)
1020
J. Richard Barnettee2820552013-03-14 16:13:46 -07001021 def get_board(self):
1022 """Return the board specified at startup, if any."""
1023 return self._board
1024
Simran Basia23c1392013-08-06 14:59:10 -07001025 def get_version(self):
1026 """Get servo board version."""
1027 return self._version
1028
Yusuf Mohsinally29e30d22014-01-14 15:29:17 -08001029 def power_long_press(self):
1030 """Simulate a long power button press."""
1031 # After a long power press, the EC may ignore the next power
1032 # button press (at least on Alex). To guarantee that this
1033 # won't happen, we need to allow the EC one second to
1034 # collect itself.
1035 self._keyboard.power_long_press()
1036 return True
1037
1038 def power_normal_press(self):
1039 """Simulate a normal power button press."""
1040 self._keyboard.power_normal_press()
1041 return True
1042
1043 def power_short_press(self):
1044 """Simulate a short power button press."""
1045 self._keyboard.power_short_press()
1046 return True
1047
1048 def power_key(self, secs=''):
1049 """Simulate a power button press.
1050
1051 Args:
1052 secs: Time in seconds to simulate the keypress.
1053 """
1054 self._keyboard.power_key(secs)
1055 return True
1056
1057 def ctrl_d(self, press_secs=''):
1058 """Simulate Ctrl-d simultaneous button presses."""
1059 self._keyboard.ctrl_d(press_secs)
1060 return True
1061
Victor Dodone539cea2016-03-29 18:50:17 -07001062 def ctrl_u(self, press_secs=''):
Yusuf Mohsinally29e30d22014-01-14 15:29:17 -08001063 """Simulate Ctrl-u simultaneous button presses."""
Victor Dodone539cea2016-03-29 18:50:17 -07001064 self._keyboard.ctrl_u(press_secs)
Yusuf Mohsinally29e30d22014-01-14 15:29:17 -08001065 return True
1066
1067 def ctrl_enter(self, press_secs=''):
1068 """Simulate Ctrl-enter simultaneous button presses."""
1069 self._keyboard.ctrl_enter(press_secs)
1070 return True
1071
1072 def d_key(self, press_secs=''):
1073 """Simulate Enter key button press."""
1074 self._keyboard.d_key(press_secs)
1075 return True
1076
1077 def ctrl_key(self, press_secs=''):
1078 """Simulate Enter key button press."""
1079 self._keyboard.ctrl_key(press_secs)
1080 return True
1081
1082 def enter_key(self, press_secs=''):
1083 """Simulate Enter key button press."""
1084 self._keyboard.enter_key(press_secs)
1085 return True
1086
1087 def refresh_key(self, press_secs=''):
1088 """Simulate Refresh key (F3) button press."""
1089 self._keyboard.refresh_key(press_secs)
1090 return True
1091
1092 def ctrl_refresh_key(self, press_secs=''):
1093 """Simulate Ctrl and Refresh (F3) simultaneous press.
1094
1095 This key combination is an alternative of Space key.
1096 """
1097 self._keyboard.ctrl_refresh_key(press_secs)
1098 return True
1099
1100 def imaginary_key(self, press_secs=''):
1101 """Simulate imaginary key button press.
1102
1103 Maps to a key that doesn't physically exist.
1104 """
1105 self._keyboard.imaginary_key(press_secs)
1106 return True
1107
Todd Brochdbb09982011-10-02 07:14:26 -07001108
Vincent Palatin3acbbe52016-07-19 17:40:12 +02001109 def sysrq_x(self, press_secs=''):
1110 """Simulate Alt VolumeUp X simultaneous press.
1111
1112 This key combination is the kernel system request (sysrq) x.
1113 """
1114 self._keyboard.sysrq_x(press_secs)
1115 return True
1116
1117
Kevin Cheng4b4f0022016-09-09 02:37:07 -07001118 def get_servo_serials(self):
1119 """Return all the serials associated with this process."""
1120 return self._serialnames
1121
1122
Todd Broche505b8d2011-03-21 18:19:54 -07001123def test():
1124 """Integration testing.
1125
1126 TODO(tbroch) Enhance integration test and add unittest (see mox)
1127 """
1128 logging.basicConfig(level=logging.DEBUG,
1129 format="%(asctime)s - %(name)s - " +
1130 "%(levelname)s - %(message)s")
1131 # configure server & listen
1132 servod_obj = Servod(1)
1133 # 4 == number of interfaces on a FT4232H device
1134 for i in xrange(4):
1135 if i == 1:
1136 # its an i2c interface ... see __init__ for details and TODO to make
1137 # this configureable
1138 servod_obj._interface_list[i].wr_rd(0x21, [0], 1)
1139 else:
1140 # its a gpio interface
1141 servod_obj._interface_list[i].wr_rd(0)
1142
1143 server = SimpleXMLRPCServer.SimpleXMLRPCServer(("localhost", 9999),
1144 allow_none=True)
1145 server.register_introspection_functions()
1146 server.register_multicall_functions()
1147 server.register_instance(servod_obj)
1148 logging.info("Listening on localhost port 9999")
1149 server.serve_forever()
1150
1151if __name__ == "__main__":
1152 test()
1153
1154 # simple client transaction would look like
1155 """
1156 remote_uri = 'http://localhost:9999'
1157 client = xmlrpclib.ServerProxy(remote_uri, verbose=False)
1158 send_str = "Hello_there"
1159 print "Sent " + send_str + ", Recv " + client.echo(send_str)
1160 """