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