mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Lee Jones <lee@kernel.org>
To: Liang Haowen <nbg2974@gmail.com>
Cc: linux-leds@vger.kernel.org, Pavel Machek <pavel@kernel.org>,
	"Martin K. Petersen" <mkp@kernel.org>,
	linux-scsi@vger.kernel.org, platform-driver-x86@vger.kernel.org,
	linux-kernel@vger.kernel.org,
	Denis Benato <denis.benato@linux.dev>,
	Armin Wolf <W_Armin@gmx.de>, Hans de Goede <hansg@kernel.org>,
	Ilpo Jarvinen <ilpo.jarvinen@linux.intel.com>
Subject: Re: [PATCH RFC v6 1/1] leds: asus-aura-scsi: Add ASUS Aura RGB LED driver for ROG NVMe enclosures
Date: Wed, 23 Sep 2026 10:45:39 +0100	[thread overview]
Message-ID: <20260923094539.GA3864833@google.com> (raw)
In-Reply-To: <202609162200.RFCv6-1.lhw@gmail.com>

On Wed, 16 Sep 2026, Liang Haowen wrote:

> From: Liang Haowen <nbg2974@gmail.com>
> Date: Wed, 16 Sep 2026 21:20:00 +0800
> Subject: [PATCH RFC v6] leds: asus-aura-scsi: Add ASUS Aura RGB LED driver for
>  ROG NVMe enclosures

What patch format is this?  How did you submit it?

> ASUS ROG external NVMe enclosures (ROG STRIX Arion, USB 0b05:1932) are
> plain USB mass-storage devices with no HID interface: the Aura LEDs
> hang off an ENE controller driven by vendor SCSI commands on the same
> LUN as the disk. The enclosure has 4 independently addressable LEDs,
> verified on hardware.
> 
> Register a scsi_device_handler matched by INQUIRY (vendor "ROG",

If the SCSI DH the right approach here?

I only see references to it in the SCSI subsystem and only in its own
dedicated driver.  This is all very unusual.

> model "ESD-S1C"); it does not claim the sdev (sd keeps owning the
> disk) and exposes each LED as a multicolor LED class device,
> /sys/class/leds/asus-arion-0-0-0-0::led-0 through led-3. The
> H:C:T:L part of the sdev name keeps the names unique when more than
> one enclosure is connected, with its colons flattened to dashes. The
> color section stays empty, since multicolor LEDs enumerate their
> palette via multi_intensity, and the four identical zones use the
> function name with a "-N" ordinal, as the naming section in
> Documentation/leds/leds-class.rst asks for.
> 
> Protocol: a 16-byte vendor CDB (opcode 0xec, 'A' 'S' signature,
> register index, argument count in cdb[13]). MODE 0x8021 (Static) must
> be written first in every sequence or the device ignores it; colours
> go to 0x8160 + 3 * led and 0x8100 + 3 * led (3 bytes, order R, B, G;
> both tables are written because firmware revisions pull from one or
> the other); APPLY 0x80a0 takes 0x01 to apply and 0xaa to save.
> 
> The CDB cannot go through scsi_execute_cmd(): it sizes the command
> via scsi_command_size(opcode), which maps vendor opcode 0xec to
> 10 bytes, so cdb[13] is dropped and the device silently ignores the
> write (GOOD status, no error). The request is built with
> scsi_alloc_request() instead, which initializes the scsi_cmnd parts a
> passthrough needs (command buffer, lengths, rcu head), with cmd_len
> forced to 16, mirroring what SG_IO does from userspace.
> 
> brightness_set only caches the colour and marks the LED in a per-zone
> dirty mask under a spinlock; led_mc_calc_color_components() runs under
> that lock too, because it writes the shared subled_info array and
> trigger events call led_set_brightness() without the led_access lock
> that serializes sysfs stores. A single work item per zone then
> snapshots the mask and colours and runs one ENE sequence for all
> pending LEDs (MODE, colour slots, APPLY, SAVE). Funneling every
> update through one work item keeps the sequences from interleaving
> between concurrent LED updates and batches multi-LED updates into a
> single APPLY/SAVE. A re-queued run with nothing pending returns
> before touching the device, so it cannot wear the flash with a
> pointless SAVE, and the lock keeps a colour write from being
> reordered after its dirty bit on weakly ordered architectures.
> 
> This is the monolithic out-of-tree version as verified on hardware;
> the Kconfig/Makefile/MAINTAINERS wiring lands with the agreed split
> into a SCSI transport helper and a shared ASUS Aura LED interface.
> 
> Signed-off-by: Liang Haowen <nbg2974@gmail.com>
> ---
>  drivers/leds/leds-asus-aura-scsi.c | 392 +++++++++++++++++++++++++++++

