# 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.
"""A base class to interact with I2C slave device.
Dependency
- This library depends on a new C shared library called "libsmogcheck.so".
"""
import ctypes, logging
# I2C constants
I2C_BUS = 2
# Path of shared library.
SMOGCHECK_C_LIB = "/usr/local/lib/libsmogcheck.so.0"
class I2cError(Exception):
"""Base class for all errors in this module."""
class I2cSlave(object):
"""A generic I2C slave object that supports basic I2C bus input/output."""
def __init__(self, adapter_nr=None, load_lib=None):
"""Constructor.
Mandatory params:
adapter_nr: adapter's number address. Default: I2C_BUS.
fd: file descriptor to communicate with I2C bus.
lib_obj: ctypes library object to interface with SMOGCHECK_C_LIB.
load_lib: a string, name of C shared library object to load.
slave_addr: slave address to set. Default: None.
Args:
lib: a string, name of C shared library object to load.
"""
self.slave_addr = None
if adapter_nr is None:
adapter_nr = I2C_BUS
self.adapter_nr = adapter_nr
if load_lib is None:
load_lib = SMOGCHECK_C_LIB
self.load_lib = load_lib
# Load shared library object.
self.lib_obj = self._loadSharedLibrary()
self.fd = self._getDeviceFile()
def _loadSharedLibrary(self):
"""Loads C shared library .so file.
Returns:
a new instance of the shared (C) library.
Raises:
I2cError: if error loading the shared library.
"""
logging.info('Attempt to load shared library %s', self.load_lib)
try:
return ctypes.cdll.LoadLibrary(self.load_lib)
except OSError, e:
raise I2cError('Error loading C library %s: %s' %
(self.load_lib, e))
logging.info('Successfully loaded shared library %s', self.load_lib)
def _getDeviceFile(self):
"""Gets a file descriptor of a device file.
Returns:
fd: an integer, file descriptor to communicate with I2C bus.
Raises:
I2cError: if error getting device file.
"""
logging.info('Attempt to get device file for adapter %s',
self.adapter_nr)
fd = self.lib_obj.GetDeviceFile(self.adapter_nr)
if fd < 0:
raise I2cError('Error getting device file for adapter %s' %
self.adapter_nr)
logging.info('Got device file for adapter %s', self.adapter_nr)
return fd
def setSlaveAddress(self, addr):
"""Sets slave address on I2C bus to be communicated with.
TODO(tgao): add retry loop and raise error if all retries fail.
(so that caller does not have to check self.err for status)
We use 7-bit address space for I2C, which has 128 addresses total.
Besides 16 reserved addresses, the total usable address space is 112.
See - http://www.i2c-bus.org/addressing/
Args:
addr: a (positive) integer, 7-bit I2C slave address.
Raises:
I2cError: if slave address is invalid or can't be set.
"""
if self.slave_addr == addr:
logging.info('Slave address already set, noop: %s', addr)
return
if addr < 0x8 or addr > 0x77:
raise I2cError('Error: invalid I2C slave address %s', addr)
logging.info('Attempt to set slave address: %s', addr)
if not self.fd:
self.fd = self._getDeviceFile()
ret = self.lib_obj.SetSlaveAddress(self.fd, addr)
if ret < 0:
raise I2cError('Error communicating to slave address %s' % addr)
self.slave_addr = addr
logging.info('Slave address set to: %s', addr)
def writeByte(self, reg, byte):
"""Writes a byte to a specific register.
TODO(tgao): add retry loop and raise error if all retries fail.
Args:
reg: a (positive) integer, register number.
byte: a char (8-bit byte), value to write.
Raises:
I2cError: if error writing byte to I2C bus.
"""
logging.info('Attempt to write byte %r to reg %r', byte, reg)
if self.lib_obj.WriteByte(self.fd, reg, byte) < 0:
raise I2cError('Error writing byte 0x%x to reg %r' % (byte, reg))
logging.info('Successfully wrote byte 0x%x to reg %r', byte, reg)
def readByte(self, reg):
"""Reads a byte from a specific register.
TODO(tgao): add retry loop and raise error if all retries fail.
Args:
reg: a (positive) integer, register number.
Returns:
byte_read: a char (8-bit byte), value read from register.
Raises:
I2cError: if error reading byte from I2C bus.
"""
logging.info('Attempt to read byte from register %r', reg)
byte_read = self.lib_obj.ReadByte(self.fd, reg)
if byte_read < 0:
raise I2cError('Error reading byte from reg %r' % reg)
logging.info('Successfully read byte 0x%x from reg %r',
byte_read, reg)
return byte_read
def writeWord(self, reg, word):
"""Writes a word to a specific register.
TODO(tgao): add retry loop and raise error if all retries fail.
Args:
reg: a (positive) integer, register number.
word: a 16-bit unsigned integer, value to write.
Raises:
I2cError: if error writing word to I2C bus.
"""
logging.info('Attempt to write word %r to reg %r', word, reg)
if self.lib_obj.WriteWord(self.fd, reg, ctypes.c_uint16(word)) < 0:
raise I2cError('Error writing word 0x%x to reg %r' % (word, reg))
logging.info('Successfully wrote word 0x%x to reg %r',
word, reg)
def readWord(self, reg):
"""Reads a word from a specific register.
TODO(tgao): add retry loop and raise error if all retries fail.
Args:
reg: a (positive) integer, register number.
Returns:
a 16-bit unsigned integer, value read from register.
Raises:
I2cError: if error reading word from I2C bus.
"""
logging.info('Attempt to read word from register %r', reg)
word_read = self.lib_obj.ReadWord(self.fd, reg)
if word_read < 0:
raise I2cError('Error reading word from reg %r' % reg)
logging.info('Successfully read word 0x%x from reg %r',
word_read, reg)
return word_read