blob: 1e0ae311758d18a6d89e5c06a5260e8c26e550da [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 logging
Simran Basia9f41032012-05-11 14:21:58 -070010import os
Kevin Chengc49494e2016-07-25 12:13:38 -070011import random
Aseda Aboagye1d8477b2017-05-10 17:24:31 -070012import re
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
Wai-Hong Tam1f9e9a72017-05-02 14:14:46 -070019import usb
Todd Broche505b8d2011-03-21 18:19:54 -070020
Wai-Hong Tam4c09eff2017-02-17 11:46:19 -080021import drv as servo_drv
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
Wai-Hong Tam4c09eff2017-02-17 11:46:19 -080039HwDriverError = servo_drv.hw_driver.HwDriverError
Aseda Aboagyea4922212015-11-20 15:19:08 -080040
Todd Broche505b8d2011-03-21 18:19:54 -070041MAX_I2C_CLOCK_HZ = 100000
42
Kevin Cheng5595b342016-09-29 15:51:01 -070043# It takes about 16-17 seconds for the entire probe usb device method,
44# let's wait double plus some buffer.
45_MAX_USB_LOCK_WAIT = 40
Todd Brochdbb09982011-10-02 07:14:26 -070046
Todd Broche505b8d2011-03-21 18:19:54 -070047class ServodError(Exception):
48 """Exception class for servod."""
49
50class Servod(object):
51 """Main class for Servo debug/controller Daemon."""
Simran Basia9f41032012-05-11 14:21:58 -070052 _USB_DETECTION_DELAY = 10
Fang Deng90377712013-06-03 15:51:48 -070053 _USB_POWEROFF_DELAY = 2
Simran Basia9f41032012-05-11 14:21:58 -070054 _HTTP_PREFIX = "http://"
Fang Deng90377712013-06-03 15:51:48 -070055 _USB_J3 = "usb_mux_sel1"
56 _USB_J3_TO_SERVO = "servo_sees_usbkey"
57 _USB_J3_TO_DUT = "dut_sees_usbkey"
58 _USB_J3_PWR = "prtctl4_pwren"
59 _USB_J3_PWR_ON = "on"
60 _USB_J3_PWR_OFF = "off"
Kevin Chengc49494e2016-07-25 12:13:38 -070061 _USB_LOCK_FILE = "/var/lib/servod/lock_file"
Simran Basia9f41032012-05-11 14:21:58 -070062
Kevin Cheng4b4f0022016-09-09 02:37:07 -070063 # This is the key to get the main serial used in the _serialnames dict.
64 MAIN_SERIAL = "main"
Aseda Aboagyea9fee382017-08-01 15:11:03 -070065 SERVO_MICRO_SERIAL = "servo_micro"
Wai-Hong Tam85b4ced2016-10-07 14:15:38 -070066 CCD_SERIAL = "ccd"
Kevin Cheng4b4f0022016-09-09 02:37:07 -070067
Kevin Chengdc3befd2016-07-15 12:34:00 -070068 def init_servo_interfaces(self, vendor, product, serialname,
69 interfaces):
70 """Init the servo interfaces with the given interfaces.
71
72 We don't use the self._{vendor,product,serialname} attributes because we
73 want to allow other callers to initialize other interfaces that may not
74 be associated with the initialized attributes (e.g. a servo v4 servod object
75 that wants to also initialize a servo micro interface).
76
77 Args:
78 vendor: USB vendor id of FTDI device.
79 product: USB product id of FTDI device.
80 serialname: String of device serialname/number as defined in FTDI
81 eeprom.
82 interfaces: List of strings of interface types the server will
83 instantiate.
84
85 Raises:
86 ServodError if unable to locate init method for particular interface.
87 """
Mary Ruthven13389642017-02-14 12:15:34 -080088 # If it is a new device add it to the list
Wai-Hong Tam1f9e9a72017-05-02 14:14:46 -070089 device = (vendor, product, serialname)
Mary Ruthven13389642017-02-14 12:15:34 -080090 if device not in self._devices:
91 self._devices.append(device)
92
Kevin Chengdc3befd2016-07-15 12:34:00 -070093 # Extend the interface list if we need to.
94 interfaces_len = len(interfaces)
95 interface_list_len = len(self._interface_list)
96 if interfaces_len > interface_list_len:
97 self._interface_list += [None] * (interfaces_len - interface_list_len)
98
Kevin Chengdc3befd2016-07-15 12:34:00 -070099 for i, interface in enumerate(interfaces):
100 is_ftdi_interface = False
101 if type(interface) is dict:
102 name = interface['name']
103 # Store interface index for those that care about it.
104 interface['index'] = i
105 elif type(interface) is str and interface != 'dummy':
106 name = interface
Wai-Hong Tam564c1702017-04-24 09:23:38 -0700107 # It's a FTDI related interface. #0 is reserved for no use.
108 interface = ((i - 1) % ftdi_common.MAX_FTDI_INTERFACES_PER_DEVICE) + 1
Kevin Chengdc3befd2016-07-15 12:34:00 -0700109 is_ftdi_interface = True
110 elif type(interface) is str and interface == 'dummy':
111 # 'dummy' reserves the interface for future use. Typically the
112 # interface will be managed by external third-party tools like
113 # openOCD for JTAG or flashrom for SPI. In the case of servo V4,
114 # it serves as a placeholder for servo micro interfaces.
115 continue
116 else:
117 raise ServodError("Illegal interface type %s" % type(interface))
118
119 # servos with multiple FTDI are guaranteed to have contiguous USB PIDs
Wai-Hong Tam564c1702017-04-24 09:23:38 -0700120 product_increment = 0
121 if is_ftdi_interface:
122 product_increment = (i - 1) / ftdi_common.MAX_FTDI_INTERFACES_PER_DEVICE
123 if product_increment:
124 self._logger.info("Use the next FTDI part @ pid = 0x%04x",
125 product + product_increment)
Kevin Chengdc3befd2016-07-15 12:34:00 -0700126
Wai-Hong Tam564c1702017-04-24 09:23:38 -0700127 self._logger.info("Initializing interface %d to %s", i, name)
Kevin Chengdc3befd2016-07-15 12:34:00 -0700128 try:
129 func = getattr(self, '_init_%s' % name)
130 except AttributeError:
131 raise ServodError("Unable to locate init for interface %s" % name)
Wai-Hong Tam564c1702017-04-24 09:23:38 -0700132 result = func(vendor, product + product_increment, serialname, interface)
Kevin Chengdc3befd2016-07-15 12:34:00 -0700133
134 if isinstance(result, tuple):
135 result_len = len(result)
Wai-Hong Tamd8a94d62017-04-28 10:11:51 -0700136 self._interface_list[i:(i + result_len)] = result
Kevin Chengdc3befd2016-07-15 12:34:00 -0700137 else:
Wai-Hong Tamd8a94d62017-04-28 10:11:51 -0700138 self._interface_list[i] = result
Kevin Chengdc3befd2016-07-15 12:34:00 -0700139
J. Richard Barnettee2820552013-03-14 16:13:46 -0700140 def __init__(self, config, vendor, product, serialname=None,
Yusuf Mohsinally29e30d22014-01-14 15:29:17 -0800141 interfaces=None, board="", version=None, usbkm232=None):
Todd Broche505b8d2011-03-21 18:19:54 -0700142 """Servod constructor.
143
144 Args:
145 config: instance of SystemConfig containing all controls for
146 particular Servod invocation
147 vendor: usb vendor id of FTDI device
148 product: usb product id of FTDI device
Todd Brochad034442011-05-25 15:05:29 -0700149 serialname: string of device serialname/number as defined in FTDI eeprom.
Todd Brochdbb09982011-10-02 07:14:26 -0700150 interfaces: list of strings of interface types the server will instantiate
Simran Basia23c1392013-08-06 14:59:10 -0700151 version: String. Servo board version. Examples: servo_v1, servo_v2,
152 servo_v2_r0, servo_v3
Yusuf Mohsinally29e30d22014-01-14 15:29:17 -0800153 usbkm232: String. Optional. Path to USB-KM232 device which allow for
Kevin Chengdc3befd2016-07-15 12:34:00 -0700154 sending keyboard commands to DUTs that do not have built in
155 keyboards. Used in FAFT tests. Use 'atmega' for on board AVR MCU.
156 e.g. '/dev/ttyUSB0' or 'atmega'
Todd Brochdbb09982011-10-02 07:14:26 -0700157
158 Raises:
159 ServodError: if unable to locate init method for particular interface
Todd Broche505b8d2011-03-21 18:19:54 -0700160 """
161 self._logger = logging.getLogger("Servod")
162 self._logger.debug("")
163 self._vendor = vendor
164 self._product = product
Mary Ruthven13389642017-02-14 12:15:34 -0800165 self._devices = []
Kevin Cheng4b4f0022016-09-09 02:37:07 -0700166 self._serialnames = {self.MAIN_SERIAL: serialname}
Todd Broche505b8d2011-03-21 18:19:54 -0700167 self._syscfg = config
Kevin Cheng9071ed92016-06-21 14:37:54 -0700168 # Hold the last image path so we can reduce downloads to the usb device.
169 self._image_path = None
Todd Broche505b8d2011-03-21 18:19:54 -0700170 # list of objects (Fi2c, Fgpio) to physical interfaces (gpio, i2c) that ftdi
171 # interfaces are mapped to
172 self._interface_list = []
173 # Dict of Dict to map control name, function name to to tuple (params, drv)
174 # Ex) _drv_dict[name]['get'] = (params, drv)
175 self._drv_dict = {}
J. Richard Barnettee2820552013-03-14 16:13:46 -0700176 self._board = board
Simran Basia23c1392013-08-06 14:59:10 -0700177 self._version = version
Yusuf Mohsinally29e30d22014-01-14 15:29:17 -0800178 self._usbkm232 = usbkm232
Kevin Chengc49494e2016-07-25 12:13:38 -0700179 # Seed the random generator with the serial to differentiate from other
180 # servod processes.
181 random.seed(serialname if serialname else time.time())
Todd Brochdbb09982011-10-02 07:14:26 -0700182 if not interfaces:
Todd Brochb21d8042014-05-15 12:54:54 -0700183 try:
184 interfaces = servo_interfaces.INTERFACE_BOARDS[board][vendor][product]
185 except KeyError:
186 interfaces = servo_interfaces.INTERFACE_DEFAULTS[vendor][product]
Todd Brochdbb09982011-10-02 07:14:26 -0700187
Kevin Chengdc3befd2016-07-15 12:34:00 -0700188 self.init_servo_interfaces(vendor, product, serialname, interfaces)
Kevin Cheng16304d12016-07-08 11:56:55 -0700189 servo_postinit.post_init(self)
Danny Chan662b6022015-11-04 17:34:53 -0800190
Mary Ruthven13389642017-02-14 12:15:34 -0800191 def reinitialize(self):
192 """Reinitialize all interfaces that support reinitialization"""
193
194 # If all of the devices that were originally connected are not connected
195 # now, wait up to max_tries * sleep_time for the devices to reconnect
196 max_tries = 10
197 sleep_time = 0.5
198 for i in range(max_tries):
Wai-Hong Tam1f9e9a72017-05-02 14:14:46 -0700199 for vid, pid, serialname in self._devices:
200 for current in usb.core.find(idVendor=vid, idProduct=pid,
201 find_all=True):
202 current_serial = usb.util.get_string(current, 256,
203 current.iSerialNumber)
204 if not serialname or serialname == current_serial:
205 # This device still available
206 break
207 else:
208 self._logger.warn('Servo USB device not found: %x:%x serial:%s',
209 vid, pid, serialname)
210 break
211 else:
212 # All the devices found. Reinitialize the interfaces...
Mary Ruthven13389642017-02-14 12:15:34 -0800213 break
Wai-Hong Tam1f9e9a72017-05-02 14:14:46 -0700214 time.sleep(sleep_time)
215 else:
216 raise ServodError('Servo USB device not found during reinitialize')
Mary Ruthven13389642017-02-14 12:15:34 -0800217
218 for i, interface in enumerate(self._interface_list):
219 if hasattr(interface, "reinitialize"):
220 interface.reinitialize()
221 else:
222 self._logger.debug("interface %d has no reset functionality", i)
223
Wai-Hong Tam4544c302017-05-24 19:44:53 -0700224 def get_servo_interfaces(self, position, size):
225 """Get the list of servo interfaces.
226
227 Args:
228 position: The index the first interface to get.
229 size: The number of the interfaces.
230 """
231 return self._interface_list[position:(position + size)]
232
233 def set_servo_interfaces(self, position, interfaces):
234 """Set the list of servo interfaces.
235
236 Args:
237 position: The index the first interface to set.
238 interfaces: The list of interfaces to set.
239 """
240 size = len(interfaces)
241 self._interface_list[position:(position + size)] = interfaces
242
Yusuf Mohsinally29e30d22014-01-14 15:29:17 -0800243 def _init_keyboard_handler(self, servo, board=''):
244 """Initialize the correct keyboard handler for board.
245
Kevin Chengdc3befd2016-07-15 12:34:00 -0700246 Args:
247 servo: servo object.
248 board: string, board name.
Yusuf Mohsinally29e30d22014-01-14 15:29:17 -0800249
Kevin Chengdc3befd2016-07-15 12:34:00 -0700250 Returns:
251 keyboard handler object.
Yusuf Mohsinally29e30d22014-01-14 15:29:17 -0800252 """
253 if board == 'parrot':
254 return keyboard_handlers.ParrotHandler(servo)
255 elif board == 'stout':
256 return keyboard_handlers.StoutHandler(servo)
PeggyChuang4f07d872015-08-07 12:11:38 +0800257 elif board in ('buddy', 'cranky', 'guado', 'jecht', 'mccloud', 'monroe',
Tom Wai-Hong Tamd64164c2015-04-29 07:59:45 +0800258 'ninja', 'nyan_kitty', 'panther', 'rikku', 'stumpy',
philipchenfc9eea12016-09-21 17:36:54 -0700259 'sumo', 'tidus', 'tricky', 'veyron_fievel', 'veyron_mickey',
Gaggery Tsaid1eabc72017-07-20 19:15:49 +0800260 'veyron_rialto', 'veyron_tiger', 'zako', 'fizz'):
Yusuf Mohsinally29e30d22014-01-14 15:29:17 -0800261 if self._usbkm232 is None:
Tom Wai-Hong Tam4c7d6472016-03-08 06:55:50 +0800262 logging.info("No device path specified for usbkm232 handler. Use "
263 "the servo atmega chip to handle.")
264 self._usbkm232 = 'atmega'
Danny Chan662b6022015-11-04 17:34:53 -0800265 if self._usbkm232 == 'atmega':
266 # Use servo onboard keyboard emulator.
Nick Sanders78423782015-11-09 14:28:19 -0800267 self.set('atmega_rst', 'on')
Nick Sandersbc836282015-12-08 21:19:23 -0800268 self.set('at_hwb', 'off')
Nick Sanders78423782015-11-09 14:28:19 -0800269 self.set('atmega_rst', 'off')
Danny Chan662b6022015-11-04 17:34:53 -0800270 self._usbkm232 = self.get('atmega_pty')
Kevin Cheng810fc782016-11-01 12:36:46 -0700271 # We don't need to set the atmega uart settings if we're a servo v4.
Aseda Aboagye1d8477b2017-05-10 17:24:31 -0700272 if 'servo_v4' not in self._version:
Kevin Cheng810fc782016-11-01 12:36:46 -0700273 self.set('atmega_baudrate', '9600')
274 self.set('atmega_bits', 'eight')
275 self.set('atmega_parity', 'none')
276 self.set('atmega_sbits', 'one')
277 self.set('usb_mux_sel4', 'on')
278 self.set('usb_mux_oe4', 'on')
279 # Allow atmega bootup time.
280 time.sleep(1.0)
Danny Chan662b6022015-11-04 17:34:53 -0800281 self._logger.info('USBKM232: %s', self._usbkm232)
Yusuf Mohsinally29e30d22014-01-14 15:29:17 -0800282 return keyboard_handlers.USBkm232Handler(servo, self._usbkm232)
283 else:
Tom Wai-Hong Tamd64164c2015-04-29 07:59:45 +0800284 # The following boards don't use Chrome EC.
285 if board in ('alex', 'butterfly', 'lumpy', 'zgb'):
286 return keyboard_handlers.MatrixKeyboardHandler(servo)
287 return keyboard_handlers.ChromeECHandler(servo)
Yusuf Mohsinally29e30d22014-01-14 15:29:17 -0800288
Todd Broch3ec8df02012-11-20 10:53:03 -0800289 def __del__(self):
290 """Servod deconstructor."""
291 for interface in self._interface_list:
292 del(interface)
293
Kevin Chengdc3befd2016-07-15 12:34:00 -0700294 def _init_ftdi_dummy(self, vendor, product, serialname, interface):
Kevin Cheng042f4932016-07-19 10:46:00 -0700295 """Dummy interface for ftdi devices.
296
297 This is a dummy function specifically for ftdi devices to not initialize
298 anything but to help pad the interface list.
299
300 Returns:
301 None.
302 """
303 return None
304
Kevin Chengdc3befd2016-07-15 12:34:00 -0700305 def _init_ftdi_gpio(self, vendor, product, serialname, interface):
Todd Broche505b8d2011-03-21 18:19:54 -0700306 """Initialize gpio driver interface and open for use.
307
308 Args:
309 interface: interface number of FTDI device to use.
310
311 Returns:
312 Instance object of interface.
Todd Broch6de9dc62012-04-09 15:23:53 -0700313
314 Raises:
315 ServodError: If init fails
Todd Broche505b8d2011-03-21 18:19:54 -0700316 """
Kevin Chengdc3befd2016-07-15 12:34:00 -0700317 fobj = ftdigpio.Fgpio(vendor, product, interface, serialname)
Todd Broch6de9dc62012-04-09 15:23:53 -0700318 try:
319 fobj.open()
320 except ftdigpio.FgpioError as e:
321 raise ServodError('Opening gpio interface. %s ( %d )' % (e.msg, e.value))
322
Todd Broche505b8d2011-03-21 18:19:54 -0700323 return fobj
324
Kevin Chengdc3befd2016-07-15 12:34:00 -0700325 def _init_stm32_uart(self, vendor, product, serialname, interface):
Nick Sanders97bc4462016-01-04 15:37:31 -0800326 """Initialize stm32 uart interface and open for use
327
328 Note, the uart runs in a separate thread. Users wishing to
329 interact with it will query control for the pty's pathname and connect
330 with their favorite console program. For example:
331 cu -l /dev/pts/22
332
333 Args:
334 interface: dict of interface parameters.
335
336 Returns:
337 Instance object of interface
338
339 Raises:
340 ServodError: Raised on init failure.
341 """
342 self._logger.info("Suart: interface: %s" % interface)
Kevin Chengdc3befd2016-07-15 12:34:00 -0700343 sobj = stm32uart.Suart(vendor, product, interface['interface'],
344 serialname)
Nick Sanders97bc4462016-01-04 15:37:31 -0800345
346 try:
347 sobj.run()
348 except stm32uart.SuartError as e:
349 raise ServodError('Running uart interface. %s ( %d )' % (e.msg, e.value))
350
351 self._logger.info("%s" % sobj.get_pty())
352 return sobj
353
Kevin Chengdc3befd2016-07-15 12:34:00 -0700354 def _init_stm32_gpio(self, vendor, product, serialname, interface):
Nick Sanders97bc4462016-01-04 15:37:31 -0800355 """Initialize stm32 gpio interface.
356 Args:
Wai-Hong Tam564c1702017-04-24 09:23:38 -0700357 interface: dict of interface parameters.
Nick Sanders97bc4462016-01-04 15:37:31 -0800358
359 Returns:
360 Instance object of interface
361
362 Raises:
363 SgpioError: Raised on init failure.
364 """
Kevin Cheng71a046f2016-06-13 16:37:58 -0700365 interface_number = interface
366 # Interface could be a dict.
367 if type(interface) is dict:
368 interface_number = interface['interface']
369 self._logger.info("Sgpio: interface: %s" % interface_number)
Kevin Chengdc3befd2016-07-15 12:34:00 -0700370 return stm32gpio.Sgpio(vendor, product, interface_number, serialname)
Nick Sanders97bc4462016-01-04 15:37:31 -0800371
Kevin Chengdc3befd2016-07-15 12:34:00 -0700372 def _init_stm32_i2c(self, vendor, product, serialname, interface):
Nick Sanders97bc4462016-01-04 15:37:31 -0800373 """Initialize stm32 USB to I2C bridge interface and open for use
374
375 Args:
Wai-Hong Tam564c1702017-04-24 09:23:38 -0700376 interface: dict of interface parameters.
Nick Sanders97bc4462016-01-04 15:37:31 -0800377
378 Returns:
379 Instance object of interface.
380
381 Raises:
382 Si2cError: Raised on init failure.
383 """
384 self._logger.info("Si2cBus: interface: %s" % interface)
Nick Sandersa3649712016-03-01 16:53:52 -0800385 port = interface.get('port', 0)
Kevin Chengdc3befd2016-07-15 12:34:00 -0700386 return stm32i2c.Si2cBus(vendor, product, interface['interface'],
387 port=port, serialname=serialname)
Nick Sanders97bc4462016-01-04 15:37:31 -0800388
Kevin Chengdc3befd2016-07-15 12:34:00 -0700389 def _init_bb_adc(self, vendor, product, serialname, interface):
Aaron.Chuang88eff332014-07-31 08:32:00 +0800390 """Initalize beaglebone ADC interface."""
391 return bbadc.BBadc()
392
Kevin Chengdc3befd2016-07-15 12:34:00 -0700393 def _init_bb_gpio(self, vendor, product, serialname, interface):
Simran Basie750a342013-03-12 13:45:26 -0700394 """Initalize beaglebone gpio interface."""
Simran Basi5492bde2013-05-16 17:08:47 -0700395 return bbgpio.BBgpio()
Simran Basie750a342013-03-12 13:45:26 -0700396
Kevin Chengdc3befd2016-07-15 12:34:00 -0700397 def _init_ftdi_i2c(self, vendor, product, serialname, interface):
Todd Broche505b8d2011-03-21 18:19:54 -0700398 """Initialize i2c interface and open for use.
399
400 Args:
401 interface: interface number of FTDI device to use
402
403 Returns:
404 Instance object of interface
Todd Broch6de9dc62012-04-09 15:23:53 -0700405
406 Raises:
407 ServodError: If init fails
Todd Broche505b8d2011-03-21 18:19:54 -0700408 """
Kevin Chengdc3befd2016-07-15 12:34:00 -0700409 fobj = ftdii2c.Fi2c(vendor, product, interface, serialname)
Todd Broch6de9dc62012-04-09 15:23:53 -0700410 try:
411 fobj.open()
412 except ftdii2c.Fi2cError as e:
413 raise ServodError('Opening i2c interface. %s ( %d )' % (e.msg, e.value))
414
Todd Broche505b8d2011-03-21 18:19:54 -0700415 # Set the frequency of operation of the i2c bus.
416 # TODO(tbroch) make configureable
417 fobj.setclock(MAX_I2C_CLOCK_HZ)
Todd Broch6de9dc62012-04-09 15:23:53 -0700418
Todd Broche505b8d2011-03-21 18:19:54 -0700419 return fobj
420
Simran Basie750a342013-03-12 13:45:26 -0700421 # TODO (sbasi) crbug.com/187489 - Implement bb_i2c.
422 def _init_bb_i2c(self, interface):
423 """Initalize beaglebone i2c interface."""
Simran Basia9ad25e2013-04-23 11:57:00 -0700424 return bbi2c.BBi2c(interface)
Simran Basie750a342013-03-12 13:45:26 -0700425
Kevin Chengdc3befd2016-07-15 12:34:00 -0700426 def _init_dev_i2c(self, vendor, product, serialname, interface):
Rong Changc6c8c022014-08-11 14:07:11 +0800427 """Initalize Linux i2c-dev interface."""
428 return i2cbus.I2CBus('/dev/i2c-%d' % interface['bus_num'])
429
Kevin Chengdc3befd2016-07-15 12:34:00 -0700430 def _init_ftdi_uart(self, vendor, product, serialname, interface):
Simran Basie750a342013-03-12 13:45:26 -0700431 """Initialize ftdi uart inteface and open for use
Todd Broch47c43f42011-05-26 15:11:31 -0700432
433 Note, the uart runs in a separate thread (pthreads). Users wishing to
434 interact with it will query control for the pty's pathname and connect
435 with there favorite console program. For example:
436 cu -l /dev/pts/22
437
438 Args:
439 interface: interface number of FTDI device to use
440
441 Returns:
442 Instance object of interface
Todd Broch6de9dc62012-04-09 15:23:53 -0700443
444 Raises:
445 ServodError: If init fails
Todd Broch47c43f42011-05-26 15:11:31 -0700446 """
Kevin Chengdc3befd2016-07-15 12:34:00 -0700447 fobj = ftdiuart.Fuart(vendor, product, interface, serialname)
Todd Broch6de9dc62012-04-09 15:23:53 -0700448 try:
449 fobj.run()
450 except ftdiuart.FuartError as e:
451 raise ServodError('Running uart interface. %s ( %d )' % (e.msg, e.value))
452
Todd Broch47c43f42011-05-26 15:11:31 -0700453 self._logger.info("%s" % fobj.get_pty())
454 return fobj
455
Simran Basie750a342013-03-12 13:45:26 -0700456 # TODO (sbasi) crbug.com/187492 - Implement bbuart.
Kevin Chengdc3befd2016-07-15 12:34:00 -0700457 def _init_bb_uart(self, vendor, product, serialname, interface):
Simran Basie750a342013-03-12 13:45:26 -0700458 """Initalize beaglebone uart interface."""
Simran Basi949309b2013-05-31 15:12:15 -0700459 logging.debug('UART INTERFACE: %s', interface)
460 return bbuart.BBuart(interface)
Simran Basie750a342013-03-12 13:45:26 -0700461
Kevin Chengdc3befd2016-07-15 12:34:00 -0700462 def _init_ftdi_gpiouart(self, vendor, product, serialname,
463 interface):
Todd Broch888da782011-10-07 14:29:09 -0700464 """Initialize special gpio + uart interface and open for use
465
466 Note, the uart runs in a separate thread (pthreads). Users wishing to
467 interact with it will query control for the pty's pathname and connect
468 with there favorite console program. For example:
469 cu -l /dev/pts/22
470
471 Args:
472 interface: interface number of FTDI device to use
473
474 Returns:
475 Instance objects of interface
Todd Broch6de9dc62012-04-09 15:23:53 -0700476
477 Raises:
478 ServodError: If init fails
Todd Broch888da782011-10-07 14:29:09 -0700479 """
Kevin Chengce7dafd2016-08-02 11:11:38 -0700480 fgpio = self._init_ftdi_gpio(vendor, product, serialname, interface)
Kevin Chengdc3befd2016-07-15 12:34:00 -0700481 fuart = ftdiuart.Fuart(vendor, product, interface, serialname, fgpio._fc)
Todd Broch6de9dc62012-04-09 15:23:53 -0700482 try:
483 fuart.run()
484 except ftdiuart.FuartError as e:
485 raise ServodError('Running uart interface. %s ( %d )' % (e.msg, e.value))
486
Todd Broch888da782011-10-07 14:29:09 -0700487 self._logger.info("uart pty: %s" % fuart.get_pty())
488 return fgpio, fuart
489
Kevin Chengdc3befd2016-07-15 12:34:00 -0700490 def _init_ec3po_uart(self, vendor, product, serialname, interface):
Aseda Aboagyea4922212015-11-20 15:19:08 -0800491 """Initialize EC-3PO console interpreter interface.
492
493 Args:
494 interface: A dictionary representing the interface.
495
496 Returns:
497 An EC3PO object representing the EC-3PO interface or None if there's no
498 interface for the USB PD UART.
499 """
Wai-Hong Tam6c0fa592017-04-21 12:41:33 -0700500 raw_uart_name = interface['raw_pty']
501 if self._syscfg.is_control(raw_uart_name):
Nick Sanders97bc4462016-01-04 15:37:31 -0800502 raw_ec_uart = self.get(raw_uart_name)
Wai-Hong Tam6c0fa592017-04-21 12:41:33 -0700503 return ec3po_interface.EC3PO(raw_ec_uart)
Aseda Aboagyea4922212015-11-20 15:19:08 -0800504 else:
Wai-Hong Tam6c0fa592017-04-21 12:41:33 -0700505 # The overlay doesn't have the raw PTY defined, therefore we can skip
506 # initializing this interface since no control relies on it.
507 self._logger.debug(
508 'Skip initializing EC3PO for %s, no control specified.',
509 raw_uart_name)
510 return None
Aseda Aboagyea4922212015-11-20 15:19:08 -0800511
Tom Wai-Hong Tam28f0a5f2012-08-21 12:49:57 +0800512 def _camel_case(self, string):
513 output = ''
514 for s in string.split('_'):
515 if output:
516 output += s.capitalize()
517 else:
518 output = s
519 return output
520
Wai-Hong Tam4544c302017-05-24 19:44:53 -0700521 def clear_cached_drv(self):
522 """Clear the cached drivers.
523
524 The drivers are cached in the Dict _drv_dict when a control is got or set.
525 When the servo interfaces are relocated, the cached values may become wrong.
526 Should call this method to clear the cached values.
527 """
528 self._drv_dict = {}
529
Todd Broche505b8d2011-03-21 18:19:54 -0700530 def _get_param_drv(self, control_name, is_get=True):
531 """Get access to driver for a given control.
532
533 Note, some controls have different parameter dictionaries for 'getting' the
534 control's value versus 'setting' it. Boolean is_get distinguishes which is
535 being requested.
536
537 Args:
538 control_name: string name of control
539 is_get: boolean to determine
540
541 Returns:
542 tuple (param, drv) where:
543 param: param dictionary for control
544 drv: instance object of driver for particular control
545
546 Raises:
547 ServodError: Error occurred while examining params dict
548 """
549 self._logger.debug("")
550 # if already setup just return tuple from driver dict
551 if control_name in self._drv_dict:
552 if is_get and ('get' in self._drv_dict[control_name]):
553 return self._drv_dict[control_name]['get']
554 if not is_get and ('set' in self._drv_dict[control_name]):
555 return self._drv_dict[control_name]['set']
556
557 params = self._syscfg.lookup_control_params(control_name, is_get)
558 if 'drv' not in params:
559 self._logger.error("Unable to determine driver for %s" % control_name)
560 raise ServodError("'drv' key not found in params dict")
561 if 'interface' not in params:
562 self._logger.error("Unable to determine interface for %s" %
563 control_name)
Todd Broche505b8d2011-03-21 18:19:54 -0700564 raise ServodError("'interface' key not found in params dict")
Simran Basi668be0e2013-08-07 11:54:50 -0700565
Aseda Aboagye1d8477b2017-05-10 17:24:31 -0700566 # Find the candidate servos. Using servo_v4 with a servo_micro connected as
567 # an example, the following shows the priority for selecting the interface.
568 #
569 # 1. The full name. (e.g. - 'servo_v4_with_servo_micro_interface')
570 # 2. servo_micro_interface
571 # 3. servo_v4_interface
572 # 4. Fallback to the default, interface.
573 candidates = [ self._version ]
574 candidates.extend(reversed(self._version.split('_with_')))
575
576 interface_id = 'unknown'
577 for c in candidates:
578 interface_name = '%s_interface' % c
579 if interface_name in params:
580 interface_id = params[interface_name]
581 self._logger.debug('Using %s parameter.' % interface_name)
582 break
583
584 # Use the default interface value if we couldn't find a more specific
585 # interface.
586 if interface_id == 'unknown':
587 interface_id = params['interface']
588 self._logger.debug('Using default interface parameter.')
589
J. Richard Barnette275d9fd2014-02-11 14:38:54 -0800590 if interface_id == 'servo':
591 interface = self
Simran Basi668be0e2013-08-07 11:54:50 -0700592 else:
Wai-Hong Tam564c1702017-04-24 09:23:38 -0700593 index = int(interface_id)
J. Richard Barnette275d9fd2014-02-11 14:38:54 -0800594 interface = self._interface_list[index]
Simran Basi668be0e2013-08-07 11:54:50 -0700595
Todd Broche505b8d2011-03-21 18:19:54 -0700596 drv_name = params['drv']
Wai-Hong Tam4c09eff2017-02-17 11:46:19 -0800597 drv_module = getattr(servo_drv, drv_name)
Tom Wai-Hong Tam28f0a5f2012-08-21 12:49:57 +0800598 drv_class = getattr(drv_module, self._camel_case(drv_name))
Todd Broche505b8d2011-03-21 18:19:54 -0700599 drv = drv_class(interface, params)
600 if control_name not in self._drv_dict:
601 self._drv_dict[control_name] = {}
602 if is_get:
603 self._drv_dict[control_name]['get'] = (params, drv)
604 else:
605 self._drv_dict[control_name]['set'] = (params, drv)
606 return (params, drv)
607
608 def doc_all(self):
609 """Return all documenation for controls.
610
611 Returns:
612 string of <doc> text in config file (xml) and the params dictionary for
613 all controls.
614
615 For example:
616 warm_reset :: Reset the device warmly
617 ------------------------> {'interface': '1', 'map': 'onoff_i', ... }
618 """
619 return self._syscfg.display_config()
620
621 def doc(self, name):
622 """Retreive doc string in system config file for given control name.
623
624 Args:
625 name: name string of control to get doc string
626
627 Returns:
628 doc string of name
629
630 Raises:
631 NameError: if fails to locate control
632 """
633 self._logger.debug("name(%s)" % (name))
634 if self._syscfg.is_control(name):
635 return self._syscfg.get_control_docstring(name)
636 else:
637 raise NameError("No control %s" %name)
638
Fang Deng90377712013-06-03 15:51:48 -0700639 def _switch_usbkey(self, mux_direction):
640 """Connect USB flash stick to either servo or DUT.
641
642 This function switches 'usb_mux_sel1' to provide electrical
643 connection between the USB port J3 and either servo or DUT side.
644
645 Switching the usb mux is accompanied by powercycling
646 of the USB stick, because it sometimes gets wedged if the mux
647 is switched while the stick power is on.
648
649 Args:
650 mux_direction: "servo_sees_usbkey" or "dut_sees_usbkey".
651 """
652 self.set(self._USB_J3_PWR, self._USB_J3_PWR_OFF)
653 time.sleep(self._USB_POWEROFF_DELAY)
654 self.set(self._USB_J3, mux_direction)
655 time.sleep(self._USB_POWEROFF_DELAY)
656 self.set(self._USB_J3_PWR, self._USB_J3_PWR_ON)
657 if mux_direction == self._USB_J3_TO_SERVO:
658 time.sleep(self._USB_DETECTION_DELAY)
659
Simran Basia9f41032012-05-11 14:21:58 -0700660 def _get_usb_port_set(self):
661 """Gets a set of USB disks currently connected to the system
662
663 Returns:
664 A set of USB disk paths.
665 """
666 usb_set = fnmatch.filter(os.listdir("/dev/"), "sd[a-z]")
667 return set(["/dev/" + dev for dev in usb_set])
668
Kevin Cheng5595b342016-09-29 15:51:01 -0700669 @contextlib.contextmanager
670 def _block_other_servod(self, timeout=None):
Kevin Chengc49494e2016-07-25 12:13:38 -0700671 """Block other servod processes by locking a file.
672
673 To enable multiple servods processes to safely probe_host_usb_dev, we use
674 a given lock file to signal other servod processes that we're probing
Kevin Cheng5595b342016-09-29 15:51:01 -0700675 for a usb device. This will be a context manager that will return
676 if the block was successful or not.
Kevin Chengc49494e2016-07-25 12:13:38 -0700677
678 If the lock file exists, we open it and try to lock it.
679 - If another servod processes has locked it already, we'll sleep a random
680 amount of time and try again, we'll keep doing that until
Kevin Cheng5595b342016-09-29 15:51:01 -0700681 timeout amount of time has passed.
Kevin Chengc49494e2016-07-25 12:13:38 -0700682
Kevin Cheng5595b342016-09-29 15:51:01 -0700683 - If we're able to lock the file, we'll yield that the block was successful
684 and upon return, unlock the file and exit out.
Kevin Chengc49494e2016-07-25 12:13:38 -0700685
686 This blocking behavior is only enabled if the lock file exists, if it
687 doesn't, then we pretend the block was successful.
688
Kevin Cheng5595b342016-09-29 15:51:01 -0700689 Args:
690 timeout: Max waiting time for the block to succeed.
Kevin Chengc49494e2016-07-25 12:13:38 -0700691 """
Kevin Cheng5595b342016-09-29 15:51:01 -0700692 if not os.path.exists(self._USB_LOCK_FILE):
693 # No lock file so we'll pretend the block was a success.
694 yield True
695 else:
Kevin Chengc49494e2016-07-25 12:13:38 -0700696 start_time = datetime.datetime.now()
697 while True:
Kevin Cheng5595b342016-09-29 15:51:01 -0700698 with open(self._USB_LOCK_FILE) as lock_file:
699 try:
700 fcntl.flock(lock_file, fcntl.LOCK_EX | fcntl.LOCK_NB)
701 yield True
702 fcntl.flock(lock_file, fcntl.LOCK_UN)
703 break
704 except IOError:
705 current_time = datetime.datetime.now()
706 current_wait_time = (current_time - start_time).total_seconds()
707 if timeout and current_wait_time > timeout:
708 yield False
709 break
Kevin Chengc49494e2016-07-25 12:13:38 -0700710 # Sleep random amount.
711 sleep_time = time.sleep(random.random())
Kevin Chengc49494e2016-07-25 12:13:38 -0700712
Kevin Cheng8fcf06c2016-10-12 08:02:44 -0700713 def safe_switch_usbkey_power(self, power_state, timeout=0):
Kevin Cheng5595b342016-09-29 15:51:01 -0700714 """Toggle the usb power safely.
715
716 We'll make sure we're the only servod process toggling the usbkey power.
Kevin Chengc49494e2016-07-25 12:13:38 -0700717
718 Args:
Kevin Cheng5595b342016-09-29 15:51:01 -0700719 power_state: The setting to set for the usbkey power.
Kevin Cheng8fcf06c2016-10-12 08:02:44 -0700720 timeout: Timeout to wait for blocking other servod processes, default is
721 no timeout.
Kevin Chengc49494e2016-07-25 12:13:38 -0700722
Kevin Cheng5595b342016-09-29 15:51:01 -0700723 Returns:
724 An empty string to appease the xmlrpc gods.
725 """
726 with self._block_other_servod(timeout=timeout):
727 if power_state != self.get(self._USB_J3_PWR):
728 self.set(self._USB_J3_PWR, power_state)
729 return ''
730
Kevin Cheng8fcf06c2016-10-12 08:02:44 -0700731 def safe_switch_usbkey(self, mux_direction, timeout=0):
Kevin Cheng5595b342016-09-29 15:51:01 -0700732 """Toggle the usb direction safely.
733
734 We'll make sure we're the only servod process toggling the usbkey direction.
735
736 Args:
737 power_state: The setting to set for the usbkey power.
Kevin Cheng8fcf06c2016-10-12 08:02:44 -0700738 timeout: Timeout to wait for blocking other servod processes, default is
739 no timeout.
Kevin Cheng5595b342016-09-29 15:51:01 -0700740
741 Returns:
742 An empty string to appease the xmlrpc gods.
743 """
744 with self._block_other_servod(timeout=timeout):
745 self._switch_usbkey(mux_direction)
746 return ''
747
748 def probe_host_usb_dev(self, timeout=_MAX_USB_LOCK_WAIT):
Simran Basia9f41032012-05-11 14:21:58 -0700749 """Probe the USB disk device plugged in the servo from the host side.
750
751 Method can fail by:
752 1) Having multiple servos connected and returning incorrect /dev/sdX of
Kevin Chengc49494e2016-07-25 12:13:38 -0700753 another servo unless _USB_LOCK_FILE exists on the servo host. If that
754 file exists, then it is safe to probe for usb devices among multiple
755 servod instances.
Simran Basia9f41032012-05-11 14:21:58 -0700756 2) Finding multiple /dev/sdX and returning None.
757
Kevin Cheng5595b342016-09-29 15:51:01 -0700758 Args:
759 timeout: Timeout to wait for blocking other servod processes.
760
Simran Basia9f41032012-05-11 14:21:58 -0700761 Returns:
Kevin Chengc49494e2016-07-25 12:13:38 -0700762 USB disk path if one and only one USB disk path is found, otherwise an
763 empty string.
Simran Basia9f41032012-05-11 14:21:58 -0700764 """
Kevin Cheng5595b342016-09-29 15:51:01 -0700765 with self._block_other_servod(timeout=timeout) as block_success:
766 if not block_success:
767 return ''
Kevin Chengc49494e2016-07-25 12:13:38 -0700768
Kevin Cheng5595b342016-09-29 15:51:01 -0700769 original_value = self.get(self._USB_J3)
770 original_usb_power = self.get(self._USB_J3_PWR)
771 # Make the host unable to see the USB disk.
772 if (original_usb_power == self._USB_J3_PWR_ON and
773 original_value != self._USB_J3_TO_DUT):
774 self._switch_usbkey(self._USB_J3_TO_DUT)
775 no_usb_set = self._get_usb_port_set()
Simran Basia9f41032012-05-11 14:21:58 -0700776
Kevin Cheng5595b342016-09-29 15:51:01 -0700777 # Make the host able to see the USB disk.
778 self._switch_usbkey(self._USB_J3_TO_SERVO)
779 has_usb_set = self._get_usb_port_set()
Fang Deng90377712013-06-03 15:51:48 -0700780
Kevin Cheng5595b342016-09-29 15:51:01 -0700781 # Back to its original value.
782 if original_value != self._USB_J3_TO_SERVO:
783 self._switch_usbkey(original_value)
784 if original_usb_power != self._USB_J3_PWR_ON:
785 self.set(self._USB_J3_PWR, self._USB_J3_PWR_OFF)
786 time.sleep(self._USB_POWEROFF_DELAY)
Fang Deng90377712013-06-03 15:51:48 -0700787
Kevin Cheng5595b342016-09-29 15:51:01 -0700788 # Subtract the two sets to find the usb device.
789 diff_set = has_usb_set - no_usb_set
790 if len(diff_set) == 1:
791 return diff_set.pop()
792 else:
793 return ''
Simran Basia9f41032012-05-11 14:21:58 -0700794
Kevin Cheng85831332016-10-13 13:14:44 -0700795 def download_image_to_usb(self, image_path, probe_timeout=_MAX_USB_LOCK_WAIT):
Simran Basia9f41032012-05-11 14:21:58 -0700796 """Download image and save to the USB device found by probe_host_usb_dev.
797 If the image_path is a URL, it will download this url to the USB path;
798 otherwise it will simply copy the image_path's contents to the USB path.
799
800 Args:
801 image_path: path or url to the recovery image.
Kevin Cheng85831332016-10-13 13:14:44 -0700802 probe_timeout: timeout for the probe to take.
Simran Basia9f41032012-05-11 14:21:58 -0700803
804 Returns:
805 True|False: True if process completed successfully, False if error
806 occurred.
807 Can't return None because XMLRPC doesn't allow it. PTAL at tbroch's
808 comment at the end of set().
809 """
810 self._logger.debug("image_path(%s)" % image_path)
811 self._logger.debug("Detecting USB stick device...")
Kevin Cheng85831332016-10-13 13:14:44 -0700812 usb_dev = self.probe_host_usb_dev(timeout=probe_timeout)
Simran Basia9f41032012-05-11 14:21:58 -0700813 if not usb_dev:
814 self._logger.error("No usb device connected to servo")
815 return False
816
Kevin Cheng9071ed92016-06-21 14:37:54 -0700817 # Let's check if we downloaded this last time and if so assume the image is
818 # still on the usb device and return True.
819 if self._image_path == image_path:
820 self._logger.debug("Image already on USB device, skipping transfer")
821 return True
822
Simran Basia9f41032012-05-11 14:21:58 -0700823 try:
824 if image_path.startswith(self._HTTP_PREFIX):
825 self._logger.debug("Image path is a URL, downloading image")
826 urllib.urlretrieve(image_path, usb_dev)
827 else:
828 shutil.copyfile(image_path, usb_dev)
829 except IOError as e:
Victor Dodonb7cddb82016-04-28 17:00:24 -0700830 self._logger.error("Failed to transfer image to USB device: %s ( %s ) ",
Simran Basia9f41032012-05-11 14:21:58 -0700831 e.strerror, e.errno)
832 return False
833 except urllib.ContentTooShortError:
834 self._logger.error("Failed to download URL: %s to USB device: %s",
835 image_path, usb_dev)
836 return False
837 except BaseException as e:
838 self._logger.error("Unexpected exception downloading %s to %s: %s",
839 image_path, usb_dev, str(e))
840 return False
J. Richard Barnettee4125af2013-02-26 18:31:56 -0800841 finally:
842 # We just plastered the partition table for a block device.
843 # Pass or fail, we mustn't go without telling the kernel about
844 # the change, or it will punish us with sporadic, hard-to-debug
845 # failures.
846 subprocess.call(["sync"])
847 subprocess.call(["blockdev", "--rereadpt", usb_dev])
Kevin Cheng9071ed92016-06-21 14:37:54 -0700848 self._image_path = image_path
Simran Basia9f41032012-05-11 14:21:58 -0700849 return True
850
851 def make_image_noninteractive(self):
852 """Makes the recovery image noninteractive.
853
854 A noninteractive image will reboot automatically after installation
855 instead of waiting for the USB device to be removed to initiate a system
856 reboot.
857
858 Mounts partition 1 of the image stored on usb_dev and creates a file
859 called "non_interactive" so that the image will become noninteractive.
860
861 Returns:
862 True|False: True if process completed successfully, False if error
863 occurred.
864 """
865 result = True
Kevin Chengc49494e2016-07-25 12:13:38 -0700866 usb_dev = self.probe_host_usb_dev()
Simran Basia9f41032012-05-11 14:21:58 -0700867 if not usb_dev:
868 self._logger.error("No usb device connected to servo")
869 return False
870 # Create TempDirectory
871 tmpdir = tempfile.mkdtemp()
872 if tmpdir:
873 # Mount drive to tmpdir.
874 partition_1 = "%s1" % usb_dev
875 rc = subprocess.call(["mount", partition_1, tmpdir])
876 if rc == 0:
877 # Create file 'non_interactive'
878 non_interactive_file = os.path.join(tmpdir, "non_interactive")
879 try:
880 open(non_interactive_file, "w").close()
881 except IOError as e:
882 self._logger.error("Failed to create file %s : %s ( %d )",
883 non_interactive_file, e.strerror, e.errno)
884 result = False
885 except BaseException as e:
886 self._logger.error("Unexpected Exception creating file %s : %s",
887 non_interactive_file, str(e))
888 result = False
889 # Unmount drive regardless if file creation worked or not.
890 rc = subprocess.call(["umount", partition_1])
891 if rc != 0:
892 self._logger.error("Failed to unmount USB Device")
893 result = False
894 else:
895 self._logger.error("Failed to mount USB Device")
896 result = False
897
898 # Delete tmpdir. May throw exception if 'umount' failed.
899 try:
900 os.rmdir(tmpdir)
901 except OSError as e:
902 self._logger.error("Failed to remove temp directory %s : %s",
903 tmpdir, str(e))
904 return False
905 except BaseException as e:
906 self._logger.error("Unexpected Exception removing tempdir %s : %s",
907 tmpdir, str(e))
908 return False
909 else:
910 self._logger.error("Failed to create temp directory.")
911 return False
912 return result
913
Todd Broch352b4b22013-03-22 09:48:40 -0700914 def set_get_all(self, cmds):
915 """Set &| get one or more control values.
916
917 Args:
918 cmds: list of control[:value] to get or set.
919
920 Returns:
921 rv: list of responses from calling get or set methods.
922 """
923 rv = []
924 for cmd in cmds:
925 if ':' in cmd:
926 (control, value) = cmd.split(':')
927 rv.append(self.set(control, value))
928 else:
929 rv.append(self.get(cmd))
930 return rv
931
Aseda Aboagye6921f602017-08-01 14:45:38 -0700932 def get_serial_number(self, name):
933 """Returns the desired serial number from the serialnames dict.
934
935 Args:
936 name: A string which is the key into the _serialnames dictionary.
937
938 Returns:
939 A string containing the serial number or "unknown".
940 """
941 if not name:
942 name = 'main'
943
944 try:
945 return self._serialnames[name]
946 except KeyError:
947 self._logger.debug("'%s_serialname' not found!", name)
948 return "unknown"
949
Todd Broche505b8d2011-03-21 18:19:54 -0700950 def get(self, name):
951 """Get control value.
952
953 Args:
954 name: name string of control
955
956 Returns:
957 Response from calling drv get method. Value is reformatted based on
958 control's dictionary parameters
959
960 Raises:
961 HwDriverError: Error occurred while using drv
962 """
963 self._logger.debug("name(%s)" % (name))
Aseda Aboagye6921f602017-08-01 14:45:38 -0700964 if 'serialname' in name:
965 return self.get_serial_number(name.split('serialname')[0].strip('_'))
966
Todd Broche505b8d2011-03-21 18:19:54 -0700967 (param, drv) = self._get_param_drv(name)
968 try:
969 val = drv.get()
970 rd_val = self._syscfg.reformat_val(param, val)
Todd Brochb042e7a2011-12-14 17:41:36 -0800971 self._logger.debug("%s = %s" % (name, rd_val))
Todd Broche505b8d2011-03-21 18:19:54 -0700972 return rd_val
Todd Brochfbc499d2011-06-16 16:09:58 -0700973 except AttributeError, error:
974 self._logger.error("Getting %s: %s" % (name, error))
975 raise
Vic Yangbe6cf262012-09-10 10:40:56 +0800976 except HwDriverError:
Todd Broche505b8d2011-03-21 18:19:54 -0700977 self._logger.error("Getting %s" % (name))
978 raise
Todd Brochd6061672012-05-11 15:52:47 -0700979
Todd Broche505b8d2011-03-21 18:19:54 -0700980 def get_all(self, verbose):
981 """Get all controls values.
982
983 Args:
984 verbose: Boolean on whether to return doc info as well
985
986 Returns:
987 string creating from trying to get all values of all controls. In case of
988 error attempting access to control, response is 'ERR'.
989 """
Vadim Bendeburyb07944c2013-01-16 10:47:10 -0800990 rsp = []
Todd Broche505b8d2011-03-21 18:19:54 -0700991 for name in self._syscfg.syscfg_dict['control']:
992 self._logger.debug("name = %s" %name)
993 try:
994 value = self.get(name)
995 except Exception:
996 value = "ERR"
997 pass
998 if verbose:
Vadim Bendeburyb07944c2013-01-16 10:47:10 -0800999 rsp.append("GET %s = %s :: %s" % (name, value, self.doc(name)))
Todd Broche505b8d2011-03-21 18:19:54 -07001000 else:
Vadim Bendeburyb07944c2013-01-16 10:47:10 -08001001 rsp.append("%s:%s" % (name, value))
1002 return '\n'.join(sorted(rsp))
Todd Broche505b8d2011-03-21 18:19:54 -07001003
1004 def set(self, name, wr_val_str):
1005 """Set control.
1006
1007 Args:
1008 name: name string of control
1009 wr_val_str: value string to write. Can be integer, float or a
1010 alpha-numerical that is mapped to a integer or float.
1011
1012 Raises:
1013 HwDriverError: Error occurred while using driver
1014 """
1015 self._logger.debug("name(%s) wr_val(%s)" % (name, wr_val_str))
1016 (params, drv) = self._get_param_drv(name, False)
1017 wr_val = self._syscfg.resolve_val(params, wr_val_str)
1018 try:
1019 drv.set(wr_val)
Vic Yangbe6cf262012-09-10 10:40:56 +08001020 except HwDriverError:
Todd Broche505b8d2011-03-21 18:19:54 -07001021 self._logger.error("Setting %s -> %s" % (name, wr_val_str))
1022 raise
1023 # TODO(tbroch) Figure out why despite allow_none=True for both xmlrpc server
1024 # & client I still have to return something to appease the
1025 # marshall/unmarshall
1026 return True
1027
Todd Brochd6061672012-05-11 15:52:47 -07001028 def hwinit(self, verbose=False):
1029 """Initialize all controls.
1030
1031 These values are part of the system config XML files of the form
1032 init=<value>. This command should be used by clients wishing to return the
1033 servo and DUT its connected to a known good/safe state.
1034
Vadim Bendeburybb51dd42013-01-31 13:47:46 -08001035 Note that initialization errors are ignored (as in some cases they could
1036 be caused by DUT firmware deficiencies). This might need to be fine tuned
1037 later.
1038
Todd Brochd6061672012-05-11 15:52:47 -07001039 Args:
1040 verbose: boolean, if True prints info about control initialized.
1041 Otherwise prints nothing.
Vadim Bendebury5934e4b2013-02-06 13:57:54 -08001042
1043 Returns:
1044 This function is called across RPC and as such is expected to return
1045 something unless transferring 'none' across is allowed. Hence adding a
1046 dummy return value to make things simpler.
Todd Brochd6061672012-05-11 15:52:47 -07001047 """
Todd Brochd9acf0a2012-12-05 13:43:06 -08001048 for control_name, value in self._syscfg.hwinit:
Todd Broch3ec8df02012-11-20 10:53:03 -08001049 try:
John Carey6fe2bbf2015-08-31 16:13:03 -07001050 # Workaround for bug chrome-os-partner:42349. Without this check, the
1051 # gpio will briefly pulse low if we set it from high to high.
1052 if self.get(control_name) != value:
Aseda Aboagyea849d462016-05-04 17:08:16 -07001053 self.set(control_name, value)
1054 if verbose:
1055 self._logger.info('Initialized %s to %s', control_name, value)
Todd Broch3ec8df02012-11-20 10:53:03 -08001056 except Exception as e:
Todd Broch3ec8df02012-11-20 10:53:03 -08001057 self._logger.error("Problem initializing %s -> %s :: %s",
1058 control_name, value, str(e))
Nick Sandersbc836282015-12-08 21:19:23 -08001059
1060 # Init keyboard after all the intefaces are up.
1061 self._keyboard = self._init_keyboard_handler(self, self._board)
Vadim Bendebury5934e4b2013-02-06 13:57:54 -08001062 return True
Todd Broch3ec8df02012-11-20 10:53:03 -08001063
Todd Broche505b8d2011-03-21 18:19:54 -07001064 def echo(self, echo):
1065 """Dummy echo function for testing/examples.
1066
1067 Args:
1068 echo: string to echo back to client
1069 """
1070 self._logger.debug("echo(%s)" % (echo))
1071 return "ECH0ING: %s" % (echo)
1072
J. Richard Barnettee2820552013-03-14 16:13:46 -07001073 def get_board(self):
1074 """Return the board specified at startup, if any."""
1075 return self._board
1076
Simran Basia23c1392013-08-06 14:59:10 -07001077 def get_version(self):
1078 """Get servo board version."""
1079 return self._version
1080
Yusuf Mohsinally29e30d22014-01-14 15:29:17 -08001081 def power_long_press(self):
1082 """Simulate a long power button press."""
1083 # After a long power press, the EC may ignore the next power
1084 # button press (at least on Alex). To guarantee that this
1085 # won't happen, we need to allow the EC one second to
1086 # collect itself.
1087 self._keyboard.power_long_press()
1088 return True
1089
1090 def power_normal_press(self):
1091 """Simulate a normal power button press."""
1092 self._keyboard.power_normal_press()
1093 return True
1094
1095 def power_short_press(self):
1096 """Simulate a short power button press."""
1097 self._keyboard.power_short_press()
1098 return True
1099
1100 def power_key(self, secs=''):
1101 """Simulate a power button press.
1102
1103 Args:
1104 secs: Time in seconds to simulate the keypress.
1105 """
1106 self._keyboard.power_key(secs)
1107 return True
1108
1109 def ctrl_d(self, press_secs=''):
1110 """Simulate Ctrl-d simultaneous button presses."""
1111 self._keyboard.ctrl_d(press_secs)
1112 return True
1113
Victor Dodone539cea2016-03-29 18:50:17 -07001114 def ctrl_u(self, press_secs=''):
Yusuf Mohsinally29e30d22014-01-14 15:29:17 -08001115 """Simulate Ctrl-u simultaneous button presses."""
Victor Dodone539cea2016-03-29 18:50:17 -07001116 self._keyboard.ctrl_u(press_secs)
Yusuf Mohsinally29e30d22014-01-14 15:29:17 -08001117 return True
1118
1119 def ctrl_enter(self, press_secs=''):
1120 """Simulate Ctrl-enter simultaneous button presses."""
1121 self._keyboard.ctrl_enter(press_secs)
1122 return True
1123
1124 def d_key(self, press_secs=''):
1125 """Simulate Enter key button press."""
1126 self._keyboard.d_key(press_secs)
1127 return True
1128
1129 def ctrl_key(self, press_secs=''):
1130 """Simulate Enter key button press."""
1131 self._keyboard.ctrl_key(press_secs)
1132 return True
1133
1134 def enter_key(self, press_secs=''):
1135 """Simulate Enter key button press."""
1136 self._keyboard.enter_key(press_secs)
1137 return True
1138
1139 def refresh_key(self, press_secs=''):
1140 """Simulate Refresh key (F3) button press."""
1141 self._keyboard.refresh_key(press_secs)
1142 return True
1143
1144 def ctrl_refresh_key(self, press_secs=''):
1145 """Simulate Ctrl and Refresh (F3) simultaneous press.
1146
1147 This key combination is an alternative of Space key.
1148 """
1149 self._keyboard.ctrl_refresh_key(press_secs)
1150 return True
1151
1152 def imaginary_key(self, press_secs=''):
1153 """Simulate imaginary key button press.
1154
1155 Maps to a key that doesn't physically exist.
1156 """
1157 self._keyboard.imaginary_key(press_secs)
1158 return True
1159
Todd Brochdbb09982011-10-02 07:14:26 -07001160
Vincent Palatin3acbbe52016-07-19 17:40:12 +02001161 def sysrq_x(self, press_secs=''):
1162 """Simulate Alt VolumeUp X simultaneous press.
1163
1164 This key combination is the kernel system request (sysrq) x.
1165 """
1166 self._keyboard.sysrq_x(press_secs)
1167 return True
1168
1169
Kevin Cheng4b4f0022016-09-09 02:37:07 -07001170 def get_servo_serials(self):
1171 """Return all the serials associated with this process."""
1172 return self._serialnames
1173
1174
Todd Broche505b8d2011-03-21 18:19:54 -07001175def test():
1176 """Integration testing.
1177
1178 TODO(tbroch) Enhance integration test and add unittest (see mox)
1179 """
1180 logging.basicConfig(level=logging.DEBUG,
1181 format="%(asctime)s - %(name)s - " +
1182 "%(levelname)s - %(message)s")
1183 # configure server & listen
1184 servod_obj = Servod(1)
Wai-Hong Tam564c1702017-04-24 09:23:38 -07001185 # 5 == number of interfaces on a FT4232H device
1186 for i in xrange(1, 5):
1187 if i == 2:
Todd Broche505b8d2011-03-21 18:19:54 -07001188 # its an i2c interface ... see __init__ for details and TODO to make
1189 # this configureable
1190 servod_obj._interface_list[i].wr_rd(0x21, [0], 1)
1191 else:
1192 # its a gpio interface
1193 servod_obj._interface_list[i].wr_rd(0)
1194
1195 server = SimpleXMLRPCServer.SimpleXMLRPCServer(("localhost", 9999),
1196 allow_none=True)
1197 server.register_introspection_functions()
1198 server.register_multicall_functions()
1199 server.register_instance(servod_obj)
1200 logging.info("Listening on localhost port 9999")
1201 server.serve_forever()
1202
1203if __name__ == "__main__":
1204 test()
1205
1206 # simple client transaction would look like
1207 """
1208 remote_uri = 'http://localhost:9999'
1209 client = xmlrpclib.ServerProxy(remote_uri, verbose=False)
1210 send_str = "Hello_there"
1211 print "Sent " + send_str + ", Recv " + client.echo(send_str)
1212 """