From: "Luke D. Jones" luke@ljones.dev
commit 3fdcf7cdfc229346d028242e73562704ad644dd0 upstream
Remove the early return on LED brightness set so that any controller application, daemon, or desktop may set the same brightness at any stage.
This is required because many ASUS ROG keyboards will default to max brightness on laptop resume if the LEDs were set to off before sleep.
Signed-off-by: Luke D Jones luke@ljones.dev Signed-off-by: Jiri Kosina jkosina@suse.cz Signed-off-by: Stefan Ghinea stefan.ghinea@windriver.com --- drivers/hid/hid-asus.c | 3 --- 1 file changed, 3 deletions(-)
diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c index 7f84ed0afdfe..d3aca8f80de4 100644 --- a/drivers/hid/hid-asus.c +++ b/drivers/hid/hid-asus.c @@ -351,9 +351,6 @@ static void asus_kbd_backlight_set(struct led_classdev *led_cdev, { struct asus_kbd_leds *led = container_of(led_cdev, struct asus_kbd_leds, cdev); - if (led->brightness == brightness) - return; - led->brightness = brightness; schedule_work(&led->work); }
From: Pietro Borrello borrello@diag.uniroma1.it
commit 315c537068a13f0b5681d33dd045a912f4bece6f upstream
asus driver has a worker that may access data concurrently. Proct the accesses using a spinlock.
Fixes: af22a610bc38 ("HID: asus: support backlight on USB keyboards") Signed-off-by: Pietro Borrello borrello@diag.uniroma1.it Link: https://lore.kernel.org/r/20230125-hid-unregister-leds-v4-4-7860c5763c38@dia... Signed-off-by: Benjamin Tissoires benjamin.tissoires@redhat.com Signed-off-by: Stefan Ghinea stefan.ghinea@windriver.com --- drivers/hid/hid-asus.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-)
diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c index d3aca8f80de4..695151522e39 100644 --- a/drivers/hid/hid-asus.c +++ b/drivers/hid/hid-asus.c @@ -92,6 +92,7 @@ struct asus_kbd_leds { struct hid_device *hdev; struct work_struct work; unsigned int brightness; + spinlock_t lock; bool removed; };
@@ -351,7 +352,12 @@ static void asus_kbd_backlight_set(struct led_classdev *led_cdev, { struct asus_kbd_leds *led = container_of(led_cdev, struct asus_kbd_leds, cdev); + unsigned long flags; + + spin_lock_irqsave(&led->lock, flags); led->brightness = brightness; + spin_unlock_irqrestore(&led->lock, flags); + schedule_work(&led->work); }
@@ -359,8 +365,14 @@ static enum led_brightness asus_kbd_backlight_get(struct led_classdev *led_cdev) { struct asus_kbd_leds *led = container_of(led_cdev, struct asus_kbd_leds, cdev); + enum led_brightness brightness; + unsigned long flags;
- return led->brightness; + 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) @@ -368,11 +380,14 @@ static void asus_kbd_backlight_work(struct work_struct *work) 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;
if (led->removed) return;
+ spin_lock_irqsave(&led->lock, flags); buf[4] = led->brightness; + spin_unlock_irqrestore(&led->lock, flags);
ret = asus_kbd_set_report(led->hdev, buf, sizeof(buf)); if (ret < 0) @@ -434,6 +449,7 @@ static int asus_kbd_register_leds(struct hid_device *hdev) drvdata->kbd_backlight->cdev.brightness_set = asus_kbd_backlight_set; drvdata->kbd_backlight->cdev.brightness_get = asus_kbd_backlight_get; INIT_WORK(&drvdata->kbd_backlight->work, asus_kbd_backlight_work); + spin_lock_init(&drvdata->kbd_backlight->lock);
ret = devm_led_classdev_register(&hdev->dev, &drvdata->kbd_backlight->cdev); if (ret < 0) { @@ -932,9 +948,13 @@ static int asus_probe(struct hid_device *hdev, const struct hid_device_id *id) static void asus_remove(struct hid_device *hdev) { struct asus_drvdata *drvdata = hid_get_drvdata(hdev); + unsigned long flags;
if (drvdata->kbd_backlight) { + spin_lock_irqsave(&drvdata->kbd_backlight->lock, flags); drvdata->kbd_backlight->removed = true; + spin_unlock_irqrestore(&drvdata->kbd_backlight->lock, flags); + cancel_work_sync(&drvdata->kbd_backlight->work); }
From: Pietro Borrello borrello@diag.uniroma1.it
commit 4ab3a086d10eeec1424f2e8a968827a6336203df upstream
Use spinlocks to deal with workers introducing a wrapper asus_schedule_work(), and several spinlock checks. Otherwise, asus_kbd_backlight_set() may schedule led->work after the structure has been freed, causing a use-after-free.
Fixes: af22a610bc38 ("HID: asus: support backlight on USB keyboards") Signed-off-by: Pietro Borrello borrello@diag.uniroma1.it Link: https://lore.kernel.org/r/20230125-hid-unregister-leds-v4-5-7860c5763c38@dia... Signed-off-by: Benjamin Tissoires benjamin.tissoires@redhat.com Signed-off-by: Stefan Ghinea stefan.ghinea@windriver.com --- drivers/hid/hid-asus.c | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-)
diff --git a/drivers/hid/hid-asus.c b/drivers/hid/hid-asus.c index 695151522e39..e1e27d87c86b 100644 --- a/drivers/hid/hid-asus.c +++ b/drivers/hid/hid-asus.c @@ -347,6 +347,16 @@ static int asus_kbd_get_functions(struct hid_device *hdev, return ret; }
+static void asus_schedule_work(struct asus_kbd_leds *led) +{ + unsigned long flags; + + spin_lock_irqsave(&led->lock, flags); + if (!led->removed) + schedule_work(&led->work); + spin_unlock_irqrestore(&led->lock, flags); +} + static void asus_kbd_backlight_set(struct led_classdev *led_cdev, enum led_brightness brightness) { @@ -358,7 +368,7 @@ static void asus_kbd_backlight_set(struct led_classdev *led_cdev, led->brightness = brightness; spin_unlock_irqrestore(&led->lock, flags);
- schedule_work(&led->work); + asus_schedule_work(led); }
static enum led_brightness asus_kbd_backlight_get(struct led_classdev *led_cdev) @@ -382,9 +392,6 @@ static void asus_kbd_backlight_work(struct work_struct *work) int ret; unsigned long flags;
- if (led->removed) - return; - spin_lock_irqsave(&led->lock, flags); buf[4] = led->brightness; spin_unlock_irqrestore(&led->lock, flags);
linux-stable-mirror@lists.linaro.org