mirror of
https://github.com/sunnypilot/sunnypilot.git
synced 2026-02-18 20:03:53 +08:00
82 lines
2.4 KiB
Python
82 lines
2.4 KiB
Python
import os
|
|
import fcntl
|
|
import ctypes
|
|
|
|
# I2C constants from /usr/include/linux/i2c-dev.h
|
|
I2C_SLAVE = 0x0703
|
|
I2C_SLAVE_FORCE = 0x0706
|
|
I2C_SMBUS = 0x0720
|
|
|
|
# SMBus transfer types
|
|
I2C_SMBUS_READ = 1
|
|
I2C_SMBUS_WRITE = 0
|
|
I2C_SMBUS_BYTE_DATA = 2
|
|
I2C_SMBUS_I2C_BLOCK_DATA = 8
|
|
|
|
I2C_SMBUS_BLOCK_MAX = 32
|
|
|
|
|
|
class _I2cSmbusData(ctypes.Union):
|
|
_fields_ = [
|
|
("byte", ctypes.c_uint8),
|
|
("word", ctypes.c_uint16),
|
|
("block", ctypes.c_uint8 * (I2C_SMBUS_BLOCK_MAX + 2)),
|
|
]
|
|
|
|
|
|
class _I2cSmbusIoctlData(ctypes.Structure):
|
|
_fields_ = [
|
|
("read_write", ctypes.c_uint8),
|
|
("command", ctypes.c_uint8),
|
|
("size", ctypes.c_uint32),
|
|
("data", ctypes.POINTER(_I2cSmbusData)),
|
|
]
|
|
|
|
|
|
class SMBus:
|
|
def __init__(self, bus: int):
|
|
self._fd = os.open(f'/dev/i2c-{bus}', os.O_RDWR)
|
|
|
|
def __enter__(self) -> 'SMBus':
|
|
return self
|
|
|
|
def __exit__(self, *args) -> None:
|
|
self.close()
|
|
|
|
def close(self) -> None:
|
|
if hasattr(self, '_fd') and self._fd >= 0:
|
|
os.close(self._fd)
|
|
self._fd = -1
|
|
|
|
def _set_address(self, addr: int, force: bool = False) -> None:
|
|
ioctl_arg = I2C_SLAVE_FORCE if force else I2C_SLAVE
|
|
fcntl.ioctl(self._fd, ioctl_arg, addr)
|
|
|
|
def _smbus_access(self, read_write: int, command: int, size: int, data: _I2cSmbusData) -> None:
|
|
ioctl_data = _I2cSmbusIoctlData(read_write, command, size, ctypes.pointer(data))
|
|
fcntl.ioctl(self._fd, I2C_SMBUS, ioctl_data)
|
|
|
|
def read_byte_data(self, addr: int, register: int, force: bool = False) -> int:
|
|
self._set_address(addr, force)
|
|
data = _I2cSmbusData()
|
|
self._smbus_access(I2C_SMBUS_READ, register, I2C_SMBUS_BYTE_DATA, data)
|
|
return int(data.byte)
|
|
|
|
def write_byte_data(self, addr: int, register: int, value: int, force: bool = False) -> None:
|
|
self._set_address(addr, force)
|
|
data = _I2cSmbusData()
|
|
data.byte = value & 0xFF
|
|
self._smbus_access(I2C_SMBUS_WRITE, register, I2C_SMBUS_BYTE_DATA, data)
|
|
|
|
def read_i2c_block_data(self, addr: int, register: int, length: int, force: bool = False) -> list[int]:
|
|
self._set_address(addr, force)
|
|
if not (0 <= length <= I2C_SMBUS_BLOCK_MAX):
|
|
raise ValueError(f"length must be 0..{I2C_SMBUS_BLOCK_MAX}")
|
|
|
|
data = _I2cSmbusData()
|
|
data.block[0] = length
|
|
self._smbus_access(I2C_SMBUS_READ, register, I2C_SMBUS_I2C_BLOCK_DATA, data)
|
|
read_len = int(data.block[0]) or length
|
|
read_len = min(read_len, length)
|
|
return [int(b) for b in data.block[1 : read_len + 1]]
|