blob: 7c9f06404d9c23c1c820fe0be0d92728d4719d57 [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."""
Simran Basia9f41032012-05-11 14:21:58 -07005import fnmatch
Todd Broche505b8d2011-03-21 18:19:54 -07006import imp
7import logging
Simran Basia9f41032012-05-11 14:21:58 -07008import os
9import shutil
Todd Broche505b8d2011-03-21 18:19:54 -070010import SimpleXMLRPCServer
Simran Basia9f41032012-05-11 14:21:58 -070011import subprocess
12import tempfile
Todd Broch7a91c252012-02-03 12:37:45 -080013import time
Simran Basia9f41032012-05-11 14:21:58 -070014import urllib
Todd Broche505b8d2011-03-21 18:19:54 -070015
16# TODO(tbroch) deprecate use of relative imports
Vic Yangbe6cf262012-09-10 10:40:56 +080017from drv.hw_driver import HwDriverError
Todd Broche505b8d2011-03-21 18:19:54 -070018import ftdigpio
19import ftdii2c
Todd Brochdbb09982011-10-02 07:14:26 -070020import ftdi_common
Todd Broch47c43f42011-05-26 15:11:31 -070021import ftdiuart
Simran Basie750a342013-03-12 13:45:26 -070022import servo_interfaces
Todd Broche505b8d2011-03-21 18:19:54 -070023
24MAX_I2C_CLOCK_HZ = 100000
25
Todd Brochdbb09982011-10-02 07:14:26 -070026
Todd Broche505b8d2011-03-21 18:19:54 -070027class ServodError(Exception):
28 """Exception class for servod."""
29
30class Servod(object):
31 """Main class for Servo debug/controller Daemon."""
Simran Basia9f41032012-05-11 14:21:58 -070032 _USB_DETECTION_DELAY = 10
33 _HTTP_PREFIX = "http://"
34
Todd Brochdbb09982011-10-02 07:14:26 -070035 def __init__(self, config, vendor, product, serialname=None, interfaces=None):
Todd Broche505b8d2011-03-21 18:19:54 -070036 """Servod constructor.
37
38 Args:
39 config: instance of SystemConfig containing all controls for
40 particular Servod invocation
41 vendor: usb vendor id of FTDI device
42 product: usb product id of FTDI device
Todd Brochad034442011-05-25 15:05:29 -070043 serialname: string of device serialname/number as defined in FTDI eeprom.
Todd Brochdbb09982011-10-02 07:14:26 -070044 interfaces: list of strings of interface types the server will instantiate
45
46 Raises:
47 ServodError: if unable to locate init method for particular interface
Todd Broche505b8d2011-03-21 18:19:54 -070048 """
49 self._logger = logging.getLogger("Servod")
50 self._logger.debug("")
51 self._vendor = vendor
52 self._product = product
Todd Brochad034442011-05-25 15:05:29 -070053 self._serialname = serialname
Todd Broche505b8d2011-03-21 18:19:54 -070054 self._syscfg = config
55 # list of objects (Fi2c, Fgpio) to physical interfaces (gpio, i2c) that ftdi
56 # interfaces are mapped to
57 self._interface_list = []
58 # Dict of Dict to map control name, function name to to tuple (params, drv)
59 # Ex) _drv_dict[name]['get'] = (params, drv)
60 self._drv_dict = {}
61
Todd Brochdbb09982011-10-02 07:14:26 -070062 # Note, interface i is (i - 1) in list
63 if not interfaces:
Simran Basie750a342013-03-12 13:45:26 -070064 interfaces = servo_interfaces.INTERFACE_DEFAULTS[vendor][product]
Todd Brochdbb09982011-10-02 07:14:26 -070065
66 for i, name in enumerate(interfaces):
Todd Broch8a77a992012-01-27 09:46:08 -080067 # servos with multiple FTDI are guaranteed to have contiguous USB PIDs
68 if i and ((i % ftdi_common.MAX_FTDI_INTERFACES_PER_DEVICE) == 0):
69 self._product += 1
70 self._logger.info("Changing to next FTDI part @ pid = 0x%04x",
71 self._product)
72
Todd Brochdbb09982011-10-02 07:14:26 -070073 self._logger.info("Initializing FTDI interface %d to %s", i + 1, name)
74 try:
75 func = getattr(self, '_init_%s' % name)
76 except AttributeError:
77 raise ServodError("Unable to locate init for interface %s" % name)
Todd Brocha9c74692012-01-24 22:54:22 -080078 result = func((i % ftdi_common.MAX_FTDI_INTERFACES_PER_DEVICE) + 1)
Todd Broch888da782011-10-07 14:29:09 -070079 if isinstance(result, tuple):
80 self._interface_list.extend(result)
81 else:
82 self._interface_list.append(result)
Todd Broche505b8d2011-03-21 18:19:54 -070083
Todd Broch3ec8df02012-11-20 10:53:03 -080084 def __del__(self):
85 """Servod deconstructor."""
86 for interface in self._interface_list:
87 del(interface)
88
Todd Brochb3048492012-01-15 21:52:41 -080089 def _init_dummy(self, interface):
90 """Initialize dummy interface.
91
92 Dummy interface is just a mechanism to reserve that interface for non servod
93 interaction. Typically the interface will be managed by external
94 third-party tools like openOCD or urjtag for JTAG or flashrom for SPI
95 interfaces.
96
97 TODO(tbroch): Investigate merits of incorporating these third-party
98 interfaces into servod or creating a communication channel between them
99
100 Returns: None
101 """
102 return None
103
Simran Basie750a342013-03-12 13:45:26 -0700104 def _init_ftdi_gpio(self, interface):
Todd Broche505b8d2011-03-21 18:19:54 -0700105 """Initialize gpio driver interface and open for use.
106
107 Args:
108 interface: interface number of FTDI device to use.
109
110 Returns:
111 Instance object of interface.
Todd Broch6de9dc62012-04-09 15:23:53 -0700112
113 Raises:
114 ServodError: If init fails
Todd Broche505b8d2011-03-21 18:19:54 -0700115 """
Todd Brochad034442011-05-25 15:05:29 -0700116 fobj = ftdigpio.Fgpio(self._vendor, self._product, interface,
117 self._serialname)
Todd Broch6de9dc62012-04-09 15:23:53 -0700118 try:
119 fobj.open()
120 except ftdigpio.FgpioError as e:
121 raise ServodError('Opening gpio interface. %s ( %d )' % (e.msg, e.value))
122
Todd Broche505b8d2011-03-21 18:19:54 -0700123 return fobj
124
Simran Basie750a342013-03-12 13:45:26 -0700125 # TODO (sbasi) crbug.com/187488 - Implement BBgpio.
126 def _init_bb_gpio(self, interface):
127 """Initalize beaglebone gpio interface."""
128 pass
129
130 def _init_ftdi_i2c(self, interface):
Todd Broche505b8d2011-03-21 18:19:54 -0700131 """Initialize i2c interface and open for use.
132
133 Args:
134 interface: interface number of FTDI device to use
135
136 Returns:
137 Instance object of interface
Todd Broch6de9dc62012-04-09 15:23:53 -0700138
139 Raises:
140 ServodError: If init fails
Todd Broche505b8d2011-03-21 18:19:54 -0700141 """
Todd Brochad034442011-05-25 15:05:29 -0700142 fobj = ftdii2c.Fi2c(self._vendor, self._product, interface,
143 self._serialname)
Todd Broch6de9dc62012-04-09 15:23:53 -0700144 try:
145 fobj.open()
146 except ftdii2c.Fi2cError as e:
147 raise ServodError('Opening i2c interface. %s ( %d )' % (e.msg, e.value))
148
Todd Broche505b8d2011-03-21 18:19:54 -0700149 # Set the frequency of operation of the i2c bus.
150 # TODO(tbroch) make configureable
151 fobj.setclock(MAX_I2C_CLOCK_HZ)
Todd Broch6de9dc62012-04-09 15:23:53 -0700152
Todd Broche505b8d2011-03-21 18:19:54 -0700153 return fobj
154
Simran Basie750a342013-03-12 13:45:26 -0700155 # TODO (sbasi) crbug.com/187489 - Implement bb_i2c.
156 def _init_bb_i2c(self, interface):
157 """Initalize beaglebone i2c interface."""
158 pass
159
160 def _init_ftdi_uart(self, interface):
161 """Initialize ftdi uart inteface and open for use
Todd Broch47c43f42011-05-26 15:11:31 -0700162
163 Note, the uart runs in a separate thread (pthreads). Users wishing to
164 interact with it will query control for the pty's pathname and connect
165 with there favorite console program. For example:
166 cu -l /dev/pts/22
167
168 Args:
169 interface: interface number of FTDI device to use
170
171 Returns:
172 Instance object of interface
Todd Broch6de9dc62012-04-09 15:23:53 -0700173
174 Raises:
175 ServodError: If init fails
Todd Broch47c43f42011-05-26 15:11:31 -0700176 """
Jeremy Thorpe9e110062012-10-25 10:40:00 -0700177 fobj = ftdiuart.Fuart(self._vendor, self._product, interface,
178 self._serialname)
Todd Broch6de9dc62012-04-09 15:23:53 -0700179 try:
180 fobj.run()
181 except ftdiuart.FuartError as e:
182 raise ServodError('Running uart interface. %s ( %d )' % (e.msg, e.value))
183
Todd Broch47c43f42011-05-26 15:11:31 -0700184 self._logger.info("%s" % fobj.get_pty())
185 return fobj
186
Simran Basie750a342013-03-12 13:45:26 -0700187 # TODO (sbasi) crbug.com/187492 - Implement bbuart.
188 def _init_bb_uart(self, interface):
189 """Initalize beaglebone uart interface."""
190 pass
191
192 def _init_ftdi_gpiouart(self, interface):
Todd Broch888da782011-10-07 14:29:09 -0700193 """Initialize special gpio + uart interface and open for use
194
195 Note, the uart runs in a separate thread (pthreads). Users wishing to
196 interact with it will query control for the pty's pathname and connect
197 with there favorite console program. For example:
198 cu -l /dev/pts/22
199
200 Args:
201 interface: interface number of FTDI device to use
202
203 Returns:
204 Instance objects of interface
Todd Broch6de9dc62012-04-09 15:23:53 -0700205
206 Raises:
207 ServodError: If init fails
Todd Broch888da782011-10-07 14:29:09 -0700208 """
Simran Basie750a342013-03-12 13:45:26 -0700209 fgpio = self._init_ftdi_gpio(interface)
Jeremy Thorpe9e110062012-10-25 10:40:00 -0700210 fuart = ftdiuart.Fuart(self._vendor, self._product, interface,
211 self._serialname, fgpio._fc)
Todd Broch6de9dc62012-04-09 15:23:53 -0700212 try:
213 fuart.run()
214 except ftdiuart.FuartError as e:
215 raise ServodError('Running uart interface. %s ( %d )' % (e.msg, e.value))
216
Todd Broch888da782011-10-07 14:29:09 -0700217 self._logger.info("uart pty: %s" % fuart.get_pty())
218 return fgpio, fuart
219
Tom Wai-Hong Tam28f0a5f2012-08-21 12:49:57 +0800220 def _camel_case(self, string):
221 output = ''
222 for s in string.split('_'):
223 if output:
224 output += s.capitalize()
225 else:
226 output = s
227 return output
228
Todd Broche505b8d2011-03-21 18:19:54 -0700229 def _get_param_drv(self, control_name, is_get=True):
230 """Get access to driver for a given control.
231
232 Note, some controls have different parameter dictionaries for 'getting' the
233 control's value versus 'setting' it. Boolean is_get distinguishes which is
234 being requested.
235
236 Args:
237 control_name: string name of control
238 is_get: boolean to determine
239
240 Returns:
241 tuple (param, drv) where:
242 param: param dictionary for control
243 drv: instance object of driver for particular control
244
245 Raises:
246 ServodError: Error occurred while examining params dict
247 """
248 self._logger.debug("")
249 # if already setup just return tuple from driver dict
250 if control_name in self._drv_dict:
251 if is_get and ('get' in self._drv_dict[control_name]):
252 return self._drv_dict[control_name]['get']
253 if not is_get and ('set' in self._drv_dict[control_name]):
254 return self._drv_dict[control_name]['set']
255
256 params = self._syscfg.lookup_control_params(control_name, is_get)
257 if 'drv' not in params:
258 self._logger.error("Unable to determine driver for %s" % control_name)
259 raise ServodError("'drv' key not found in params dict")
260 if 'interface' not in params:
261 self._logger.error("Unable to determine interface for %s" %
262 control_name)
263
264 raise ServodError("'interface' key not found in params dict")
265 index = int(params['interface']) - 1
266 interface = self._interface_list[index]
267 servo_pkg = imp.load_module('servo', *imp.find_module('servo'))
268 drv_pkg = imp.load_module('drv',
269 *imp.find_module('drv', servo_pkg.__path__))
270 drv_name = params['drv']
271 drv_module = getattr(drv_pkg, drv_name)
Tom Wai-Hong Tam28f0a5f2012-08-21 12:49:57 +0800272 drv_class = getattr(drv_module, self._camel_case(drv_name))
Todd Broche505b8d2011-03-21 18:19:54 -0700273 drv = drv_class(interface, params)
274 if control_name not in self._drv_dict:
275 self._drv_dict[control_name] = {}
276 if is_get:
277 self._drv_dict[control_name]['get'] = (params, drv)
278 else:
279 self._drv_dict[control_name]['set'] = (params, drv)
280 return (params, drv)
281
282 def doc_all(self):
283 """Return all documenation for controls.
284
285 Returns:
286 string of <doc> text in config file (xml) and the params dictionary for
287 all controls.
288
289 For example:
290 warm_reset :: Reset the device warmly
291 ------------------------> {'interface': '1', 'map': 'onoff_i', ... }
292 """
293 return self._syscfg.display_config()
294
295 def doc(self, name):
296 """Retreive doc string in system config file for given control name.
297
298 Args:
299 name: name string of control to get doc string
300
301 Returns:
302 doc string of name
303
304 Raises:
305 NameError: if fails to locate control
306 """
307 self._logger.debug("name(%s)" % (name))
308 if self._syscfg.is_control(name):
309 return self._syscfg.get_control_docstring(name)
310 else:
311 raise NameError("No control %s" %name)
312
Simran Basia9f41032012-05-11 14:21:58 -0700313 def _get_usb_port_set(self):
314 """Gets a set of USB disks currently connected to the system
315
316 Returns:
317 A set of USB disk paths.
318 """
319 usb_set = fnmatch.filter(os.listdir("/dev/"), "sd[a-z]")
320 return set(["/dev/" + dev for dev in usb_set])
321
322 def _probe_host_usb_dev(self):
323 """Probe the USB disk device plugged in the servo from the host side.
324
325 Method can fail by:
326 1) Having multiple servos connected and returning incorrect /dev/sdX of
327 another servo.
328 2) Finding multiple /dev/sdX and returning None.
329
330 Returns:
331 USB disk path if one and only one USB disk path is found, otherwise None.
332 """
333 original_value = self.get("usb_mux_sel1")
334 # Make the host unable to see the USB disk.
335 if original_value != "dut_sees_usbkey":
336 self.set("usb_mux_sel1", "dut_sees_usbkey")
337 time.sleep(self._USB_DETECTION_DELAY)
338
339 no_usb_set = self._get_usb_port_set()
340 # Make the host able to see the USB disk.
341 self.set("usb_mux_sel1", "servo_sees_usbkey")
342 time.sleep(self._USB_DETECTION_DELAY)
343
344 has_usb_set = self._get_usb_port_set()
345 # Back to its original value.
346 if original_value != "servo_sees_usbkey":
347 self.set("usb_mux_sel1", original_value)
348 time.sleep(self._USB_DETECTION_DELAY)
349 # Subtract the two sets to find the usb device.
350 diff_set = has_usb_set - no_usb_set
351 if len(diff_set) == 1:
352 return diff_set.pop()
353 else:
354 return None
355
356 def download_image_to_usb(self, image_path):
357 """Download image and save to the USB device found by probe_host_usb_dev.
358 If the image_path is a URL, it will download this url to the USB path;
359 otherwise it will simply copy the image_path's contents to the USB path.
360
361 Args:
362 image_path: path or url to the recovery image.
363
364 Returns:
365 True|False: True if process completed successfully, False if error
366 occurred.
367 Can't return None because XMLRPC doesn't allow it. PTAL at tbroch's
368 comment at the end of set().
369 """
370 self._logger.debug("image_path(%s)" % image_path)
371 self._logger.debug("Detecting USB stick device...")
372 usb_dev = self._probe_host_usb_dev()
373 if not usb_dev:
374 self._logger.error("No usb device connected to servo")
375 return False
376
377 try:
378 if image_path.startswith(self._HTTP_PREFIX):
379 self._logger.debug("Image path is a URL, downloading image")
380 urllib.urlretrieve(image_path, usb_dev)
381 else:
382 shutil.copyfile(image_path, usb_dev)
383 except IOError as e:
384 self._logger.error("Failed to transfer image to USB device: %s ( %d ) ",
385 e.strerror, e.errno)
386 return False
387 except urllib.ContentTooShortError:
388 self._logger.error("Failed to download URL: %s to USB device: %s",
389 image_path, usb_dev)
390 return False
391 except BaseException as e:
392 self._logger.error("Unexpected exception downloading %s to %s: %s",
393 image_path, usb_dev, str(e))
394 return False
J. Richard Barnettee4125af2013-02-26 18:31:56 -0800395 finally:
396 # We just plastered the partition table for a block device.
397 # Pass or fail, we mustn't go without telling the kernel about
398 # the change, or it will punish us with sporadic, hard-to-debug
399 # failures.
400 subprocess.call(["sync"])
401 subprocess.call(["blockdev", "--rereadpt", usb_dev])
Simran Basia9f41032012-05-11 14:21:58 -0700402 return True
403
404 def make_image_noninteractive(self):
405 """Makes the recovery image noninteractive.
406
407 A noninteractive image will reboot automatically after installation
408 instead of waiting for the USB device to be removed to initiate a system
409 reboot.
410
411 Mounts partition 1 of the image stored on usb_dev and creates a file
412 called "non_interactive" so that the image will become noninteractive.
413
414 Returns:
415 True|False: True if process completed successfully, False if error
416 occurred.
417 """
418 result = True
419 usb_dev = self._probe_host_usb_dev()
420 if not usb_dev:
421 self._logger.error("No usb device connected to servo")
422 return False
423 # Create TempDirectory
424 tmpdir = tempfile.mkdtemp()
425 if tmpdir:
426 # Mount drive to tmpdir.
427 partition_1 = "%s1" % usb_dev
428 rc = subprocess.call(["mount", partition_1, tmpdir])
429 if rc == 0:
430 # Create file 'non_interactive'
431 non_interactive_file = os.path.join(tmpdir, "non_interactive")
432 try:
433 open(non_interactive_file, "w").close()
434 except IOError as e:
435 self._logger.error("Failed to create file %s : %s ( %d )",
436 non_interactive_file, e.strerror, e.errno)
437 result = False
438 except BaseException as e:
439 self._logger.error("Unexpected Exception creating file %s : %s",
440 non_interactive_file, str(e))
441 result = False
442 # Unmount drive regardless if file creation worked or not.
443 rc = subprocess.call(["umount", partition_1])
444 if rc != 0:
445 self._logger.error("Failed to unmount USB Device")
446 result = False
447 else:
448 self._logger.error("Failed to mount USB Device")
449 result = False
450
451 # Delete tmpdir. May throw exception if 'umount' failed.
452 try:
453 os.rmdir(tmpdir)
454 except OSError as e:
455 self._logger.error("Failed to remove temp directory %s : %s",
456 tmpdir, str(e))
457 return False
458 except BaseException as e:
459 self._logger.error("Unexpected Exception removing tempdir %s : %s",
460 tmpdir, str(e))
461 return False
462 else:
463 self._logger.error("Failed to create temp directory.")
464 return False
465 return result
466
Todd Broche505b8d2011-03-21 18:19:54 -0700467 def get(self, name):
468 """Get control value.
469
470 Args:
471 name: name string of control
472
473 Returns:
474 Response from calling drv get method. Value is reformatted based on
475 control's dictionary parameters
476
477 Raises:
478 HwDriverError: Error occurred while using drv
479 """
480 self._logger.debug("name(%s)" % (name))
481 (param, drv) = self._get_param_drv(name)
482 try:
483 val = drv.get()
484 rd_val = self._syscfg.reformat_val(param, val)
Todd Brochb042e7a2011-12-14 17:41:36 -0800485 self._logger.debug("%s = %s" % (name, rd_val))
Todd Broche505b8d2011-03-21 18:19:54 -0700486 return rd_val
Todd Brochfbc499d2011-06-16 16:09:58 -0700487 except AttributeError, error:
488 self._logger.error("Getting %s: %s" % (name, error))
489 raise
Vic Yangbe6cf262012-09-10 10:40:56 +0800490 except HwDriverError:
Todd Broche505b8d2011-03-21 18:19:54 -0700491 self._logger.error("Getting %s" % (name))
492 raise
Todd Brochd6061672012-05-11 15:52:47 -0700493
Todd Broche505b8d2011-03-21 18:19:54 -0700494 def get_all(self, verbose):
495 """Get all controls values.
496
497 Args:
498 verbose: Boolean on whether to return doc info as well
499
500 Returns:
501 string creating from trying to get all values of all controls. In case of
502 error attempting access to control, response is 'ERR'.
503 """
Vadim Bendeburyb07944c2013-01-16 10:47:10 -0800504 rsp = []
Todd Broche505b8d2011-03-21 18:19:54 -0700505 for name in self._syscfg.syscfg_dict['control']:
506 self._logger.debug("name = %s" %name)
507 try:
508 value = self.get(name)
509 except Exception:
510 value = "ERR"
511 pass
512 if verbose:
Vadim Bendeburyb07944c2013-01-16 10:47:10 -0800513 rsp.append("GET %s = %s :: %s" % (name, value, self.doc(name)))
Todd Broche505b8d2011-03-21 18:19:54 -0700514 else:
Vadim Bendeburyb07944c2013-01-16 10:47:10 -0800515 rsp.append("%s:%s" % (name, value))
516 return '\n'.join(sorted(rsp))
Todd Broche505b8d2011-03-21 18:19:54 -0700517
518 def set(self, name, wr_val_str):
519 """Set control.
520
521 Args:
522 name: name string of control
523 wr_val_str: value string to write. Can be integer, float or a
524 alpha-numerical that is mapped to a integer or float.
525
526 Raises:
527 HwDriverError: Error occurred while using driver
528 """
Todd Broch7a91c252012-02-03 12:37:45 -0800529 if name == 'sleep':
530 time.sleep(float(wr_val_str))
531 return True
532
Todd Broche505b8d2011-03-21 18:19:54 -0700533 self._logger.debug("name(%s) wr_val(%s)" % (name, wr_val_str))
534 (params, drv) = self._get_param_drv(name, False)
535 wr_val = self._syscfg.resolve_val(params, wr_val_str)
536 try:
537 drv.set(wr_val)
Vic Yangbe6cf262012-09-10 10:40:56 +0800538 except HwDriverError:
Todd Broche505b8d2011-03-21 18:19:54 -0700539 self._logger.error("Setting %s -> %s" % (name, wr_val_str))
540 raise
541 # TODO(tbroch) Figure out why despite allow_none=True for both xmlrpc server
542 # & client I still have to return something to appease the
543 # marshall/unmarshall
544 return True
545
Todd Brochd6061672012-05-11 15:52:47 -0700546 def hwinit(self, verbose=False):
547 """Initialize all controls.
548
549 These values are part of the system config XML files of the form
550 init=<value>. This command should be used by clients wishing to return the
551 servo and DUT its connected to a known good/safe state.
552
Vadim Bendeburybb51dd42013-01-31 13:47:46 -0800553 Note that initialization errors are ignored (as in some cases they could
554 be caused by DUT firmware deficiencies). This might need to be fine tuned
555 later.
556
Todd Brochd6061672012-05-11 15:52:47 -0700557 Args:
558 verbose: boolean, if True prints info about control initialized.
559 Otherwise prints nothing.
Vadim Bendebury5934e4b2013-02-06 13:57:54 -0800560
561 Returns:
562 This function is called across RPC and as such is expected to return
563 something unless transferring 'none' across is allowed. Hence adding a
564 dummy return value to make things simpler.
Todd Brochd6061672012-05-11 15:52:47 -0700565 """
Todd Brochd9acf0a2012-12-05 13:43:06 -0800566 for control_name, value in self._syscfg.hwinit:
Todd Broch3ec8df02012-11-20 10:53:03 -0800567 try:
568 self.set(control_name, value)
569 except Exception as e:
Todd Broch3ec8df02012-11-20 10:53:03 -0800570 self._logger.error("Problem initializing %s -> %s :: %s",
571 control_name, value, str(e))
Todd Brochd6061672012-05-11 15:52:47 -0700572 if verbose:
573 self._logger.info('Initialized %s to %s', control_name, value)
Vadim Bendebury5934e4b2013-02-06 13:57:54 -0800574 return True
Todd Broch3ec8df02012-11-20 10:53:03 -0800575
Todd Broche505b8d2011-03-21 18:19:54 -0700576 def echo(self, echo):
577 """Dummy echo function for testing/examples.
578
579 Args:
580 echo: string to echo back to client
581 """
582 self._logger.debug("echo(%s)" % (echo))
583 return "ECH0ING: %s" % (echo)
584
Todd Brochdbb09982011-10-02 07:14:26 -0700585
Todd Broche505b8d2011-03-21 18:19:54 -0700586def test():
587 """Integration testing.
588
589 TODO(tbroch) Enhance integration test and add unittest (see mox)
590 """
591 logging.basicConfig(level=logging.DEBUG,
592 format="%(asctime)s - %(name)s - " +
593 "%(levelname)s - %(message)s")
594 # configure server & listen
595 servod_obj = Servod(1)
596 # 4 == number of interfaces on a FT4232H device
597 for i in xrange(4):
598 if i == 1:
599 # its an i2c interface ... see __init__ for details and TODO to make
600 # this configureable
601 servod_obj._interface_list[i].wr_rd(0x21, [0], 1)
602 else:
603 # its a gpio interface
604 servod_obj._interface_list[i].wr_rd(0)
605
606 server = SimpleXMLRPCServer.SimpleXMLRPCServer(("localhost", 9999),
607 allow_none=True)
608 server.register_introspection_functions()
609 server.register_multicall_functions()
610 server.register_instance(servod_obj)
611 logging.info("Listening on localhost port 9999")
612 server.serve_forever()
613
614if __name__ == "__main__":
615 test()
616
617 # simple client transaction would look like
618 """
619 remote_uri = 'http://localhost:9999'
620 client = xmlrpclib.ServerProxy(remote_uri, verbose=False)
621 send_str = "Hello_there"
622 print "Sent " + send_str + ", Recv " + client.echo(send_str)
623 """