class MTSMBus(I2CBus): """ Multi-thread compatible SMBus bus. This is just a wrapper of SMBus, serializing I/O on the bus for use in multi-threaded context and adding _i2c_ variants of block transfers. """ def __init__(self, bus_id=1, **kwargs): """ :param int bus_id: the SMBus id (see Raspberry Pi documentation) :param kwargs: parameters transmitted to :py:class:`smbus.SMBus` initializer """ I2CBus.__init__(self, **kwargs) self._bus = SMBus(bus_id) # I/O serialization lock self._lock = threading.Lock() def read_byte(self, addr): with self._lock: return self._bus.read_byte(addr) def write_byte(self, addr, data): with self._lock: self._bus.write_byte(addr, data) def read_byte_data(self, addr, reg): with self._lock: return self._bus.read_byte_data(addr, reg) def write_byte_data(self, addr, reg, data): with self._lock: self._bus.write_byte_data(addr, reg, data) def read_word_data(self, addr, reg): with self._lock: return self._bus.read_word_data(addr, reg) def write_word_data(self, addr, reg, data): with self._lock: self._bus.write_word_data(addr, reg, data) def read_block_data(self, addr, reg): with self._lock: return self._bus.read_block_data(addr, reg) def write_block_data(self, addr, reg, data): with self._lock: self._bus.write_block_data(addr, reg, data) def read_i2c_block_data(self, addr, reg, count): with self._lock: return self._bus.read_i2c_block_data(addr, reg, count) def write_i2c_block_data(self, addr, reg, data): with self._lock: self._bus.write_i2c_block_data(addr, reg, data)
class I2c(object): def __init__(self): self.bus_id = 0 self.smbus = SMBus(self.bus_id) sda_pin = I2C_MAPPINGS['I2C_SDA'] scl_pin = I2C_MAPPINGS['I2C_SCL'] sda_pin.select() scl_pin.select() def value(self): vals = self.smbus.read_block_data(0x02, 0x42) print vals
class LocalhostSMBusSlave(SMBusSlave): def __init__(self, bus, address): SMBusSlave.__init__(self, bus, address) self.bus = SMBus(bus) self.address = address #return: one byte hex string without '0x': '12' def get_byte(self, offset): # print "%s %s" % (datetime.datetime.utcnow().strftime("%H:%M:%S.%f")[:-4], sys._getframe().f_code.co_name) ret = self.bus.read_byte_data(self.address, offset) time.sleep(self.interval) data = hex(ret) return data[2:].zfill(2) #input: offset:str, data:int def set_byte(self, offset, data): # print "%s %s" % (datetime.datetime.utcnow().strftime("%H:%M:%S.%f")[:-4], sys._getframe().f_code.co_name) self.bus.write_byte_data(self.address, offset, data) time.sleep(self.interval) #return: two byte hex string without '0x': '1234' def get_word(self, offset): # print "%s %s" % (datetime.datetime.utcnow().strftime("%H:%M:%S.%f")[:-4], sys._getframe().f_code.co_name) ret = self.bus.read_word_data(self.address, offset) data = hex(ret) time.sleep(self.interval) return data[2:].zfill(4) def set_word(self, offset, data): # print "%s %s" % (datetime.datetime.utcnow().strftime("%H:%M:%S.%f")[:-4], sys._getframe().f_code.co_name) if sys.byteorder == "big": data = ((data & 0xff00) >> 8) + ((data & 0x00ff) << 8) self.bus.write_word_data(self.address, offset, data) time.sleep(self.interval) #return: hex string list per byte,without '0x' :['12', '0a'] def get_block(self, offset, length="32"): # print "%s %s" % (datetime.datetime.utcnow().strftime("%H:%M:%S.%f")[:-4], sys._getframe().f_code.co_name) ret = self.bus.read_block_data(self.address, offset) time.sleep(self.interval) hex_ret = [hex(i)[2:].zfill(2) for i in ret] return hex_ret def set_block(self, offset, data): # print "%s %s" % (datetime.datetime.utcnow().strftime("%H:%M:%S.%f")[:-4], sys._getframe().f_code.co_name) data_arr = [(data & 0x00ff), ((data & 0xff00) >> 8)] #self.bus.write_i2c_block_data(self.address, offset, data_arr) self.bus.write_block_data(self.address, offset, data_arr) time.sleep(self.interval)
class I2CDevice: def __init__(self, addr=None, addr_default=None, bus=BUS_NUMBER): if not addr: # try autodetect address, else use default if provided try: self.addr = int('0x{}'.format( findall("[0-9a-z]{2}(?!:)", check_output(['/usr/sbin/i2cdetect', '-y', str(BUS_NUMBER)]).decode())[0]), base=16) \ if exists('/usr/sbin/i2cdetect') else addr_default except: self.addr = addr_default else: self.addr = addr self.bus = SMBus(bus) # write a single command def write_cmd(self, cmd): self.bus.write_byte(self.addr, cmd) sleep(0.0001) # write a command and argument def write_cmd_arg(self, cmd, data): self.bus.write_byte_data(self.addr, cmd, data) sleep(0.0001) # write a block of data def write_block_data(self, cmd, data): self.bus.write_block_data(self.addr, cmd, data) sleep(0.0001) # read a single byte def read(self): return self.bus.read_byte(self.addr) # read def read_data(self, cmd): return self.bus.read_byte_data(self.addr, cmd) # read a block of data def read_block_data(self, cmd): return self.bus.read_block_data(self.addr, cmd)
class LinuxI2cBus: """A Linux I²C device, which is itself an I²C bus. Should not be instantiated directly; use `LinuxI2c.find_devices` instead. This type mimics the `smbus.SMBus` read/write/close APIs. However, `open` does not take any parameters, and not all APIs are available. """ # note: this is not a liquidctl BaseBus, as that would cause # find_liquidctl_devices to try to directly instantiate it def __init__(self, i2c_dev): self._i2c_dev = i2c_dev self._smbus = None try: assert i2c_dev.name.startswith('i2c-') self._number = int(i2c_dev.name[4:]) except: raise ValueError(f'cannot infer bus number') def find_devices(self, drivers, **kwargs): """Probe drivers and find compatible devices in this bus.""" for drv in drivers: yield from drv.probe(self, **kwargs) def open(self): """Open the I²C bus.""" if not self._smbus: try: self._smbus = SMBus(self._number) except FileNotFoundError: if Path('/sys/class/i2c-dev').exists(): raise raise OSError('kernel module i2c-dev not loaded') from None def read_byte(self, address): """Read a single byte from a device.""" value = self._smbus.read_byte(address) _LOGGER.debug('read byte @ 0x%02x: 0x%02x', address, value) return value def read_byte_data(self, address, register): """Read a single byte from a designated register.""" value = self._smbus.read_byte_data(address, register) _LOGGER.debug('read byte data @ 0x%02x:0x%02x: 0x%02x', address, register, value) return value def read_word_data(self, address, register): """Read a single 2-byte word from a given register.""" value = self._smbus.read_word_data(address, register) _LOGGER.debug('read word data @ 0x%02x:0x%02x: 0x%04x', address, register, value) return value def read_block_data(self, address, register): """Read a block of up to 32 bytes from a given register.""" data = self._smbus.read_block_data(address, register) _LOGGER.debug('read block data @ 0x%02x:0x%02x: %r', address, register, LazyHexRepr(data)) return data def write_byte(self, address, value): """Write a single byte to a device.""" _LOGGER.debug('writing byte @ 0x%02x: 0x%02x', address, value) return self._smbus.write_byte(address, value) def write_byte_data(self, address, register, value): """Write a single byte to a designated register.""" _LOGGER.debug('writing byte data @ 0x%02x:0x%02x: 0x%02x', address, register, value) return self._smbus.write_byte_data(address, register, value) def write_word_data(self, address, register, value): """Write a single 2-byte word to a designated register.""" _LOGGER.debug('writing word data @ 0x%02x:0x%02x: 0x%04x', address, register, value) return self._smbus.write_word_data(address, register, value) def write_block_data(self, address, register, data): """Write a block of byte data to a given register.""" _LOGGER.debug('writing block data @ 0x%02x:0x%02x: %r', address, register, LazyHexRepr(data)) return self._smbus.write_block_data(address, register, data) def close(self): """Close the I²C connection.""" if self._smbus: self._smbus.close() self._smbus = None def load_eeprom(self, address): """Return EEPROM name and data in `address`, or None if N/A.""" # uses kernel facilities to avoid directly reading from the EEPROM # or managing its pages, also avoiding the need for unsafe=smbus dev = f'{self._number}-{address:04x}' try: name = self._i2c_dev.joinpath(dev, 'name').read_text().strip() eeprom = self._i2c_dev.joinpath(dev, 'eeprom').read_bytes() return LinuxEeprom(name, eeprom) except Exception as err: return None @property def name(self): return self._i2c_dev.name @property def description(self): return self._try_sysfs_read('name') @property def parent_vendor(self): return self._try_sysfs_read_hex('device/vendor') @property def parent_device(self): return self._try_sysfs_read_hex('device/device') @property def parent_subsystem_vendor(self): return self._try_sysfs_read_hex('device/subsystem_vendor') @property def parent_subsystem_device(self): return self._try_sysfs_read_hex('device/subsystem_device') @property def parent_driver(self): try: return Path( os.readlink(self._i2c_dev.joinpath('device/driver'))).name except FileNotFoundError: return None def __str__(self): if self.description: return f'{self.name}: {self.description}' return self.name def __repr__(self): def hexid(maybe): if maybe is not None: return f'{maybe:#06x}' return 'None' return f'{self.__class__.__name__}: name: {self.name!r}, ' \ f'description: {self.description!r}, ' \ f'parent_vendor: {hexid(self.parent_vendor)}, ' \ f'parent_device: {hexid(self.parent_device)}, ' \ f'parent_subsystem_vendor: {hexid(self.parent_subsystem_vendor)}, ' \ f'parent_subsystem_device: {hexid(self.parent_subsystem_device)}, ' \ f'parent_driver: {self.parent_driver!r}' def _try_sysfs_read(self, *sub, default=None): try: return self._i2c_dev.joinpath(*sub).read_text().rstrip() except FileNotFoundError: return default def _try_sysfs_read_hex(self, *sub, default=None): try: return int(self._i2c_dev.joinpath(*sub).read_text(), base=16) except FileNotFoundError: return default
class LinuxI2cBus: """A Linux I²C device, which is itself an I²C bus. Should not be instantiated directly; use `LinuxI2c.find_devices` instead. This type mimics the `smbus.SMBus` read/write/close APIs. However, `open` does not take any parameters, and not all APIs are available. """ # note: this is not a liquidctl BaseBus, as that would cause # find_liquidctl_devices to try to directly instantiate it def __init__(self, i2c_dev): if not i2c_dev.name.startswith('i2c-'): raise ValueError('not a bus or unsupported adapter') self._i2c_dev = i2c_dev self._smbus = None self._number = int(i2c_dev.name[4:]) def find_devices(self, drivers, **kwargs): """Probe drivers and find compatible devices in this bus.""" for drv in drivers: yield from drv.probe(self, **kwargs) def open(self): """Open the I²C bus.""" if not self._smbus: try: self._smbus = SMBus(self._number) except FileNotFoundError: if Path('/sys/class/i2c-dev').exists(): raise raise OSError('kernel module i2c-dev not loaded') from None def read_byte(self, address): """Read a single byte from a device.""" value = self._smbus.read_byte(address) _LOGGER.debug('read byte @ 0x%02x:0x%02x', address, value) return value def read_byte_data(self, address, register): """Read a single byte from a designated register.""" value = self._smbus.read_byte_data(address, register) _LOGGER.debug('read byte data @ 0x%02x:0x%02x 0x%02x', address, register, value) return value def read_word_data(self, address, register): """Read a single 2-byte word from a given register.""" value = self._smbus.read_word_data(address, register) _LOGGER.debug('read word data @ 0x%02x:0x%02x 0x%02x', address, register, value) return value def read_block_data(self, address, register): """Read a block of up to 32 bytes from a given register.""" data = self._smbus.read_block_data(address, register) _LOGGER.debug('read block data @ 0x%02x:0x%02x: %r', address, register, LazyHexRepr(data)) return data def write_byte(self, address, value): """Write a single byte to a device.""" _LOGGER.debug('writing byte @ 0x%02x: 0x%02x', address, value) return self._smbus.write_byte(address, value) def write_byte_data(self, address, register, value): """Write a single byte to a designated register.""" _LOGGER.debug('writing byte data @ 0x%02x:0x%02x 0x%02x', address, register, value) return self._smbus.write_byte_data(address, register, value) def write_word_data(self, address, register, value): """Write a single 2-byte word to a designated register.""" _LOGGER.debug('writing word data @ 0x%02x:0x%02x 0x%02x', address, register, value) return self._smbus.write_word_data(address, register, value) def write_block_data(self, address, register, data): """Write a block of byte data to a given register.""" _LOGGER.debug('writing block data @ 0x%02x:0x%02x %r', address, register, LazyHexRepr(data)) return self._smbus.write_block_data(address, register, data) def close(self): """Close the I²C connection.""" if self._smbus: self._smbus.close() self._smbus = None def load_eeprom(self, address): """Return EEPROM name and data in `address`, or None if N/A.""" # uses kernel facilities to avoid directly reading from the EEPROM # or managing its pages, also avoiding the need for unsafe=smbus dev = f'{self._number}-{address:04x}' try: name = self._i2c_dev.joinpath(dev, 'name').read_text().strip() # work around a bug in the kernel by reading the ee1004 eeprom # in small enough chunks # related: #416 ("DDR4 support broken on Linux 5.16.3") # related: https://lore.kernel.org/lkml/[email protected]/ eeprom_path = self._i2c_dev.joinpath(dev, 'eeprom') eeprom_size = eeprom_path.stat().st_size _LOGGER.debug('path=%s, size=%s', eeprom_path, eeprom_size) with eeprom_path.open(mode='rb', buffering=0) as f: eeprom = bytearray() while len(eeprom) < eeprom_size: b = f.read(128) if not b: break eeprom.extend(b) return LinuxEeprom(name, eeprom) except FileNotFoundError: return None @property def name(self): return self._i2c_dev.name @property def description(self): return self._try_sysfs_read('name') @property def parent_vendor(self): return self._try_sysfs_read_hex('device/vendor') @property def parent_device(self): return self._try_sysfs_read_hex('device/device') @property def parent_subsystem_vendor(self): return self._try_sysfs_read_hex('device/subsystem_vendor') @property def parent_subsystem_device(self): return self._try_sysfs_read_hex('device/subsystem_device') @property def parent_driver(self): try: return Path(os.readlink(self._i2c_dev.joinpath('device/driver'))).name except FileNotFoundError: return None def __str__(self): if self.description: return f'{self.name}: {self.description}' return self.name def __repr__(self): def hexid(maybe): if maybe is not None: return f'{maybe:#06x}' return 'None' return f'{self.__class__.__name__}: name: {self.name!r}, description:' \ f' {self.description!r}, parent_vendor: {hexid(self.parent_vendor)},' \ f' parent_device: {hexid(self.parent_device)}, parent_subsystem_vendor:' \ f' {hexid(self.parent_subsystem_vendor)},' \ f' parent_subsystem_device: {hexid(self.parent_subsystem_device)},' \ f' parent_driver: {self.parent_driver!r}' def _try_sysfs_read(self, *sub, default=None): try: return self._i2c_dev.joinpath(*sub).read_text().rstrip() except FileNotFoundError: return default def _try_sysfs_read_hex(self, *sub, default=None): try: return int(self._i2c_dev.joinpath(*sub).read_text(), base=16) except FileNotFoundError: return default