blob: 43cc05f9ad722a5cefe4bfefc764d36cef9b4df7 [file] [log] [blame]
# Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Base class for servo drivers."""
import logging
VALID_IO_TYPES = ['PU', 'PP']
class HwDriverError(Exception):
"""Exception class for HwDriver."""
def _get_io_type(params):
"""Retrieve IO driver type.
Valid driver types are:
PU == Pull-up
PP == Push-pull
Returns: string either 'PP' or 'PU'
Raises: HwDriverError if:
io type is invalid
controls width > 1
"""
if 'od' in params:
width = params.get('width', 1)
if width > 1:
raise HwDriverError('Open drain not implemented for widths != 1')
io_type = params['od'].upper()
if io_type not in VALID_IO_TYPES:
raise HwDriverError("Unrecognized io type (param 'od') %s" % io_type)
return io_type
return False
class HwDriver(object):
"""Base class for all hardware drivers."""
def __init__(self, interface, params):
"""Driver constructor.
Args:
interface: FTDI interface object to handle low-level communication to
control
params: dictionary of params needed to perform operations on gpio driver
params dictionary can have k=v pairs necessary to communicate with the
underlying hardware. Notables are:
width: integer of width of the control in bits.
Default: 1
offset: integer of position of bit(s) with respect to lsb.
Default: 0
map: name string of map dictionary to use for unmapping / remapping
fmt: function name string to call to format the result.
Additional param keys will be described in sub-class drivers.
Attributes:
_logger: logger object. May be accessed via sub-class
_interface: interface object. May be accessed via sub-class
_params: parameter dictionary. May be accessed via sub-class
_io_type: String of io type or False if not explicitly assigned.
"""
self._logger = logging.getLogger(type(self).__name__)
self._logger.debug('')
self._interface = interface
self._params = params
self._io_type = _get_io_type(params)
def set(self, logical_value):
"""Set hardware control to a particular value.
In the case of simpler drivers, a single 'set' method is
implemented in the subclass. If however the driver is deemed
worthy of more complication, this method will dispatch to the
subclasses "_Set_%s" % params['subtype'] method.
TODO(tbroch) logical_value will need float support for DAC's
Args:
logical_value: Integer value to write to hardware.
Returns:
Value from subclass method.
Raises:
HwDriverError: If unable to locate subclass method.
NotImplementedError: There's no subtype param and set method
wasn't implemented in the subclass.
"""
self._logger.debug('logical_value = %s' % str(logical_value))
if 'subtype' in self._params:
fn_name = '_Set_%s' % self._params['subtype']
if hasattr(self, fn_name):
return getattr(self, fn_name)(logical_value)
else:
raise HwDriverError('Finding set function %s' % fn_name)
else:
raise NotImplementedError('Set should be implemented in subclass.')
def get(self):
"""Get control value and return it
In the case of simpler drivers, a single 'get' method is
implemented in the subclass. If however the driver is deemed
worthy of more complication, this method will dispatch to the
subclasses "_Get_%s" % params['subtype'] method.
Returns:
Value from subclass method.
Raises:
HwDriverError: If unable to locate subclass method.
NotImplementedError: There's no subtype param and get method
wasn't implemented in the subclass.
"""
self._logger.debug('')
if 'subtype' in self._params:
fn_name = '_Get_%s' % self._params['subtype']
if hasattr(self, fn_name):
return getattr(self, fn_name)()
else:
raise HwDriverError('Finding get function %s' % fn_name)
else:
raise NotImplementedError('Get method should be implemented in subclass.')
def _create_logical_value(self, hw_value):
"""Create logical value using mask & offset.
logical_value = (hw_value & mask) >> offset
In MOST cases, subclass drivers should use this for data coming back from
get method.
Args:
hw_value: value to convert to logical value
Returns:
integer in logical representation
"""
(offset, mask) = self._get_offset_mask()
if offset is not None:
fmt_value = (hw_value & mask) >> offset
else:
fmt_value = hw_value
return fmt_value
def _create_hw_value(self, logical_value):
"""Create hardware value using mask & offset.
hw_value = (logical_value << offset) & mask
In MOST cases, subclass drivers should use this for data going to value
argument in set method.
Args:
logical_value: value to convert to hardware value
Returns:
integer in hardware representation
Raises:
HwDriverError: when logical_value would assert bits outside the mask
"""
(offset, mask) = self._get_offset_mask()
if offset is not None:
hw_value = (logical_value << offset)
if hw_value != (hw_value & mask):
raise HwDriverError('format value asserts bits outside mask')
else:
hw_value = logical_value
return hw_value
def _get_offset_mask(self):
"""Retrieve offset and mask.
Subclasses should use to obtain &| create these ints from param dict
Returns:
tuple (offset, mask) where:
offset: integer offset or None if not defined in param dict
mask: integer mask or None if offset not defined in param dict
For example if width=2 and offset=4 then mask=0x30
"""
if 'offset' in self._params:
offset = int(self._params['offset'])
if 'width' not in self._params:
mask = 1 << offset
else:
mask = (pow(2, int(self._params['width'])) - 1) << offset
else:
offset = None
mask = None
return (offset, mask)