blob: 43cc000f40c3b213777e3a589745ff822792955c [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"
Wai-Hong Tam4b235922016-10-07 12:26:22 -070065 MICRO_SERVO_SERIAL = "micro_servo"
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
Yusuf Mohsinally29e30d22014-01-14 15:29:17 -0800224 def _init_keyboard_handler(self, servo, board=''):
225 """Initialize the correct keyboard handler for board.
226
Kevin Chengdc3befd2016-07-15 12:34:00 -0700227 Args:
228 servo: servo object.
229 board: string, board name.
Yusuf Mohsinally29e30d22014-01-14 15:29:17 -0800230
Kevin Chengdc3befd2016-07-15 12:34:00 -0700231 Returns:
232 keyboard handler object.
Yusuf Mohsinally29e30d22014-01-14 15:29:17 -0800233 """
234 if board == 'parrot':
235 return keyboard_handlers.ParrotHandler(servo)
236 elif board == 'stout':
237 return keyboard_handlers.StoutHandler(servo)
PeggyChuang4f07d872015-08-07 12:11:38 +0800238 elif board in ('buddy', 'cranky', 'guado', 'jecht', 'mccloud', 'monroe',
Tom Wai-Hong Tamd64164c2015-04-29 07:59:45 +0800239 'ninja', 'nyan_kitty', 'panther', 'rikku', 'stumpy',
philipchenfc9eea12016-09-21 17:36:54 -0700240 'sumo', 'tidus', 'tricky', 'veyron_fievel', 'veyron_mickey',
241 'veyron_rialto', 'veyron_tiger', 'zako'):
Yusuf Mohsinally29e30d22014-01-14 15:29:17 -0800242 if self._usbkm232 is None:
Tom Wai-Hong Tam4c7d6472016-03-08 06:55:50 +0800243 logging.info("No device path specified for usbkm232 handler. Use "
244 "the servo atmega chip to handle.")
245 self._usbkm232 = 'atmega'
Danny Chan662b6022015-11-04 17:34:53 -0800246 if self._usbkm232 == 'atmega':
247 # Use servo onboard keyboard emulator.
Nick Sanders78423782015-11-09 14:28:19 -0800248 self.set('atmega_rst', 'on')
Nick Sandersbc836282015-12-08 21:19:23 -0800249 self.set('at_hwb', 'off')
Nick Sanders78423782015-11-09 14:28:19 -0800250 self.set('atmega_rst', 'off')
Danny Chan662b6022015-11-04 17:34:53 -0800251 self._usbkm232 = self.get('atmega_pty')
Kevin Cheng810fc782016-11-01 12:36:46 -0700252 # We don't need to set the atmega uart settings if we're a servo v4.
Aseda Aboagye1d8477b2017-05-10 17:24:31 -0700253 if 'servo_v4' not in self._version:
Kevin Cheng810fc782016-11-01 12:36:46 -0700254 self.set('atmega_baudrate', '9600')
255 self.set('atmega_bits', 'eight')
256 self.set('atmega_parity', 'none')
257 self.set('atmega_sbits', 'one')
258 self.set('usb_mux_sel4', 'on')
259 self.set('usb_mux_oe4', 'on')
260 # Allow atmega bootup time.
261 time.sleep(1.0)
Danny Chan662b6022015-11-04 17:34:53 -0800262 self._logger.info('USBKM232: %s', self._usbkm232)
Yusuf Mohsinally29e30d22014-01-14 15:29:17 -0800263 return keyboard_handlers.USBkm232Handler(servo, self._usbkm232)
264 else:
Tom Wai-Hong Tamd64164c2015-04-29 07:59:45 +0800265 # The following boards don't use Chrome EC.
266 if board in ('alex', 'butterfly', 'lumpy', 'zgb'):
267 return keyboard_handlers.MatrixKeyboardHandler(servo)
268 return keyboard_handlers.ChromeECHandler(servo)
Yusuf Mohsinally29e30d22014-01-14 15:29:17 -0800269
Todd Broch3ec8df02012-11-20 10:53:03 -0800270 def __del__(self):
271 """Servod deconstructor."""
272 for interface in self._interface_list:
273 del(interface)
274
Kevin Chengdc3befd2016-07-15 12:34:00 -0700275 def _init_ftdi_dummy(self, vendor, product, serialname, interface):
Kevin Cheng042f4932016-07-19 10:46:00 -0700276 """Dummy interface for ftdi devices.
277
278 This is a dummy function specifically for ftdi devices to not initialize
279 anything but to help pad the interface list.
280
281 Returns:
282 None.
283 """
284 return None
285
Kevin Chengdc3befd2016-07-15 12:34:00 -0700286 def _init_ftdi_gpio(self, vendor, product, serialname, interface):
Todd Broche505b8d2011-03-21 18:19:54 -0700287 """Initialize gpio driver interface and open for use.
288
289 Args:
290 interface: interface number of FTDI device to use.
291
292 Returns:
293 Instance object of interface.
Todd Broch6de9dc62012-04-09 15:23:53 -0700294
295 Raises:
296 ServodError: If init fails
Todd Broche505b8d2011-03-21 18:19:54 -0700297 """
Kevin Chengdc3befd2016-07-15 12:34:00 -0700298 fobj = ftdigpio.Fgpio(vendor, product, interface, serialname)
Todd Broch6de9dc62012-04-09 15:23:53 -0700299 try:
300 fobj.open()
301 except ftdigpio.FgpioError as e:
302 raise ServodError('Opening gpio interface. %s ( %d )' % (e.msg, e.value))
303
Todd Broche505b8d2011-03-21 18:19:54 -0700304 return fobj
305
Kevin Chengdc3befd2016-07-15 12:34:00 -0700306 def _init_stm32_uart(self, vendor, product, serialname, interface):
Nick Sanders97bc4462016-01-04 15:37:31 -0800307 """Initialize stm32 uart interface and open for use
308
309 Note, the uart runs in a separate thread. Users wishing to
310 interact with it will query control for the pty's pathname and connect
311 with their favorite console program. For example:
312 cu -l /dev/pts/22
313
314 Args:
315 interface: dict of interface parameters.
316
317 Returns:
318 Instance object of interface
319
320 Raises:
321 ServodError: Raised on init failure.
322 """
323 self._logger.info("Suart: interface: %s" % interface)
Kevin Chengdc3befd2016-07-15 12:34:00 -0700324 sobj = stm32uart.Suart(vendor, product, interface['interface'],
325 serialname)
Nick Sanders97bc4462016-01-04 15:37:31 -0800326
327 try:
328 sobj.run()
329 except stm32uart.SuartError as e:
330 raise ServodError('Running uart interface. %s ( %d )' % (e.msg, e.value))
331
332 self._logger.info("%s" % sobj.get_pty())
333 return sobj
334
Kevin Chengdc3befd2016-07-15 12:34:00 -0700335 def _init_stm32_gpio(self, vendor, product, serialname, interface):
Nick Sanders97bc4462016-01-04 15:37:31 -0800336 """Initialize stm32 gpio interface.
337 Args:
Wai-Hong Tam564c1702017-04-24 09:23:38 -0700338 interface: dict of interface parameters.
Nick Sanders97bc4462016-01-04 15:37:31 -0800339
340 Returns:
341 Instance object of interface
342
343 Raises:
344 SgpioError: Raised on init failure.
345 """
Kevin Cheng71a046f2016-06-13 16:37:58 -0700346 interface_number = interface
347 # Interface could be a dict.
348 if type(interface) is dict:
349 interface_number = interface['interface']
350 self._logger.info("Sgpio: interface: %s" % interface_number)
Kevin Chengdc3befd2016-07-15 12:34:00 -0700351 return stm32gpio.Sgpio(vendor, product, interface_number, serialname)
Nick Sanders97bc4462016-01-04 15:37:31 -0800352
Kevin Chengdc3befd2016-07-15 12:34:00 -0700353 def _init_stm32_i2c(self, vendor, product, serialname, interface):
Nick Sanders97bc4462016-01-04 15:37:31 -0800354 """Initialize stm32 USB to I2C bridge interface and open for use
355
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 Si2cError: Raised on init failure.
364 """
365 self._logger.info("Si2cBus: interface: %s" % interface)
Nick Sandersa3649712016-03-01 16:53:52 -0800366 port = interface.get('port', 0)
Kevin Chengdc3befd2016-07-15 12:34:00 -0700367 return stm32i2c.Si2cBus(vendor, product, interface['interface'],
368 port=port, serialname=serialname)
Nick Sanders97bc4462016-01-04 15:37:31 -0800369
Kevin Chengdc3befd2016-07-15 12:34:00 -0700370 def _init_bb_adc(self, vendor, product, serialname, interface):
Aaron.Chuang88eff332014-07-31 08:32:00 +0800371 """Initalize beaglebone ADC interface."""
372 return bbadc.BBadc()
373
Kevin Chengdc3befd2016-07-15 12:34:00 -0700374 def _init_bb_gpio(self, vendor, product, serialname, interface):
Simran Basie750a342013-03-12 13:45:26 -0700375 """Initalize beaglebone gpio interface."""
Simran Basi5492bde2013-05-16 17:08:47 -0700376 return bbgpio.BBgpio()
Simran Basie750a342013-03-12 13:45:26 -0700377
Kevin Chengdc3befd2016-07-15 12:34:00 -0700378 def _init_ftdi_i2c(self, vendor, product, serialname, interface):
Todd Broche505b8d2011-03-21 18:19:54 -0700379 """Initialize i2c interface and open for use.
380
381 Args:
382 interface: interface number of FTDI device to use
383
384 Returns:
385 Instance object of interface
Todd Broch6de9dc62012-04-09 15:23:53 -0700386
387 Raises:
388 ServodError: If init fails
Todd Broche505b8d2011-03-21 18:19:54 -0700389 """
Kevin Chengdc3befd2016-07-15 12:34:00 -0700390 fobj = ftdii2c.Fi2c(vendor, product, interface, serialname)
Todd Broch6de9dc62012-04-09 15:23:53 -0700391 try:
392 fobj.open()
393 except ftdii2c.Fi2cError as e:
394 raise ServodError('Opening i2c interface. %s ( %d )' % (e.msg, e.value))
395
Todd Broche505b8d2011-03-21 18:19:54 -0700396 # Set the frequency of operation of the i2c bus.
397 # TODO(tbroch) make configureable
398 fobj.setclock(MAX_I2C_CLOCK_HZ)
Todd Broch6de9dc62012-04-09 15:23:53 -0700399
Todd Broche505b8d2011-03-21 18:19:54 -0700400 return fobj
401
Simran Basie750a342013-03-12 13:45:26 -0700402 # TODO (sbasi) crbug.com/187489 - Implement bb_i2c.
403 def _init_bb_i2c(self, interface):
404 """Initalize beaglebone i2c interface."""
Simran Basia9ad25e2013-04-23 11:57:00 -0700405 return bbi2c.BBi2c(interface)
Simran Basie750a342013-03-12 13:45:26 -0700406
Kevin Chengdc3befd2016-07-15 12:34:00 -0700407 def _init_dev_i2c(self, vendor, product, serialname, interface):
Rong Changc6c8c022014-08-11 14:07:11 +0800408 """Initalize Linux i2c-dev interface."""
409 return i2cbus.I2CBus('/dev/i2c-%d' % interface['bus_num'])
410
Kevin Chengdc3befd2016-07-15 12:34:00 -0700411 def _init_ftdi_uart(self, vendor, product, serialname, interface):
Simran Basie750a342013-03-12 13:45:26 -0700412 """Initialize ftdi uart inteface and open for use
Todd Broch47c43f42011-05-26 15:11:31 -0700413
414 Note, the uart runs in a separate thread (pthreads). Users wishing to
415 interact with it will query control for the pty's pathname and connect
416 with there favorite console program. For example:
417 cu -l /dev/pts/22
418
419 Args:
420 interface: interface number of FTDI device to use
421
422 Returns:
423 Instance object of interface
Todd Broch6de9dc62012-04-09 15:23:53 -0700424
425 Raises:
426 ServodError: If init fails
Todd Broch47c43f42011-05-26 15:11:31 -0700427 """
Kevin Chengdc3befd2016-07-15 12:34:00 -0700428 fobj = ftdiuart.Fuart(vendor, product, interface, serialname)
Todd Broch6de9dc62012-04-09 15:23:53 -0700429 try:
430 fobj.run()
431 except ftdiuart.FuartError as e:
432 raise ServodError('Running uart interface. %s ( %d )' % (e.msg, e.value))
433
Todd Broch47c43f42011-05-26 15:11:31 -0700434 self._logger.info("%s" % fobj.get_pty())
435 return fobj
436
Simran Basie750a342013-03-12 13:45:26 -0700437 # TODO (sbasi) crbug.com/187492 - Implement bbuart.
Kevin Chengdc3befd2016-07-15 12:34:00 -0700438 def _init_bb_uart(self, vendor, product, serialname, interface):
Simran Basie750a342013-03-12 13:45:26 -0700439 """Initalize beaglebone uart interface."""
Simran Basi949309b2013-05-31 15:12:15 -0700440 logging.debug('UART INTERFACE: %s', interface)
441 return bbuart.BBuart(interface)
Simran Basie750a342013-03-12 13:45:26 -0700442
Kevin Chengdc3befd2016-07-15 12:34:00 -0700443 def _init_ftdi_gpiouart(self, vendor, product, serialname,
444 interface):
Todd Broch888da782011-10-07 14:29:09 -0700445 """Initialize special gpio + uart interface and open for use
446
447 Note, the uart runs in a separate thread (pthreads). Users wishing to
448 interact with it will query control for the pty's pathname and connect
449 with there favorite console program. For example:
450 cu -l /dev/pts/22
451
452 Args:
453 interface: interface number of FTDI device to use
454
455 Returns:
456 Instance objects of interface
Todd Broch6de9dc62012-04-09 15:23:53 -0700457
458 Raises:
459 ServodError: If init fails
Todd Broch888da782011-10-07 14:29:09 -0700460 """
Kevin Chengce7dafd2016-08-02 11:11:38 -0700461 fgpio = self._init_ftdi_gpio(vendor, product, serialname, interface)
Kevin Chengdc3befd2016-07-15 12:34:00 -0700462 fuart = ftdiuart.Fuart(vendor, product, interface, serialname, fgpio._fc)
Todd Broch6de9dc62012-04-09 15:23:53 -0700463 try:
464 fuart.run()
465 except ftdiuart.FuartError as e:
466 raise ServodError('Running uart interface. %s ( %d )' % (e.msg, e.value))
467
Todd Broch888da782011-10-07 14:29:09 -0700468 self._logger.info("uart pty: %s" % fuart.get_pty())
469 return fgpio, fuart
470
Kevin Chengdc3befd2016-07-15 12:34:00 -0700471 def _init_ec3po_uart(self, vendor, product, serialname, interface):
Aseda Aboagyea4922212015-11-20 15:19:08 -0800472 """Initialize EC-3PO console interpreter interface.
473
474 Args:
475 interface: A dictionary representing the interface.
476
477 Returns:
478 An EC3PO object representing the EC-3PO interface or None if there's no
479 interface for the USB PD UART.
480 """
Wai-Hong Tam6c0fa592017-04-21 12:41:33 -0700481 raw_uart_name = interface['raw_pty']
482 if self._syscfg.is_control(raw_uart_name):
Nick Sanders97bc4462016-01-04 15:37:31 -0800483 raw_ec_uart = self.get(raw_uart_name)
Wai-Hong Tam6c0fa592017-04-21 12:41:33 -0700484 return ec3po_interface.EC3PO(raw_ec_uart)
Aseda Aboagyea4922212015-11-20 15:19:08 -0800485 else:
Wai-Hong Tam6c0fa592017-04-21 12:41:33 -0700486 # The overlay doesn't have the raw PTY defined, therefore we can skip
487 # initializing this interface since no control relies on it.
488 self._logger.debug(
489 'Skip initializing EC3PO for %s, no control specified.',
490 raw_uart_name)
491 return None
Aseda Aboagyea4922212015-11-20 15:19:08 -0800492
Tom Wai-Hong Tam28f0a5f2012-08-21 12:49:57 +0800493 def _camel_case(self, string):
494 output = ''
495 for s in string.split('_'):
496 if output:
497 output += s.capitalize()
498 else:
499 output = s
500 return output
501
Todd Broche505b8d2011-03-21 18:19:54 -0700502 def _get_param_drv(self, control_name, is_get=True):
503 """Get access to driver for a given control.
504
505 Note, some controls have different parameter dictionaries for 'getting' the
506 control's value versus 'setting' it. Boolean is_get distinguishes which is
507 being requested.
508
509 Args:
510 control_name: string name of control
511 is_get: boolean to determine
512
513 Returns:
514 tuple (param, drv) where:
515 param: param dictionary for control
516 drv: instance object of driver for particular control
517
518 Raises:
519 ServodError: Error occurred while examining params dict
520 """
521 self._logger.debug("")
522 # if already setup just return tuple from driver dict
523 if control_name in self._drv_dict:
524 if is_get and ('get' in self._drv_dict[control_name]):
525 return self._drv_dict[control_name]['get']
526 if not is_get and ('set' in self._drv_dict[control_name]):
527 return self._drv_dict[control_name]['set']
528
529 params = self._syscfg.lookup_control_params(control_name, is_get)
530 if 'drv' not in params:
531 self._logger.error("Unable to determine driver for %s" % control_name)
532 raise ServodError("'drv' key not found in params dict")
533 if 'interface' not in params:
534 self._logger.error("Unable to determine interface for %s" %
535 control_name)
Todd Broche505b8d2011-03-21 18:19:54 -0700536 raise ServodError("'interface' key not found in params dict")
Simran Basi668be0e2013-08-07 11:54:50 -0700537
Aseda Aboagye1d8477b2017-05-10 17:24:31 -0700538 # Find the candidate servos. Using servo_v4 with a servo_micro connected as
539 # an example, the following shows the priority for selecting the interface.
540 #
541 # 1. The full name. (e.g. - 'servo_v4_with_servo_micro_interface')
542 # 2. servo_micro_interface
543 # 3. servo_v4_interface
544 # 4. Fallback to the default, interface.
545 candidates = [ self._version ]
546 candidates.extend(reversed(self._version.split('_with_')))
547
548 interface_id = 'unknown'
549 for c in candidates:
550 interface_name = '%s_interface' % c
551 if interface_name in params:
552 interface_id = params[interface_name]
553 self._logger.debug('Using %s parameter.' % interface_name)
554 break
555
556 # Use the default interface value if we couldn't find a more specific
557 # interface.
558 if interface_id == 'unknown':
559 interface_id = params['interface']
560 self._logger.debug('Using default interface parameter.')
561
J. Richard Barnette275d9fd2014-02-11 14:38:54 -0800562 if interface_id == 'servo':
563 interface = self
Simran Basi668be0e2013-08-07 11:54:50 -0700564 else:
Wai-Hong Tam564c1702017-04-24 09:23:38 -0700565 index = int(interface_id)
J. Richard Barnette275d9fd2014-02-11 14:38:54 -0800566 interface = self._interface_list[index]
Simran Basi668be0e2013-08-07 11:54:50 -0700567
Todd Broche505b8d2011-03-21 18:19:54 -0700568 drv_name = params['drv']
Wai-Hong Tam4c09eff2017-02-17 11:46:19 -0800569 drv_module = getattr(servo_drv, drv_name)
Tom Wai-Hong Tam28f0a5f2012-08-21 12:49:57 +0800570 drv_class = getattr(drv_module, self._camel_case(drv_name))
Todd Broche505b8d2011-03-21 18:19:54 -0700571 drv = drv_class(interface, params)
572 if control_name not in self._drv_dict:
573 self._drv_dict[control_name] = {}
574 if is_get:
575 self._drv_dict[control_name]['get'] = (params, drv)
576 else:
577 self._drv_dict[control_name]['set'] = (params, drv)
578 return (params, drv)
579
580 def doc_all(self):
581 """Return all documenation for controls.
582
583 Returns:
584 string of <doc> text in config file (xml) and the params dictionary for
585 all controls.
586
587 For example:
588 warm_reset :: Reset the device warmly
589 ------------------------> {'interface': '1', 'map': 'onoff_i', ... }
590 """
591 return self._syscfg.display_config()
592
593 def doc(self, name):
594 """Retreive doc string in system config file for given control name.
595
596 Args:
597 name: name string of control to get doc string
598
599 Returns:
600 doc string of name
601
602 Raises:
603 NameError: if fails to locate control
604 """
605 self._logger.debug("name(%s)" % (name))
606 if self._syscfg.is_control(name):
607 return self._syscfg.get_control_docstring(name)
608 else:
609 raise NameError("No control %s" %name)
610
Fang Deng90377712013-06-03 15:51:48 -0700611 def _switch_usbkey(self, mux_direction):
612 """Connect USB flash stick to either servo or DUT.
613
614 This function switches 'usb_mux_sel1' to provide electrical
615 connection between the USB port J3 and either servo or DUT side.
616
617 Switching the usb mux is accompanied by powercycling
618 of the USB stick, because it sometimes gets wedged if the mux
619 is switched while the stick power is on.
620
621 Args:
622 mux_direction: "servo_sees_usbkey" or "dut_sees_usbkey".
623 """
624 self.set(self._USB_J3_PWR, self._USB_J3_PWR_OFF)
625 time.sleep(self._USB_POWEROFF_DELAY)
626 self.set(self._USB_J3, mux_direction)
627 time.sleep(self._USB_POWEROFF_DELAY)
628 self.set(self._USB_J3_PWR, self._USB_J3_PWR_ON)
629 if mux_direction == self._USB_J3_TO_SERVO:
630 time.sleep(self._USB_DETECTION_DELAY)
631
Simran Basia9f41032012-05-11 14:21:58 -0700632 def _get_usb_port_set(self):
633 """Gets a set of USB disks currently connected to the system
634
635 Returns:
636 A set of USB disk paths.
637 """
638 usb_set = fnmatch.filter(os.listdir("/dev/"), "sd[a-z]")
639 return set(["/dev/" + dev for dev in usb_set])
640
Kevin Cheng5595b342016-09-29 15:51:01 -0700641 @contextlib.contextmanager
642 def _block_other_servod(self, timeout=None):
Kevin Chengc49494e2016-07-25 12:13:38 -0700643 """Block other servod processes by locking a file.
644
645 To enable multiple servods processes to safely probe_host_usb_dev, we use
646 a given lock file to signal other servod processes that we're probing
Kevin Cheng5595b342016-09-29 15:51:01 -0700647 for a usb device. This will be a context manager that will return
648 if the block was successful or not.
Kevin Chengc49494e2016-07-25 12:13:38 -0700649
650 If the lock file exists, we open it and try to lock it.
651 - If another servod processes has locked it already, we'll sleep a random
652 amount of time and try again, we'll keep doing that until
Kevin Cheng5595b342016-09-29 15:51:01 -0700653 timeout amount of time has passed.
Kevin Chengc49494e2016-07-25 12:13:38 -0700654
Kevin Cheng5595b342016-09-29 15:51:01 -0700655 - If we're able to lock the file, we'll yield that the block was successful
656 and upon return, unlock the file and exit out.
Kevin Chengc49494e2016-07-25 12:13:38 -0700657
658 This blocking behavior is only enabled if the lock file exists, if it
659 doesn't, then we pretend the block was successful.
660
Kevin Cheng5595b342016-09-29 15:51:01 -0700661 Args:
662 timeout: Max waiting time for the block to succeed.
Kevin Chengc49494e2016-07-25 12:13:38 -0700663 """
Kevin Cheng5595b342016-09-29 15:51:01 -0700664 if not os.path.exists(self._USB_LOCK_FILE):
665 # No lock file so we'll pretend the block was a success.
666 yield True
667 else:
Kevin Chengc49494e2016-07-25 12:13:38 -0700668 start_time = datetime.datetime.now()
669 while True:
Kevin Cheng5595b342016-09-29 15:51:01 -0700670 with open(self._USB_LOCK_FILE) as lock_file:
671 try:
672 fcntl.flock(lock_file, fcntl.LOCK_EX | fcntl.LOCK_NB)
673 yield True
674 fcntl.flock(lock_file, fcntl.LOCK_UN)
675 break
676 except IOError:
677 current_time = datetime.datetime.now()
678 current_wait_time = (current_time - start_time).total_seconds()
679 if timeout and current_wait_time > timeout:
680 yield False
681 break
Kevin Chengc49494e2016-07-25 12:13:38 -0700682 # Sleep random amount.
683 sleep_time = time.sleep(random.random())
Kevin Chengc49494e2016-07-25 12:13:38 -0700684
Kevin Cheng8fcf06c2016-10-12 08:02:44 -0700685 def safe_switch_usbkey_power(self, power_state, timeout=0):
Kevin Cheng5595b342016-09-29 15:51:01 -0700686 """Toggle the usb power safely.
687
688 We'll make sure we're the only servod process toggling the usbkey power.
Kevin Chengc49494e2016-07-25 12:13:38 -0700689
690 Args:
Kevin Cheng5595b342016-09-29 15:51:01 -0700691 power_state: The setting to set for the usbkey power.
Kevin Cheng8fcf06c2016-10-12 08:02:44 -0700692 timeout: Timeout to wait for blocking other servod processes, default is
693 no timeout.
Kevin Chengc49494e2016-07-25 12:13:38 -0700694
Kevin Cheng5595b342016-09-29 15:51:01 -0700695 Returns:
696 An empty string to appease the xmlrpc gods.
697 """
698 with self._block_other_servod(timeout=timeout):
699 if power_state != self.get(self._USB_J3_PWR):
700 self.set(self._USB_J3_PWR, power_state)
701 return ''
702
Kevin Cheng8fcf06c2016-10-12 08:02:44 -0700703 def safe_switch_usbkey(self, mux_direction, timeout=0):
Kevin Cheng5595b342016-09-29 15:51:01 -0700704 """Toggle the usb direction safely.
705
706 We'll make sure we're the only servod process toggling the usbkey direction.
707
708 Args:
709 power_state: The setting to set for the usbkey power.
Kevin Cheng8fcf06c2016-10-12 08:02:44 -0700710 timeout: Timeout to wait for blocking other servod processes, default is
711 no timeout.
Kevin Cheng5595b342016-09-29 15:51:01 -0700712
713 Returns:
714 An empty string to appease the xmlrpc gods.
715 """
716 with self._block_other_servod(timeout=timeout):
717 self._switch_usbkey(mux_direction)
718 return ''
719
720 def probe_host_usb_dev(self, timeout=_MAX_USB_LOCK_WAIT):
Simran Basia9f41032012-05-11 14:21:58 -0700721 """Probe the USB disk device plugged in the servo from the host side.
722
723 Method can fail by:
724 1) Having multiple servos connected and returning incorrect /dev/sdX of
Kevin Chengc49494e2016-07-25 12:13:38 -0700725 another servo unless _USB_LOCK_FILE exists on the servo host. If that
726 file exists, then it is safe to probe for usb devices among multiple
727 servod instances.
Simran Basia9f41032012-05-11 14:21:58 -0700728 2) Finding multiple /dev/sdX and returning None.
729
Kevin Cheng5595b342016-09-29 15:51:01 -0700730 Args:
731 timeout: Timeout to wait for blocking other servod processes.
732
Simran Basia9f41032012-05-11 14:21:58 -0700733 Returns:
Kevin Chengc49494e2016-07-25 12:13:38 -0700734 USB disk path if one and only one USB disk path is found, otherwise an
735 empty string.
Simran Basia9f41032012-05-11 14:21:58 -0700736 """
Kevin Cheng5595b342016-09-29 15:51:01 -0700737 with self._block_other_servod(timeout=timeout) as block_success:
738 if not block_success:
739 return ''
Kevin Chengc49494e2016-07-25 12:13:38 -0700740
Kevin Cheng5595b342016-09-29 15:51:01 -0700741 original_value = self.get(self._USB_J3)
742 original_usb_power = self.get(self._USB_J3_PWR)
743 # Make the host unable to see the USB disk.
744 if (original_usb_power == self._USB_J3_PWR_ON and
745 original_value != self._USB_J3_TO_DUT):
746 self._switch_usbkey(self._USB_J3_TO_DUT)
747 no_usb_set = self._get_usb_port_set()
Simran Basia9f41032012-05-11 14:21:58 -0700748
Kevin Cheng5595b342016-09-29 15:51:01 -0700749 # Make the host able to see the USB disk.
750 self._switch_usbkey(self._USB_J3_TO_SERVO)
751 has_usb_set = self._get_usb_port_set()
Fang Deng90377712013-06-03 15:51:48 -0700752
Kevin Cheng5595b342016-09-29 15:51:01 -0700753 # Back to its original value.
754 if original_value != self._USB_J3_TO_SERVO:
755 self._switch_usbkey(original_value)
756 if original_usb_power != self._USB_J3_PWR_ON:
757 self.set(self._USB_J3_PWR, self._USB_J3_PWR_OFF)
758 time.sleep(self._USB_POWEROFF_DELAY)
Fang Deng90377712013-06-03 15:51:48 -0700759
Kevin Cheng5595b342016-09-29 15:51:01 -0700760 # Subtract the two sets to find the usb device.
761 diff_set = has_usb_set - no_usb_set
762 if len(diff_set) == 1:
763 return diff_set.pop()
764 else:
765 return ''
Simran Basia9f41032012-05-11 14:21:58 -0700766
Kevin Cheng85831332016-10-13 13:14:44 -0700767 def download_image_to_usb(self, image_path, probe_timeout=_MAX_USB_LOCK_WAIT):
Simran Basia9f41032012-05-11 14:21:58 -0700768 """Download image and save to the USB device found by probe_host_usb_dev.
769 If the image_path is a URL, it will download this url to the USB path;
770 otherwise it will simply copy the image_path's contents to the USB path.
771
772 Args:
773 image_path: path or url to the recovery image.
Kevin Cheng85831332016-10-13 13:14:44 -0700774 probe_timeout: timeout for the probe to take.
Simran Basia9f41032012-05-11 14:21:58 -0700775
776 Returns:
777 True|False: True if process completed successfully, False if error
778 occurred.
779 Can't return None because XMLRPC doesn't allow it. PTAL at tbroch's
780 comment at the end of set().
781 """
782 self._logger.debug("image_path(%s)" % image_path)
783 self._logger.debug("Detecting USB stick device...")
Kevin Cheng85831332016-10-13 13:14:44 -0700784 usb_dev = self.probe_host_usb_dev(timeout=probe_timeout)
Simran Basia9f41032012-05-11 14:21:58 -0700785 if not usb_dev:
786 self._logger.error("No usb device connected to servo")
787 return False
788
Kevin Cheng9071ed92016-06-21 14:37:54 -0700789 # Let's check if we downloaded this last time and if so assume the image is
790 # still on the usb device and return True.
791 if self._image_path == image_path:
792 self._logger.debug("Image already on USB device, skipping transfer")
793 return True
794
Simran Basia9f41032012-05-11 14:21:58 -0700795 try:
796 if image_path.startswith(self._HTTP_PREFIX):
797 self._logger.debug("Image path is a URL, downloading image")
798 urllib.urlretrieve(image_path, usb_dev)
799 else:
800 shutil.copyfile(image_path, usb_dev)
801 except IOError as e:
Victor Dodonb7cddb82016-04-28 17:00:24 -0700802 self._logger.error("Failed to transfer image to USB device: %s ( %s ) ",
Simran Basia9f41032012-05-11 14:21:58 -0700803 e.strerror, e.errno)
804 return False
805 except urllib.ContentTooShortError:
806 self._logger.error("Failed to download URL: %s to USB device: %s",
807 image_path, usb_dev)
808 return False
809 except BaseException as e:
810 self._logger.error("Unexpected exception downloading %s to %s: %s",
811 image_path, usb_dev, str(e))
812 return False
J. Richard Barnettee4125af2013-02-26 18:31:56 -0800813 finally:
814 # We just plastered the partition table for a block device.
815 # Pass or fail, we mustn't go without telling the kernel about
816 # the change, or it will punish us with sporadic, hard-to-debug
817 # failures.
818 subprocess.call(["sync"])
819 subprocess.call(["blockdev", "--rereadpt", usb_dev])
Kevin Cheng9071ed92016-06-21 14:37:54 -0700820 self._image_path = image_path
Simran Basia9f41032012-05-11 14:21:58 -0700821 return True
822
823 def make_image_noninteractive(self):
824 """Makes the recovery image noninteractive.
825
826 A noninteractive image will reboot automatically after installation
827 instead of waiting for the USB device to be removed to initiate a system
828 reboot.
829
830 Mounts partition 1 of the image stored on usb_dev and creates a file
831 called "non_interactive" so that the image will become noninteractive.
832
833 Returns:
834 True|False: True if process completed successfully, False if error
835 occurred.
836 """
837 result = True
Kevin Chengc49494e2016-07-25 12:13:38 -0700838 usb_dev = self.probe_host_usb_dev()
Simran Basia9f41032012-05-11 14:21:58 -0700839 if not usb_dev:
840 self._logger.error("No usb device connected to servo")
841 return False
842 # Create TempDirectory
843 tmpdir = tempfile.mkdtemp()
844 if tmpdir:
845 # Mount drive to tmpdir.
846 partition_1 = "%s1" % usb_dev
847 rc = subprocess.call(["mount", partition_1, tmpdir])
848 if rc == 0:
849 # Create file 'non_interactive'
850 non_interactive_file = os.path.join(tmpdir, "non_interactive")
851 try:
852 open(non_interactive_file, "w").close()
853 except IOError as e:
854 self._logger.error("Failed to create file %s : %s ( %d )",
855 non_interactive_file, e.strerror, e.errno)
856 result = False
857 except BaseException as e:
858 self._logger.error("Unexpected Exception creating file %s : %s",
859 non_interactive_file, str(e))
860 result = False
861 # Unmount drive regardless if file creation worked or not.
862 rc = subprocess.call(["umount", partition_1])
863 if rc != 0:
864 self._logger.error("Failed to unmount USB Device")
865 result = False
866 else:
867 self._logger.error("Failed to mount USB Device")
868 result = False
869
870 # Delete tmpdir. May throw exception if 'umount' failed.
871 try:
872 os.rmdir(tmpdir)
873 except OSError as e:
874 self._logger.error("Failed to remove temp directory %s : %s",
875 tmpdir, str(e))
876 return False
877 except BaseException as e:
878 self._logger.error("Unexpected Exception removing tempdir %s : %s",
879 tmpdir, str(e))
880 return False
881 else:
882 self._logger.error("Failed to create temp directory.")
883 return False
884 return result
885
Todd Broch352b4b22013-03-22 09:48:40 -0700886 def set_get_all(self, cmds):
887 """Set &| get one or more control values.
888
889 Args:
890 cmds: list of control[:value] to get or set.
891
892 Returns:
893 rv: list of responses from calling get or set methods.
894 """
895 rv = []
896 for cmd in cmds:
897 if ':' in cmd:
898 (control, value) = cmd.split(':')
899 rv.append(self.set(control, value))
900 else:
901 rv.append(self.get(cmd))
902 return rv
903
Todd Broche505b8d2011-03-21 18:19:54 -0700904 def get(self, name):
905 """Get control value.
906
907 Args:
908 name: name string of control
909
910 Returns:
911 Response from calling drv get method. Value is reformatted based on
912 control's dictionary parameters
913
914 Raises:
915 HwDriverError: Error occurred while using drv
916 """
917 self._logger.debug("name(%s)" % (name))
Vadim Bendeburyc3a83cf2015-03-24 13:07:00 -0700918 if name == 'serialname':
Kevin Cheng4b4f0022016-09-09 02:37:07 -0700919 if self._serialnames[self.MAIN_SERIAL]:
920 return self._serialnames[self.MAIN_SERIAL]
Vadim Bendeburyc3a83cf2015-03-24 13:07:00 -0700921 return 'unknown'
Todd Broche505b8d2011-03-21 18:19:54 -0700922 (param, drv) = self._get_param_drv(name)
923 try:
924 val = drv.get()
925 rd_val = self._syscfg.reformat_val(param, val)
Todd Brochb042e7a2011-12-14 17:41:36 -0800926 self._logger.debug("%s = %s" % (name, rd_val))
Todd Broche505b8d2011-03-21 18:19:54 -0700927 return rd_val
Todd Brochfbc499d2011-06-16 16:09:58 -0700928 except AttributeError, error:
929 self._logger.error("Getting %s: %s" % (name, error))
930 raise
Vic Yangbe6cf262012-09-10 10:40:56 +0800931 except HwDriverError:
Todd Broche505b8d2011-03-21 18:19:54 -0700932 self._logger.error("Getting %s" % (name))
933 raise
Todd Brochd6061672012-05-11 15:52:47 -0700934
Todd Broche505b8d2011-03-21 18:19:54 -0700935 def get_all(self, verbose):
936 """Get all controls values.
937
938 Args:
939 verbose: Boolean on whether to return doc info as well
940
941 Returns:
942 string creating from trying to get all values of all controls. In case of
943 error attempting access to control, response is 'ERR'.
944 """
Vadim Bendeburyb07944c2013-01-16 10:47:10 -0800945 rsp = []
Todd Broche505b8d2011-03-21 18:19:54 -0700946 for name in self._syscfg.syscfg_dict['control']:
947 self._logger.debug("name = %s" %name)
948 try:
949 value = self.get(name)
950 except Exception:
951 value = "ERR"
952 pass
953 if verbose:
Vadim Bendeburyb07944c2013-01-16 10:47:10 -0800954 rsp.append("GET %s = %s :: %s" % (name, value, self.doc(name)))
Todd Broche505b8d2011-03-21 18:19:54 -0700955 else:
Vadim Bendeburyb07944c2013-01-16 10:47:10 -0800956 rsp.append("%s:%s" % (name, value))
957 return '\n'.join(sorted(rsp))
Todd Broche505b8d2011-03-21 18:19:54 -0700958
959 def set(self, name, wr_val_str):
960 """Set control.
961
962 Args:
963 name: name string of control
964 wr_val_str: value string to write. Can be integer, float or a
965 alpha-numerical that is mapped to a integer or float.
966
967 Raises:
968 HwDriverError: Error occurred while using driver
969 """
970 self._logger.debug("name(%s) wr_val(%s)" % (name, wr_val_str))
971 (params, drv) = self._get_param_drv(name, False)
972 wr_val = self._syscfg.resolve_val(params, wr_val_str)
973 try:
974 drv.set(wr_val)
Vic Yangbe6cf262012-09-10 10:40:56 +0800975 except HwDriverError:
Todd Broche505b8d2011-03-21 18:19:54 -0700976 self._logger.error("Setting %s -> %s" % (name, wr_val_str))
977 raise
978 # TODO(tbroch) Figure out why despite allow_none=True for both xmlrpc server
979 # & client I still have to return something to appease the
980 # marshall/unmarshall
981 return True
982
Todd Brochd6061672012-05-11 15:52:47 -0700983 def hwinit(self, verbose=False):
984 """Initialize all controls.
985
986 These values are part of the system config XML files of the form
987 init=<value>. This command should be used by clients wishing to return the
988 servo and DUT its connected to a known good/safe state.
989
Vadim Bendeburybb51dd42013-01-31 13:47:46 -0800990 Note that initialization errors are ignored (as in some cases they could
991 be caused by DUT firmware deficiencies). This might need to be fine tuned
992 later.
993
Todd Brochd6061672012-05-11 15:52:47 -0700994 Args:
995 verbose: boolean, if True prints info about control initialized.
996 Otherwise prints nothing.
Vadim Bendebury5934e4b2013-02-06 13:57:54 -0800997
998 Returns:
999 This function is called across RPC and as such is expected to return
1000 something unless transferring 'none' across is allowed. Hence adding a
1001 dummy return value to make things simpler.
Todd Brochd6061672012-05-11 15:52:47 -07001002 """
Todd Brochd9acf0a2012-12-05 13:43:06 -08001003 for control_name, value in self._syscfg.hwinit:
Todd Broch3ec8df02012-11-20 10:53:03 -08001004 try:
John Carey6fe2bbf2015-08-31 16:13:03 -07001005 # Workaround for bug chrome-os-partner:42349. Without this check, the
1006 # gpio will briefly pulse low if we set it from high to high.
1007 if self.get(control_name) != value:
Aseda Aboagyea849d462016-05-04 17:08:16 -07001008 self.set(control_name, value)
1009 if verbose:
1010 self._logger.info('Initialized %s to %s', control_name, value)
Todd Broch3ec8df02012-11-20 10:53:03 -08001011 except Exception as e:
Todd Broch3ec8df02012-11-20 10:53:03 -08001012 self._logger.error("Problem initializing %s -> %s :: %s",
1013 control_name, value, str(e))
Nick Sandersbc836282015-12-08 21:19:23 -08001014
1015 # Init keyboard after all the intefaces are up.
1016 self._keyboard = self._init_keyboard_handler(self, self._board)
Vadim Bendebury5934e4b2013-02-06 13:57:54 -08001017 return True
Todd Broch3ec8df02012-11-20 10:53:03 -08001018
Todd Broche505b8d2011-03-21 18:19:54 -07001019 def echo(self, echo):
1020 """Dummy echo function for testing/examples.
1021
1022 Args:
1023 echo: string to echo back to client
1024 """
1025 self._logger.debug("echo(%s)" % (echo))
1026 return "ECH0ING: %s" % (echo)
1027
J. Richard Barnettee2820552013-03-14 16:13:46 -07001028 def get_board(self):
1029 """Return the board specified at startup, if any."""
1030 return self._board
1031
Simran Basia23c1392013-08-06 14:59:10 -07001032 def get_version(self):
1033 """Get servo board version."""
1034 return self._version
1035
Yusuf Mohsinally29e30d22014-01-14 15:29:17 -08001036 def power_long_press(self):
1037 """Simulate a long power button press."""
1038 # After a long power press, the EC may ignore the next power
1039 # button press (at least on Alex). To guarantee that this
1040 # won't happen, we need to allow the EC one second to
1041 # collect itself.
1042 self._keyboard.power_long_press()
1043 return True
1044
1045 def power_normal_press(self):
1046 """Simulate a normal power button press."""
1047 self._keyboard.power_normal_press()
1048 return True
1049
1050 def power_short_press(self):
1051 """Simulate a short power button press."""
1052 self._keyboard.power_short_press()
1053 return True
1054
1055 def power_key(self, secs=''):
1056 """Simulate a power button press.
1057
1058 Args:
1059 secs: Time in seconds to simulate the keypress.
1060 """
1061 self._keyboard.power_key(secs)
1062 return True
1063
1064 def ctrl_d(self, press_secs=''):
1065 """Simulate Ctrl-d simultaneous button presses."""
1066 self._keyboard.ctrl_d(press_secs)
1067 return True
1068
Victor Dodone539cea2016-03-29 18:50:17 -07001069 def ctrl_u(self, press_secs=''):
Yusuf Mohsinally29e30d22014-01-14 15:29:17 -08001070 """Simulate Ctrl-u simultaneous button presses."""
Victor Dodone539cea2016-03-29 18:50:17 -07001071 self._keyboard.ctrl_u(press_secs)
Yusuf Mohsinally29e30d22014-01-14 15:29:17 -08001072 return True
1073
1074 def ctrl_enter(self, press_secs=''):
1075 """Simulate Ctrl-enter simultaneous button presses."""
1076 self._keyboard.ctrl_enter(press_secs)
1077 return True
1078
1079 def d_key(self, press_secs=''):
1080 """Simulate Enter key button press."""
1081 self._keyboard.d_key(press_secs)
1082 return True
1083
1084 def ctrl_key(self, press_secs=''):
1085 """Simulate Enter key button press."""
1086 self._keyboard.ctrl_key(press_secs)
1087 return True
1088
1089 def enter_key(self, press_secs=''):
1090 """Simulate Enter key button press."""
1091 self._keyboard.enter_key(press_secs)
1092 return True
1093
1094 def refresh_key(self, press_secs=''):
1095 """Simulate Refresh key (F3) button press."""
1096 self._keyboard.refresh_key(press_secs)
1097 return True
1098
1099 def ctrl_refresh_key(self, press_secs=''):
1100 """Simulate Ctrl and Refresh (F3) simultaneous press.
1101
1102 This key combination is an alternative of Space key.
1103 """
1104 self._keyboard.ctrl_refresh_key(press_secs)
1105 return True
1106
1107 def imaginary_key(self, press_secs=''):
1108 """Simulate imaginary key button press.
1109
1110 Maps to a key that doesn't physically exist.
1111 """
1112 self._keyboard.imaginary_key(press_secs)
1113 return True
1114
Todd Brochdbb09982011-10-02 07:14:26 -07001115
Vincent Palatin3acbbe52016-07-19 17:40:12 +02001116 def sysrq_x(self, press_secs=''):
1117 """Simulate Alt VolumeUp X simultaneous press.
1118
1119 This key combination is the kernel system request (sysrq) x.
1120 """
1121 self._keyboard.sysrq_x(press_secs)
1122 return True
1123
1124
Kevin Cheng4b4f0022016-09-09 02:37:07 -07001125 def get_servo_serials(self):
1126 """Return all the serials associated with this process."""
1127 return self._serialnames
1128
1129
Todd Broche505b8d2011-03-21 18:19:54 -07001130def test():
1131 """Integration testing.
1132
1133 TODO(tbroch) Enhance integration test and add unittest (see mox)
1134 """
1135 logging.basicConfig(level=logging.DEBUG,
1136 format="%(asctime)s - %(name)s - " +
1137 "%(levelname)s - %(message)s")
1138 # configure server & listen
1139 servod_obj = Servod(1)
Wai-Hong Tam564c1702017-04-24 09:23:38 -07001140 # 5 == number of interfaces on a FT4232H device
1141 for i in xrange(1, 5):
1142 if i == 2:
Todd Broche505b8d2011-03-21 18:19:54 -07001143 # its an i2c interface ... see __init__ for details and TODO to make
1144 # this configureable
1145 servod_obj._interface_list[i].wr_rd(0x21, [0], 1)
1146 else:
1147 # its a gpio interface
1148 servod_obj._interface_list[i].wr_rd(0)
1149
1150 server = SimpleXMLRPCServer.SimpleXMLRPCServer(("localhost", 9999),
1151 allow_none=True)
1152 server.register_introspection_functions()
1153 server.register_multicall_functions()
1154 server.register_instance(servod_obj)
1155 logging.info("Listening on localhost port 9999")
1156 server.serve_forever()
1157
1158if __name__ == "__main__":
1159 test()
1160
1161 # simple client transaction would look like
1162 """
1163 remote_uri = 'http://localhost:9999'
1164 client = xmlrpclib.ServerProxy(remote_uri, verbose=False)
1165 send_str = "Hello_there"
1166 print "Sent " + send_str + ", Recv " + client.echo(send_str)
1167 """