[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID:
<CAGwozwGAFqbiFGdgrKOwc8rGBxQ47T7_vw=28R6FCxwn3MWDaQ@mail.gmail.com>
Date: Sun, 23 Mar 2025 15:45:59 +0100
From: Antheas Kapenekakis <lkml@...heas.dev>
To: "Luke D. Jones" <luke@...nes.dev>
Cc: platform-driver-x86@...r.kernel.org, linux-input@...r.kernel.org,
linux-kernel@...r.kernel.org, Jiri Kosina <jikos@...nel.org>,
Benjamin Tissoires <bentiss@...nel.org>,
Corentin Chary <corentin.chary@...il.com>,
Hans de Goede <hdegoede@...hat.com>,
Ilpo Järvinen <ilpo.jarvinen@...ux.intel.com>
Subject: Re: [PATCH v3 09/10] HID: asus: add basic RGB support
On Sun, 23 Mar 2025 at 12:37, Antheas Kapenekakis <lkml@...heas.dev> wrote:
>
> On Sun, 23 Mar 2025 at 07:40, Luke D. Jones <luke@...nes.dev> wrote:
> >
> > On 22/03/25 23:28, Antheas Kapenekakis wrote:
> > > Adds basic RGB support to hid-asus through multi-index. The interface
> > > works quite well, but has not gone through much stability testing.
> > > Applied on demand, if userspace does not touch the RGB sysfs, not
> > > even initialization is done. Ensuring compatibility with existing
> > > userspace programs.
> > >
> > > Signed-off-by: Antheas Kapenekakis <lkml@...heas.dev>
> > > ---
> > > drivers/hid/hid-asus.c | 169 +++++++++++++++++++++++++++++++++++++----
> > > 1 file changed, 155 insertions(+), 14 deletions(-)
> > >
> > > diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c
> > > index 905453a4eb5b7..9d8ccfde5912e 100644
> > > --- a/drivers/hid/hid-asus.c
> > > +++ b/drivers/hid/hid-asus.c
> > > @@ -30,6 +30,7 @@
> > > #include <linux/input/mt.h>
> > > #include <linux/usb.h> /* For to_usb_interface for T100 touchpad intf check */
> > > #include <linux/power_supply.h>
> > > +#include <linux/led-class-multicolor.h>
> > > #include <linux/leds.h>
> > >
> > > #include "hid-ids.h"
> > > @@ -85,6 +86,7 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
> > > #define QUIRK_ROG_NKEY_KEYBOARD BIT(11)
> > > #define QUIRK_ROG_CLAYMORE_II_KEYBOARD BIT(12)
> > > #define QUIRK_HANDLE_GENERIC BIT(13)
> > > +#define QUIRK_ROG_NKEY_RGB BIT(14)
> > >
> > > #define I2C_KEYBOARD_QUIRKS (QUIRK_FIX_NOTEBOOK_REPORT | \
> > > QUIRK_NO_INIT_REPORTS | \
> > > @@ -97,9 +99,15 @@ MODULE_DESCRIPTION("Asus HID Keyboard and TouchPad");
> > >
> > > struct asus_kbd_leds {
> > > struct asus_hid_listener listener;
> > > + struct led_classdev_mc mc_led;
> > > + struct mc_subled subled_info[3];
> > > struct hid_device *hdev;
> > > struct work_struct work;
> > > unsigned int brightness;
> > > + uint8_t rgb_colors[3];
> > > + bool rgb_init;
> > > + bool rgb_set;
> > > + bool rgb_registered;
> > > spinlock_t lock;
> > > bool removed;
> > > };
> > > @@ -504,23 +512,67 @@ static void asus_schedule_work(struct asus_kbd_leds *led)
> > > spin_unlock_irqrestore(&led->lock, flags);
> > > }
> > >
> > > -static void asus_kbd_backlight_set(struct asus_hid_listener *listener,
> > > +static void do_asus_kbd_backlight_set(struct asus_kbd_leds *led, int brightness)
> > > +{
> > > + unsigned long flags;
> > > +
> > > + spin_lock_irqsave(&led->lock, flags);
> > > + led->brightness = brightness;
> > > + spin_unlock_irqrestore(&led->lock, flags);
> > > +
> > > + asus_schedule_work(led);
> > > +}
> > > +
> > > +static void asus_kbd_listener_set(struct asus_hid_listener *listener,
> > > int brightness)
> > > {
> > > struct asus_kbd_leds *led = container_of(listener, struct asus_kbd_leds,
> > > listener);
> > > + do_asus_kbd_backlight_set(led, brightness);
> > > + if (led->rgb_registered) {
> > > + led->mc_led.led_cdev.brightness = brightness;
> > > + led_classdev_notify_brightness_hw_changed(&led->mc_led.led_cdev,
> > > + brightness);
> > > + }
> > > +}
> > > +
> > > +static void asus_kbd_brightness_set(struct led_classdev *led_cdev,
> > > + enum led_brightness brightness)
> > > +{
> > > + struct led_classdev_mc *mc_cdev = lcdev_to_mccdev(led_cdev);
> > > + struct asus_kbd_leds *led = container_of(mc_cdev, struct asus_kbd_leds,
> > > + mc_led);
> > > unsigned long flags;
> > >
> > > spin_lock_irqsave(&led->lock, flags);
> > > - led->brightness = brightness;
> > > + led->rgb_colors[0] = mc_cdev->subled_info[0].intensity;
> > > + led->rgb_colors[1] = mc_cdev->subled_info[1].intensity;
> > > + led->rgb_colors[2] = mc_cdev->subled_info[2].intensity;
> > > + led->rgb_set = true;
> > > spin_unlock_irqrestore(&led->lock, flags);
> > >
> > > - asus_schedule_work(led);
> > > + do_asus_kbd_backlight_set(led, brightness);
> > > +}
> > > +
> > > +static enum led_brightness asus_kbd_brightness_get(struct led_classdev *led_cdev)
> > > +{
> > > + struct led_classdev_mc *mc_led;
> > > + struct asus_kbd_leds *led;
> > > + enum led_brightness brightness;
> > > + unsigned long flags;
> > > +
> > > + mc_led = lcdev_to_mccdev(led_cdev);
> > > + led = container_of(mc_led, struct asus_kbd_leds, mc_led);
> > > +
> > > + spin_lock_irqsave(&led->lock, flags);
> > > + brightness = led->brightness;
> > > + spin_unlock_irqrestore(&led->lock, flags);
> > > +
> > > + return brightness;
> > > }
> > >
> > > -static void asus_kbd_backlight_work(struct work_struct *work)
> > > +static void asus_kbd_backlight_work(struct asus_kbd_leds *led)
> > > {
> > > - struct asus_kbd_leds *led = container_of(work, struct asus_kbd_leds, work);
> > > u8 buf[] = { FEATURE_KBD_REPORT_ID, 0xba, 0xc5, 0xc4, 0x00 };
> > > int ret;
> > > unsigned long flags;
> > > @@ -534,10 +586,69 @@ static void asus_kbd_backlight_work(struct work_struct *work)
> > > hid_err(led->hdev, "Asus failed to set keyboard backlight: %d\n", ret);
> > > }
> > >
> > > +static void asus_kbd_rgb_work(struct asus_kbd_leds *led)
> > > +{
> > > + u8 rgb_buf[][7] = {
> > > + { FEATURE_KBD_LED_REPORT_ID1, 0xB3 }, /* set mode */
> > > + { FEATURE_KBD_LED_REPORT_ID1, 0xB5 }, /* apply mode */
> > > + { FEATURE_KBD_LED_REPORT_ID1, 0xB4 }, /* save to mem */
> > > + };
> > > + unsigned long flags;
> > > + uint8_t colors[3];
> > > + bool rgb_init, rgb_set;
> > > + int ret;
> > > +
> > > + spin_lock_irqsave(&led->lock, flags);
> > > + rgb_init = led->rgb_init;
> > > + rgb_set = led->rgb_set;
> > > + led->rgb_set = false;
> > > + colors[0] = led->rgb_colors[0];
> > > + colors[1] = led->rgb_colors[1];
> > > + colors[2] = led->rgb_colors[2];
> > > + spin_unlock_irqrestore(&led->lock, flags);
> > > +
> > > + if (!rgb_set)
> > > + return;
> > > +
> > > + if (rgb_init) {
> > > + ret = asus_kbd_init(led->hdev, FEATURE_KBD_LED_REPORT_ID1);
> > > + if (ret < 0) {
> > > + hid_err(led->hdev, "Asus failed to init RGB: %d\n", ret);
> > > + return;
> > > + }
> > > + spin_lock_irqsave(&led->lock, flags);
> > > + led->rgb_init = false;
> > > + spin_unlock_irqrestore(&led->lock, flags);
> > > + }
> > > +
> > > + /* Protocol is: 54b3 zone (0=all) mode (0=solid) RGB */
> > > + rgb_buf[0][4] = colors[0];
> > > + rgb_buf[0][5] = colors[1];
> > > + rgb_buf[0][6] = colors[2];
> > > +
> > > + for (size_t i = 0; i < ARRAY_SIZE(rgb_buf); i++) {
> > > + ret = asus_kbd_set_report(led->hdev, rgb_buf[i], sizeof(rgb_buf[i]));
> > > + if (ret < 0) {
> > > + hid_err(led->hdev, "Asus failed to set RGB: %d\n", ret);
> > > + return;
> > > + }
> > > + }
> > > +}
> > > +
> > > +static void asus_kbd_work(struct work_struct *work)
> > > +{
> > > + struct asus_kbd_leds *led = container_of(work, struct asus_kbd_leds,
> > > + work);
> > > + asus_kbd_backlight_work(led);
> > > + asus_kbd_rgb_work(led);
> > > +}
> > > +
> > > static int asus_kbd_register_leds(struct hid_device *hdev)
> > > {
> > > struct asus_drvdata *drvdata = hid_get_drvdata(hdev);
> > > unsigned char kbd_func;
> > > + struct asus_kbd_leds *leds;
> > > + bool no_led;
> > > int ret;
> > >
> > > ret = asus_kbd_init(hdev, FEATURE_KBD_REPORT_ID);
> > > @@ -565,21 +676,51 @@ static int asus_kbd_register_leds(struct hid_device *hdev)
> > > if (!drvdata->kbd_backlight)
> > > return -ENOMEM;
> > >
> > > - drvdata->kbd_backlight->removed = false;
> > > - drvdata->kbd_backlight->brightness = 0;
> > > - drvdata->kbd_backlight->hdev = hdev;
> > > - drvdata->kbd_backlight->listener.brightness_set = asus_kbd_backlight_set;
> > > - INIT_WORK(&drvdata->kbd_backlight->work, asus_kbd_backlight_work);
> > > + leds = drvdata->kbd_backlight;
> > > + leds->removed = false;
> > > + leds->brightness = 3;
> > > + leds->hdev = hdev;
> > > + leds->listener.brightness_set = asus_kbd_listener_set;
> > > +
> > > + leds->rgb_colors[0] = 0;
> > > + leds->rgb_colors[1] = 0;
> > > + leds->rgb_colors[2] = 0;
> > > + leds->rgb_init = true;
> > > + leds->rgb_set = false;
> > > + leds->mc_led.led_cdev.name = devm_kasprintf(&hdev->dev, GFP_KERNEL,
> > > + "asus-%s-led",
> > > + strlen(hdev->uniq) ?
> > > + hdev->uniq : dev_name(&hdev->dev));
> >
> > A quick note. This breaks convention for LED names. The style guide is
> > at Documentation/leds/leds-class.rst. Per my parallel email to this one
> > I would like to see the mentioned naming scheme `asus:rgb:kbd_backlight`
> > adopted.
>
> Perhaps. It would be the first kbd_backlight driver to have "rgb" in
> it. It is a bit out of scope for this series as I do not touch the
> functionality of it but I can add a patch for it and a fixes
> e305a71cea37a64c75 tag.
>
> > Expanding further on one of the points there you might need to
> > move the led_classdev_mc in to asus-wmi to fulfil having the single
> > sysfs endpoint. Since you're using the listner pattern it shouldn't be
> > much work.
>
> I only want the brightness to sync, not the color. Only the brightness
> between Aura devices needs to be the same. In this case
> asus::kbd_backlight if it has a color controls the wmi color, and the
> asus- devices control the usb.
>
> Also, groups are not dynamic so this is not possible. E.g., if you
> setup a WMI listener that does not have RGB, and then the USB keyboard
> connects you can no longer change the groups unless you reconnect the
> device.
Sorry, I confused the patches. Yes you are right. I cannot do
kbd_backlight because userspace might pick the wrong handler. And with
this patch series on the Z13 there are 3, one for the common
backlight, one for the keyboard, and one for the lightbar.
I can do asus-UNIQ:rgb:peripheral though. There is no appropriate
function that is close enough currently. Also, since there can be
multiple devices, UNIQ or something similar needs to be added,
otherwise the led handler will suffix _X.
Antheas
> > > + leds->mc_led.led_cdev.flags = LED_BRIGHT_HW_CHANGED;
> > > + leds->mc_led.led_cdev.max_brightness = 3,
> > > + leds->mc_led.led_cdev.brightness_set = asus_kbd_brightness_set,
> > > + leds->mc_led.led_cdev.brightness_get = asus_kbd_brightness_get,
> > > + leds->mc_led.subled_info = leds->subled_info,
> > > + leds->mc_led.num_colors = ARRAY_SIZE(leds->subled_info),
> > > + leds->subled_info[0].color_index = LED_COLOR_ID_RED;
> > > + leds->subled_info[1].color_index = LED_COLOR_ID_GREEN;
> > > + leds->subled_info[2].color_index = LED_COLOR_ID_BLUE;
> > > +
> > > + INIT_WORK(&drvdata->kbd_backlight->work, asus_kbd_work);
> > > spin_lock_init(&drvdata->kbd_backlight->lock);
> > >
> > > ret = asus_hid_register_listener(&drvdata->kbd_backlight->listener);
> > > + no_led = !!ret;
> > > +
> > > + if (drvdata->quirks & QUIRK_ROG_NKEY_RGB) {
> > > + ret = devm_led_classdev_multicolor_register(
> > > + &hdev->dev, &leds->mc_led);
> > > + if (!ret)
> > > + leds->rgb_registered = true;
> > > + no_led &= !!ret;
> > > + }
> > >
> > > - if (ret < 0) {
> > > + if (no_led) {
> > > /* No need to have this still around */
> > > devm_kfree(&hdev->dev, drvdata->kbd_backlight);
> > > }
> > >
> > > - return ret;
> > > + return no_led ? -ENODEV : 0;
> > > }
> > >
> > > /*
> > > @@ -1289,7 +1430,7 @@ static const struct hid_device_id asus_devices[] = {
> > > QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD },
> > > { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
> > > USB_DEVICE_ID_ASUSTEK_ROG_Z13_LIGHTBAR),
> > > - QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD },
> > > + QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD | QUIRK_ROG_NKEY_RGB },
> > > { HID_USB_DEVICE(USB_VENDOR_ID_ASUSTEK,
> > > USB_DEVICE_ID_ASUSTEK_ROG_NKEY_ALLY),
> > > QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD },
> > > @@ -1318,7 +1459,7 @@ static const struct hid_device_id asus_devices[] = {
> > > */
> > > { HID_DEVICE(BUS_USB, HID_GROUP_GENERIC,
> > > USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_ROG_Z13_FOLIO),
> > > - QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD },
> > > + QUIRK_USE_KBD_BACKLIGHT | QUIRK_ROG_NKEY_KEYBOARD | QUIRK_ROG_NKEY_RGB },
> > > { HID_DEVICE(BUS_USB, HID_GROUP_GENERIC,
> > > USB_VENDOR_ID_ASUSTEK, USB_DEVICE_ID_ASUSTEK_T101HA_KEYBOARD) },
> > > { }
> >
Powered by blists - more mailing lists