blob: c815dc6b3f7f98f37acb4008235e5bd7109a0d51 [file] [log] [blame]
# Copyright 2014 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.
"""Accesses I2C devies via Linux i2c-dev driver."""
from __future__ import print_function
import fcntl
import io
import struct
import factory_common # pylint: disable=unused-import
from cros.factory.device import types
class I2CSlave(types.DeviceComponent):
"""Access a slave device on I2C bus."""
_I2C_SLAVE_FORCE = 0x0706
def __init__(self, dut, bus, slave, reg_width):
"""Constructor.
Args:
dut: A reference to device under test. See DeviceComponent for more info.
bus: A path to I2C bus device.
slave: 7 bit I2C slave address.
reg_width: Number of bits to write for register address.
"""
super(I2CSlave, self).__init__(dut)
self._bus = bus
self._slave = slave
self._reg_width = reg_width
def _EncodeRegisterAddress(self, address):
"""Encodes a register address in big endian."""
if self._reg_width == 0:
return ''
return struct.pack('>I', address)[-(self._reg_width // 8):]
def WriteRead(self, write_data, read_count=None):
"""Implements hdctools wr_rd() interface.
This function writes a list or byte values to an I2C device, then reads
byte values from the same device.
Args:
write_data: A string of data to write into device.
read_count: Nnumber of bytes to read from device.
Returns:
A string for data read from device, if read_count is not zero.
"""
if not self._device.link.IsLocal():
raise NotImplementedError('I2CBus currently supports only local targets')
bus = io.open(self._bus, mode='r+b', buffering=0)
fcntl.ioctl(bus.fileno(), self._I2C_SLAVE_FORCE, self._slave)
if write_data:
bus.write(write_data)
if read_count:
return bus.read(read_count)
def Read(self, address, count):
"""Reads data from I2C device.
Args:
address: Data address (register number).
count: Number of bytes to read.
Returns:
A string for data read from device.
"""
return self.WriteRead(self._EncodeRegisterAddress(address), count)
def Write(self, address, value):
"""Writes data into I2C device.
Args:
address: Data address (register number).
value: A string for data to write.
"""
return self.WriteRead(self._EncodeRegisterAddress(address) + value)
class I2CBus(types.DeviceComponent):
"""Provides access to devices on I2C bus.
Usage:
# Declare an address using bus 0, slave 0x48, reg width 8 bit.
from cros.factory.device import device_utils
i2c = device_utils.CreateDUTInterface().i2c
slave = i2c.GetSlave(0, 0x48, 8)
slave1 = i2c.GetSlave('/dev/i2c-1', 0x48, 8)
# Read 1 byte from register(0x16)
print ord(slave.Read(0x16, 1))
# Write 2 bytes register(0x20)
slave.Write(0x20, '\x01\x02')
# For more complicated I/O composition you should use struct.pack.
slave.write(0x30, struct.pack('>I', myvalue))
"""
def GetSlave(self, bus, slave, reg_width):
"""Gets an I2CSlave instance.
Args:
bus: I2C bus number, or a path to I2C bus device.
slave: 7 bit I2C slave address, or known as "chipset address".
reg_width: Number of bits to write for register.
"""
if isinstance(bus, int):
bus = '/dev/i2c-%d' % bus
assert slave & (0xfe) == 0, 'I2C Slave address has only 7 bits.'
assert reg_width % 8 == 0, 'Register must be aligned with 8 bits.'
assert reg_width <= 32, 'Only 0~32 bits of reg addresses are supported.'
return I2CSlave(self._device, bus, slave, reg_width)