blob: 1190eed313fd551d0db5a5ded3bfaa88011ac520 [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
Todd Broche505b8d2011-03-21 18:19:54 -070010import ftdigpio
11import ftdii2c
Todd Brochdbb09982011-10-02 07:14:26 -070012import ftdi_common
Todd Broche505b8d2011-03-21 18:19:54 -070013
14MAX_I2C_CLOCK_HZ = 100000
15
Todd Brochdbb09982011-10-02 07:14:26 -070016
Todd Broche505b8d2011-03-21 18:19:54 -070017class ServodError(Exception):
18 """Exception class for servod."""
19
20class Servod(object):
21 """Main class for Servo debug/controller Daemon."""
Todd Brochdbb09982011-10-02 07:14:26 -070022 def __init__(self, config, vendor, product, serialname=None, interfaces=None):
Todd Broche505b8d2011-03-21 18:19:54 -070023 """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
Todd Brochad034442011-05-25 15:05:29 -070030 serialname: string of device serialname/number as defined in FTDI eeprom.
Todd Brochdbb09982011-10-02 07:14:26 -070031 interfaces: list of strings of interface types the server will instantiate
32
33 Raises:
34 ServodError: if unable to locate init method for particular interface
Todd Broche505b8d2011-03-21 18:19:54 -070035 """
36 self._logger = logging.getLogger("Servod")
37 self._logger.debug("")
38 self._vendor = vendor
39 self._product = product
Todd Brochad034442011-05-25 15:05:29 -070040 self._serialname = serialname
Todd Broche505b8d2011-03-21 18:19:54 -070041 self._syscfg = config
42 # list of objects (Fi2c, Fgpio) to physical interfaces (gpio, i2c) that ftdi
43 # interfaces are mapped to
44 self._interface_list = []
45 # Dict of Dict to map control name, function name to to tuple (params, drv)
46 # Ex) _drv_dict[name]['get'] = (params, drv)
47 self._drv_dict = {}
48
Todd Brochdbb09982011-10-02 07:14:26 -070049 # Note, interface i is (i - 1) in list
50 if not interfaces:
51 interfaces = ftdi_common.INTERFACE_DEFAULTS[vendor][product]
52
53 for i, name in enumerate(interfaces):
54 self._logger.info("Initializing FTDI interface %d to %s", i + 1, name)
55 try:
56 func = getattr(self, '_init_%s' % name)
57 except AttributeError:
58 raise ServodError("Unable to locate init for interface %s" % name)
59 self._interface_list.append(func(i + 1))
Todd Broche505b8d2011-03-21 18:19:54 -070060
61 def _init_gpio(self, interface):
62 """Initialize gpio driver interface and open for use.
63
64 Args:
65 interface: interface number of FTDI device to use.
66
67 Returns:
68 Instance object of interface.
69 """
Todd Brochad034442011-05-25 15:05:29 -070070 fobj = ftdigpio.Fgpio(self._vendor, self._product, interface,
71 self._serialname)
Todd Broche505b8d2011-03-21 18:19:54 -070072 fobj.open()
73 return fobj
74
75 def _init_i2c(self, interface):
76 """Initialize i2c interface and open for use.
77
78 Args:
79 interface: interface number of FTDI device to use
80
81 Returns:
82 Instance object of interface
83 """
Todd Brochad034442011-05-25 15:05:29 -070084 fobj = ftdii2c.Fi2c(self._vendor, self._product, interface,
85 self._serialname)
Todd Broche505b8d2011-03-21 18:19:54 -070086 fobj.open()
87 # Set the frequency of operation of the i2c bus.
88 # TODO(tbroch) make configureable
89 fobj.setclock(MAX_I2C_CLOCK_HZ)
90 return fobj
91
92 def _get_param_drv(self, control_name, is_get=True):
93 """Get access to driver for a given control.
94
95 Note, some controls have different parameter dictionaries for 'getting' the
96 control's value versus 'setting' it. Boolean is_get distinguishes which is
97 being requested.
98
99 Args:
100 control_name: string name of control
101 is_get: boolean to determine
102
103 Returns:
104 tuple (param, drv) where:
105 param: param dictionary for control
106 drv: instance object of driver for particular control
107
108 Raises:
109 ServodError: Error occurred while examining params dict
110 """
111 self._logger.debug("")
112 # if already setup just return tuple from driver dict
113 if control_name in self._drv_dict:
114 if is_get and ('get' in self._drv_dict[control_name]):
115 return self._drv_dict[control_name]['get']
116 if not is_get and ('set' in self._drv_dict[control_name]):
117 return self._drv_dict[control_name]['set']
118
119 params = self._syscfg.lookup_control_params(control_name, is_get)
120 if 'drv' not in params:
121 self._logger.error("Unable to determine driver for %s" % control_name)
122 raise ServodError("'drv' key not found in params dict")
123 if 'interface' not in params:
124 self._logger.error("Unable to determine interface for %s" %
125 control_name)
126
127 raise ServodError("'interface' key not found in params dict")
128 index = int(params['interface']) - 1
129 interface = self._interface_list[index]
130 servo_pkg = imp.load_module('servo', *imp.find_module('servo'))
131 drv_pkg = imp.load_module('drv',
132 *imp.find_module('drv', servo_pkg.__path__))
133 drv_name = params['drv']
134 drv_module = getattr(drv_pkg, drv_name)
135 drv_class = getattr(drv_module, drv_name)
136 drv = drv_class(interface, params)
137 if control_name not in self._drv_dict:
138 self._drv_dict[control_name] = {}
139 if is_get:
140 self._drv_dict[control_name]['get'] = (params, drv)
141 else:
142 self._drv_dict[control_name]['set'] = (params, drv)
143 return (params, drv)
144
145 def doc_all(self):
146 """Return all documenation for controls.
147
148 Returns:
149 string of <doc> text in config file (xml) and the params dictionary for
150 all controls.
151
152 For example:
153 warm_reset :: Reset the device warmly
154 ------------------------> {'interface': '1', 'map': 'onoff_i', ... }
155 """
156 return self._syscfg.display_config()
157
158 def doc(self, name):
159 """Retreive doc string in system config file for given control name.
160
161 Args:
162 name: name string of control to get doc string
163
164 Returns:
165 doc string of name
166
167 Raises:
168 NameError: if fails to locate control
169 """
170 self._logger.debug("name(%s)" % (name))
171 if self._syscfg.is_control(name):
172 return self._syscfg.get_control_docstring(name)
173 else:
174 raise NameError("No control %s" %name)
175
176 def get(self, name):
177 """Get control value.
178
179 Args:
180 name: name string of control
181
182 Returns:
183 Response from calling drv get method. Value is reformatted based on
184 control's dictionary parameters
185
186 Raises:
187 HwDriverError: Error occurred while using drv
188 """
189 self._logger.debug("name(%s)" % (name))
190 (param, drv) = self._get_param_drv(name)
191 try:
192 val = drv.get()
193 rd_val = self._syscfg.reformat_val(param, val)
194 self._logger.info("%s = %s" % (name, rd_val))
195 return rd_val
Todd Brochfbc499d2011-06-16 16:09:58 -0700196 except AttributeError, error:
197 self._logger.error("Getting %s: %s" % (name, error))
198 raise
Todd Broche505b8d2011-03-21 18:19:54 -0700199 except drv.hw_driver.HwDriverError:
200 self._logger.error("Getting %s" % (name))
201 raise
202 def get_all(self, verbose):
203 """Get all controls values.
204
205 Args:
206 verbose: Boolean on whether to return doc info as well
207
208 Returns:
209 string creating from trying to get all values of all controls. In case of
210 error attempting access to control, response is 'ERR'.
211 """
212 rsp = ""
213 for name in self._syscfg.syscfg_dict['control']:
214 self._logger.debug("name = %s" %name)
215 try:
216 value = self.get(name)
217 except Exception:
218 value = "ERR"
219 pass
220 if verbose:
221 rsp += "GET %s = %s :: %s\n" % (name, value, self.doc(name))
222 else:
223 rsp += "%s:%s\n" % (name, value)
224 return rsp
225
226 def set(self, name, wr_val_str):
227 """Set control.
228
229 Args:
230 name: name string of control
231 wr_val_str: value string to write. Can be integer, float or a
232 alpha-numerical that is mapped to a integer or float.
233
234 Raises:
235 HwDriverError: Error occurred while using driver
236 """
237 self._logger.debug("name(%s) wr_val(%s)" % (name, wr_val_str))
238 (params, drv) = self._get_param_drv(name, False)
239 wr_val = self._syscfg.resolve_val(params, wr_val_str)
240 try:
241 drv.set(wr_val)
242 except drv.hw_driver.HwDriverError:
243 self._logger.error("Setting %s -> %s" % (name, wr_val_str))
244 raise
245 # TODO(tbroch) Figure out why despite allow_none=True for both xmlrpc server
246 # & client I still have to return something to appease the
247 # marshall/unmarshall
248 return True
249
250 def echo(self, echo):
251 """Dummy echo function for testing/examples.
252
253 Args:
254 echo: string to echo back to client
255 """
256 self._logger.debug("echo(%s)" % (echo))
257 return "ECH0ING: %s" % (echo)
258
Todd Brochdbb09982011-10-02 07:14:26 -0700259
Todd Broche505b8d2011-03-21 18:19:54 -0700260def test():
261 """Integration testing.
262
263 TODO(tbroch) Enhance integration test and add unittest (see mox)
264 """
265 logging.basicConfig(level=logging.DEBUG,
266 format="%(asctime)s - %(name)s - " +
267 "%(levelname)s - %(message)s")
268 # configure server & listen
269 servod_obj = Servod(1)
270 # 4 == number of interfaces on a FT4232H device
271 for i in xrange(4):
272 if i == 1:
273 # its an i2c interface ... see __init__ for details and TODO to make
274 # this configureable
275 servod_obj._interface_list[i].wr_rd(0x21, [0], 1)
276 else:
277 # its a gpio interface
278 servod_obj._interface_list[i].wr_rd(0)
279
280 server = SimpleXMLRPCServer.SimpleXMLRPCServer(("localhost", 9999),
281 allow_none=True)
282 server.register_introspection_functions()
283 server.register_multicall_functions()
284 server.register_instance(servod_obj)
285 logging.info("Listening on localhost port 9999")
286 server.serve_forever()
287
288if __name__ == "__main__":
289 test()
290
291 # simple client transaction would look like
292 """
293 remote_uri = 'http://localhost:9999'
294 client = xmlrpclib.ServerProxy(remote_uri, verbose=False)
295 send_str = "Hello_there"
296 print "Sent " + send_str + ", Recv " + client.echo(send_str)
297 """