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