Shouldn't this live in drivers/leds/rgb ?

>  1 file changed, 392 insertions(+)
>  create mode 100644 drivers/leds/leds-asus-aura-scsi.c
> 
> diff --git a/drivers/leds/leds-asus-aura-scsi.c b/drivers/leds/leds-asus-aura-scsi.c
> new file mode 100644
> index 0000000..4e039bd
> --- /dev/null
> +++ b/drivers/leds/leds-asus-aura-scsi.c
> @@ -0,0 +1,392 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * ASUS Aura RGB over SCSI for ROG external NVMe enclosures
> + * (e.g. ROG STRIX Arion, USB 0b05:1932).
> + *
> + * USB mass-storage device, no HID; the ENE LED controller is driven via
> + * vendor SCSI commands. Matched by INQUIRY (vendor "ROG", model "ESD-S1C"),
> + * does NOT claim the sdev (sd keeps owning the disk).
> + *
> + * The Arion exposes 4 independently addressable LEDs (verified on hardware):
> + * each is a multicolor LED class device (asus-arion-<H-C-T-L>::led-0..led-3,
> + * unique per enclosure). A colour change writes that LED's slot only:
> + * EFFECT 0x8160 + 3*led, DIRECT 0x8100 + 3*led (3 bytes, byte order R, B, G),
> + * then APPLY (0x01) and SAVE (0xaa). MODE (0x8021 = Static) is written first
> + * in every sequence; skipping it makes the device ignore the whole sequence.
> + *
> + * Scheduling: brightness_set (LED core fast path) caches the colour and
> + * marks the LED in a per-zone dirty mask under a spinlock; a single work
> + * item per zone snapshots the mask and colours, then runs one ENE
> + * sequence for all pending LEDs (MODE once, colour slots, APPLY, SAVE).
> + * Funneling every update through that one work item also serializes the
> + * sequences: the MODE/colour/APPLY/SAVE chain must never interleave
> + * between concurrent LED updates. The snapshot makes re-queued runs with
> + * nothing left to do return before touching the device, so a re-queue
> + * cannot wear the flash with a pointless SAVE, and the lock keeps a
> + * colour write from being reordered after its dirty bit on weakly
> + * ordered architectures.
> + *
> + * CDB length: scsi_execute_cmd() sizes the CDB via COMMAND_SIZE(opcode),
> + * which maps vendor opcode 0xec to 10 bytes. The ENE protocol uses a 16-byte
> + * CDB with the data length in cdb[13], so scsi_execute_cmd() drops cdb[13]
> + * and the device silently ignores the write. ene_write() therefore mirrors
> + * scsi_execute_cmd() on top of scsi_alloc_request() and forces cmd_len = 16
> + * (what SG_IO does from userspace).
> + *
> + * Attach manually until a notifier lands:
> + * echo asus_aura > /sys/block/sdX/device/dh_state
> + */
> +
> +#include <linux/module.h>
> +#include <linux/bits.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +#include <linux/string.h>
> +#include <linux/leds.h>
> +#include <linux/led-class-multicolor.h>
> +#include <linux/blk_types.h>
> +#include <linux/blkdev.h>
> +#include <linux/blk-mq.h>
> +#include <linux/workqueue.h>
> +#include <scsi/scsi.h>
> +#include <scsi/scsi_cmnd.h>
> +#include <scsi/scsi_device.h>
> +#include <scsi/scsi_dh.h>

