Skip to content

Instantly share code, notes, and snippets.

@rmuxnet
Created March 19, 2026 11:51
Show Gist options
  • Select an option

  • Save rmuxnet/002a2ae105ffca290ada36a39d645f70 to your computer and use it in GitHub Desktop.

Select an option

Save rmuxnet/002a2ae105ffca290ada36a39d645f70 to your computer and use it in GitHub Desktop.
ps4 icc led
"""
ps4_icc_led.py
--------------
LED / indicator control via ICC major 0x09, minor 0x20.
set_led(color_name) set LED to a named color/effect
set_led_custom(blue, white, prog) set LED with explicit channel values
Hardware: PS4 Fat (Aeolia CXD90025G)
Kernel: Linux 6.18.18-Strawberry-ThinLTO-LTS+ (CachyOS x86_64)
DEVICE PERMISSIONS
------------------
If /dev/icc does not exist or you get PermissionError:
cat /proc/devices | grep icc
sudo mknod /dev/icc c <major> 0
sudo chmod 666 /dev/icc
Permanent udev rule (survives reboots):
echo 'KERNEL=="icc", MODE="0666"' | sudo tee /etc/udev/rules.d/99-ps4-icc.rules
sudo udevadm control --reload-rules
sudo udevadm trigger
HARDWARE CHANNELS
-----------------
0x10 Blue
0x11 White
0x02 Orange (controlled via a 3-operation timed sequence)
Red is not achievable. There is no red LED element on any PS4 hardware revision.
PAYLOAD FORMAT (35 bytes)
--------------------------
bytes [0-3] header: 03 01 00 00
bytes [4-10] Blue channel: 10 01 02 [BLUE_VAL] 02 01 00
bytes [11-17] White channel: 11 01 02 [WHITE_VAL] 02 01 00
bytes [18-19] Orange header: 02 03
bytes [20-34] Orange program: 3 operations x 5 bytes each
Operation format (5 bytes):
byte[0] opcode: 0x01=FADE-FROM, 0x02=HOLD/FADE-TO
0x03 and 0x04 accepted but visual behavior not yet observed
byte[1] value: 0x00=off, 0xFF=full
byte[2] timing_hi
byte[3] timing_lo
byte[4] flags: 0xFF=loop forever, 0x00=one-shot, 0xBF=loop with hold
ORANGE PROGRAMS (bytes 20-34)
------------------------------
OFF 01 00 04 01 bf 02 00 05 01 ff 02 00 05 01 ff
ON 02 ff 02 01 00 02 ff 05 01 ff 02 ff 05 01 00
COMBINED 01 00 04 01 bf 02 ff 05 01 ff 02 00 05 01 ff
PULSATE 01 ff 04 01 00 02 ff 05 01 ff 02 ff 05 01 00
COLOR TABLE
-----------
off blue=0x00 white=0x00 orange=OFF
blue blue=0xFF white=0x00 orange=OFF
white blue=0x00 white=0xFF orange=OFF
white_blue blue=0xFF white=0xFF orange=OFF
orange blue=0x00 white=0x00 orange=ON
orange_blue blue=0xFF white=0x00 orange=COMBINED
orange_white blue=0x00 white=0xFF orange=COMBINED
orange_white_blue blue=0xFF white=0xFF orange=COMBINED
violet_blue blue=0x57 white=0x00 orange=ON
pink blue=0x00 white=0x30 orange=ON
pink_blue blue=0x20 white=0x00 orange=ON
pulsate_orange blue=0x00 white=0x00 orange=PULSATE
HDMI WARNING
------------
Orange channel at timing (0x00, 0x01) causes monitor dropout via voltage noise
on a shared power rail. This is a hardware electrical issue. Minimum safe
timing is (0x01, 0x01). The programs defined in this module use safe timings.
Usage:
python ps4_icc_led.py blue
python ps4_icc_led.py pulsate_orange
python ps4_icc_led.py list
"""
import struct
import ctypes
import fcntl
import sys
from ps4_icc_core import ICC_IOCTL, ICC_STRUCT, ICCError
LED_MAJOR = 0x09
LED_MINOR = 0x20
LED_PAYLOAD_LEN = 35
LED_REPLY_LEN = 0x30
ORANGE_OFF = bytes([
0x01, 0x00, 0x04, 0x01, 0xbf,
0x02, 0x00, 0x05, 0x01, 0xff,
0x02, 0x00, 0x05, 0x01, 0xff,
])
ORANGE_ON = bytes([
0x02, 0xff, 0x02, 0x01, 0x00,
0x02, 0xff, 0x05, 0x01, 0xff,
0x02, 0xff, 0x05, 0x01, 0x00,
])
ORANGE_COMBINED = bytes([
0x01, 0x00, 0x04, 0x01, 0xbf,
0x02, 0xff, 0x05, 0x01, 0xff,
0x02, 0x00, 0x05, 0x01, 0xff,
])
ORANGE_PULSATE = bytes([
0x01, 0xff, 0x04, 0x01, 0x00,
0x02, 0xff, 0x05, 0x01, 0xff,
0x02, 0xff, 0x05, 0x01, 0x00,
])
COLOR_TABLE = {
'off': (0x00, 0x00, ORANGE_OFF),
'blue': (0xFF, 0x00, ORANGE_OFF),
'white': (0x00, 0xFF, ORANGE_OFF),
'white_blue': (0xFF, 0xFF, ORANGE_OFF),
'orange': (0x00, 0x00, ORANGE_ON),
'orange_blue': (0xFF, 0x00, ORANGE_COMBINED),
'orange_white': (0x00, 0xFF, ORANGE_COMBINED),
'orange_white_blue': (0xFF, 0xFF, ORANGE_COMBINED),
'violet_blue': (0x57, 0x00, ORANGE_ON),
'pink': (0x00, 0x30, ORANGE_ON),
'pink_blue': (0x20, 0x00, ORANGE_ON),
'pulsate_orange': (0x00, 0x00, ORANGE_PULSATE),
}
def build_led_payload(blue_val, white_val, orange_program):
assert 0x00 <= blue_val <= 0xFF
assert 0x00 <= white_val <= 0xFF
assert len(orange_program) == 15
payload = (
bytes([0x03, 0x01, 0x00, 0x00]) +
bytes([0x10, 0x01, 0x02, blue_val, 0x02, 0x01, 0x00]) +
bytes([0x11, 0x01, 0x02, white_val, 0x02, 0x01, 0x00]) +
bytes([0x02, 0x03]) +
orange_program
)
assert len(payload) == LED_PAYLOAD_LEN
return payload
def _send_payload(payload):
data_buf = ctypes.create_string_buffer(bytes(payload), LED_PAYLOAD_LEN)
reply_buf = ctypes.create_string_buffer(LED_REPLY_LEN)
cmd = struct.pack(
ICC_STRUCT,
LED_MAJOR,
LED_MINOR,
ctypes.addressof(data_buf),
LED_PAYLOAD_LEN,
ctypes.addressof(reply_buf),
LED_REPLY_LEN,
)
cmd_buf = ctypes.create_string_buffer(cmd)
try:
with open('/dev/icc', 'rb') as fd:
fcntl.ioctl(fd, ICC_IOCTL, cmd_buf)
except PermissionError:
raise ICCError("Permission denied on /dev/icc. Fix: sudo chmod 666 /dev/icc")
except FileNotFoundError:
raise ICCError("/dev/icc not found. Run: cat /proc/devices | grep icc -> sudo mknod /dev/icc c <major> 0")
if reply_buf[0] != 0:
raise ICCError(f"LED command rejected: status=0x{reply_buf[0]:02X}")
return True
def set_led(color_name):
if color_name not in COLOR_TABLE:
raise ValueError(f"Unknown color '{color_name}'. Valid: {', '.join(sorted(COLOR_TABLE))}")
blue_val, white_val, orange_prog = COLOR_TABLE[color_name]
return _send_payload(build_led_payload(blue_val, white_val, orange_prog))
def set_led_custom(blue_val, white_val, orange_program=ORANGE_OFF):
return _send_payload(build_led_payload(blue_val, white_val, orange_program))
if __name__ == '__main__':
PROG_NAMES = {
bytes(ORANGE_OFF): 'ORANGE_OFF',
bytes(ORANGE_ON): 'ORANGE_ON',
bytes(ORANGE_COMBINED): 'ORANGE_COMBINED',
bytes(ORANGE_PULSATE): 'ORANGE_PULSATE',
}
if len(sys.argv) == 2 and sys.argv[1] == 'list':
for name in sorted(COLOR_TABLE):
bv, wv, op = COLOR_TABLE[name]
print(f" {name:<22s} blue=0x{bv:02X} white=0x{wv:02X} orange={PROG_NAMES.get(bytes(op), '?')}")
sys.exit(0)
if len(sys.argv) == 2:
set_led(sys.argv[1])
print(f"LED set to: {sys.argv[1]}")
sys.exit(0)
print(f"Usage: python {sys.argv[0]} <color>")
print(f" python {sys.argv[0]} list")
sys.exit(1)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment