blob: 0d51276d323a3410ae0c86ce16a137bef9146794 [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
Todd Broche505b8d2011-03-21 18:19:54 -070022
23MAX_I2C_CLOCK_HZ = 100000
24
Todd Brochdbb09982011-10-02 07:14:26 -070025
Todd Broche505b8d2011-03-21 18:19:54 -070026class ServodError(Exception):
27 """Exception class for servod."""
28
29class Servod(object):
30 """Main class for Servo debug/controller Daemon."""
Simran Basia9f41032012-05-11 14:21:58 -070031 _USB_DETECTION_DELAY = 10
32 _HTTP_PREFIX = "http://"
33
Todd Brochdbb09982011-10-02 07:14:26 -070034 def __init__(self, config, vendor, product, serialname=None, interfaces=None):
Todd Broche505b8d2011-03-21 18:19:54 -070035 """Servod constructor.
36
37 Args:
38 config: instance of SystemConfig containing all controls for
39 particular Servod invocation
40 vendor: usb vendor id of FTDI device
41 product: usb product id of FTDI device
Todd Brochad034442011-05-25 15:05:29 -070042 serialname: string of device serialname/number as defined in FTDI eeprom.
Todd Brochdbb09982011-10-02 07:14:26 -070043 interfaces: list of strings of interface types the server will instantiate
44
45 Raises:
46 ServodError: if unable to locate init method for particular interface
Todd Broche505b8d2011-03-21 18:19:54 -070047 """
48 self._logger = logging.getLogger("Servod")
49 self._logger.debug("")
50 self._vendor = vendor
51 self._product = product
Todd Brochad034442011-05-25 15:05:29 -070052 self._serialname = serialname
Todd Broche505b8d2011-03-21 18:19:54 -070053 self._syscfg = config
54 # list of objects (Fi2c, Fgpio) to physical interfaces (gpio, i2c) that ftdi
55 # interfaces are mapped to
56 self._interface_list = []
57 # Dict of Dict to map control name, function name to to tuple (params, drv)
58 # Ex) _drv_dict[name]['get'] = (params, drv)
59 self._drv_dict = {}
60
Todd Brochdbb09982011-10-02 07:14:26 -070061 # Note, interface i is (i - 1) in list
62 if not interfaces:
63 interfaces = ftdi_common.INTERFACE_DEFAULTS[vendor][product]
64
65 for i, name in enumerate(interfaces):
Todd Broch8a77a992012-01-27 09:46:08 -080066 # servos with multiple FTDI are guaranteed to have contiguous USB PIDs
67 if i and ((i % ftdi_common.MAX_FTDI_INTERFACES_PER_DEVICE) == 0):
68 self._product += 1
69 self._logger.info("Changing to next FTDI part @ pid = 0x%04x",
70 self._product)
71
Todd Brochdbb09982011-10-02 07:14:26 -070072 self._logger.info("Initializing FTDI interface %d to %s", i + 1, name)
73 try:
74 func = getattr(self, '_init_%s' % name)
75 except AttributeError:
76 raise ServodError("Unable to locate init for interface %s" % name)
Todd Brocha9c74692012-01-24 22:54:22 -080077 result = func((i % ftdi_common.MAX_FTDI_INTERFACES_PER_DEVICE) + 1)
Todd Broch888da782011-10-07 14:29:09 -070078 if isinstance(result, tuple):
79 self._interface_list.extend(result)
80 else:
81 self._interface_list.append(result)
Todd Broche505b8d2011-03-21 18:19:54 -070082
Todd Broch3ec8df02012-11-20 10:53:03 -080083 def __del__(self):
84 """Servod deconstructor."""
85 for interface in self._interface_list:
86 del(interface)
87
Todd Brochb3048492012-01-15 21:52:41 -080088 def _init_dummy(self, interface):
89 """Initialize dummy interface.
90
91 Dummy interface is just a mechanism to reserve that interface for non servod
92 interaction. Typically the interface will be managed by external
93 third-party tools like openOCD or urjtag for JTAG or flashrom for SPI
94 interfaces.
95
96 TODO(tbroch): Investigate merits of incorporating these third-party
97 interfaces into servod or creating a communication channel between them
98
99 Returns: None
100 """
101 return None
102
Todd Broche505b8d2011-03-21 18:19:54 -0700103 def _init_gpio(self, interface):
104 """Initialize gpio driver interface and open for use.
105
106 Args:
107 interface: interface number of FTDI device to use.
108
109 Returns:
110 Instance object of interface.
Todd Broch6de9dc62012-04-09 15:23:53 -0700111
112 Raises:
113 ServodError: If init fails
Todd Broche505b8d2011-03-21 18:19:54 -0700114 """
Todd Brochad034442011-05-25 15:05:29 -0700115 fobj = ftdigpio.Fgpio(self._vendor, self._product, interface,
116 self._serialname)
Todd Broch6de9dc62012-04-09 15:23:53 -0700117 try:
118 fobj.open()
119 except ftdigpio.FgpioError as e:
120 raise ServodError('Opening gpio interface. %s ( %d )' % (e.msg, e.value))
121
Todd Broche505b8d2011-03-21 18:19:54 -0700122 return fobj
123
124 def _init_i2c(self, interface):
125 """Initialize i2c interface and open for use.
126
127 Args:
128 interface: interface number of FTDI device to use
129
130 Returns:
131 Instance object of interface
Todd Broch6de9dc62012-04-09 15:23:53 -0700132
133 Raises:
134 ServodError: If init fails
Todd Broche505b8d2011-03-21 18:19:54 -0700135 """
Todd Brochad034442011-05-25 15:05:29 -0700136 fobj = ftdii2c.Fi2c(self._vendor, self._product, interface,
137 self._serialname)
Todd Broch6de9dc62012-04-09 15:23:53 -0700138 try:
139 fobj.open()
140 except ftdii2c.Fi2cError as e:
141 raise ServodError('Opening i2c interface. %s ( %d )' % (e.msg, e.value))
142
Todd Broche505b8d2011-03-21 18:19:54 -0700143 # Set the frequency of operation of the i2c bus.
144 # TODO(tbroch) make configureable
145 fobj.setclock(MAX_I2C_CLOCK_HZ)
Todd Broch6de9dc62012-04-09 15:23:53 -0700146
Todd Broche505b8d2011-03-21 18:19:54 -0700147 return fobj
148
Todd Broch47c43f42011-05-26 15:11:31 -0700149 def _init_uart(self, interface):
150 """Initialize uart inteface and open for use
151
152 Note, the uart runs in a separate thread (pthreads). Users wishing to
153 interact with it will query control for the pty's pathname and connect
154 with there favorite console program. For example:
155 cu -l /dev/pts/22
156
157 Args:
158 interface: interface number of FTDI device to use
159
160 Returns:
161 Instance object of interface
Todd Broch6de9dc62012-04-09 15:23:53 -0700162
163 Raises:
164 ServodError: If init fails
Todd Broch47c43f42011-05-26 15:11:31 -0700165 """
Jeremy Thorpe9e110062012-10-25 10:40:00 -0700166 fobj = ftdiuart.Fuart(self._vendor, self._product, interface,
167 self._serialname)
Todd Broch6de9dc62012-04-09 15:23:53 -0700168 try:
169 fobj.run()
170 except ftdiuart.FuartError as e:
171 raise ServodError('Running uart interface. %s ( %d )' % (e.msg, e.value))
172
Todd Broch47c43f42011-05-26 15:11:31 -0700173 self._logger.info("%s" % fobj.get_pty())
174 return fobj
175
Todd Broch888da782011-10-07 14:29:09 -0700176 def _init_gpiouart(self, interface):
177 """Initialize special gpio + uart interface and open for use
178
179 Note, the uart runs in a separate thread (pthreads). Users wishing to
180 interact with it will query control for the pty's pathname and connect
181 with there favorite console program. For example:
182 cu -l /dev/pts/22
183
184 Args:
185 interface: interface number of FTDI device to use
186
187 Returns:
188 Instance objects of interface
Todd Broch6de9dc62012-04-09 15:23:53 -0700189
190 Raises:
191 ServodError: If init fails
Todd Broch888da782011-10-07 14:29:09 -0700192 """
193 fgpio = self._init_gpio(interface)
Jeremy Thorpe9e110062012-10-25 10:40:00 -0700194 fuart = ftdiuart.Fuart(self._vendor, self._product, interface,
195 self._serialname, fgpio._fc)
Todd Broch6de9dc62012-04-09 15:23:53 -0700196 try:
197 fuart.run()
198 except ftdiuart.FuartError as e:
199 raise ServodError('Running uart interface. %s ( %d )' % (e.msg, e.value))
200
Todd Broch888da782011-10-07 14:29:09 -0700201 self._logger.info("uart pty: %s" % fuart.get_pty())
202 return fgpio, fuart
203
Tom Wai-Hong Tam28f0a5f2012-08-21 12:49:57 +0800204 def _camel_case(self, string):
205 output = ''
206 for s in string.split('_'):
207 if output:
208 output += s.capitalize()
209 else:
210 output = s
211 return output
212
Todd Broche505b8d2011-03-21 18:19:54 -0700213 def _get_param_drv(self, control_name, is_get=True):
214 """Get access to driver for a given control.
215
216 Note, some controls have different parameter dictionaries for 'getting' the
217 control's value versus 'setting' it. Boolean is_get distinguishes which is
218 being requested.
219
220 Args:
221 control_name: string name of control
222 is_get: boolean to determine
223
224 Returns:
225 tuple (param, drv) where:
226 param: param dictionary for control
227 drv: instance object of driver for particular control
228
229 Raises:
230 ServodError: Error occurred while examining params dict
231 """
232 self._logger.debug("")
233 # if already setup just return tuple from driver dict
234 if control_name in self._drv_dict:
235 if is_get and ('get' in self._drv_dict[control_name]):
236 return self._drv_dict[control_name]['get']
237 if not is_get and ('set' in self._drv_dict[control_name]):
238 return self._drv_dict[control_name]['set']
239
240 params = self._syscfg.lookup_control_params(control_name, is_get)
241 if 'drv' not in params:
242 self._logger.error("Unable to determine driver for %s" % control_name)
243 raise ServodError("'drv' key not found in params dict")
244 if 'interface' not in params:
245 self._logger.error("Unable to determine interface for %s" %
246 control_name)
247
248 raise ServodError("'interface' key not found in params dict")
249 index = int(params['interface']) - 1
250 interface = self._interface_list[index]
251 servo_pkg = imp.load_module('servo', *imp.find_module('servo'))
252 drv_pkg = imp.load_module('drv',
253 *imp.find_module('drv', servo_pkg.__path__))
254 drv_name = params['drv']
255 drv_module = getattr(drv_pkg, drv_name)
Tom Wai-Hong Tam28f0a5f2012-08-21 12:49:57 +0800256 drv_class = getattr(drv_module, self._camel_case(drv_name))
Todd Broche505b8d2011-03-21 18:19:54 -0700257 drv = drv_class(interface, params)
258 if control_name not in self._drv_dict:
259 self._drv_dict[control_name] = {}
260 if is_get:
261 self._drv_dict[control_name]['get'] = (params, drv)
262 else:
263 self._drv_dict[control_name]['set'] = (params, drv)
264 return (params, drv)
265
266 def doc_all(self):
267 """Return all documenation for controls.
268
269 Returns:
270 string of <doc> text in config file (xml) and the params dictionary for
271 all controls.
272
273 For example:
274 warm_reset :: Reset the device warmly
275 ------------------------> {'interface': '1', 'map': 'onoff_i', ... }
276 """
277 return self._syscfg.display_config()
278
279 def doc(self, name):
280 """Retreive doc string in system config file for given control name.
281
282 Args:
283 name: name string of control to get doc string
284
285 Returns:
286 doc string of name
287
288 Raises:
289 NameError: if fails to locate control
290 """
291 self._logger.debug("name(%s)" % (name))
292 if self._syscfg.is_control(name):
293 return self._syscfg.get_control_docstring(name)
294 else:
295 raise NameError("No control %s" %name)
296
Simran Basia9f41032012-05-11 14:21:58 -0700297 def _get_usb_port_set(self):
298 """Gets a set of USB disks currently connected to the system
299
300 Returns:
301 A set of USB disk paths.
302 """
303 usb_set = fnmatch.filter(os.listdir("/dev/"), "sd[a-z]")
304 return set(["/dev/" + dev for dev in usb_set])
305
306 def _probe_host_usb_dev(self):
307 """Probe the USB disk device plugged in the servo from the host side.
308
309 Method can fail by:
310 1) Having multiple servos connected and returning incorrect /dev/sdX of
311 another servo.
312 2) Finding multiple /dev/sdX and returning None.
313
314 Returns:
315 USB disk path if one and only one USB disk path is found, otherwise None.
316 """
317 original_value = self.get("usb_mux_sel1")
318 # Make the host unable to see the USB disk.
319 if original_value != "dut_sees_usbkey":
320 self.set("usb_mux_sel1", "dut_sees_usbkey")
321 time.sleep(self._USB_DETECTION_DELAY)
322
323 no_usb_set = self._get_usb_port_set()
324 # Make the host able to see the USB disk.
325 self.set("usb_mux_sel1", "servo_sees_usbkey")
326 time.sleep(self._USB_DETECTION_DELAY)
327
328 has_usb_set = self._get_usb_port_set()
329 # Back to its original value.
330 if original_value != "servo_sees_usbkey":
331 self.set("usb_mux_sel1", original_value)
332 time.sleep(self._USB_DETECTION_DELAY)
333 # Subtract the two sets to find the usb device.
334 diff_set = has_usb_set - no_usb_set
335 if len(diff_set) == 1:
336 return diff_set.pop()
337 else:
338 return None
339
340 def download_image_to_usb(self, image_path):
341 """Download image and save to the USB device found by probe_host_usb_dev.
342 If the image_path is a URL, it will download this url to the USB path;
343 otherwise it will simply copy the image_path's contents to the USB path.
344
345 Args:
346 image_path: path or url to the recovery image.
347
348 Returns:
349 True|False: True if process completed successfully, False if error
350 occurred.
351 Can't return None because XMLRPC doesn't allow it. PTAL at tbroch's
352 comment at the end of set().
353 """
354 self._logger.debug("image_path(%s)" % image_path)
355 self._logger.debug("Detecting USB stick device...")
356 usb_dev = self._probe_host_usb_dev()
357 if not usb_dev:
358 self._logger.error("No usb device connected to servo")
359 return False
360
361 try:
362 if image_path.startswith(self._HTTP_PREFIX):
363 self._logger.debug("Image path is a URL, downloading image")
364 urllib.urlretrieve(image_path, usb_dev)
365 else:
366 shutil.copyfile(image_path, usb_dev)
367 except IOError as e:
368 self._logger.error("Failed to transfer image to USB device: %s ( %d ) ",
369 e.strerror, e.errno)
370 return False
371 except urllib.ContentTooShortError:
372 self._logger.error("Failed to download URL: %s to USB device: %s",
373 image_path, usb_dev)
374 return False
375 except BaseException as e:
376 self._logger.error("Unexpected exception downloading %s to %s: %s",
377 image_path, usb_dev, str(e))
378 return False
J. Richard Barnettee4125af2013-02-26 18:31:56 -0800379 finally:
380 # We just plastered the partition table for a block device.
381 # Pass or fail, we mustn't go without telling the kernel about
382 # the change, or it will punish us with sporadic, hard-to-debug
383 # failures.
384 subprocess.call(["sync"])
385 subprocess.call(["blockdev", "--rereadpt", usb_dev])
Simran Basia9f41032012-05-11 14:21:58 -0700386 return True
387
388 def make_image_noninteractive(self):
389 """Makes the recovery image noninteractive.
390
391 A noninteractive image will reboot automatically after installation
392 instead of waiting for the USB device to be removed to initiate a system
393 reboot.
394
395 Mounts partition 1 of the image stored on usb_dev and creates a file
396 called "non_interactive" so that the image will become noninteractive.
397
398 Returns:
399 True|False: True if process completed successfully, False if error
400 occurred.
401 """
402 result = True
403 usb_dev = self._probe_host_usb_dev()
404 if not usb_dev:
405 self._logger.error("No usb device connected to servo")
406 return False
407 # Create TempDirectory
408 tmpdir = tempfile.mkdtemp()
409 if tmpdir:
410 # Mount drive to tmpdir.
411 partition_1 = "%s1" % usb_dev
412 rc = subprocess.call(["mount", partition_1, tmpdir])
413 if rc == 0:
414 # Create file 'non_interactive'
415 non_interactive_file = os.path.join(tmpdir, "non_interactive")
416 try:
417 open(non_interactive_file, "w").close()
418 except IOError as e:
419 self._logger.error("Failed to create file %s : %s ( %d )",
420 non_interactive_file, e.strerror, e.errno)
421 result = False
422 except BaseException as e:
423 self._logger.error("Unexpected Exception creating file %s : %s",
424 non_interactive_file, str(e))
425 result = False
426 # Unmount drive regardless if file creation worked or not.
427 rc = subprocess.call(["umount", partition_1])
428 if rc != 0:
429 self._logger.error("Failed to unmount USB Device")
430 result = False
431 else:
432 self._logger.error("Failed to mount USB Device")
433 result = False
434
435 # Delete tmpdir. May throw exception if 'umount' failed.
436 try:
437 os.rmdir(tmpdir)
438 except OSError as e:
439 self._logger.error("Failed to remove temp directory %s : %s",
440 tmpdir, str(e))
441 return False
442 except BaseException as e:
443 self._logger.error("Unexpected Exception removing tempdir %s : %s",
444 tmpdir, str(e))
445 return False
446 else:
447 self._logger.error("Failed to create temp directory.")
448 return False
449 return result
450
Todd Broche505b8d2011-03-21 18:19:54 -0700451 def get(self, name):
452 """Get control value.
453
454 Args:
455 name: name string of control
456
457 Returns:
458 Response from calling drv get method. Value is reformatted based on
459 control's dictionary parameters
460
461 Raises:
462 HwDriverError: Error occurred while using drv
463 """
464 self._logger.debug("name(%s)" % (name))
465 (param, drv) = self._get_param_drv(name)
466 try:
467 val = drv.get()
468 rd_val = self._syscfg.reformat_val(param, val)
Todd Brochb042e7a2011-12-14 17:41:36 -0800469 self._logger.debug("%s = %s" % (name, rd_val))
Todd Broche505b8d2011-03-21 18:19:54 -0700470 return rd_val
Todd Brochfbc499d2011-06-16 16:09:58 -0700471 except AttributeError, error:
472 self._logger.error("Getting %s: %s" % (name, error))
473 raise
Vic Yangbe6cf262012-09-10 10:40:56 +0800474 except HwDriverError:
Todd Broche505b8d2011-03-21 18:19:54 -0700475 self._logger.error("Getting %s" % (name))
476 raise
Todd Brochd6061672012-05-11 15:52:47 -0700477
Todd Broche505b8d2011-03-21 18:19:54 -0700478 def get_all(self, verbose):
479 """Get all controls values.
480
481 Args:
482 verbose: Boolean on whether to return doc info as well
483
484 Returns:
485 string creating from trying to get all values of all controls. In case of
486 error attempting access to control, response is 'ERR'.
487 """
Vadim Bendeburyb07944c2013-01-16 10:47:10 -0800488 rsp = []
Todd Broche505b8d2011-03-21 18:19:54 -0700489 for name in self._syscfg.syscfg_dict['control']:
490 self._logger.debug("name = %s" %name)
491 try:
492 value = self.get(name)
493 except Exception:
494 value = "ERR"
495 pass
496 if verbose:
Vadim Bendeburyb07944c2013-01-16 10:47:10 -0800497 rsp.append("GET %s = %s :: %s" % (name, value, self.doc(name)))
Todd Broche505b8d2011-03-21 18:19:54 -0700498 else:
Vadim Bendeburyb07944c2013-01-16 10:47:10 -0800499 rsp.append("%s:%s" % (name, value))
500 return '\n'.join(sorted(rsp))
Todd Broche505b8d2011-03-21 18:19:54 -0700501
502 def set(self, name, wr_val_str):
503 """Set control.
504
505 Args:
506 name: name string of control
507 wr_val_str: value string to write. Can be integer, float or a
508 alpha-numerical that is mapped to a integer or float.
509
510 Raises:
511 HwDriverError: Error occurred while using driver
512 """
Todd Broch7a91c252012-02-03 12:37:45 -0800513 if name == 'sleep':
514 time.sleep(float(wr_val_str))
515 return True
516
Todd Broche505b8d2011-03-21 18:19:54 -0700517 self._logger.debug("name(%s) wr_val(%s)" % (name, wr_val_str))
518 (params, drv) = self._get_param_drv(name, False)
519 wr_val = self._syscfg.resolve_val(params, wr_val_str)
520 try:
521 drv.set(wr_val)
Vic Yangbe6cf262012-09-10 10:40:56 +0800522 except HwDriverError:
Todd Broche505b8d2011-03-21 18:19:54 -0700523 self._logger.error("Setting %s -> %s" % (name, wr_val_str))
524 raise
525 # TODO(tbroch) Figure out why despite allow_none=True for both xmlrpc server
526 # & client I still have to return something to appease the
527 # marshall/unmarshall
528 return True
529
Todd Brochd6061672012-05-11 15:52:47 -0700530 def hwinit(self, verbose=False):
531 """Initialize all controls.
532
533 These values are part of the system config XML files of the form
534 init=<value>. This command should be used by clients wishing to return the
535 servo and DUT its connected to a known good/safe state.
536
Vadim Bendeburybb51dd42013-01-31 13:47:46 -0800537 Note that initialization errors are ignored (as in some cases they could
538 be caused by DUT firmware deficiencies). This might need to be fine tuned
539 later.
540
Todd Brochd6061672012-05-11 15:52:47 -0700541 Args:
542 verbose: boolean, if True prints info about control initialized.
543 Otherwise prints nothing.
Vadim Bendebury5934e4b2013-02-06 13:57:54 -0800544
545 Returns:
546 This function is called across RPC and as such is expected to return
547 something unless transferring 'none' across is allowed. Hence adding a
548 dummy return value to make things simpler.
Todd Brochd6061672012-05-11 15:52:47 -0700549 """
Todd Brochd9acf0a2012-12-05 13:43:06 -0800550 for control_name, value in self._syscfg.hwinit:
Todd Broch3ec8df02012-11-20 10:53:03 -0800551 try:
552 self.set(control_name, value)
553 except Exception as e:
Todd Broch3ec8df02012-11-20 10:53:03 -0800554 self._logger.error("Problem initializing %s -> %s :: %s",
555 control_name, value, str(e))
Todd Brochd6061672012-05-11 15:52:47 -0700556 if verbose:
557 self._logger.info('Initialized %s to %s', control_name, value)
Vadim Bendebury5934e4b2013-02-06 13:57:54 -0800558 return True
Todd Broch3ec8df02012-11-20 10:53:03 -0800559
Todd Broche505b8d2011-03-21 18:19:54 -0700560 def echo(self, echo):
561 """Dummy echo function for testing/examples.
562
563 Args:
564 echo: string to echo back to client
565 """
566 self._logger.debug("echo(%s)" % (echo))
567 return "ECH0ING: %s" % (echo)
568
Todd Brochdbb09982011-10-02 07:14:26 -0700569
Todd Broche505b8d2011-03-21 18:19:54 -0700570def test():
571 """Integration testing.
572
573 TODO(tbroch) Enhance integration test and add unittest (see mox)
574 """
575 logging.basicConfig(level=logging.DEBUG,
576 format="%(asctime)s - %(name)s - " +
577 "%(levelname)s - %(message)s")
578 # configure server & listen
579 servod_obj = Servod(1)
580 # 4 == number of interfaces on a FT4232H device
581 for i in xrange(4):
582 if i == 1:
583 # its an i2c interface ... see __init__ for details and TODO to make
584 # this configureable
585 servod_obj._interface_list[i].wr_rd(0x21, [0], 1)
586 else:
587 # its a gpio interface
588 servod_obj._interface_list[i].wr_rd(0)
589
590 server = SimpleXMLRPCServer.SimpleXMLRPCServer(("localhost", 9999),
591 allow_none=True)
592 server.register_introspection_functions()
593 server.register_multicall_functions()
594 server.register_instance(servod_obj)
595 logging.info("Listening on localhost port 9999")
596 server.serve_forever()
597
598if __name__ == "__main__":
599 test()
600
601 # simple client transaction would look like
602 """
603 remote_uri = 'http://localhost:9999'
604 client = xmlrpclib.ServerProxy(remote_uri, verbose=False)
605 send_str = "Hello_there"
606 print "Sent " + send_str + ", Recv " + client.echo(send_str)
607 """