Alphabetical.

> +#define ARION_INQ_VENDOR	"ROG"
> +#define ARION_INQ_MODEL		"ESD-S1C"
> +
> +#define ENE_OPCODE		0xec
> +#define ENE_REG_MODE		0x8021	/* AuraMode value: Static=1, Breathe=2, ... */
> +#define ENE_REG_APPLY		0x80a0
> +#define ENE_REG_COLORS		0x8160	/* + 3*led, 3 bytes per LED, order R,B,G */
> +#define ENE_REG_COLORS_DIRECT	0x8100	/* + 3*led, same layout */

Not sure these comments make things any clearer?

> +#define ENE_APPLY		0x01
> +#define ENE_SAVE		0xaa
> +#define ENE_MODE_STATIC		1
> +#define ENE_CDB_LEN		16
> +#define ENE_RGB_LEN		3
> +#define ENE_TIMEOUT		(10 * HZ)

Isn't 10s a lifetime?

> +
> +/*
> + * Verified on hardware: the enclosure has 4 independently settable LEDs.
> + * (The colour table reserves 16 slots; only the first 4 drive anything.)
> + */

Do we really need to know about the trial and error during development?

> +#define ARION_NUM_LEDS		4
> +
> +struct asus_aura_led {
> +	struct asus_aura_zone	*zone;

Is 'zone' SCSI terminology?

> +	int			index;
> +	struct mc_subled	subled[3];
> +	u8			rgb[ENE_RGB_LEN];
> +};
> +
> +struct asus_aura_zone {
> +	struct scsi_device	*sdev;
> +	struct asus_aura_led	leds[ARION_NUM_LEDS];
> +	spinlock_t		lock;	/* protects dirty and cached colours */
> +	u8			dirty;	/* bit i: led i needs a colour write */

What does dirty even mean in this context?  I suggest the nomenclature
needs improvement.

If you make this an LED-level attribute, you can remove the faux
LED indexing.

> +	struct work_struct	work;
> +};
> +
> +static void ene_build_cdb(u8 *cdb, u16 reg, u8 arg_count)
> +{
> +	memset(cdb, 0, ENE_CDB_LEN);
> +	cdb[0] = ENE_OPCODE;

Would defining these offsets make sense?

Nicer to read if they have a name.

> +	cdb[1] = 'A';
> +	cdb[2] = 'S';
> +	cdb[3] = (reg >> 8) & 0xff;
> +	cdb[4] = reg & 0xff;
> +	cdb[13] = arg_count;
> +}
> +
> +/*
> + * scsi_execute_cmd() with cmd_len forced to 16. scsi_alloc_request()
> + * initializes the parts of the scsi_cmnd a passthrough needs (zeroed
> + * cmnd, cmd_len = MAX_COMMAND_SIZE, sense_len, rcu head, retries);
> + * a raw blk_mq_alloc_request() does none of that.

And you're telling us this because?

Was there a decision based off of this?  Please elaborate.

> + */
> +static int ene_write(struct scsi_device *sdev, u16 reg,
> +		     const void *data, u8 arg_count)

Data is a terrible variable name.

It's also odd that we're passing back read-data in a write() function!

