Created
March 19, 2026 11:55
-
-
Save rmuxnet/c2783c5a56a9955e08a5d572e466c017 to your computer and use it in GitHub Desktop.
ps4 icc i2c
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| """ | |
| ps4_icc_i2c.py | |
| -------------- | |
| I2C bus access for PS4 Aeolia southbridge peripherals via /dev/i2c-0. | |
| detect_devices() probe all addresses, return responding list | |
| print_device_map() print i2cdetect-style map | |
| i2c_read_byte_data(fd, addr, reg) read one byte | |
| i2c_write_byte_data(fd, addr, reg, val) write one byte | |
| i2c_read_block(fd, addr, reg, n) read n bytes | |
| I2CBus context manager for /dev/i2c-0 | |
| Hardware: PS4 Fat (Aeolia CXD90025G) | |
| Kernel: Linux 6.18.18-Strawberry-ThinLTO-LTS+ (CachyOS x86_64) | |
| DEVICE PERMISSIONS | |
| ------------------ | |
| If /dev/i2c-0 does not exist or you get PermissionError: | |
| sudo modprobe i2c-dev | |
| sudo chmod 666 /dev/i2c-0 | |
| For /dev/icc (if also using ps4_icc_core.py): | |
| cat /proc/devices | grep icc | |
| sudo mknod /dev/icc c <major> 0 | |
| sudo chmod 666 /dev/icc | |
| Permanent udev rules: | |
| echo 'KERNEL=="i2c-0", MODE="0666"' | sudo tee /etc/udev/rules.d/99-ps4-i2c.rules | |
| echo 'KERNEL=="icc", MODE="0666"' | sudo tee -a /etc/udev/rules.d/99-ps4-i2c.rules | |
| sudo udevadm control --reload-rules | |
| sudo udevadm trigger | |
| DO NOT USE RAW ICC IOCTLS FOR I2C | |
| ----------------------------------- | |
| Major 0x10 exposes the I2C bus, but the ICC kernel driver implements the I2C | |
| framing internally and registers a standard Linux adapter at boot: | |
| i2c i2c-0: adapter [icc] registered | |
| i2c_dev: adapter [icc] registered as minor 0 | |
| All raw ICC format attempts to major 0x10 return zeros because the kernel | |
| handles the protocol layer. Use /dev/i2c-0 via i2c-dev instead. | |
| KNOWN I2C PERIPHERALS (not yet enumerated) | |
| ------------------------------------------ | |
| VRM likely near address 0x60 | |
| Power management IC address unknown | |
| HDMI repeater address unknown | |
| Bus enumeration via i2cdetect -y 0 is the highest-priority remaining | |
| open investigation. Run it and document results. | |
| Usage: | |
| python ps4_icc_i2c.py detect | |
| python ps4_icc_i2c.py read 60 9e | |
| python ps4_icc_i2c.py write 60 9e 00 | |
| Or use i2c-tools directly: | |
| i2cdetect -y 0 | |
| i2cget -y 0 0x60 0x9E | |
| i2cset -y 0 0x60 0x9E 0x00 | |
| """ | |
| import os | |
| import fcntl | |
| I2C_DEV = '/dev/i2c-0' | |
| I2C_SLAVE = 0x0703 | |
| class I2CError(Exception): | |
| pass | |
| def i2c_read_byte_data(bus_fd, addr, reg): | |
| try: | |
| fcntl.ioctl(bus_fd, I2C_SLAVE, addr) | |
| except OSError as e: | |
| raise I2CError(f"Failed to set slave 0x{addr:02X}: {e}") | |
| try: | |
| os.write(bus_fd.fileno(), bytes([reg & 0xFF])) | |
| return os.read(bus_fd.fileno(), 1)[0] | |
| except OSError as e: | |
| raise I2CError(f"Read failed addr=0x{addr:02X} reg=0x{reg:02X}: {e}") | |
| def i2c_write_byte_data(bus_fd, addr, reg, value): | |
| try: | |
| fcntl.ioctl(bus_fd, I2C_SLAVE, addr) | |
| except OSError as e: | |
| raise I2CError(f"Failed to set slave 0x{addr:02X}: {e}") | |
| try: | |
| os.write(bus_fd.fileno(), bytes([reg & 0xFF, value & 0xFF])) | |
| except OSError as e: | |
| raise I2CError(f"Write failed addr=0x{addr:02X} reg=0x{reg:02X}: {e}") | |
| def i2c_read_block(bus_fd, addr, reg, length): | |
| fcntl.ioctl(bus_fd, I2C_SLAVE, addr) | |
| os.write(bus_fd.fileno(), bytes([reg & 0xFF])) | |
| return os.read(bus_fd.fileno(), length) | |
| def detect_devices(): | |
| present = [] | |
| try: | |
| with open(I2C_DEV, 'r+b', buffering=0) as fd: | |
| for addr in range(0x03, 0x78): | |
| try: | |
| fcntl.ioctl(fd, I2C_SLAVE, addr) | |
| os.write(fd.fileno(), b'') | |
| present.append(addr) | |
| except OSError: | |
| pass | |
| except FileNotFoundError: | |
| raise I2CError(f"{I2C_DEV} not found. Run: sudo modprobe i2c-dev") | |
| except PermissionError: | |
| raise I2CError(f"Permission denied on {I2C_DEV}. Fix: sudo chmod 666 /dev/i2c-0") | |
| return present | |
| def print_device_map(): | |
| print(f"Probing {I2C_DEV} (ICC-backed I2C adapter)...") | |
| present = detect_devices() | |
| if not present: | |
| print(" No devices detected.") | |
| print(" Tip: i2cdetect -y 0 from i2c-tools may give more accurate results.") | |
| return | |
| print(f" {len(present)} device(s) found:") | |
| for addr in present: | |
| note = ' (possible VRM)' if addr == 0x60 else '' | |
| print(f" 0x{addr:02X}{note}") | |
| class I2CBus: | |
| def __init__(self, device=I2C_DEV): | |
| self.device = device | |
| self._fd = None | |
| def __enter__(self): | |
| try: | |
| self._fd = open(self.device, 'r+b', buffering=0) | |
| except FileNotFoundError: | |
| raise I2CError(f"{self.device} not found. Run: sudo modprobe i2c-dev") | |
| except PermissionError: | |
| raise I2CError(f"Permission denied. Fix: sudo chmod 666 {self.device}") | |
| return self | |
| def __exit__(self, *args): | |
| if self._fd: | |
| self._fd.close() | |
| def read_byte(self, addr, reg): | |
| return i2c_read_byte_data(self._fd, addr, reg) | |
| def write_byte(self, addr, reg, value): | |
| i2c_write_byte_data(self._fd, addr, reg, value) | |
| def read_block(self, addr, reg, length): | |
| return i2c_read_block(self._fd, addr, reg, length) | |
| if __name__ == '__main__': | |
| import sys | |
| if len(sys.argv) == 2 and sys.argv[1] == 'detect': | |
| print_device_map() | |
| sys.exit(0) | |
| if len(sys.argv) == 4 and sys.argv[1] == 'read': | |
| addr = int(sys.argv[2], 16) | |
| reg = int(sys.argv[3], 16) | |
| with I2CBus() as bus: | |
| val = bus.read_byte(addr, reg) | |
| print(f"0x{addr:02X}[0x{reg:02X}] = 0x{val:02X} ({val})") | |
| sys.exit(0) | |
| if len(sys.argv) == 5 and sys.argv[1] == 'write': | |
| addr = int(sys.argv[2], 16) | |
| reg = int(sys.argv[3], 16) | |
| value = int(sys.argv[4], 16) | |
| with I2CBus() as bus: | |
| bus.write_byte(addr, reg, value) | |
| print(f"Wrote 0x{value:02X} to 0x{addr:02X}[0x{reg:02X}]") | |
| sys.exit(0) | |
| print(f"Usage: python {sys.argv[0]} detect") | |
| print(f" python {sys.argv[0]} read <addr_hex> <reg_hex>") | |
| print(f" python {sys.argv[0]} write <addr_hex> <reg_hex> <val_hex>") | |
| sys.exit(1) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment