blob: 4f5e12c9b6f38984409c6abcb2a5b1c44760b9e6 [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
Aaron.Chuang88eff332014-07-31 08:32:00 +080018import bbadc
Simran Basia9ad25e2013-04-23 11:57:00 -070019import bbi2c
Simran Basi5492bde2013-05-16 17:08:47 -070020import bbgpio
Simran Basi949309b2013-05-31 15:12:15 -070021import bbuart
Todd Broche505b8d2011-03-21 18:19:54 -070022import ftdigpio
23import ftdii2c
Todd Brochdbb09982011-10-02 07:14:26 -070024import ftdi_common
Todd Broch47c43f42011-05-26 15:11:31 -070025import ftdiuart
Rong Changc6c8c022014-08-11 14:07:11 +080026import i2cbus
Yusuf Mohsinally29e30d22014-01-14 15:29:17 -080027import keyboard_handlers
Simran Basie750a342013-03-12 13:45:26 -070028import servo_interfaces
Todd Broche505b8d2011-03-21 18:19:54 -070029
30MAX_I2C_CLOCK_HZ = 100000
31
Todd Brochdbb09982011-10-02 07:14:26 -070032
Todd Broche505b8d2011-03-21 18:19:54 -070033class ServodError(Exception):
34 """Exception class for servod."""
35
36class Servod(object):
37 """Main class for Servo debug/controller Daemon."""
Simran Basia9f41032012-05-11 14:21:58 -070038 _USB_DETECTION_DELAY = 10
Fang Deng90377712013-06-03 15:51:48 -070039 _USB_POWEROFF_DELAY = 2
Simran Basia9f41032012-05-11 14:21:58 -070040 _HTTP_PREFIX = "http://"
Fang Deng90377712013-06-03 15:51:48 -070041 _USB_J3 = "usb_mux_sel1"
42 _USB_J3_TO_SERVO = "servo_sees_usbkey"
43 _USB_J3_TO_DUT = "dut_sees_usbkey"
44 _USB_J3_PWR = "prtctl4_pwren"
45 _USB_J3_PWR_ON = "on"
46 _USB_J3_PWR_OFF = "off"
Simran Basia9f41032012-05-11 14:21:58 -070047
J. Richard Barnettee2820552013-03-14 16:13:46 -070048 def __init__(self, config, vendor, product, serialname=None,
Yusuf Mohsinally29e30d22014-01-14 15:29:17 -080049 interfaces=None, board="", version=None, usbkm232=None):
Todd Broche505b8d2011-03-21 18:19:54 -070050 """Servod constructor.
51
52 Args:
53 config: instance of SystemConfig containing all controls for
54 particular Servod invocation
55 vendor: usb vendor id of FTDI device
56 product: usb product id of FTDI device
Todd Brochad034442011-05-25 15:05:29 -070057 serialname: string of device serialname/number as defined in FTDI eeprom.
Todd Brochdbb09982011-10-02 07:14:26 -070058 interfaces: list of strings of interface types the server will instantiate
Simran Basia23c1392013-08-06 14:59:10 -070059 version: String. Servo board version. Examples: servo_v1, servo_v2,
60 servo_v2_r0, servo_v3
Yusuf Mohsinally29e30d22014-01-14 15:29:17 -080061 usbkm232: String. Optional. Path to USB-KM232 device which allow for
62 sending keyboard commands to DUTs that do not have built in
63 keyboards. Used in FAFT tests.
64 e.g. /dev/ttyUSB0
Todd Brochdbb09982011-10-02 07:14:26 -070065
66 Raises:
67 ServodError: if unable to locate init method for particular interface
Todd Broche505b8d2011-03-21 18:19:54 -070068 """
69 self._logger = logging.getLogger("Servod")
70 self._logger.debug("")
71 self._vendor = vendor
72 self._product = product
Todd Brochad034442011-05-25 15:05:29 -070073 self._serialname = serialname
Todd Broche505b8d2011-03-21 18:19:54 -070074 self._syscfg = config
75 # list of objects (Fi2c, Fgpio) to physical interfaces (gpio, i2c) that ftdi
76 # interfaces are mapped to
77 self._interface_list = []
78 # Dict of Dict to map control name, function name to to tuple (params, drv)
79 # Ex) _drv_dict[name]['get'] = (params, drv)
80 self._drv_dict = {}
J. Richard Barnettee2820552013-03-14 16:13:46 -070081 self._board = board
Simran Basia23c1392013-08-06 14:59:10 -070082 self._version = version
Yusuf Mohsinally29e30d22014-01-14 15:29:17 -080083 self._usbkm232 = usbkm232
84
85 self._keyboard = self._init_keyboard_handler(self, self._board)
Todd Broche505b8d2011-03-21 18:19:54 -070086
Todd Brochdbb09982011-10-02 07:14:26 -070087 # Note, interface i is (i - 1) in list
88 if not interfaces:
Todd Brochb21d8042014-05-15 12:54:54 -070089 try:
90 interfaces = servo_interfaces.INTERFACE_BOARDS[board][vendor][product]
91 except KeyError:
92 interfaces = servo_interfaces.INTERFACE_DEFAULTS[vendor][product]
Todd Brochdbb09982011-10-02 07:14:26 -070093
Simran Basia9ad25e2013-04-23 11:57:00 -070094 for i, interface in enumerate(interfaces):
95 is_ftdi_interface = False
96 if type(interface) is dict:
97 name = interface['name']
98 elif type(interface) is str:
99 # Its FTDI related interface
100 name = interface
101 interface = (i % ftdi_common.MAX_FTDI_INTERFACES_PER_DEVICE) + 1
102 is_ftdi_interface = True
103 else:
104 raise ServodError("Illegal interface type %s" % type(interface))
105
Todd Broch8a77a992012-01-27 09:46:08 -0800106 # servos with multiple FTDI are guaranteed to have contiguous USB PIDs
Simran Basia9ad25e2013-04-23 11:57:00 -0700107 if is_ftdi_interface and i and \
108 ((i % ftdi_common.MAX_FTDI_INTERFACES_PER_DEVICE) == 0):
Todd Broch8a77a992012-01-27 09:46:08 -0800109 self._product += 1
110 self._logger.info("Changing to next FTDI part @ pid = 0x%04x",
111 self._product)
112
Simran Basia9ad25e2013-04-23 11:57:00 -0700113 self._logger.info("Initializing interface %d to %s", i + 1, name)
Todd Brochdbb09982011-10-02 07:14:26 -0700114 try:
115 func = getattr(self, '_init_%s' % name)
116 except AttributeError:
117 raise ServodError("Unable to locate init for interface %s" % name)
Simran Basia9ad25e2013-04-23 11:57:00 -0700118 result = func(interface)
Todd Broch888da782011-10-07 14:29:09 -0700119 if isinstance(result, tuple):
120 self._interface_list.extend(result)
121 else:
122 self._interface_list.append(result)
Todd Broche505b8d2011-03-21 18:19:54 -0700123
Yusuf Mohsinally29e30d22014-01-14 15:29:17 -0800124 def _init_keyboard_handler(self, servo, board=''):
125 """Initialize the correct keyboard handler for board.
126
127 @param servo: servo object.
128 @param board: string, board name.
129
130 """
131 if board == 'parrot':
132 return keyboard_handlers.ParrotHandler(servo)
133 elif board == 'stout':
134 return keyboard_handlers.StoutHandler(servo)
PeggyChuang4f07d872015-08-07 12:11:38 +0800135 elif board in ('buddy', 'cranky', 'guado', 'jecht', 'mccloud', 'monroe',
Tom Wai-Hong Tamd64164c2015-04-29 07:59:45 +0800136 'ninja', 'nyan_kitty', 'panther', 'rikku', 'stumpy',
Jason Simmonse5cccd12015-08-25 16:05:25 -0700137 'sumo', 'tidus', 'tricky', 'veyron_mickey', 'veyron_rialto',
138 'zako'):
Yusuf Mohsinally29e30d22014-01-14 15:29:17 -0800139 if self._usbkm232 is None:
Yusuf Mohsinally66844692014-05-22 10:37:52 -0700140 logging.warn("No device path specified for usbkm232 handler. Returning "
Tom Wai-Hong Tamd64164c2015-04-29 07:59:45 +0800141 "the MatrixKeyboardHandler, which is likely the wrong "
142 "keyboard handler for the board type specified.")
143 return keyboard_handlers.MatrixKeyboardHandler(servo)
Yusuf Mohsinally29e30d22014-01-14 15:29:17 -0800144 return keyboard_handlers.USBkm232Handler(servo, self._usbkm232)
145 else:
Tom Wai-Hong Tamd64164c2015-04-29 07:59:45 +0800146 # The following boards don't use Chrome EC.
147 if board in ('alex', 'butterfly', 'lumpy', 'zgb'):
148 return keyboard_handlers.MatrixKeyboardHandler(servo)
149 return keyboard_handlers.ChromeECHandler(servo)
Yusuf Mohsinally29e30d22014-01-14 15:29:17 -0800150
Todd Broch3ec8df02012-11-20 10:53:03 -0800151 def __del__(self):
152 """Servod deconstructor."""
153 for interface in self._interface_list:
154 del(interface)
155
Todd Brochb3048492012-01-15 21:52:41 -0800156 def _init_dummy(self, interface):
157 """Initialize dummy interface.
158
159 Dummy interface is just a mechanism to reserve that interface for non servod
160 interaction. Typically the interface will be managed by external
161 third-party tools like openOCD or urjtag for JTAG or flashrom for SPI
162 interfaces.
163
164 TODO(tbroch): Investigate merits of incorporating these third-party
165 interfaces into servod or creating a communication channel between them
166
167 Returns: None
168 """
169 return None
170
Simran Basie750a342013-03-12 13:45:26 -0700171 def _init_ftdi_gpio(self, interface):
Todd Broche505b8d2011-03-21 18:19:54 -0700172 """Initialize gpio driver interface and open for use.
173
174 Args:
175 interface: interface number of FTDI device to use.
176
177 Returns:
178 Instance object of interface.
Todd Broch6de9dc62012-04-09 15:23:53 -0700179
180 Raises:
181 ServodError: If init fails
Todd Broche505b8d2011-03-21 18:19:54 -0700182 """
Todd Brochad034442011-05-25 15:05:29 -0700183 fobj = ftdigpio.Fgpio(self._vendor, self._product, interface,
184 self._serialname)
Todd Broch6de9dc62012-04-09 15:23:53 -0700185 try:
186 fobj.open()
187 except ftdigpio.FgpioError as e:
188 raise ServodError('Opening gpio interface. %s ( %d )' % (e.msg, e.value))
189
Todd Broche505b8d2011-03-21 18:19:54 -0700190 return fobj
191
Aaron.Chuang88eff332014-07-31 08:32:00 +0800192 def _init_bb_adc(self, interface):
193 """Initalize beaglebone ADC interface."""
194 return bbadc.BBadc()
195
Simran Basie750a342013-03-12 13:45:26 -0700196 def _init_bb_gpio(self, interface):
197 """Initalize beaglebone gpio interface."""
Simran Basi5492bde2013-05-16 17:08:47 -0700198 return bbgpio.BBgpio()
Simran Basie750a342013-03-12 13:45:26 -0700199
200 def _init_ftdi_i2c(self, interface):
Todd Broche505b8d2011-03-21 18:19:54 -0700201 """Initialize i2c interface and open for use.
202
203 Args:
204 interface: interface number of FTDI device to use
205
206 Returns:
207 Instance object of interface
Todd Broch6de9dc62012-04-09 15:23:53 -0700208
209 Raises:
210 ServodError: If init fails
Todd Broche505b8d2011-03-21 18:19:54 -0700211 """
Todd Brochad034442011-05-25 15:05:29 -0700212 fobj = ftdii2c.Fi2c(self._vendor, self._product, interface,
213 self._serialname)
Todd Broch6de9dc62012-04-09 15:23:53 -0700214 try:
215 fobj.open()
216 except ftdii2c.Fi2cError as e:
217 raise ServodError('Opening i2c interface. %s ( %d )' % (e.msg, e.value))
218
Todd Broche505b8d2011-03-21 18:19:54 -0700219 # Set the frequency of operation of the i2c bus.
220 # TODO(tbroch) make configureable
221 fobj.setclock(MAX_I2C_CLOCK_HZ)
Todd Broch6de9dc62012-04-09 15:23:53 -0700222
Todd Broche505b8d2011-03-21 18:19:54 -0700223 return fobj
224
Simran Basie750a342013-03-12 13:45:26 -0700225 # TODO (sbasi) crbug.com/187489 - Implement bb_i2c.
226 def _init_bb_i2c(self, interface):
227 """Initalize beaglebone i2c interface."""
Simran Basia9ad25e2013-04-23 11:57:00 -0700228 return bbi2c.BBi2c(interface)
Simran Basie750a342013-03-12 13:45:26 -0700229
Rong Changc6c8c022014-08-11 14:07:11 +0800230 def _init_dev_i2c(self, interface):
231 """Initalize Linux i2c-dev interface."""
232 return i2cbus.I2CBus('/dev/i2c-%d' % interface['bus_num'])
233
Simran Basie750a342013-03-12 13:45:26 -0700234 def _init_ftdi_uart(self, interface):
235 """Initialize ftdi uart inteface and open for use
Todd Broch47c43f42011-05-26 15:11:31 -0700236
237 Note, the uart runs in a separate thread (pthreads). Users wishing to
238 interact with it will query control for the pty's pathname and connect
239 with there favorite console program. For example:
240 cu -l /dev/pts/22
241
242 Args:
243 interface: interface number of FTDI device to use
244
245 Returns:
246 Instance object of interface
Todd Broch6de9dc62012-04-09 15:23:53 -0700247
248 Raises:
249 ServodError: If init fails
Todd Broch47c43f42011-05-26 15:11:31 -0700250 """
Jeremy Thorpe9e110062012-10-25 10:40:00 -0700251 fobj = ftdiuart.Fuart(self._vendor, self._product, interface,
252 self._serialname)
Todd Broch6de9dc62012-04-09 15:23:53 -0700253 try:
254 fobj.run()
255 except ftdiuart.FuartError as e:
256 raise ServodError('Running uart interface. %s ( %d )' % (e.msg, e.value))
257
Todd Broch47c43f42011-05-26 15:11:31 -0700258 self._logger.info("%s" % fobj.get_pty())
259 return fobj
260
Simran Basie750a342013-03-12 13:45:26 -0700261 # TODO (sbasi) crbug.com/187492 - Implement bbuart.
262 def _init_bb_uart(self, interface):
263 """Initalize beaglebone uart interface."""
Simran Basi949309b2013-05-31 15:12:15 -0700264 logging.debug('UART INTERFACE: %s', interface)
265 return bbuart.BBuart(interface)
Simran Basie750a342013-03-12 13:45:26 -0700266
267 def _init_ftdi_gpiouart(self, interface):
Todd Broch888da782011-10-07 14:29:09 -0700268 """Initialize special gpio + uart interface and open for use
269
270 Note, the uart runs in a separate thread (pthreads). Users wishing to
271 interact with it will query control for the pty's pathname and connect
272 with there favorite console program. For example:
273 cu -l /dev/pts/22
274
275 Args:
276 interface: interface number of FTDI device to use
277
278 Returns:
279 Instance objects of interface
Todd Broch6de9dc62012-04-09 15:23:53 -0700280
281 Raises:
282 ServodError: If init fails
Todd Broch888da782011-10-07 14:29:09 -0700283 """
Simran Basie750a342013-03-12 13:45:26 -0700284 fgpio = self._init_ftdi_gpio(interface)
Jeremy Thorpe9e110062012-10-25 10:40:00 -0700285 fuart = ftdiuart.Fuart(self._vendor, self._product, interface,
286 self._serialname, fgpio._fc)
Todd Broch6de9dc62012-04-09 15:23:53 -0700287 try:
288 fuart.run()
289 except ftdiuart.FuartError as e:
290 raise ServodError('Running uart interface. %s ( %d )' % (e.msg, e.value))
291
Todd Broch888da782011-10-07 14:29:09 -0700292 self._logger.info("uart pty: %s" % fuart.get_pty())
293 return fgpio, fuart
294
Tom Wai-Hong Tam28f0a5f2012-08-21 12:49:57 +0800295 def _camel_case(self, string):
296 output = ''
297 for s in string.split('_'):
298 if output:
299 output += s.capitalize()
300 else:
301 output = s
302 return output
303
Todd Broche505b8d2011-03-21 18:19:54 -0700304 def _get_param_drv(self, control_name, is_get=True):
305 """Get access to driver for a given control.
306
307 Note, some controls have different parameter dictionaries for 'getting' the
308 control's value versus 'setting' it. Boolean is_get distinguishes which is
309 being requested.
310
311 Args:
312 control_name: string name of control
313 is_get: boolean to determine
314
315 Returns:
316 tuple (param, drv) where:
317 param: param dictionary for control
318 drv: instance object of driver for particular control
319
320 Raises:
321 ServodError: Error occurred while examining params dict
322 """
323 self._logger.debug("")
324 # if already setup just return tuple from driver dict
325 if control_name in self._drv_dict:
326 if is_get and ('get' in self._drv_dict[control_name]):
327 return self._drv_dict[control_name]['get']
328 if not is_get and ('set' in self._drv_dict[control_name]):
329 return self._drv_dict[control_name]['set']
330
331 params = self._syscfg.lookup_control_params(control_name, is_get)
332 if 'drv' not in params:
333 self._logger.error("Unable to determine driver for %s" % control_name)
334 raise ServodError("'drv' key not found in params dict")
335 if 'interface' not in params:
336 self._logger.error("Unable to determine interface for %s" %
337 control_name)
Todd Broche505b8d2011-03-21 18:19:54 -0700338 raise ServodError("'interface' key not found in params dict")
Simran Basi668be0e2013-08-07 11:54:50 -0700339
J. Richard Barnette275d9fd2014-02-11 14:38:54 -0800340 interface_id = params.get(
341 '%s_interface' % self._version, params['interface'])
342 if interface_id == 'servo':
343 interface = self
Simran Basi668be0e2013-08-07 11:54:50 -0700344 else:
J. Richard Barnette275d9fd2014-02-11 14:38:54 -0800345 index = int(interface_id) - 1
346 interface = self._interface_list[index]
Simran Basi668be0e2013-08-07 11:54:50 -0700347
Todd Broche505b8d2011-03-21 18:19:54 -0700348 servo_pkg = imp.load_module('servo', *imp.find_module('servo'))
349 drv_pkg = imp.load_module('drv',
350 *imp.find_module('drv', servo_pkg.__path__))
351 drv_name = params['drv']
352 drv_module = getattr(drv_pkg, drv_name)
Tom Wai-Hong Tam28f0a5f2012-08-21 12:49:57 +0800353 drv_class = getattr(drv_module, self._camel_case(drv_name))
Todd Broche505b8d2011-03-21 18:19:54 -0700354 drv = drv_class(interface, params)
355 if control_name not in self._drv_dict:
356 self._drv_dict[control_name] = {}
357 if is_get:
358 self._drv_dict[control_name]['get'] = (params, drv)
359 else:
360 self._drv_dict[control_name]['set'] = (params, drv)
361 return (params, drv)
362
363 def doc_all(self):
364 """Return all documenation for controls.
365
366 Returns:
367 string of <doc> text in config file (xml) and the params dictionary for
368 all controls.
369
370 For example:
371 warm_reset :: Reset the device warmly
372 ------------------------> {'interface': '1', 'map': 'onoff_i', ... }
373 """
374 return self._syscfg.display_config()
375
376 def doc(self, name):
377 """Retreive doc string in system config file for given control name.
378
379 Args:
380 name: name string of control to get doc string
381
382 Returns:
383 doc string of name
384
385 Raises:
386 NameError: if fails to locate control
387 """
388 self._logger.debug("name(%s)" % (name))
389 if self._syscfg.is_control(name):
390 return self._syscfg.get_control_docstring(name)
391 else:
392 raise NameError("No control %s" %name)
393
Fang Deng90377712013-06-03 15:51:48 -0700394 def _switch_usbkey(self, mux_direction):
395 """Connect USB flash stick to either servo or DUT.
396
397 This function switches 'usb_mux_sel1' to provide electrical
398 connection between the USB port J3 and either servo or DUT side.
399
400 Switching the usb mux is accompanied by powercycling
401 of the USB stick, because it sometimes gets wedged if the mux
402 is switched while the stick power is on.
403
404 Args:
405 mux_direction: "servo_sees_usbkey" or "dut_sees_usbkey".
406 """
407 self.set(self._USB_J3_PWR, self._USB_J3_PWR_OFF)
408 time.sleep(self._USB_POWEROFF_DELAY)
409 self.set(self._USB_J3, mux_direction)
410 time.sleep(self._USB_POWEROFF_DELAY)
411 self.set(self._USB_J3_PWR, self._USB_J3_PWR_ON)
412 if mux_direction == self._USB_J3_TO_SERVO:
413 time.sleep(self._USB_DETECTION_DELAY)
414
Simran Basia9f41032012-05-11 14:21:58 -0700415 def _get_usb_port_set(self):
416 """Gets a set of USB disks currently connected to the system
417
418 Returns:
419 A set of USB disk paths.
420 """
421 usb_set = fnmatch.filter(os.listdir("/dev/"), "sd[a-z]")
422 return set(["/dev/" + dev for dev in usb_set])
423
424 def _probe_host_usb_dev(self):
425 """Probe the USB disk device plugged in the servo from the host side.
426
427 Method can fail by:
428 1) Having multiple servos connected and returning incorrect /dev/sdX of
429 another servo.
430 2) Finding multiple /dev/sdX and returning None.
431
432 Returns:
433 USB disk path if one and only one USB disk path is found, otherwise None.
434 """
Fang Deng90377712013-06-03 15:51:48 -0700435 original_value = self.get(self._USB_J3)
436 original_usb_power = self.get(self._USB_J3_PWR)
Simran Basia9f41032012-05-11 14:21:58 -0700437 # Make the host unable to see the USB disk.
Fang Deng90377712013-06-03 15:51:48 -0700438 if (original_usb_power == self._USB_J3_PWR_ON and
439 original_value != self._USB_J3_TO_DUT):
440 self._switch_usbkey(self._USB_J3_TO_DUT)
Simran Basia9f41032012-05-11 14:21:58 -0700441 no_usb_set = self._get_usb_port_set()
Simran Basia9f41032012-05-11 14:21:58 -0700442
Fang Deng90377712013-06-03 15:51:48 -0700443 # Make the host able to see the USB disk.
444 self._switch_usbkey(self._USB_J3_TO_SERVO)
Simran Basia9f41032012-05-11 14:21:58 -0700445 has_usb_set = self._get_usb_port_set()
Fang Deng90377712013-06-03 15:51:48 -0700446
Simran Basia9f41032012-05-11 14:21:58 -0700447 # Back to its original value.
Fang Deng90377712013-06-03 15:51:48 -0700448 if original_value != self._USB_J3_TO_SERVO:
449 self._switch_usbkey(original_value)
450 if original_usb_power != self._USB_J3_PWR_ON:
451 self.set(self._USB_J3_PWR, self._USB_J3_PWR_OFF)
452 time.sleep(self._USB_POWEROFF_DELAY)
453
Simran Basia9f41032012-05-11 14:21:58 -0700454 # Subtract the two sets to find the usb device.
455 diff_set = has_usb_set - no_usb_set
456 if len(diff_set) == 1:
457 return diff_set.pop()
458 else:
459 return None
460
461 def download_image_to_usb(self, image_path):
462 """Download image and save to the USB device found by probe_host_usb_dev.
463 If the image_path is a URL, it will download this url to the USB path;
464 otherwise it will simply copy the image_path's contents to the USB path.
465
466 Args:
467 image_path: path or url to the recovery image.
468
469 Returns:
470 True|False: True if process completed successfully, False if error
471 occurred.
472 Can't return None because XMLRPC doesn't allow it. PTAL at tbroch's
473 comment at the end of set().
474 """
475 self._logger.debug("image_path(%s)" % image_path)
476 self._logger.debug("Detecting USB stick device...")
477 usb_dev = self._probe_host_usb_dev()
478 if not usb_dev:
479 self._logger.error("No usb device connected to servo")
480 return False
481
482 try:
483 if image_path.startswith(self._HTTP_PREFIX):
484 self._logger.debug("Image path is a URL, downloading image")
485 urllib.urlretrieve(image_path, usb_dev)
486 else:
487 shutil.copyfile(image_path, usb_dev)
488 except IOError as e:
489 self._logger.error("Failed to transfer image to USB device: %s ( %d ) ",
490 e.strerror, e.errno)
491 return False
492 except urllib.ContentTooShortError:
493 self._logger.error("Failed to download URL: %s to USB device: %s",
494 image_path, usb_dev)
495 return False
496 except BaseException as e:
497 self._logger.error("Unexpected exception downloading %s to %s: %s",
498 image_path, usb_dev, str(e))
499 return False
J. Richard Barnettee4125af2013-02-26 18:31:56 -0800500 finally:
501 # We just plastered the partition table for a block device.
502 # Pass or fail, we mustn't go without telling the kernel about
503 # the change, or it will punish us with sporadic, hard-to-debug
504 # failures.
505 subprocess.call(["sync"])
506 subprocess.call(["blockdev", "--rereadpt", usb_dev])
Simran Basia9f41032012-05-11 14:21:58 -0700507 return True
508
509 def make_image_noninteractive(self):
510 """Makes the recovery image noninteractive.
511
512 A noninteractive image will reboot automatically after installation
513 instead of waiting for the USB device to be removed to initiate a system
514 reboot.
515
516 Mounts partition 1 of the image stored on usb_dev and creates a file
517 called "non_interactive" so that the image will become noninteractive.
518
519 Returns:
520 True|False: True if process completed successfully, False if error
521 occurred.
522 """
523 result = True
524 usb_dev = self._probe_host_usb_dev()
525 if not usb_dev:
526 self._logger.error("No usb device connected to servo")
527 return False
528 # Create TempDirectory
529 tmpdir = tempfile.mkdtemp()
530 if tmpdir:
531 # Mount drive to tmpdir.
532 partition_1 = "%s1" % usb_dev
533 rc = subprocess.call(["mount", partition_1, tmpdir])
534 if rc == 0:
535 # Create file 'non_interactive'
536 non_interactive_file = os.path.join(tmpdir, "non_interactive")
537 try:
538 open(non_interactive_file, "w").close()
539 except IOError as e:
540 self._logger.error("Failed to create file %s : %s ( %d )",
541 non_interactive_file, e.strerror, e.errno)
542 result = False
543 except BaseException as e:
544 self._logger.error("Unexpected Exception creating file %s : %s",
545 non_interactive_file, str(e))
546 result = False
547 # Unmount drive regardless if file creation worked or not.
548 rc = subprocess.call(["umount", partition_1])
549 if rc != 0:
550 self._logger.error("Failed to unmount USB Device")
551 result = False
552 else:
553 self._logger.error("Failed to mount USB Device")
554 result = False
555
556 # Delete tmpdir. May throw exception if 'umount' failed.
557 try:
558 os.rmdir(tmpdir)
559 except OSError as e:
560 self._logger.error("Failed to remove temp directory %s : %s",
561 tmpdir, str(e))
562 return False
563 except BaseException as e:
564 self._logger.error("Unexpected Exception removing tempdir %s : %s",
565 tmpdir, str(e))
566 return False
567 else:
568 self._logger.error("Failed to create temp directory.")
569 return False
570 return result
571
Todd Broch352b4b22013-03-22 09:48:40 -0700572 def set_get_all(self, cmds):
573 """Set &| get one or more control values.
574
575 Args:
576 cmds: list of control[:value] to get or set.
577
578 Returns:
579 rv: list of responses from calling get or set methods.
580 """
581 rv = []
582 for cmd in cmds:
583 if ':' in cmd:
584 (control, value) = cmd.split(':')
585 rv.append(self.set(control, value))
586 else:
587 rv.append(self.get(cmd))
588 return rv
589
Todd Broche505b8d2011-03-21 18:19:54 -0700590 def get(self, name):
591 """Get control value.
592
593 Args:
594 name: name string of control
595
596 Returns:
597 Response from calling drv get method. Value is reformatted based on
598 control's dictionary parameters
599
600 Raises:
601 HwDriverError: Error occurred while using drv
602 """
603 self._logger.debug("name(%s)" % (name))
Vadim Bendeburyc3a83cf2015-03-24 13:07:00 -0700604 if name == 'serialname':
605 if self._serialname:
606 return self._serialname
607 return 'unknown'
Todd Broche505b8d2011-03-21 18:19:54 -0700608 (param, drv) = self._get_param_drv(name)
609 try:
610 val = drv.get()
611 rd_val = self._syscfg.reformat_val(param, val)
Todd Brochb042e7a2011-12-14 17:41:36 -0800612 self._logger.debug("%s = %s" % (name, rd_val))
Todd Broche505b8d2011-03-21 18:19:54 -0700613 return rd_val
Todd Brochfbc499d2011-06-16 16:09:58 -0700614 except AttributeError, error:
615 self._logger.error("Getting %s: %s" % (name, error))
616 raise
Vic Yangbe6cf262012-09-10 10:40:56 +0800617 except HwDriverError:
Todd Broche505b8d2011-03-21 18:19:54 -0700618 self._logger.error("Getting %s" % (name))
619 raise
Todd Brochd6061672012-05-11 15:52:47 -0700620
Todd Broche505b8d2011-03-21 18:19:54 -0700621 def get_all(self, verbose):
622 """Get all controls values.
623
624 Args:
625 verbose: Boolean on whether to return doc info as well
626
627 Returns:
628 string creating from trying to get all values of all controls. In case of
629 error attempting access to control, response is 'ERR'.
630 """
Vadim Bendeburyb07944c2013-01-16 10:47:10 -0800631 rsp = []
Todd Broche505b8d2011-03-21 18:19:54 -0700632 for name in self._syscfg.syscfg_dict['control']:
633 self._logger.debug("name = %s" %name)
634 try:
635 value = self.get(name)
636 except Exception:
637 value = "ERR"
638 pass
639 if verbose:
Vadim Bendeburyb07944c2013-01-16 10:47:10 -0800640 rsp.append("GET %s = %s :: %s" % (name, value, self.doc(name)))
Todd Broche505b8d2011-03-21 18:19:54 -0700641 else:
Vadim Bendeburyb07944c2013-01-16 10:47:10 -0800642 rsp.append("%s:%s" % (name, value))
643 return '\n'.join(sorted(rsp))
Todd Broche505b8d2011-03-21 18:19:54 -0700644
645 def set(self, name, wr_val_str):
646 """Set control.
647
648 Args:
649 name: name string of control
650 wr_val_str: value string to write. Can be integer, float or a
651 alpha-numerical that is mapped to a integer or float.
652
653 Raises:
654 HwDriverError: Error occurred while using driver
655 """
656 self._logger.debug("name(%s) wr_val(%s)" % (name, wr_val_str))
657 (params, drv) = self._get_param_drv(name, False)
658 wr_val = self._syscfg.resolve_val(params, wr_val_str)
659 try:
660 drv.set(wr_val)
Vic Yangbe6cf262012-09-10 10:40:56 +0800661 except HwDriverError:
Todd Broche505b8d2011-03-21 18:19:54 -0700662 self._logger.error("Setting %s -> %s" % (name, wr_val_str))
663 raise
664 # TODO(tbroch) Figure out why despite allow_none=True for both xmlrpc server
665 # & client I still have to return something to appease the
666 # marshall/unmarshall
667 return True
668
Todd Brochd6061672012-05-11 15:52:47 -0700669 def hwinit(self, verbose=False):
670 """Initialize all controls.
671
672 These values are part of the system config XML files of the form
673 init=<value>. This command should be used by clients wishing to return the
674 servo and DUT its connected to a known good/safe state.
675
Vadim Bendeburybb51dd42013-01-31 13:47:46 -0800676 Note that initialization errors are ignored (as in some cases they could
677 be caused by DUT firmware deficiencies). This might need to be fine tuned
678 later.
679
Todd Brochd6061672012-05-11 15:52:47 -0700680 Args:
681 verbose: boolean, if True prints info about control initialized.
682 Otherwise prints nothing.
Vadim Bendebury5934e4b2013-02-06 13:57:54 -0800683
684 Returns:
685 This function is called across RPC and as such is expected to return
686 something unless transferring 'none' across is allowed. Hence adding a
687 dummy return value to make things simpler.
Todd Brochd6061672012-05-11 15:52:47 -0700688 """
Todd Brochd9acf0a2012-12-05 13:43:06 -0800689 for control_name, value in self._syscfg.hwinit:
Todd Broch3ec8df02012-11-20 10:53:03 -0800690 try:
691 self.set(control_name, value)
692 except Exception as e:
Todd Broch3ec8df02012-11-20 10:53:03 -0800693 self._logger.error("Problem initializing %s -> %s :: %s",
694 control_name, value, str(e))
Todd Brochd6061672012-05-11 15:52:47 -0700695 if verbose:
696 self._logger.info('Initialized %s to %s', control_name, value)
Vadim Bendebury5934e4b2013-02-06 13:57:54 -0800697 return True
Todd Broch3ec8df02012-11-20 10:53:03 -0800698
Todd Broche505b8d2011-03-21 18:19:54 -0700699 def echo(self, echo):
700 """Dummy echo function for testing/examples.
701
702 Args:
703 echo: string to echo back to client
704 """
705 self._logger.debug("echo(%s)" % (echo))
706 return "ECH0ING: %s" % (echo)
707
J. Richard Barnettee2820552013-03-14 16:13:46 -0700708 def get_board(self):
709 """Return the board specified at startup, if any."""
710 return self._board
711
Simran Basia23c1392013-08-06 14:59:10 -0700712 def get_version(self):
713 """Get servo board version."""
714 return self._version
715
Yusuf Mohsinally29e30d22014-01-14 15:29:17 -0800716 def power_long_press(self):
717 """Simulate a long power button press."""
718 # After a long power press, the EC may ignore the next power
719 # button press (at least on Alex). To guarantee that this
720 # won't happen, we need to allow the EC one second to
721 # collect itself.
722 self._keyboard.power_long_press()
723 return True
724
725 def power_normal_press(self):
726 """Simulate a normal power button press."""
727 self._keyboard.power_normal_press()
728 return True
729
730 def power_short_press(self):
731 """Simulate a short power button press."""
732 self._keyboard.power_short_press()
733 return True
734
735 def power_key(self, secs=''):
736 """Simulate a power button press.
737
738 Args:
739 secs: Time in seconds to simulate the keypress.
740 """
741 self._keyboard.power_key(secs)
742 return True
743
744 def ctrl_d(self, press_secs=''):
745 """Simulate Ctrl-d simultaneous button presses."""
746 self._keyboard.ctrl_d(press_secs)
747 return True
748
749 def ctrl_u(self):
750 """Simulate Ctrl-u simultaneous button presses."""
751 self._keyboard.ctrl_u()
752 return True
753
754 def ctrl_enter(self, press_secs=''):
755 """Simulate Ctrl-enter simultaneous button presses."""
756 self._keyboard.ctrl_enter(press_secs)
757 return True
758
759 def d_key(self, press_secs=''):
760 """Simulate Enter key button press."""
761 self._keyboard.d_key(press_secs)
762 return True
763
764 def ctrl_key(self, press_secs=''):
765 """Simulate Enter key button press."""
766 self._keyboard.ctrl_key(press_secs)
767 return True
768
769 def enter_key(self, press_secs=''):
770 """Simulate Enter key button press."""
771 self._keyboard.enter_key(press_secs)
772 return True
773
774 def refresh_key(self, press_secs=''):
775 """Simulate Refresh key (F3) button press."""
776 self._keyboard.refresh_key(press_secs)
777 return True
778
779 def ctrl_refresh_key(self, press_secs=''):
780 """Simulate Ctrl and Refresh (F3) simultaneous press.
781
782 This key combination is an alternative of Space key.
783 """
784 self._keyboard.ctrl_refresh_key(press_secs)
785 return True
786
787 def imaginary_key(self, press_secs=''):
788 """Simulate imaginary key button press.
789
790 Maps to a key that doesn't physically exist.
791 """
792 self._keyboard.imaginary_key(press_secs)
793 return True
794
Todd Brochdbb09982011-10-02 07:14:26 -0700795
Todd Broche505b8d2011-03-21 18:19:54 -0700796def test():
797 """Integration testing.
798
799 TODO(tbroch) Enhance integration test and add unittest (see mox)
800 """
801 logging.basicConfig(level=logging.DEBUG,
802 format="%(asctime)s - %(name)s - " +
803 "%(levelname)s - %(message)s")
804 # configure server & listen
805 servod_obj = Servod(1)
806 # 4 == number of interfaces on a FT4232H device
807 for i in xrange(4):
808 if i == 1:
809 # its an i2c interface ... see __init__ for details and TODO to make
810 # this configureable
811 servod_obj._interface_list[i].wr_rd(0x21, [0], 1)
812 else:
813 # its a gpio interface
814 servod_obj._interface_list[i].wr_rd(0)
815
816 server = SimpleXMLRPCServer.SimpleXMLRPCServer(("localhost", 9999),
817 allow_none=True)
818 server.register_introspection_functions()
819 server.register_multicall_functions()
820 server.register_instance(servod_obj)
821 logging.info("Listening on localhost port 9999")
822 server.serve_forever()
823
824if __name__ == "__main__":
825 test()
826
827 # simple client transaction would look like
828 """
829 remote_uri = 'http://localhost:9999'
830 client = xmlrpclib.ServerProxy(remote_uri, verbose=False)
831 send_str = "Hello_there"
832 print "Sent " + send_str + ", Recv " + client.echo(send_str)
833 """