> +{
> +	struct request *rq;
> +	struct scsi_cmnd *scmd;
> +	u8 cdb[ENE_CDB_LEN];
> +	int ret;
> +
> +	ene_build_cdb(cdb, reg, arg_count);
> +
> +	rq = scsi_alloc_request(sdev->request_queue, REQ_OP_DRV_OUT, 0);
> +	if (IS_ERR(rq))
> +		return PTR_ERR(rq);
> +
> +	if (arg_count) {

When is this not true?

> +		ret = blk_rq_map_kern(rq, (void *)data, arg_count, GFP_NOIO);
> +		if (ret)
> +			goto out;
> +	}
> +
> +	scmd = blk_mq_rq_to_pdu(rq);
> +	scmd->cmd_len = ENE_CDB_LEN;
> +	memcpy(scmd->cmnd, cdb, ENE_CDB_LEN);
> +	scmd->allowed = 1;
> +	rq->timeout = ENE_TIMEOUT;
> +	rq->rq_flags |= RQF_QUIET;
> +
> +	blk_execute_rq(rq, true);
> +	ret = scmd->result;

I have no idea what any of this means.  I'd need a SCSI person to help here.

> +out:
> +	blk_mq_free_request(rq);
> +	return ret;
> +}
> +
> +/*
> + * Sleepable: runs on the system workqueue. One ENE sequence for every LED
> + * marked in the dirty mask. The mask and colours are snapshotted under the
> + * zone lock: asus_aura_set() may run concurrently on another CPU, and the
> + * lock keeps a colour write from being reordered after its dirty bit on
> + * weakly ordered architectures. A colour cached while this runs requeues
> + * the work and is picked up by the next sequence.
> + */
> +static void asus_aura_zone_work(struct work_struct *work)
> +{
> +	struct asus_aura_zone *zone =
> +		container_of(work, struct asus_aura_zone, work);
> +	struct scsi_device *sdev = zone->sdev;
> +	u8 rgb[ARION_NUM_LEDS][ENE_RGB_LEN];
> +	u8 apply = ENE_APPLY;
> +	u8 save = ENE_SAVE;
> +	u8 mode = ENE_MODE_STATIC;
> +	unsigned long flags;
> +	u8 pending;
> +	int i, ret;
> +
> +	spin_lock_irqsave(&zone->lock, flags);
> +	pending = zone->dirty;
> +	zone->dirty = 0;
> +	for (i = 0; i < ARION_NUM_LEDS; i++)
> +		memcpy(rgb[i], zone->leds[i].rgb, ENE_RGB_LEN);
> +	spin_unlock_irqrestore(&zone->lock, flags);
> +
> +	/*
> +	 * schedule_work() while this function runs requeues it, and the
> +	 * pending colour may already have been consumed above; the requeued
> +	 * run then has nothing to do. Return before touching the device:
> +	 * SAVE writes its flash.
> +	 */
> +	if (!pending)
> +		return;

All of the above is pointless if (zone->dirty == 0), so why not move
this check to the top and skip all of it?

> +
> +	if (!scsi_device_online(sdev))
> +		return;
> +
> +	/* Mode first: without it the device ignores the whole sequence. */
> +	ret = ene_write(sdev, ENE_REG_MODE, &mode, 1);

Are we sure it is safe to pass stack variables (like 'mode', 'apply',
'save', and 'rgb') to 'blk_rq_map_kern()'?  Stack memory is not DMA-safe
and will cause issues with VMAP_STACK.  Should we allocate a DMA-safe
bounce buffer in 'struct asus_aura_zone' instead?

> +	if (ret)
> +		goto err;
> +
> +	for (i = 0; i < ARION_NUM_LEDS; i++) {

for (int leds = 0; ...

> +		if (!(pending & BIT(i)))

zone->leds[led]->pending ?

> +			continue;
> +
> +		ret = ene_write(sdev, ENE_REG_COLORS + i * ENE_RGB_LEN,
> +				rgb[i], ENE_RGB_LEN);

Use 100-chars to unwrap some of these.

> +		if (ret)
> +			goto err;
> +
> +		/*
> +		 * Cover the DIRECT colour set too; some firmware revisions
> +		 * pull from 0x8100 instead of 0x8160.
> +		 */
> +		ret = ene_write(sdev, ENE_REG_COLORS_DIRECT + i * ENE_RGB_LEN,
> +				rgb[i], ENE_RGB_LEN);
> +		if (ret)
> +			goto err;
> +	}
> +
> +	ret = ene_write(sdev, ENE_REG_APPLY, &apply, 1);

Are these variables even used?

> +	if (ret)
> +		goto err;
> +
> +	/*
> +	 * The change only takes effect after SAVE (0xaa). NOTE: saving on
> +	 * every brightness change writes flash each time; revisit for wear
> +	 * once confirmed.
> +	 */
> +	ret = ene_write(sdev, ENE_REG_APPLY, &save, 1);
> +	if (ret)
> +		goto err;
> +
> +	return;
> +err:
> +	dev_err(&sdev->sdev_gendev,
> +		"asus_aura: colour update failed: %d\n", ret);

Shouldn't we be telling the caller than there was an error?

> +}
> +
> +/* Non-blocking LED callback (LED core fast path). Cache colour, defer SCSI. */
> +static void asus_aura_set(struct led_classdev *cdev,
> +			  enum led_brightness brightness)

Un-wrap.

> +{
> +	struct led_classdev_mc *mc = lcdev_to_mccdev(cdev);
> +	struct asus_aura_led *led =
> +		container_of(mc, struct asus_aura_led, mc_cdev);

Un-wrap.

Etc.

> +	struct asus_aura_zone *zone = led->zone;
> +	unsigned long flags;
> +
> +	/*
> +	 * led_mc_calc_color_components() writes the shared subled_info
> +	 * array. The LED core serializes sysfs stores with led_access,
> +	 * but trigger events call led_set_brightness() without it, so
> +	 * computing and copying the components under the same lock keeps
> +	 * a trigger-driven update and a sysfs store from reading a mix of
> +	 * each other's colours.
> +	 */
> +	spin_lock_irqsave(&zone->lock, flags);
> +	led_mc_calc_color_components(mc, brightness);
> +	/* ENE colour register byte order is R, B, G. */
> +	led->rgb[0] = led->subled[0].brightness;
> +	led->rgb[1] = led->subled[2].brightness;
> +	led->rgb[2] = led->subled[1].brightness;
> +	zone->dirty |= BIT(led->index);
> +	spin_unlock_irqrestore(&zone->lock, flags);

This is all very claustrophobic.  Can you space some of these out in groups?

Other places too.  Nicely spaced out code is easier to parse.

> +
> +	schedule_work(&zone->work);
> +}
> +
> +static int asus_aura_register_led(struct asus_aura_zone *zone, int index)
> +{
> +	struct asus_aura_led *led = &zone->leds[index];
> +	struct led_classdev *cdev = &led->mc_cdev.led_cdev;
> +	char hctl[32];
> +	int ret;
> +
> +	led->zone = zone;
> +	led->index = index;
> +
> +	led->subled[0].color_index = LED_COLOR_ID_RED;
> +	led->subled[1].color_index = LED_COLOR_ID_GREEN;
> +	led->subled[2].color_index = LED_COLOR_ID_BLUE;
> +	led->mc_cdev.num_colors = 3;
> +	led->mc_cdev.subled_info = led->subled;
> +
> +	/*
> +	 * Include the sdev's H:C:T:L: every enclosure gets its own SCSI
> +	 * host, so the names stay unique when more than one is connected.
> +	 * With a static name the LED core would register the second
> +	 * enclosure's LEDs under renamed nodes (asus-arion::led-0_1),
> +	 * which is the wrong device identity. The names are per-attachment,
> +	 * like sd X letters, and userspace is expected to enumerate.
> +	 *
> +	 * dev_name() renders the sdev as H:C:T:L; the extra colons would
> +	 * break the devicename:color:function scheme userspace parses LED
> +	 * class names with, so they are flattened to dashes. The color
> +	 * section stays empty (these are multicolor LEDs, the palette is
> +	 * enumerated via multi_intensity), and the four identical zones
> +	 * get the function name with a "-N" ordinal, like the
> +	 * Documentation/leds/leds-class.rst naming section asks for.
> +	 */
> +	strscpy(hctl, dev_name(&zone->sdev->sdev_gendev), sizeof(hctl));
> +	strreplace(hctl, ':', '-');
> +	cdev->name = kasprintf(GFP_KERNEL, "asus-arion-%s::led-%d", hctl, index);
> +	if (!cdev->name)
> +		return -ENOMEM;
> +	cdev->max_brightness = 255;
> +	cdev->brightness_set = asus_aura_set;
> +
> +	led_mc_calc_color_components(&led->mc_cdev, cdev->brightness);
> +
> +	/*
> +	 * Register with NULL parent: parenting the LED to the sdev takes a
> +	 * device reference, which blocks the sdev's final release on unplug,
> +	 * which is what calls scsi_dh_release_device() -> our .detach() that
> +	 * unregisters the LEDs. That reference cycle leaked the LED nodes and
> +	 * the module refcount on every hot-unplug.
> +	 */
> +	ret = led_classdev_multicolor_register(NULL, &led->mc_cdev);
> +	if (ret)
> +		kfree(cdev->name);
> +	return ret;
> +}
> +
> +/*
> + * Unregister the LED devices before cancelling the work: unregistering
> + * removes the sysfs attributes, so no new brightness_set can schedule the
> + * zone work afterwards, and it waits for in-flight sysfs callbacks.
> + * Cancelling first would leave a window where a brightness write requeues
> + * the work after cancel_work_sync() returned, and the work would then run
> + * on freed memory.
> + */
> +static void asus_aura_release(struct asus_aura_zone *zone, int num_leds)
> +{
> +	int i;
> +
> +	for (i = 0; i < num_leds; i++)
> +		led_classdev_multicolor_unregister(&zone->leds[i].mc_cdev);
> +	cancel_work_sync(&zone->work);
> +	for (i = 0; i < num_leds; i++)
> +		kfree(zone->leds[i].mc_cdev.led_cdev.name);
> +	kfree(zone);
> +}
> +
> +static int asus_aura_attach(struct scsi_device *sdev)
> +{
> +	struct asus_aura_zone *zone;
> +	int i, ret;
> +
> +	if (strncmp(sdev->vendor, ARION_INQ_VENDOR, strlen(ARION_INQ_VENDOR)) ||
> +	    strncmp(sdev->model, ARION_INQ_MODEL, strlen(ARION_INQ_MODEL)))
> +		return SCSI_DH_DEV_UNSUPP;
> +
> +	zone = kzalloc_obj(*zone, GFP_KERNEL);
> +	if (!zone)
> +		return SCSI_DH_NOMEM;
> +	zone->sdev = sdev;
> +	spin_lock_init(&zone->lock);
> +	INIT_WORK(&zone->work, asus_aura_zone_work);
> +
> +	for (i = 0; i < ARION_NUM_LEDS; i++) {
> +		ret = asus_aura_register_led(zone, i);
> +		if (ret) {
> +			asus_aura_release(zone, i);

Shouldn't asus_aura_register_led unwind itself on failure?

> +			return SCSI_DH_NOMEM;
> +		}
> +	}
> +
> +	sdev->handler_data = zone;
> +	return SCSI_DH_OK;
> +}
> +
> +static void asus_aura_detach(struct scsi_device *sdev)
> +{
> +	struct asus_aura_zone *zone = sdev->handler_data;
> +
> +	if (!zone)
> +		return;
> +	asus_aura_release(zone, ARION_NUM_LEDS);
> +	sdev->handler_data = NULL;
> +}
> +
> +static struct scsi_device_handler asus_aura_dh = {
> +	.name	= "asus_aura",
> +	.module	= THIS_MODULE,

Are you sure this isn't handled for you by the subsystem?

> +	.attach	= asus_aura_attach,
> +	.detach	= asus_aura_detach,
> +};
> +
> +static int __init asus_aura_init(void)
> +{
> +	return scsi_register_device_handler(&asus_aura_dh);
> +}
> +
> +static void __exit asus_aura_exit(void)
> +{
> +	scsi_unregister_device_handler(&asus_aura_dh);
> +}
> +
> +module_init(asus_aura_init);
> +module_exit(asus_aura_exit);
> +
> +MODULE_DESCRIPTION("ASUS Aura RGB over SCSI for ROG NVMe enclosures (per-LED)");
> +MODULE_AUTHOR("Liang Haowen");
> +MODULE_LICENSE("GPL");
> -- 
> 2.55.0
> 
> 

-- 
Lee Jones

  parent reply	other threads:[~2026-09-23  9:45 UTC|newest]

Thread overview: 27+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-01 14:26 [PATCH RFC 0/1] leds: add ASUS Aura SCSI " Liang Haowen
2026-09-01 14:34 ` [PATCH RFC 1/1] " Liang Haowen
2026-09-03 12:00   ` [PATCH RFC v2 1/1] leds: asus-aura-scsi: Add ASUS Aura RGB LED " Liang Haowen
     [not found]     ` <20260903161357.GX2133376@google.com>
2026-09-04 12:30       ` [PATCH RFC v3 0/1] " Liang Haowen
2026-09-04 12:30         ` [PATCH RFC v3 1/1] " Liang Haowen
     [not found]           ` <20260904131503.355C41F00A3E@smtp.kernel.org>
     [not found]             ` <20260910093232.GO2133376@google.com>
2026-09-15 12:48               ` [PATCH RFC v4 0/1] " Liang Haowen
2026-09-15 12:49                 ` [PATCH RFC v4 1/1] " Liang Haowen
     [not found]                   ` <20260915130050.E8CDB1F000FF@smtp.kernel.org>
     [not found]                     ` <20260916104305.GM11487@google.com>
2026-09-16 12:15                       ` [PATCH RFC v5 0/1] " Liang Haowen
2026-09-16 12:15                         ` [PATCH RFC v5 1/1] " Liang Haowen
     [not found]                           ` <20260916122740.921E51F000FF@smtp.kernel.org>
     [not found]                             ` <20260916130602.GS11487@google.com>
2026-09-16 14:22                               ` [PATCH RFC v6 0/1] " Liang Haowen
2026-09-16 14:22                                 ` [PATCH RFC v6 1/1] " Liang Haowen
     [not found]                                   ` <20260916143832.520721F000FF@smtp.kernel.org>
     [not found]                                     ` <20260917112840.GJ1605367@google.com>
2026-09-17 11:49                                       ` Liang Haowen
2026-09-23  9:45                                   ` Lee Jones [this message]
2026-09-23 10:18                                     ` Liang Haowen
     [not found]                                       ` <20260923102812.2353292-1-nbg2974@gmail.com>
2026-09-23 10:12                                   ` Ilpo Järvinen
2026-09-16 14:23                               ` [PATCH RFC v5 " Liang Haowen
2026-09-16 12:15                       ` [PATCH RFC v4 " Liang Haowen
2026-09-15 12:49               ` [PATCH RFC v3 " Liang Haowen
2026-09-03 12:00   ` [PATCH RFC v2 0/1] " Liang Haowen
2026-09-23 10:30 [RFC v7 " Liang Haowen
2026-09-23 10:30 ` [RFC v7 1/1] " Liang Haowen
2026-09-23 10:45   ` Lee Jones
2026-09-23 11:19     ` Liang Haowen
2026-09-23 10:41 ` [RFC v7 0/1] " Ilpo Järvinen
2026-09-23 12:35   ` Denis Benato
2026-09-23 12:59     ` Liang Haowen
2026-09-23 15:11       ` Marco Scardovi

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260923094539.GA3864833@google.com \
    --to=lee@kernel.org \
    --cc=W_Armin@gmx.de \
    --cc=denis.benato@linux.dev \
    --cc=hansg@kernel.org \
    --cc=ilpo.jarvinen@linux.intel.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-leds@vger.kernel.org \
    --cc=linux-scsi@vger.kernel.org \
    --cc=mkp@kernel.org \
    --cc=nbg2974@gmail.com \
    --cc=pavel@kernel.org \
    --cc=platform-driver-x86@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®