How to set default color and brightness of LEDs of the DualShock 4 controller on Linux?

I've bought a DualShock 4 controller and don't own a PlayStation 4 console. It's recognized on my ArchLinux systems out of the box. Newer kernels support it natively.

The sole issue is that by default, once it's connected to Bluetooth, it sets its LED to blue on full brightness. Besides the light being too strong, it reduces controller's battery time a lot.

Is there a way to configure a default brightness and/or color without additional and/or alternative drivers?


Solution 1:

I've created a udev rule for this that works just fine. First it's needed to identify device attributes once the controller is connected, e.g.:

udevadm info -a -p $(udevadm info -q path -n /dev/input/js0)

This will print a bunch of useful information that can be used to create a udev rule to match the device once it's connected, and execute something.

Here, I've picked ATTRS{uniq} to indentify my controller in specific (one can also use a more general DualShock 4 match, picking ATTRS{name}=="Wireless Controller" for example):

/etc/udev/rules.d/10-local.rules

# Set First DualShock 4 LED color

ACTION=="add", SUBSYSTEM=="input", ATTRS{uniq}=="dc:0c:2d:d3:a7:0c" RUN+="/usr/local/bin/ds4led '%p' 000100"

It's possible to control LED brightness and color by writing RGB values to paths similar to the following:

echo 255 | sudo tee /sys/class/leds/0005:054C:09CC.000B:blue/brightness

The above sets my controller's blue component to maximum brightness. The 0005:054C:09CC.000B part of the path may vary per connection.

The previous udev rule executes the following script, which accepts two arguments, a path that contains a pattern like 0005:054C:09CC.000B, which it extracts to build the /sys/class/leds/0005:054C:09CC.000B:blue/brightness path, and a color in the RRGGBB form:

/usr/local/bin/ds4led (do not forget to set executable permission for this)

#!/bin/bash

RED=$((16#${2:0:2}))
GREEN=$((16#${2:2:2}))
BLUE=$((16#${2:4:2}))

LED=$(echo "$1" | egrep -o '[[:xdigit:]]{4}:[[:xdigit:]]{4}:[[:xdigit:]]{4}\.[[:xdigit:]]{4}')

[[ -z "$LED" || ! -d "/sys/class/leds/$LED:global" ]] && exit

echo 0 > /sys/class/leds/$LED:red/brightness
echo 0 > /sys/class/leds/$LED:green/brightness
echo 0 > /sys/class/leds/$LED:blue/brightness

echo $RED > /sys/class/leds/$LED:red/brightness
echo $GREEN > /sys/class/leds/$LED:green/brightness
echo $BLUE > /sys/class/leds/$LED:blue/brightness

ds4led can both be called manually like sudo ds4led /sys/class/leds/0005:054C:09CC.000B:global 0000FF or from a udev rule by passing %p as the first argument. Even though udev will pass a devpath that's completely different from /sys/class/leds/..., it'll still contain a pattern like 0005:054C:09CC.000B in it.

Solution 2:

Here's where Linux's hid-sony driver defines the default LED colors if you feel like fixing this at the source level:

https://github.com/torvalds/linux/blob/1e2a199f6ccdc15cf111d68d212e2fd4ce65682e/drivers/hid/hid-sony.c#L1944