blob: 7f9bd1e330c90de0d8a49c62d149551b381813de [file] [log] [blame]
Todd Broche505b8d2011-03-21 18:19:54 -07001# Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4"""Servo Server."""
5import imp
6import logging
7import SimpleXMLRPCServer
8
9# TODO(tbroch) deprecate use of relative imports
10import ftdi_common
11import ftdigpio
12import ftdii2c
13
14MAX_I2C_CLOCK_HZ = 100000
15
16class ServodError(Exception):
17 """Exception class for servod."""
18
19class Servod(object):
20 """Main class for Servo debug/controller Daemon."""
21 def __init__(self, config, vendor=ftdi_common.DEFAULT_VID,
22 product=ftdi_common.DEFAULT_PID):
23 """Servod constructor.
24
25 Args:
26 config: instance of SystemConfig containing all controls for
27 particular Servod invocation
28 vendor: usb vendor id of FTDI device
29 product: usb product id of FTDI device
30 """
31 self._logger = logging.getLogger("Servod")
32 self._logger.debug("")
33 self._vendor = vendor
34 self._product = product
35 self._syscfg = config
36 # list of objects (Fi2c, Fgpio) to physical interfaces (gpio, i2c) that ftdi
37 # interfaces are mapped to
38 self._interface_list = []
39 # Dict of Dict to map control name, function name to to tuple (params, drv)
40 # Ex) _drv_dict[name]['get'] = (params, drv)
41 self._drv_dict = {}
42
43 # TODO(tbroch) make this configuraeable. Presently we hardwire these
44 # interfaces with 1,3,4 as Fgpio objects and 2 Fi2c objects. Future
45 # revisions will provide alternate and perhaps mechanism to re-allocate the
46 # interface altogether. Note, interface i is (i - 1) in list
47 self._interface_list.append(self._init_gpio(1))
48 self._interface_list.append(self._init_i2c(2))
49 self._interface_list.append(self._init_gpio(3))
50 self._interface_list.append(self._init_gpio(4))
51
52 def _init_gpio(self, interface):
53 """Initialize gpio driver interface and open for use.
54
55 Args:
56 interface: interface number of FTDI device to use.
57
58 Returns:
59 Instance object of interface.
60 """
61 fobj = ftdigpio.Fgpio(self._vendor, self._product, interface)
62 fobj.open()
63 return fobj
64
65 def _init_i2c(self, interface):
66 """Initialize i2c interface and open for use.
67
68 Args:
69 interface: interface number of FTDI device to use
70
71 Returns:
72 Instance object of interface
73 """
74 fobj = ftdii2c.Fi2c(self._vendor, self._product, interface)
75 fobj.open()
76 # Set the frequency of operation of the i2c bus.
77 # TODO(tbroch) make configureable
78 fobj.setclock(MAX_I2C_CLOCK_HZ)
79 return fobj
80
81 def _get_param_drv(self, control_name, is_get=True):
82 """Get access to driver for a given control.
83
84 Note, some controls have different parameter dictionaries for 'getting' the
85 control's value versus 'setting' it. Boolean is_get distinguishes which is
86 being requested.
87
88 Args:
89 control_name: string name of control
90 is_get: boolean to determine
91
92 Returns:
93 tuple (param, drv) where:
94 param: param dictionary for control
95 drv: instance object of driver for particular control
96
97 Raises:
98 ServodError: Error occurred while examining params dict
99 """
100 self._logger.debug("")
101 # if already setup just return tuple from driver dict
102 if control_name in self._drv_dict:
103 if is_get and ('get' in self._drv_dict[control_name]):
104 return self._drv_dict[control_name]['get']
105 if not is_get and ('set' in self._drv_dict[control_name]):
106 return self._drv_dict[control_name]['set']
107
108 params = self._syscfg.lookup_control_params(control_name, is_get)
109 if 'drv' not in params:
110 self._logger.error("Unable to determine driver for %s" % control_name)
111 raise ServodError("'drv' key not found in params dict")
112 if 'interface' not in params:
113 self._logger.error("Unable to determine interface for %s" %
114 control_name)
115
116 raise ServodError("'interface' key not found in params dict")
117 index = int(params['interface']) - 1
118 interface = self._interface_list[index]
119 servo_pkg = imp.load_module('servo', *imp.find_module('servo'))
120 drv_pkg = imp.load_module('drv',
121 *imp.find_module('drv', servo_pkg.__path__))
122 drv_name = params['drv']
123 drv_module = getattr(drv_pkg, drv_name)
124 drv_class = getattr(drv_module, drv_name)
125 drv = drv_class(interface, params)
126 if control_name not in self._drv_dict:
127 self._drv_dict[control_name] = {}
128 if is_get:
129 self._drv_dict[control_name]['get'] = (params, drv)
130 else:
131 self._drv_dict[control_name]['set'] = (params, drv)
132 return (params, drv)
133
134 def doc_all(self):
135 """Return all documenation for controls.
136
137 Returns:
138 string of <doc> text in config file (xml) and the params dictionary for
139 all controls.
140
141 For example:
142 warm_reset :: Reset the device warmly
143 ------------------------> {'interface': '1', 'map': 'onoff_i', ... }
144 """
145 return self._syscfg.display_config()
146
147 def doc(self, name):
148 """Retreive doc string in system config file for given control name.
149
150 Args:
151 name: name string of control to get doc string
152
153 Returns:
154 doc string of name
155
156 Raises:
157 NameError: if fails to locate control
158 """
159 self._logger.debug("name(%s)" % (name))
160 if self._syscfg.is_control(name):
161 return self._syscfg.get_control_docstring(name)
162 else:
163 raise NameError("No control %s" %name)
164
165 def get(self, name):
166 """Get control value.
167
168 Args:
169 name: name string of control
170
171 Returns:
172 Response from calling drv get method. Value is reformatted based on
173 control's dictionary parameters
174
175 Raises:
176 HwDriverError: Error occurred while using drv
177 """
178 self._logger.debug("name(%s)" % (name))
179 (param, drv) = self._get_param_drv(name)
180 try:
181 val = drv.get()
182 rd_val = self._syscfg.reformat_val(param, val)
183 self._logger.info("%s = %s" % (name, rd_val))
184 return rd_val
185 except drv.hw_driver.HwDriverError:
186 self._logger.error("Getting %s" % (name))
187 raise
188 def get_all(self, verbose):
189 """Get all controls values.
190
191 Args:
192 verbose: Boolean on whether to return doc info as well
193
194 Returns:
195 string creating from trying to get all values of all controls. In case of
196 error attempting access to control, response is 'ERR'.
197 """
198 rsp = ""
199 for name in self._syscfg.syscfg_dict['control']:
200 self._logger.debug("name = %s" %name)
201 try:
202 value = self.get(name)
203 except Exception:
204 value = "ERR"
205 pass
206 if verbose:
207 rsp += "GET %s = %s :: %s\n" % (name, value, self.doc(name))
208 else:
209 rsp += "%s:%s\n" % (name, value)
210 return rsp
211
212 def set(self, name, wr_val_str):
213 """Set control.
214
215 Args:
216 name: name string of control
217 wr_val_str: value string to write. Can be integer, float or a
218 alpha-numerical that is mapped to a integer or float.
219
220 Raises:
221 HwDriverError: Error occurred while using driver
222 """
223 self._logger.debug("name(%s) wr_val(%s)" % (name, wr_val_str))
224 (params, drv) = self._get_param_drv(name, False)
225 wr_val = self._syscfg.resolve_val(params, wr_val_str)
226 try:
227 drv.set(wr_val)
228 except drv.hw_driver.HwDriverError:
229 self._logger.error("Setting %s -> %s" % (name, wr_val_str))
230 raise
231 # TODO(tbroch) Figure out why despite allow_none=True for both xmlrpc server
232 # & client I still have to return something to appease the
233 # marshall/unmarshall
234 return True
235
236 def echo(self, echo):
237 """Dummy echo function for testing/examples.
238
239 Args:
240 echo: string to echo back to client
241 """
242 self._logger.debug("echo(%s)" % (echo))
243 return "ECH0ING: %s" % (echo)
244
245def test():
246 """Integration testing.
247
248 TODO(tbroch) Enhance integration test and add unittest (see mox)
249 """
250 logging.basicConfig(level=logging.DEBUG,
251 format="%(asctime)s - %(name)s - " +
252 "%(levelname)s - %(message)s")
253 # configure server & listen
254 servod_obj = Servod(1)
255 # 4 == number of interfaces on a FT4232H device
256 for i in xrange(4):
257 if i == 1:
258 # its an i2c interface ... see __init__ for details and TODO to make
259 # this configureable
260 servod_obj._interface_list[i].wr_rd(0x21, [0], 1)
261 else:
262 # its a gpio interface
263 servod_obj._interface_list[i].wr_rd(0)
264
265 server = SimpleXMLRPCServer.SimpleXMLRPCServer(("localhost", 9999),
266 allow_none=True)
267 server.register_introspection_functions()
268 server.register_multicall_functions()
269 server.register_instance(servod_obj)
270 logging.info("Listening on localhost port 9999")
271 server.serve_forever()
272
273if __name__ == "__main__":
274 test()
275
276 # simple client transaction would look like
277 """
278 remote_uri = 'http://localhost:9999'
279 client = xmlrpclib.ServerProxy(remote_uri, verbose=False)
280 send_str = "Hello_there"
281 print "Sent " + send_str + ", Recv " + client.echo(send_str)
282 """