1# Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""A base class to interact with I2C slave device.
6
7Dependency
8 - This library depends on a new C shared library called "libsmogcheck.so".
9"""
10
11import ctypes, logging
12
13
14# I2C constants
15I2C_BUS = 2
16
17# Path of shared library.
18SMOGCHECK_C_LIB = "/usr/local/lib/libsmogcheck.so.0"
19
20
21class I2cError(Exception):
22    """Base class for all errors in this module."""
23
24
25class I2cSlave(object):
26    """A generic I2C slave object that supports basic I2C bus input/output."""
27
28    def __init__(self, adapter_nr=None, load_lib=None):
29        """Constructor.
30
31        Mandatory params:
32          adapter_nr: adapter's number address. Default: I2C_BUS.
33          fd: file descriptor to communicate with I2C bus.
34          lib_obj: ctypes library object to interface with SMOGCHECK_C_LIB.
35          load_lib: a string, name of C shared library object to load.
36          slave_addr: slave address to set. Default: None.
37
38        Args:
39          lib: a string, name of C shared library object to load.
40        """
41        self.slave_addr = None
42
43        if adapter_nr is None:
44            adapter_nr = I2C_BUS
45        self.adapter_nr = adapter_nr
46
47        if load_lib is None:
48            load_lib = SMOGCHECK_C_LIB
49        self.load_lib = load_lib
50
51        # Load shared library object.
52        self.lib_obj = self._loadSharedLibrary()
53        self.fd = self._getDeviceFile()
54
55    def _loadSharedLibrary(self):
56        """Loads C shared library .so file.
57
58        Returns:
59          a new instance of the shared (C) library.
60
61        Raises:
62          I2cError: if error loading the shared library.
63        """
64        logging.info('Attempt to load shared library %s', self.load_lib)
65        try:
66            return ctypes.cdll.LoadLibrary(self.load_lib)
67        except OSError, e:
68            raise I2cError('Error loading C library %s: %s' %
69                            (self.load_lib, e))
70        logging.info('Successfully loaded shared library %s', self.load_lib)
71
72    def _getDeviceFile(self):
73        """Gets a file descriptor of a device file.
74
75        Returns:
76          fd: an integer, file descriptor to communicate with I2C bus.
77
78        Raises:
79          I2cError: if error getting device file.
80        """
81        logging.info('Attempt to get device file for adapter %s',
82                     self.adapter_nr)
83        fd = self.lib_obj.GetDeviceFile(self.adapter_nr)
84        if fd < 0:
85            raise I2cError('Error getting device file for adapter %s' %
86                            self.adapter_nr)
87
88        logging.info('Got device file for adapter %s', self.adapter_nr)
89        return fd
90
91    def setSlaveAddress(self, addr):
92        """Sets slave address on I2C bus to be communicated with.
93
94        TODO(tgao): add retry loop and raise error if all retries fail.
95        (so that caller does not have to check self.err for status)
96
97        We use 7-bit address space for I2C, which has 128 addresses total.
98        Besides 16 reserved addresses, the total usable address space is 112.
99        See - http://www.i2c-bus.org/addressing/
100
101        Args:
102          addr: a (positive) integer, 7-bit I2C slave address.
103
104        Raises:
105          I2cError: if slave address is invalid or can't be set.
106        """
107        if self.slave_addr == addr:
108            logging.info('Slave address already set, noop: %s', addr)
109            return
110
111        if addr < 0x8 or addr > 0x77:
112            raise I2cError('Error: invalid I2C slave address %s', addr)
113
114        logging.info('Attempt to set slave address: %s', addr)
115        if not self.fd:
116            self.fd = self._getDeviceFile()
117
118        ret = self.lib_obj.SetSlaveAddress(self.fd, addr)
119        if ret < 0:
120            raise I2cError('Error communicating to slave address %s' % addr)
121
122        self.slave_addr = addr
123        logging.info('Slave address set to: %s', addr)
124
125    def writeByte(self, reg, byte):
126        """Writes a byte to a specific register.
127
128        TODO(tgao): add retry loop and raise error if all retries fail.
129
130        Args:
131          reg: a (positive) integer, register number.
132          byte: a char (8-bit byte), value to write.
133
134        Raises:
135          I2cError: if error writing byte to I2C bus.
136        """
137        logging.info('Attempt to write byte %r to reg %r', byte, reg)
138        if self.lib_obj.WriteByte(self.fd, reg, byte) < 0:
139            raise I2cError('Error writing byte 0x%x to reg %r' % (byte, reg))
140
141        logging.info('Successfully wrote byte 0x%x to reg %r', byte, reg)
142
143    def readByte(self, reg):
144        """Reads a byte from a specific register.
145
146        TODO(tgao): add retry loop and raise error if all retries fail.
147
148        Args:
149          reg: a (positive) integer, register number.
150
151        Returns:
152          byte_read: a char (8-bit byte), value read from register.
153
154        Raises:
155          I2cError: if error reading byte from I2C bus.
156        """
157        logging.info('Attempt to read byte from register %r', reg)
158        byte_read = self.lib_obj.ReadByte(self.fd, reg)
159        if byte_read < 0:
160            raise I2cError('Error reading byte from reg %r' % reg)
161
162        logging.info('Successfully read byte 0x%x from reg %r',
163                     byte_read, reg)
164        return byte_read
165
166    def writeWord(self, reg, word):
167        """Writes a word to a specific register.
168
169        TODO(tgao): add retry loop and raise error if all retries fail.
170
171        Args:
172          reg: a (positive) integer, register number.
173          word: a 16-bit unsigned integer, value to write.
174
175        Raises:
176          I2cError: if error writing word to I2C bus.
177        """
178        logging.info('Attempt to write word %r to reg %r', word, reg)
179        if self.lib_obj.WriteWord(self.fd, reg, ctypes.c_uint16(word)) < 0:
180            raise I2cError('Error writing word 0x%x to reg %r' % (word, reg))
181
182        logging.info('Successfully wrote word 0x%x to reg %r',
183                     word, reg)
184
185    def readWord(self, reg):
186        """Reads a word from a specific register.
187
188        TODO(tgao): add retry loop and raise error if all retries fail.
189
190        Args:
191          reg: a (positive) integer, register number.
192
193        Returns:
194          a 16-bit unsigned integer, value read from register.
195
196        Raises:
197          I2cError: if error reading word from I2C bus.
198        """
199        logging.info('Attempt to read word from register %r', reg)
200        word_read = self.lib_obj.ReadWord(self.fd, reg)
201        if word_read < 0:
202            raise I2cError('Error reading word from reg %r' % reg)
203
204        logging.info('Successfully read word 0x%x from reg %r',
205                     word_read, reg)
206        return word_read
207