blob: ad3f09e9892c37831eb1200f5cb1eca1cad02860 [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 i2c interface via libftdii2c (C) library for FTDI devices.
"""
import ctypes
import logging
import ftdi_common
import ftdi_utils
class Fi2cError(Exception):
"""Class for exceptions of Fi2c.
"""
pass
class Fi2cContext(ctypes.Structure):
"""Defines primary context structure for libftdii2c.
Declared in ftdii2c.h and named fi2c_context.
"""
_fields_ = [("fc", ctypes.POINTER(ftdi_common.FtdiContext)),
("gpio", ftdi_common.Gpio),
("clk", ctypes.c_uint),
("error", ctypes.c_int),
("slv", ctypes.c_ubyte),
("buf", ctypes.POINTER(ctypes.c_ubyte)),
("bufcnt", ctypes.c_int),
("bufsize", ctypes.c_int)]
class Fi2c(object):
"""Provide interface to libftdii2c c-library via python ctypes module.
"""
def __init__(self, vendor=ftdi_common.DEFAULT_VID,
product=ftdi_common.DEFAULT_PID, interface=2):
"""Fi2c constructor.
Loads libraries for libftdi, libftdii2c. Creates instance objects
(Structures), Fi2cContext, 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 of FTDI device to use
Raises:
Fi2cError: If either ftdi or fi2c inits fail
"""
self._logger = logging.getLogger("Fi2c")
self._logger.debug("")
(self._flib, self._lib) = ftdi_utils.load_libs("ftdi", "ftdii2c")
self._fargs = ftdi_common.FtdiCommonArgs(vendor_id=vendor,
product_id=product,
interface=interface)
self._fc = ftdi_common.FtdiContext()
self._fic = Fi2cContext()
self._is_closed = True
if self._flib.ftdi_init(ctypes.byref(self._fc)):
raise Fi2cError("doing ftdi_init")
if self._lib.fi2c_init(ctypes.byref(self._fic), ctypes.byref(self._fc)):
raise Fi2cError("doing fi2c_init")
def __del__(self):
"""Fi2c destructor.
Calls close to release device
"""
if not self._is_closed:
self.close()
def open(self):
"""Opens access to FTDI interface as a i2c (MPSSE mode) interface.
Raises:
Fi2cError: If open fails
"""
if self._lib.fi2c_open(ctypes.byref(self._fic), ctypes.byref(self._fargs)):
raise Fi2cError("doing fi2c_open")
self.is_closed = False
def setclock(self, speed=100000):
"""Sets i2c clock speed.
Args:
speed: clock speed in hertz. Default is 100kHz
"""
if self._lib.fi2c_setclock(ctypes.byref(self._fic), speed):
raise Fi2cError("doing fi2c_setclock")
def wr_rd(self, slv, wlist, rcnt):
"""Write and/or read a slave i2c device.
Args:
slv: 7-bit address of the slave device
wlist: list of bytes to write to the slave. If list length is zero its
just a read
rcnt: number of bytes to read from the device. If zero, its just a write
Returns:
list of c_ubyte's read from i2c device.
"""
self._fic.slv = slv
wcnt = len(wlist)
wbuf_type = ctypes.c_ubyte * wcnt
wbuf = wbuf_type()
for i in xrange(wcnt):
wbuf[i] = wlist[i]
rbuf_type = ctypes.c_ubyte * rcnt
rbuf = rbuf_type()
for i, wval in enumerate(wbuf):
self._logger.debug("wbuf[%i] = 0x%02x" % (i, wval))
if self._lib.fi2c_wr_rd(ctypes.byref(self._fic), ctypes.byref(wbuf), wcnt,
ctypes.byref(rbuf), rcnt):
raise Fi2cError("doing fi2c_wr_rd")
for i, rval in enumerate(rbuf):
self._logger.debug("rbuf[%i] = 0x%02x" % (i, rval))
return list(rbuf)
def close(self):
"""Close connection to FTDI device and cleanup.
"""
if self._lib.fi2c_close(ctypes.byref(self._fic)):
raise Fi2cError("doing fi2c_close")
def test():
"""Test code.
(TODO) tbroch: enhance and make Googley & pythonic from a unittest perspective
"""
(options, args) = ftdi_utils.parse_common_args(interface=2)
loglevel=logging.INFO
if options.debug:
loglevel = logging.DEBUG
logging.basicConfig(level=loglevel,
format="%(asctime)s - %(name)s - " +
"%(levelname)s - %(message)s")
fobj = Fi2c(options.vendor, options.product, options.interface)
fobj.open()
fobj.setclock(100000)
wbuf = [0]
slv = 0x21
rbuf = fobj.wr_rd(slv, wbuf, 1)
logging.info("first: i2c read of slv=0x%02x reg=0x%02x == 0x%02x" %
(slv, wbuf[0], rbuf[0]))
errcnt = 0
for cnt in xrange(1000):
try:
rbuf = fobj.wr_rd(slv, [], 1)
except:
errcnt += 1
logging.error("errs = %d cnt = %d" % (errcnt, cnt))
logging.info("last: i2c read of slv=0x%02x reg=0x%02x == 0x%02x" %
(slv, wbuf[0], rbuf[0]))
fobj.close()
if __name__ == "__main__":
test()