blob: 218ee0cda20c929b9a0451a4791514310bff85fa [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.
"""Allows creation of gpio interface via libftdigpio c library for FTDI
devices."""
import logging
import ctypes
import ftdi_utils
import ftdi_common
import gpio_interface
class FgpioError(Exception):
"""Class for exceptions of Fgpio."""
def __init__(self, msg, value=0):
"""FgpioError constructor.
Args:
msg: string, message describing error in detail
value: integer, value of error when non-zero status returned. Default=0
"""
super(FgpioError, self).__init__(msg, value)
self.msg = msg
self.value = value
class FgpioContext(ctypes.Structure):
"""Defines primary context structure for libftdigpio.
Declared in ftdigpio.h and named fgpio_context.
"""
_fields_ = [('fc', ctypes.POINTER(ftdi_common.FtdiContext)),
('gpio', ftdi_common.Gpio)]
class Fgpio(gpio_interface.GpioInterface):
"""Provide interface to libftdigpio c-library via python ctypes module.
Instance Variables:
_fargs: FTDI args for this interface.
_is_closed: Whether or not the connection to this UART is closed.
_gpio: _gpio: Instance of ftdi_common.GPIO()
_fc: FTDI Context object.
_fgc: FgpioContext object.
"""
def __init__(self, vendor=ftdi_common.DEFAULT_VID,
product=ftdi_common.DEFAULT_PID, interface=4, serialname=None):
"""Fgpio constructor.
Loads libraries for libftdi, libftdigpio. Creates instance objects
(Structures), FgpioContext, FtdiContext and Gpio to iteract with the library
and intializes them.
Args:
vendor : usb vendor id of FTDI device
product : usb product id of FTDI device
interface : interface number ( 1 - 4 ) of FTDI device to use
serialname: string of device serialname/number as defined in FTDI eeprom.
Raises:
FgpioError: An error accessing Fgpio object
"""
self._logger = logging.getLogger('Fgpio')
self._logger.debug('')
(self._flib, self._lib) = ftdi_utils.load_libs('ftdi', 'ftdigpio')
self._fargs = ftdi_common.FtdiCommonArgs(
vendor_id=vendor, product_id=product, interface=interface,
serialname=serialname)
self._is_closed = True
self._gpio = ftdi_common.Gpio()
self._fc = ftdi_common.FtdiContext()
self._fgc = FgpioContext()
# initialize
if self._flib.ftdi_init(ctypes.byref(self._fc)):
raise FgpioError('doing ftdi_init')
if self._lib.fgpio_init(ctypes.byref(self._fgc), ctypes.byref(self._fc)):
raise FgpioError('doing fgpio_init')
def __del__(self):
"""Fgpio destructor."""
self._logger.debug('')
if not self._is_closed:
self.close()
def open(self):
"""Opens access to FTDI interface as a GPIO (bitbang).
Raises:
FgpioError: If open fails
"""
err = self._lib.fgpio_open(
ctypes.byref(self._fgc), ctypes.byref(self._fargs))
if err:
raise FgpioError('doing fgpio_open', err)
self._is_closed = False
def close(self):
"""Close access to FTDI interface as a GPIO (bitbang).
Raises:
FgpioError: If close fails
"""
err = self._lib.fgpio_close(ctypes.byref(self._fgc))
if err:
raise FgpioError('doing fgpio_close', err)
self._is_closed = True
def wr_rd(self, offset, width, dir_val=None, wr_val=None, chip=None,
muxfile=None):
"""Write and/or read GPIO bit.
Args:
offset : bit offset of the gpio to read or write
width : integer, number of contiguous bits in gpio to read or write
dir_val : direction value of the gpio. dir_val is interpretted as:
None : read the pins via libftdi's ftdi_read_pins
0 : configure as input
1 : configure as output
wr_val : value to write to the GPIO. Note wr_val is irrelevant if
dir_val = 0
chip : Not used. defaulted to None.
muxfile : Not used. defaulted to None.
Returns:
integer value from reading the gpio value ( masked & aligned )
"""
rd_val = ctypes.c_ubyte()
self._gpio.mask = (pow(2, width) - 1) << offset
if wr_val is not None and dir_val is not None:
self._gpio.direction = self._gpio.mask if dir_val else 0
self._gpio.value = wr_val << offset
self._lib.fgpio_wr_rd(
ctypes.byref(self._fgc), ctypes.byref(self._gpio),
ctypes.byref(rd_val), ftdi_common.INTERFACE_TYPE_GPIO)
else:
self._lib.fgpio_wr_rd(
ctypes.byref(self._fgc), 0, ctypes.byref(rd_val),
ftdi_common.INTERFACE_TYPE_GPIO)
self._logger.debug('dir:%s val:%s returned %d' % (str(dir_val), str(wr_val),
rd_val.value))
return (rd_val.value & self._gpio.mask) >> offset
def test():
"""Test code.
(TODO) tbroch: enhance and make Googley & pythonic from a unittest
perspective.
"""
(options, args) = ftdi_utils.parse_common_args()
loglevel = logging.INFO
if options.debug:
loglevel = logging.DEBUG
logging.basicConfig(
level=loglevel,
format='%(asctime)s - %(name)s - ' + '%(levelname)s - %(message)s')
for i in range(1, 5):
fobj = Fgpio(options.vendor, options.product, i)
fobj.open()
rd_val = fobj.wr_rd(6, 1, 0)
logging.debug('rd_val = %d after <6> -> 0', (rd_val))
if rd_val != 0:
logging.error('rd_val = %d != 0', (rd_val))
rd_val = fobj.wr_rd(6, 1, 1)
logging.debug('rd_val = %d after <6> -> 1', (rd_val))
if rd_val != 1:
logging.error('rd_val = %d != 1', (rd_val))
rd_val = fobj.wr_rd(6, 1, 0)
logging.debug('rd_val = %d after <6> -> 0', (rd_val))
if rd_val != 0:
logging.error('rd_val = %d != 0', (rd_val))
# release as output
rd_val = fobj.wr_rd(6, 0, 0)
logging.debug('rd_val = %d after <6> dir released', (rd_val))
logging.info('rd_val = %d should match pu/pd', (rd_val))
fobj.close()
if __name__ == '__main__':
test()