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