Linux Input/HID development
 help / color / mirror / Atom feed
* Re: [PATCH] HID: lenovo: Fix buffer over-read and unaligned access in X12 Tab raw_event handler
From: Benjamin Tissoires @ 2026-05-21 15:23 UTC (permalink / raw)
  To: sashiko-bot, derekjohn.clark, Kean
  Cc: linux-input, mpearson-lenovo, jikos, linux-kernel
In-Reply-To: <20260514125838.3307386-1-rh_king@163.com>

On Thu, 14 May 2026 20:58:38 +0800, Kean wrote:
> In lenovo_raw_event(), the X12 Tab keyboard handler reads a 4-byte
> little-endian value from the raw HID report buffer but:
> 
>   1. The size guard is size >= 3, while the access reads 4 bytes.
>      A malformed 3-byte report with ID 0x03 would over-read the
>      buffer by one byte.
> 
> [...]

Applied to https://git.kernel.org/pub/scm/linux/kernel/git/hid/hid.git (for-7.1/upstream-fixes), thanks!

[1/1] HID: lenovo: Fix buffer over-read and unaligned access in X12 Tab raw_event handler
      https://git.kernel.org/hid/hid/c/c7ee0b73c8c4

Cheers,
-- 
Benjamin Tissoires <bentiss@kernel.org>


^ permalink raw reply

* Re: [PATCH v2 1/2] HID: lenovo-go: reject non-USB transports in probe
From: Benjamin Tissoires @ 2026-05-21 15:22 UTC (permalink / raw)
  To: sashiko-reviews; +Cc: Louis Clinckx, linux-input, dmitry.torokhov
In-Reply-To: <20260515152059.D38D6C2BCB0@smtp.kernel.org>

On May 15 2026, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 6 potential issue(s) to consider:
> - [High] This problem wasn't introduced by this patch, but both drivers use a static global variable (`drvdata`) to store device state rather than allocating it per-device.
> - [High] This problem wasn't introduced by this patch, but multicolor LED configuration structs (`go_cdev_rgb` and `gos_cdev_rgb`) are declared as static global variables but are passed to `devm_led_classdev_multicolor_register()`.
> - [High] This problem wasn't introduced by this patch, but in `hid-lenovo-go.c`, the removal function `hid_go_cfg_remove()` fails to cancel the delayed workqueue `go_cfg_setup`.
> - [High] This problem wasn't introduced by this patch, but probe failures do not correctly unwind started hardware states and sysfs groups.
> - [High] This problem wasn't introduced by this patch, but a NULL pointer is passed to `memcpy` in `mcu_property_out()` when performing `GET_*` property requests.
> - [High] This problem wasn't introduced by this patch, but completion synchronization is broken in `mcu_property_out()` due to incorrect placement of `reinit_completion()`.
> --
> 
> commit 7a455849d3633575795dff10836095e31a4bbca3
> Author: Louis Clinckx <clinckx.louis@gmail.com>
> 
> HID: lenovo-go: reject non-USB transports in probe
> 
> These drivers only match HID_USB_DEVICE() entries and assume the
> underlying bus is USB. Make that explicit at probe by rejecting any
> non-USB hdev.
> 
> > diff --git a/drivers/hid/hid-lenovo-go-s.c b/drivers/hid/hid-lenovo-go-s.c
> > index 01c7bdd4fbe04..abf477e680428 100644
> > --- a/drivers/hid/hid-lenovo-go-s.c
> > +++ b/drivers/hid/hid-lenovo-go-s.c
> > @@ -1432,6 +1432,9 @@ static int hid_gos_probe(struct hid_device *hdev,
> >  {
> >  	int ret, ep;
> >  
> > +	if (!hid_is_usb(hdev))
> > +		return -EINVAL;
> > +
> 
> [ ... ]
> 
> >  	ret = hid_gos_cfg_probe(hdev, id);
> >  	if (ret)
> >  		dev_err_probe(&hdev->dev, ret, "Failed to start configuration interface");
> 
> This isn't a regression from this patch, but do these probe failures
> leave the hardware state active?
> 
> If hid_gos_cfg_probe() fails, the code returns the error immediately without
> calling hid_hw_close(hdev) and hid_hw_stop(hdev).
> 
> Additionally, does sysfs_create_groups() need to be rolled back if later
> steps in the config probe fail?
> 
> >  	dev_dbg(&hdev->dev, "Started interface %x as Go S configuration interface\n", ep);
> >  	return ret;
> >  }
> 
> [ ... ]
> 
> > diff --git a/drivers/hid/hid-lenovo-go.c b/drivers/hid/hid-lenovo-go.c
> > index d4d26c7833563..3fa1fe83f7e5e 100644
> > --- a/drivers/hid/hid-lenovo-go.c
> > +++ b/drivers/hid/hid-lenovo-go.c
> > @@ -2419,6 +2419,9 @@ static int hid_go_probe(struct hid_device *hdev, const struct hid_device_id *id)
> >  {
> >  	int ret, ep;
> >  
> > +	if (!hid_is_usb(hdev))
> > +		return -EINVAL;
> > +
> 
> [ ... ]
> 
> >  	ret = hid_go_cfg_probe(hdev, id);
> >  	if (ret)
> >  		dev_err_probe(&hdev->dev, ret, "Failed to start configuration interface\n");
> 
> This isn't a regression from this patch, but does this early return
> bypass hid_hw_close(hdev) and hid_hw_stop(hdev) as well?
> 
> >  	dev_dbg(&hdev->dev, "Started Legion Go HID Device: %x\n", ep);
> >  
> >  	return ret;
> >  }
> 
> [ ... ]
> 
> This isn't a regression from this patch, but does the use of a static
> global drvdata struct cause data races when multiple devices are connected?

I suspect this is relatively safe because of the nature of the device: a
handled one so the chances that 2 USB devices using the same driver are
low, but it would be nice to address sashiko's comments in a followup
series. Derek, could you have a depper look, please?

Cheers,
Benjamin

> 
> Since drvdata is shared, functions like mutex_init() and init_completion()
> called during probe() would re-execute on the same memory space. Could this
> cause type confusion over drvdata.hdev and memory corruption from concurrent
> sysfs accesses or input events?
> 
> This isn't a regression from this patch, but do the static global
> go_cdev_rgb and gos_cdev_rgb structs corrupt the kernel's global leds_list?
> 
> The led framework embeds a struct list_head into the led_classdev structure.
> If devm_led_classdev_multicolor_register() is called for multiple devices
> using the same static struct, it would double-add the list node.
> 
> Would unbinding one device then unlink the shared struct, leading to a
> use-after-free when the other device is later accessed or removed?
> 
> This isn't a regression from this patch, but does hid_go_cfg_remove()
> need to cancel the go_cfg_setup delayed workqueue?
> 
> If the controller is rapidly unplugged after hid_go_cfg_probe() schedules
> drvdata.go_cfg_setup, hid_go_cfg_remove() will tear down the device.
> Without calling cancel_delayed_work_sync(), could the workqueue execute
> and dereference the freed hdev pointer in mcu_property_out()?
> 
> This isn't a regression from this patch, but does mcu_property_out() pass
> a NULL pointer to memcpy() during get property commands?
> 
> Callers intentionally pass NULL as the data pointer and 0 as len for these
> commands. Since passing NULL to memcpy() is undefined behavior even when
> len is 0, could this trigger sanitizer violations?
> 
> This isn't a regression from this patch, but is the completion
> synchronization in mcu_property_out() susceptible to spontaneous hardware
> events?
> 
> Because reinit_completion() is called at the end of mcu_property_out(), if
> hid_go_raw_event() invokes complete() due to spontaneous events or multiple
> responses, the completion object could be left in a signaled state.
> 
> Would the next call to wait_for_completion_interruptible_timeout() then
> return instantly and provide stale or uninitialized memory to user-space?
> 
> -- 
> Sashiko AI review · https://sashiko.dev/#/patchset/cover.1778701376.git.clinckx.louis@gmail.com?part=1
> 

^ permalink raw reply

* Re: [PATCH] Input: wacom_w8001 - avoid double release of pen input device
From: Benjamin Tissoires @ 2026-05-21 15:02 UTC (permalink / raw)
  To: Guangshuo Li
  Cc: Dmitry Torokhov, Kees Cook, Peter Hutterer, linux-input,
	linux-kernel
In-Reply-To: <20260430071311.451957-1-lgs201920130244@gmail.com>

On Apr 30 2026, Guangshuo Li wrote:
> When registering the touch input device fails after the pen input device
> has already been registered, w8001_connect() jumps to fail4 and
> unregisters w8001->pen_dev.  It then falls through to fail1 where
> input_dev_pen is passed to input_free_device().
> 
> Once input_register_device() has succeeded, the device must be released
> with input_unregister_device(), and input_free_device() must not be used
> on the same object afterwards.  Since input_dev_pen still aliases
> w8001->pen_dev, this can result in a use-after-free or kref underflow.
> 
> Clear the local and container aliases after unregistering the pen device
> so that the common cleanup path does not try to free it again.
> 
> This issue was found by a static analysis tool I am developing.
> 
> Fixes: e0361b70175f0 ("Input: wacom_w8001 - split the touch and pen devices into two devices")
> Signed-off-by: Guangshuo Li <lgs201920130244@gmail.com>

Reviewed-by: Benjamin Tissoires <bentiss@kernel.org>

Cheers,
Benjamin

> ---
>  drivers/input/touchscreen/wacom_w8001.c | 5 ++++-
>  1 file changed, 4 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/input/touchscreen/wacom_w8001.c b/drivers/input/touchscreen/wacom_w8001.c
> index 45930d731873..a3b283c59cdd 100644
> --- a/drivers/input/touchscreen/wacom_w8001.c
> +++ b/drivers/input/touchscreen/wacom_w8001.c
> @@ -665,8 +665,11 @@ static int w8001_connect(struct serio *serio, struct serio_driver *drv)
>  	return 0;
>  
>  fail4:
> -	if (w8001->pen_dev)
> +	if (w8001->pen_dev) {
>  		input_unregister_device(w8001->pen_dev);
> +		input_dev_pen = NULL;
> +		w8001->pen_dev = NULL;
> +	}
>  fail3:
>  	serio_close(serio);
>  fail2:
> -- 
> 2.43.0
> 
> 

^ permalink raw reply

* Re: [PATCH] HID: quirks: Add ALWAYS_POLL quirk for SIGMACHIP USB mouse
From: Benjamin Tissoires @ 2026-05-21 14:52 UTC (permalink / raw)
  To: jikos, hlleng; +Cc: linux-input, linux-kernel, stable
In-Reply-To: <20260512015737.8919-1-a909204013@gmail.com>

On Tue, 12 May 2026 09:57:37 +0800, hlleng wrote:
> The SIGMACHIP USB mouse with VID/PID 1c4f:0034 can disconnect and
> re-enumerate repeatedly after it has been enumerated if its interrupt
> endpoint is not continuously polled.
> 
> This was observed with the device reporting itself as "SIGMACHIP Usb
> Mouse". Keeping the input event device open avoids the disconnects.
> 
> [...]

Applied to https://git.kernel.org/pub/scm/linux/kernel/git/hid/hid.git (for-7.1/upstream-fixes), thanks!

[1/1] HID: quirks: Add ALWAYS_POLL quirk for SIGMACHIP USB mouse
      https://git.kernel.org/hid/hid/c/07466fc91c55

Cheers,
-- 
Benjamin Tissoires <bentiss@kernel.org>


^ permalink raw reply

* Re: [PATCH 00/11] HID: storing pointers in 'hid_device_id::driver_data'
From: Benjamin Tissoires @ 2026-05-21 14:50 UTC (permalink / raw)
  To: Pawel Zalewski (The Capable Hub)
  Cc: Jiri Kosina, Ping Cheng, Jason Gerecke, linux-kernel, linux-input,
	Christian A. Ehrhardt, Christian A. Ehrhardt
In-Reply-To: <20260518-mod-devicetable-hid_device_id-v1-0-a08e3989c283@thegoodpenguin.co.uk>

On May 18 2026, Pawel Zalewski (The Capable Hub) wrote:
> The <linux/mod_devicetable.h> has multiple structs that follow
> the pattern of having either 'driver_data' or 'driver_info'
> fields which are of the 'kernel_ulong_t' type. Then how to 
> interpret that field is user defined, some users will treat
> the value as an actual integer, others as a valid pointer to
> dereference. 
> 
> One of instances of the above is the 'hid_device_id::driver_data'
> field, for the most part it is used for setting HID quirks and
> treated as an integer value for storing metadata in the subsystem
> drivers. But in a few instances it is used as a valid pointer to
> dereference, namely in:
> - hid-tmff
> - wacom

I would agree with sashiko that the series introduces an anti-pattern by
allowing .driver_data to be an arbitrary pointer. The reason is that we
can dynamically set driver_data through the kernel command line, and
when it's used as a pointer, bad things will happen.

I would think we should fix the 2 offenders to enforce not using a
direct pointer but an offset in a table of pointers.

How does that sound for you?

Cheers,
Benjamin

>  
> One of the ways to fixing this duality and improve code readability
> and type-safety a bit is to use a '{kernel_ulong_t, const void *}'
> union. That way the current drivers that treat 'hid_device_id::driver_data'
> as an integer value for storing metadata are unaffected. The drivers
> that actually store pointers in there benefit from a removed cast
> (and more clear intent) at the cost of using the new 'const void *'
> field instead.
> 
> With the union in place, some of the existing initializers for static
> const data now need a named field for the 'driver_data' - this is
> also addressed in the series as part of the pre-clean up in 
> patches 1-4. 
> 
> It was found that some modules use a bit of a type-unsafe way of storing
> integers in the 'void *driver_data' pointer of the 'struct hid_device'
> - this required a cast during storage via 'hid_set_drvdata' and a cast
> during retrieval when using 'hid_get_drvdata'. I can see why this was 
> done - as we potentially save on an allocation - but really code is
> more readable and better quality without resorting to this. This issue
> is also addressed in this patch series in patches 5-8 as part of the
> pre-clean up.
> 
> The actual implementation and post-clean up can be found in 
> patches 9-11.
> 
> The change also makes the code more portable on architecture 
> like CHERI [1], where a pointer is replaced with a new primitive
> (called the capability) at the architecture level and is as twice as
> wide as the greatest representable address, ie. for 64 bit address
> space capabilities are 128 bits wide (the other 64 bits are used to
> store meta-data relating to the 64 bit address). So you can not store 
> valid pointers inside 'unsigned long' as effectively a different set of
> instructions is being generated by the compiler based on the data-type
> that was used in C (ie. capabilities have their own set of load/store
> that also copy over the meta-data which are orthogonal to the load/store
> instructions used for plain integers that would invalidate the meta-data).
> There is slightly more detail to this, but the above is enough to
> explain the motivation - the proposed changes make the code a bit
> better even without considering CHERI at all - as it is more readable 
> and type-safe.
> 
> The series was built and tested under QEMU (boots with relevant 
> configs set to Y) on arm64.
> 
> This series is part of a larger effort led by Uwe [2]
> 
> [1] https://cheri-alliance.org/discover-cheri/
> [2] https://lore.kernel.org/all/cover.1776429984.git.u.kleine-koenig@baylibre.com/ 
> 
> ---
> Pawel Zalewski (The Capable Hub) (11):
>       HID: hid-input: use named initializer for 'hid_battery_quirks[]'
>       HID: hid-quirks: use named initializer in 'hid_quirks[]'
>       HID: hid-asus: use named initializer for 'asus_devices[]'
>       HID: i2c-hid-dmi-quirks: use named initializer for 'i2c_hid_elan_flipped_quirks[]'
>       HID: hid-belkin: clean up usage of 'driver_data'
>       HID: hid-cypress: clean up usage of 'driver_data'
>       HID: hid-gfrm: clean up usage of 'driver_data'
>       HID: hid-ite: clean up usage of 'driver_data'
>       HID: mod_devicetable: 'hid_device_id::driver_data' add union
>       HID: hid-tmff: use 'hid_device_id::driver_data_ptr'
>       HID: wacom: use 'hid_device_id::driver_data_ptr'
> 
>  drivers/hid/hid-asus.c                   |  46 ++-
>  drivers/hid/hid-belkin.c                 |   5 +-
>  drivers/hid/hid-cypress.c                |  32 +-
>  drivers/hid/hid-gfrm.c                   |   8 +-
>  drivers/hid/hid-input.c                  |  38 +-
>  drivers/hid/hid-ite.c                    |   9 +-
>  drivers/hid/hid-quirks.c                 | 575 ++++++++++++++++++++-----------
>  drivers/hid/hid-tmff.c                   |  22 +-
>  drivers/hid/i2c-hid/i2c-hid-dmi-quirks.c |   2 +-
>  drivers/hid/wacom_sys.c                  |  14 +-
>  drivers/hid/wacom_wac.c                  |  10 +-
>  include/linux/mod_devicetable.h          |   5 +-
>  12 files changed, 496 insertions(+), 270 deletions(-)
> ---
> base-commit: 25ccf4586bead3fe3cf2c57ff0480f31a0e335ad
> change-id: 20260427-mod-devicetable-hid_device_id-7f30d877387c
> 
> Best regards,
> --  
> Pawel Zalewski (The Capable Hub) <pzalewski@thegoodpenguin.co.uk>
> 

^ permalink raw reply

* Re: [PATCH] HID: remove duplicate hid_warn_ratelimited definition
From: Benjamin Tissoires @ 2026-05-21 14:45 UTC (permalink / raw)
  To: jikos, 刘 凯; +Cc: vi, linux-input, linux-kernel
In-Reply-To: <OS3PR01MB7969C09559DC6C0DCA520720C73C2@OS3PR01MB7969.jpnprd01.prod.outlook.com>

On Thu, 07 May 2026 08:41:08 +0000, 刘 凯 wrote:
> The hid_warn_ratelimited macro is defined twice in include/linux/hid.h:
> at line 1317 (added by commit 4051ead99888) and at line 1339 (added by
> commit 1d64624243af).
> 
> The second definition is correctly grouped with other ratelimited macros.
> Remove the duplicate definition at line 1317.
> 
> [...]

Applied to https://git.kernel.org/pub/scm/linux/kernel/git/hid/hid.git (for-7.1/upstream-fixes), thanks!

[1/1] HID: remove duplicate hid_warn_ratelimited definition
      https://git.kernel.org/hid/hid/c/dd2147375a8f

Cheers,
-- 
Benjamin Tissoires <bentiss@kernel.org>


^ permalink raw reply

* Re: [PATCH] HID: u2fzero: free allocated URB on probe errors
From: Benjamin Tissoires @ 2026-05-21 14:45 UTC (permalink / raw)
  To: Jiri Kosina, Myeonghun Pak; +Cc: linux-input, linux-kernel
In-Reply-To: <20260424-u2fzero-probe-urb-unwind-v1-1-mhun512@gmail.com>

On Fri, 24 Apr 2026 22:21:31 +0900, Myeonghun Pak wrote:
> u2fzero_fill_in_urb() allocates dev->urb with usb_alloc_urb(), but
> u2fzero_probe() ignored its return value and only freed the URB from
> u2fzero_remove().
> 
> If LED or hwrng registration fails after the URB allocation, probe returns
> an error and the driver core does not call .remove(), leaking the URB. A
> failed URB setup was also allowed to continue probing with an unusable
> device.
> 
> [...]

Applied to https://git.kernel.org/pub/scm/linux/kernel/git/hid/hid.git (for-7.1/upstream-fixes), thanks!

[1/1] HID: u2fzero: free allocated URB on probe errors
      https://git.kernel.org/hid/hid/c/2e78b21864dd

Cheers,
-- 
Benjamin Tissoires <bentiss@kernel.org>


^ permalink raw reply

* Re: [syzbot] [input?] possible deadlock in tasklet_action_common (2)
From: Sebastian Andrzej Siewior @ 2026-05-21 14:34 UTC (permalink / raw)
  To: Hillf Danton
  Cc: syzbot, dmitry.torokhov, linux-input, Tetsuo Handa, linux-kernel,
	syzkaller-bugs
In-Reply-To: <20260521015233.873-1-hdanton@sina.com>

On 2026-05-21 09:52:32 [+0800], Hillf Danton wrote:
> > Date: Wed, 20 May 2026 10:05:38 -0700	[thread overview]
> > Hello,
> > 
> > syzbot found the following issue on:
> > 
> > HEAD commit:    e98d21c170b0 Add linux-next specific files for 20260508
> > git tree:       linux-next
> > console output: https://syzkaller.appspot.com/x/log.txt?x=1354bfce580000
> > kernel config:  https://syzkaller.appspot.com/x/.config?x=59b98218d9b2edf4
> > dashboard link: https://syzkaller.appspot.com/bug?extid=b5d7ab56d43de3fd5aac
> > compiler:       Debian clang version 21.1.8 (++20251221033036+2078da43e25a-1~exp1~20251221153213.50), Debian LLD 21.1.8
> > 
> > Unfortunately, I don't have any reproducer for this issue yet.
> > 
> > Downloadable assets:
> > disk image: https://storage.googleapis.com/syzbot-assets/66f2a00ee290/disk-e98d21c1.raw.xz
> > vmlinux: https://storage.googleapis.com/syzbot-assets/6b982257ce9e/vmlinux-e98d21c1.xz
> > kernel image: https://storage.googleapis.com/syzbot-assets/a73fbea43e1a/bzImage-e98d21c1.xz
> > 
> > IMPORTANT: if you fix the issue, please add the following tag to the commit:
> > Reported-by: syzbot+b5d7ab56d43de3fd5aac@syzkaller.appspotmail.com
> > 
> > ======================================================
> > WARNING: possible circular locking dependency detected
> > syzkaller #0 Tainted: G             L     
> > ------------------------------------------------------
> > syz.2.4328/32663 is trying to acquire lock:
> > ffff8880b8724168 (tasklet_sync_callback.cb_lock){+...}-{3:3}, at: spin_lock include/linux/spinlock_rt.h:45 [inline]
> > ffff8880b8724168 (tasklet_sync_callback.cb_lock){+...}-{3:3}, at: tasklet_lock_callback kernel/softirq.c:881 [inline]
> > ffff8880b8724168 (tasklet_sync_callback.cb_lock){+...}-{3:3}, at: tasklet_action_common+0xc5/0x610 kernel/softirq.c:931
> > 
> > but task is already holding lock:
> > ffff888058598270 (&dev->event_lock#2){+.+.}-{3:3}, at: spin_lock include/linux/spinlock_rt.h:45 [inline]
> > ffff888058598270 (&dev->event_lock#2){+.+.}-{3:3}, at: class_spinlock_irqsave_constructor include/linux/spinlock.h:619 [inline]
> > ffff888058598270 (&dev->event_lock#2){+.+.}-{3:3}, at: input_inject_event+0xa4/0x320 drivers/input/input.c:419
> > 
> > which lock already depends on the new lock.
> > 
> > 
> > the existing dependency chain (in reverse order) is:
> > 
> > -> #1 (&dev->event_lock#2){+.+.}-{3:3}:
> >        rt_spin_lock+0x83/0x400 kernel/locking/spinlock_rt.c:56
> >        spin_lock include/linux/spinlock_rt.h:45 [inline]
> >        class_spinlock_irqsave_constructor include/linux/spinlock.h:619 [inline]
> >        input_inject_event+0xa4/0x320 drivers/input/input.c:419
> >        led_trigger_event+0x13b/0x220 drivers/leds/led-triggers.c:420
> >        kbd_propagate_led_state drivers/tty/vt/keyboard.c:1118 [inline]
> >        kbd_bh+0x1b4/0x2c0 drivers/tty/vt/keyboard.c:1297
> >        tasklet_action_common+0x31c/0x610 kernel/softirq.c:942
> >        handle_softirqs+0x1de/0x6d0 kernel/softirq.c:626
> >        __do_softirq kernel/softirq.c:660 [inline]
> >        run_ktimerd+0x69/0x100 kernel/softirq.c:1155
> >        smpboot_thread_fn+0x541/0xa50 kernel/smpboot.c:160
> >        kthread+0x388/0x470 kernel/kthread.c:436
> >        ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158
> >        ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245

This is obvious and looks fine.

> Deadlock if the timer thread is preempted
> 
> 	timer thread
> 	tasklet_sync_callback.cb_lock
> 
> 	// preempted
> 		&dev->event_lock
> 		do softirq
> 		acquire tasklet_sync_callback.cb_lock
> 
> 	acquire &dev->event_lock

This shouldn't happen.

> > -> #0 (tasklet_sync_callback.cb_lock){+...}-{3:3}:
> >        check_prev_add kernel/locking/lockdep.c:3167 [inline]
> >        check_prevs_add kernel/locking/lockdep.c:3286 [inline]
> >        validate_chain kernel/locking/lockdep.c:3910 [inline]
> >        __lock_acquire+0x15a5/0x2d10 kernel/locking/lockdep.c:5239
> >        lock_acquire+0x106/0x350 kernel/locking/lockdep.c:5870
> >        rt_spin_lock+0x83/0x400 kernel/locking/spinlock_rt.c:56
> >        spin_lock include/linux/spinlock_rt.h:45 [inline]
> >        tasklet_lock_callback kernel/softirq.c:881 [inline]
> >        tasklet_action_common+0xc5/0x610 kernel/softirq.c:931
> >        handle_softirqs+0x1de/0x6d0 kernel/softirq.c:626
> >        __do_softirq kernel/softirq.c:660 [inline]
> >        __local_bh_enable_ip+0x170/0x2b0 kernel/softirq.c:302
> >        local_bh_enable include/linux/bottom_half.h:33 [inline]
> >        __alloc_skb+0x1aa/0x7d0 net/core/skbuff.c:697
> >        alloc_skb include/linux/skbuff.h:1381 [inline]
> >        hidp_send_message+0xb5/0x230 net/bluetooth/hidp/core.c:111
> >        hidp_send_intr_message net/bluetooth/hidp/core.c:143 [inline]
> >        hidp_input_event+0x2a4/0x380 net/bluetooth/hidp/core.c:175
> >        input_event_dispose+0x80/0x6b0 drivers/input/input.c:322
> >        input_inject_event+0x1d7/0x320 drivers/input/input.c:424

up to kbd_led_trigger_activate(), I can follow. In
kbd_led_trigger_activate() it disables the keyboard tasklet and then
does led_set_brightness(). Not sure how it ends up in
input_inject_event().

Now, input_inject_event() does spin_lock_irqsave() and alloc_skb() does
local_bh_disable()/ local_bh_enable(). On !RT this is not legal. On RT
it is okay but then local_bh_enable() here should not invoke any
softirqs because none were raised within the section (alloc_skb()).
Not sure if this really did occur as such or lockdep comes with the
shortest backtrace based on the lockclass and this is it.

> >        kbd_led_trigger_activate+0xbc/0x100 drivers/tty/vt/keyboard.c:1074
> >        led_trigger_set+0x53b/0x960 drivers/leds/led-triggers.c:220
> >        led_match_default_trigger drivers/leds/led-triggers.c:277 [inline]
> >        led_trigger_set_default+0x266/0x2a0 drivers/leds/led-triggers.c:300
> >        led_classdev_register_ext+0x787/0x9c0 drivers/leds/led-class.c:581
> >        led_classdev_register include/linux/leds.h:274 [inline]
> >        input_leds_connect+0x517/0x790 drivers/input/input-leds.c:145
> >        input_attach_handler drivers/input/input.c:1011 [inline]
> >        input_register_device+0xce0/0x1140 drivers/input/input.c:2395
> >        hidp_session_dev_add net/bluetooth/hidp/core.c:861 [inline]
> >        hidp_session_probe+0x1a5/0x8a0 net/bluetooth/hidp/core.c:1139
> >        l2cap_register_user+0xc2/0x1d0 net/bluetooth/l2cap_core.c:1725
> >        hidp_connection_add+0x158b/0x1a20 net/bluetooth/hidp/core.c:1411
> >        do_hidp_sock_ioctl net/bluetooth/hidp/sock.c:81 [inline]
> >        hidp_sock_ioctl+0x403/0x650 net/bluetooth/hidp/sock.c:128
> >        sock_do_ioctl+0x101/0x320 net/socket.c:1328
> >        sock_ioctl+0x5c9/0x7f0 net/socket.c:1449
> >        vfs_ioctl fs/ioctl.c:51 [inline]
> >        __do_sys_ioctl fs/ioctl.c:597 [inline]
> >        __se_sys_ioctl+0xff/0x170 fs/ioctl.c:583
> >        do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
> >        do_syscall_64+0x15f/0xf80 arch/x86/entry/syscall_64.c:94

Sebastian

^ permalink raw reply

* Re: [PATCH] HID: remove duplicate hid_warn_ratelimited definition
From: Benjamin Tissoires @ 2026-05-21 14:33 UTC (permalink / raw)
  To: 刘 凯
  Cc: jikos@kernel.org, vi@endrift.com, linux-input@vger.kernel.org,
	linux-kernel@vger.kernel.org
In-Reply-To: <OS3PR01MB7969C09559DC6C0DCA520720C73C2@OS3PR01MB7969.jpnprd01.prod.outlook.com>

Hi,

On May 07 2026, 刘 凯 wrote:
> From 565c81e1b2e1aaad675d9844428af7b35f4328c6 Mon Sep 17 00:00:00 2001
> From: Liu Kai <lukace97@outlook.com>
> Date: Thu, 7 May 2026 16:32:04 +0800
> Subject: [PATCH] HID: remove duplicate hid_warn_ratelimited definition
> 
> The hid_warn_ratelimited macro is defined twice in include/linux/hid.h:
> at line 1317 (added by commit 4051ead99888) and at line 1339 (added by
> commit 1d64624243af).

Checkpatch complained about not using the proper format for commits
(i.e. 'commit <12+ chars of sha1> ("<title line>")'). Also, putting
lines numbers in a commit description is not great, because they are
only valid for the time you wrote the patch, and if we apply other
things in the middle, they get wrong.

Last, your email client (outlook) mangled the patch and converted tabs
to space. I managed to apply it with --ignore-whitespace in git am, but
this adds extra steps. Please fix your process for your next submission.

Anyway, I've taken your patch in my local queue, and reformulated the
commit description with the remarks I made above. I should push this
soon.

Cheers,
Benjamin

> 
> The second definition is correctly grouped with other ratelimited macros.
> Remove the duplicate definition at line 1317.
> 
> Fixes: 1d64624243af ("HID: core: Add printk_ratelimited variants to hid_warn() etc")
> Signed-off-by: Liu Kai <lukace97@outlook.com>
> ---
>  include/linux/hid.h | 2 --
>  1 file changed, 2 deletions(-)
> 
> diff --git a/include/linux/hid.h b/include/linux/hid.h
> index 442a80d79e89..e1877a9a58e2 100644
> --- a/include/linux/hid.h
> +++ b/include/linux/hid.h
> @@ -1314,8 +1314,6 @@ void hid_quirks_exit(__u16 bus);
>         dev_notice(&(hid)->dev, fmt, ##__VA_ARGS__)
>  #define hid_warn(hid, fmt, ...)                                \
>         dev_warn(&(hid)->dev, fmt, ##__VA_ARGS__)
> -#define hid_warn_ratelimited(hid, fmt, ...)                            \
> -       dev_warn_ratelimited(&(hid)->dev, fmt, ##__VA_ARGS__)
>  #define hid_info(hid, fmt, ...)                                \
>         dev_info(&(hid)->dev, fmt, ##__VA_ARGS__)
>  #define hid_dbg(hid, fmt, ...)                         \
> --
> 2.43.0

^ permalink raw reply

* Re: [PATCH] HID: u2fzero: fix general protection fault in u2fzero_recv
From: Benjamin Tissoires @ 2026-05-21 14:27 UTC (permalink / raw)
  To: l1za0.sec; +Cc: jikos, benjamin.tissoires, linux-input, linux-kernel
In-Reply-To: <20260421134824.35027-1-l1za0.sec@gmail.com>


Hi,

Thanks for the patch and sorry for the delay.

On Apr 21 2026, l1za0.sec@gmail.com wrote:
> From: Haocheng Yu <l1za0.sec@gmail.com>
> 
> A general protection fault in u2fzero_recv is reported by a
> modified Syzkaller-based kernel fuzzing tool we developed.
> 
> The cause is that u2fzero_probe() calls the u2fzero_fill_in_urb()
> function but ignores its return value. When the urb setting fails,
> dev->urb remains NULL, but u2fzero_probe() continues to run. When
> `dev->urb->context = &ctx;` in u2fzero_recv() is executed, the
> KASAN null pointer dereference crash will occur.
> 
> To fix this vulnerability, I added a check for the return value of
> u2fzero_fill_in_urb() and aborted u2fzero_probe() on error. And I
> added a NULL value check for dev->urb in u2fzero_recv() to further
> ensure its integrity.

Couple of things:
- sorry, but a couple of days later someone else send a patch to tackle
  the same problem at a better level IMO:
  https://lore.kernel.org/linux-input/20260424-u2fzero-probe-urb-unwind-v1-1-mhun512@gmail.com/
  So I'm going to take this one instead of yours. I would appreciate if
  you can confirm this fixes your current reproducer.

- Your commit description is not great. Please have a look at
  Documentation/process/submitting-patches.rst The thing that made me
  notice this is the non imperative form (quoting
  submitting-patches.rst):
  """
  Describe your changes in imperative mood,
  e.g. "make xyzzy do frotz" instead of "[This patch] makes xyzzy do
  frotz" or "[I] changed xyzzy to do frotz", as if you are giving orders
  to the codebase to change its behaviour.
  """
  The paragraph above is also hard to follow but valuable for
  understanding why it needs to be fixed.

Again, in the end the problem is that is dev->urb is NULL, there are
little chances the system is healthy and the device will work correctly,
so it's best to bail out early in probe like the other patch I
mentioned.

Cheers,
Benjamin

> 
> Signed-off-by: Haocheng Yu <l1za0.sec@gmail.com>
> ---
>  drivers/hid/hid-u2fzero.c | 12 +++++++++++-
>  1 file changed, 11 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/hid/hid-u2fzero.c b/drivers/hid/hid-u2fzero.c
> index 744a91e6e78c..c51f6dd80635 100644
> --- a/drivers/hid/hid-u2fzero.c
> +++ b/drivers/hid/hid-u2fzero.c
> @@ -134,6 +134,12 @@ static int u2fzero_recv(struct u2fzero_device *dev,
>  
>  	memcpy(dev->buf_out, req, sizeof(struct u2f_hid_report));
>  
> +	if (!dev->urb) {
> +		hid_err(hdev, "recv called without initialized URB");
> +		ret = -ENODEV;
> +		goto err;
> +	}
> +
>  	dev->urb->context = &ctx;
>  	init_completion(&ctx.done);
>  
> @@ -341,7 +347,11 @@ static int u2fzero_probe(struct hid_device *hdev,
>  	if (ret)
>  		return ret;
>  
> -	u2fzero_fill_in_urb(dev);
> +	ret = u2fzero_fill_in_urb(dev);
> +	if (ret) {
> +		hid_hw_stop(hdev);
> +		return ret;
> +	}
>  
>  	dev->present = true;
>  
> 
> base-commit: ffc253263a1375a65fa6c9f62a893e9767fbebfa
> -- 
> 2.51.0
> 
> 

^ permalink raw reply

* Re: [PATCH] HID: appletb-kbd: add option to switch default layer on double pressing fn key
From: sashiko-bot @ 2026-05-21 14:05 UTC (permalink / raw)
  To: Aditya Garg; +Cc: dmitry.torokhov, linux-input
In-Reply-To: <20260521132447.19241-1-gargaditya08@proton.me>

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:

New issues:
- [High] Zero-initialized timestamp causes single keypresses to be erroneously treated as double-presses during the first 5 minutes of system uptime.
- [Medium] Dynamic, lockless modification of a read-only global module parameter from a per-device event handler.
- [Low] The module parameter `double_press_switch_time` is defined as a signed integer but used as an unsigned value, allowing negative values to cause abnormally large timeouts.

Pre-existing issues:
- [High] The input event handler calls a function that can sleep, leading to a schedule-while-atomic bug because input handlers execute with spinlocks held and interrupts disabled.
--

commit ff52ac5f6303e0ff3ca2a68218cce8a8beb04bf2
Author: Aditya Garg <gargaditya08@proton.me>

HID: appletb-kbd: add option to switch default layer on double pressing fn key

This patch enables a user to switch the default layer from media to fn
keys and vice-versa upon double pressing the fn key within a configurable
time interval.

> diff --git a/drivers/hid/hid-appletb-kbd.c b/drivers/hid/hid-appletb-kbd.c
> index 462010a758993..4f900338b5871 100644
> --- a/drivers/hid/hid-appletb-kbd.c
> +++ b/drivers/hid/hid-appletb-kbd.c
> @@ -56,6 +56,12 @@ static int appletb_tb_idle_timeout = 15;
>  module_param_named(idle_timeout, appletb_tb_idle_timeout, int, 0644);
>  MODULE_PARM_DESC(idle_timeout, "Idle timeout in sec");
>  
> +static int appletb_tb_double_press_switch_layers;
> +module_param_named(double_press_switch_time,
> +		   appletb_tb_double_press_switch_layers, int, 0644);

[Severity: Low]
If this parameter is set to a negative value by root at runtime, will it
be implicitly cast to a massive unsigned value when passed to
msecs_to_jiffies() below?

This could result in an extremely large timeout (e.g. 49 days) where every
sequential press acts as a double-press. Should this parameter be typed as
a uint or have bounds checking applied?

[ ... ]
> @@ -250,15 +269,42 @@ static void appletb_kbd_inp_event(struct input_handle *handle, unsigned int type
[ ... ]
> +			if (appletb_tb_double_press_switch_layers) {
> +				unsigned long now = jiffies;
> +
> +				if (time_before(now, kbd->last_fn_press +
> +					msecs_to_jiffies(appletb_tb_double_press_switch_layers))) {

[Severity: High]
Does this logic correctly handle the first 5 minutes of system uptime?

Because the kernel initializes jiffies to a large negative value (typically
-300 * HZ to detect wraparound bugs), and kbd->last_fn_press is initialized
and reset to 0, won't time_before() evaluate to unconditionally true during
early boot?

This might cause every single keypress to be falsely treated as a
double-press until the system has been running long enough for jiffies to
become positive.

> +
> +					appletb_tb_def_mode =
> +						appletb_switch_mode(
> +							appletb_tb_def_mode);

[Severity: Medium]
Is it safe to modify appletb_tb_def_mode here?

This variable is exposed as a module parameter with 0444 (read-only)
permissions. Modifying it programmatically alters the default state for all
current and future connected instances of the device based on a single
device's event.

Should this default mode be managed as a per-device state inside
struct appletb_kbd instead?

> +
> +					appletb_kbd_set_mode(kbd,
> +						appletb_tb_def_mode);

[Severity: High]
This is a pre-existing issue, but does calling appletb_kbd_set_mode() here
cause a scheduling while atomic bug?

This handler executes with dev->event_lock held and interrupts disabled.
However, appletb_kbd_set_mode() calls hid_hw_power(..., PM_HINT_FULLON),
which for USB HID devices invokes usbhid_power() and
usb_autopm_get_interface().

Since those functions lock a mutex and can sleep while waiting on power
management operations, calling them from an atomic context might cause a
kernel panic.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260521132447.19241-1-gargaditya08@proton.me?part=1

^ permalink raw reply

* [PATCH] HID: appletb-kbd: add option to switch default layer on double pressing fn key
From: Aditya Garg @ 2026-05-21 13:25 UTC (permalink / raw)
  To: Jiri Kosina, Benjamin Tissoires
  Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org,
	Aditya Garg

From: Aditya Garg <gargaditya08@proton.me>

This patch enables a user to switch the default layer from media to fn
keys and vice-versa upon double pressing the fn key. This behaviour can
be configured using the double_press_switch_time module parameter whose
value depicts the time in milliseconds within which the fn key must be
pressed again to switch the default layer. If set to 0, it simply
disables this behaviour.

Signed-off-by: Aditya Garg <gargaditya08@proton.me>
---
 drivers/hid/hid-appletb-kbd.c | 60 +++++++++++++++++++++++++++++++----
 1 file changed, 53 insertions(+), 7 deletions(-)

diff --git a/drivers/hid/hid-appletb-kbd.c b/drivers/hid/hid-appletb-kbd.c
index 462010a75..4f900338b 100644
--- a/drivers/hid/hid-appletb-kbd.c
+++ b/drivers/hid/hid-appletb-kbd.c
@@ -56,6 +56,12 @@ static int appletb_tb_idle_timeout = 15;
 module_param_named(idle_timeout, appletb_tb_idle_timeout, int, 0644);
 MODULE_PARM_DESC(idle_timeout, "Idle timeout in sec");
 
+static int appletb_tb_double_press_switch_layers;
+module_param_named(double_press_switch_time,
+		   appletb_tb_double_press_switch_layers, int, 0644);
+MODULE_PARM_DESC(double_press_switch_time, "Time in ms within which if fn key is double "
+					   "pressed will switch layers");
+
 struct appletb_kbd {
 	struct hid_field *mode_field;
 	struct input_handler inp_handler;
@@ -68,6 +74,7 @@ struct appletb_kbd {
 	bool has_turned_off;
 	u8 saved_mode;
 	u8 current_mode;
+	unsigned long last_fn_press;
 };
 
 static const struct key_entry appletb_kbd_keymap[] = {
@@ -243,6 +250,18 @@ static int appletb_kbd_hid_event(struct hid_device *hdev, struct hid_field *fiel
 	return kbd->current_mode == APPLETB_KBD_MODE_OFF;
 }
 
+static u8 appletb_switch_mode(u8 mode)
+{
+	switch (mode) {
+	case APPLETB_KBD_MODE_SPCL:
+		return APPLETB_KBD_MODE_FN;
+	case APPLETB_KBD_MODE_FN:
+		return APPLETB_KBD_MODE_SPCL;
+	default:
+		return mode;
+	}
+}
+
 static void appletb_kbd_inp_event(struct input_handle *handle, unsigned int type,
 			      unsigned int code, int value)
 {
@@ -250,15 +269,42 @@ static void appletb_kbd_inp_event(struct input_handle *handle, unsigned int type
 
 	reset_inactivity_timer(kbd);
 
-	if (type == EV_KEY && code == KEY_FN && appletb_tb_fn_toggle &&
-		(kbd->current_mode == APPLETB_KBD_MODE_SPCL ||
-		 kbd->current_mode == APPLETB_KBD_MODE_FN)) {
+	if (type == EV_KEY && code == KEY_FN &&
+	    (kbd->current_mode == APPLETB_KBD_MODE_SPCL ||
+	     kbd->current_mode == APPLETB_KBD_MODE_FN)) {
+
 		if (value == 1) {
-			kbd->saved_mode = kbd->current_mode;
-			appletb_kbd_set_mode(kbd, kbd->current_mode == APPLETB_KBD_MODE_SPCL
-						? APPLETB_KBD_MODE_FN : APPLETB_KBD_MODE_SPCL);
+			if (appletb_tb_double_press_switch_layers) {
+				unsigned long now = jiffies;
+
+				if (time_before(now, kbd->last_fn_press +
+					msecs_to_jiffies(appletb_tb_double_press_switch_layers))) {
+
+					appletb_tb_def_mode =
+						appletb_switch_mode(
+							appletb_tb_def_mode);
+
+					appletb_kbd_set_mode(kbd,
+						appletb_tb_def_mode);
+
+					kbd->saved_mode = appletb_tb_def_mode;
+					kbd->last_fn_press = 0;
+
+					return;
+				}
+
+				kbd->last_fn_press = now;
+			}
+			if (appletb_tb_fn_toggle) {
+				kbd->saved_mode = kbd->current_mode;
+
+				appletb_kbd_set_mode(kbd,
+					appletb_switch_mode(kbd->current_mode));
+			}
+
 		} else if (value == 0) {
-			if (kbd->saved_mode != kbd->current_mode)
+			if (appletb_tb_fn_toggle &&
+			    kbd->saved_mode != kbd->current_mode)
 				appletb_kbd_set_mode(kbd, kbd->saved_mode);
 		}
 	}
-- 
2.53.0



^ permalink raw reply related

* Re: [PATCH] HID: multitouch: Fix stale MT slots when contact count drops to zero
From: Benjamin Tissoires @ 2026-05-21 13:17 UTC (permalink / raw)
  To: Dave Carey; +Cc: linux-input, jikos
In-Reply-To: <20260515175253.873796-1-carvsdriver@gmail.com>

On May 15 2026, Dave Carey wrote:
> The INGENIC 17EF:6161 touchscreen (Lenovo Yoga Book 9 14IAH10) reports
> HID_DG_CONTACTCOUNT=0 in the frame immediately following the last finger
> lift rather than omitting the frame entirely.  In mt_touch_report() the
> existing code only updates num_expected when contact_count is non-zero,
> so a zero contact count on the first packet of a new frame leaves
> num_expected at its previous value (e.g. 2 for a two-finger gesture).
> The sync check "num_received >= num_expected" then evaluates "0 >= 2"
> and never fires, preventing INPUT_MT_DROP_UNUSED from releasing the
> stale slots.  Those slots remain active in the kernel MT layer until the
> next touch, at which point they are released in a batch alongside the
> new contact — causing the userspace event consumer to miss the intervening
> finger-up sequence and corrupt its gesture session state.
> 
> Fix by resetting num_expected to 0 when contact_count is zero and
> num_received is still 0 (i.e., this is the first and only packet of the
> frame, not a continuation packet in a multi-packet sequence).  With
> num_expected=0 the sync check "0 >= 0" fires immediately, calling
> input_mt_sync_frame() which drops the stale slots via INPUT_MT_DROP_UNUSED.
> 
> The num_received==0 guard is critical: continuation packets in a
> multi-packet frame arrive after at least one contact has already been
> processed (num_received>0), so they are correctly excluded from this
> path and the existing multi-packet logic is unaffected.

I've just tested the change, and the testsuite seem to still be happy
and I can't find a flaw in the design.
However: (inlined comment)

> 
> Signed-off-by: Dave Carey <carvsdriver@gmail.com>
> Tested-by: Dave Carey <carvsdriver@gmail.com>
> ---
> This follows commit 108ac841 ("HID: multitouch: Fix Yoga Book 9 14IAH10
> touchscreen misclassification"), applied to hid/for-next on 2026-05-12.
> That commit established MT_CLS_YOGABOOK9I.
> 
> This fix is independent of the companion ghost-contacts patch ("HID:
> multitouch: Honor ContactCount for Yoga Book 9 to suppress ghost contacts",
> sent 2026-05-14), which adds MT_QUIRK_CONTACT_CNT_ACCURATE to the same
> class.  Both apply cleanly on top of 108ac841.
> 
> Tested on Lenovo Yoga Book 9 14IAH10 (83KJ).
> 
>  drivers/hid/hid-multitouch.c | 12 ++++++++++++
>  1 file changed, 12 insertions(+)
> 
> diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
> index ec04dba..e91ba89 100644
> --- a/drivers/hid/hid-multitouch.c
> +++ b/drivers/hid/hid-multitouch.c
> @@ -1336,6 +1336,18 @@ static void mt_touch_report(struct hid_device *hid,
>  		/* A non 0 contact count always indicates a first packet */
>  		else if (contact_count)
>  			app->num_expected = contact_count;
> +		/*
> +		 * contact_count == 0 on the first packet of a new frame means
> +		 * all contacts have lifted (the firmware sends an explicit zero
> +		 * to signal all-up rather than simply omitting the frame).
> +		 * Reset num_expected so that the sync check below fires and
> +		 * INPUT_MT_DROP_UNUSED releases any stale slots.  This is safe
> +		 * for multi-packet continuation frames because those arrive with
> +		 * num_received > 0 (at least one contact was already processed
> +		 * from the preceding first-packet in the same frame).
> +		 */
> +		else if (app->num_received == 0)
> +			app->num_expected = 0;

this succession of if/else if makes the code harder to understand,
especially with long blocks of comments.

How about this instead (wouldn't mind a second pair of eyes):

        /*
         * Includes multi-packet support where subsequent
         * packets are sent with zero contactcount.
         */
        if (contact_count > 0)
                app->num_expected = contact_count;
        else if (!contact_count && app->num_received == 0 &&  app->prev_scantime != scantime) {
                /*
                 * New multi-report frame:
                 *
                 * For Win8 PTPs the first packet (td->num_received == 0) may
                 * have a contactcount of 0 if there only is a button event.
                 *
                 * Some other devices use a sentinel frame with 0 to release all contacts
                 */
                app->num_expected = 0;
        }

At least the test-suite is happy with this change.

Cheers,
Benjamin

>  	}
>  	app->prev_scantime = scantime;
>  
> -- 
> 2.54.0
> 
> 

^ permalink raw reply

* Re: [PATCH v2 5/6] tools/build: selftests: Allow versioning LLVM lld
From: James Clark @ 2026-05-21 13:16 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo, Ihor Solodrai
  Cc: sashiko-reviews, dmitry.torokhov, linux-perf-users, bpf,
	linux-input
In-Reply-To: <ag4QkPOCQ5LWy-U3@x1>



On 20/05/2026 8:50 pm, Arnaldo Carvalho de Melo wrote:
> On Mon, May 18, 2026 at 10:47:53AM +0100, James Clark wrote:
>>
>>
>> On 18/05/2026 10:32 am, sashiko-bot@kernel.org wrote:
>>> Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
>>> - [Medium] Empty USE_LD variable causes clang build failure in selftests when ld.lld is not in PATH.
>>> --
>>>
>>> commit 371ca22c55bb0fa6502ac06f7b33d63418ad16ff
>>> Author: James Clark <james.clark@linaro.org>
>>>
>>> tools/build: selftests: Allow versioning LLVM lld
>>>
>>> This commit adds an LLD variable to Makefile.include to allow versioning of
>>> LLVM's linker, keeping it consistent with the rest of the LLVM toolchain. It
>>> modifies the BPF selftests Makefile to resolve the full path of the linker
>>> using the command -v shell utility.
>>>
>>>> diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile
>>>> index 6ef6872adbc3e..836e783f0170e 100644
>>>> --- a/tools/testing/selftests/bpf/Makefile
>>>> +++ b/tools/testing/selftests/bpf/Makefile
>>>
>>> [ ... ]
>>>
>>>> @@ -245,9 +245,9 @@ $(OUTPUT)/%:%.c
>>>>    # LLVM's ld.lld doesn't support all the architectures, so use it only on x86
>>>>    ifeq ($(SRCARCH),$(filter $(SRCARCH),x86 riscv))
>>>> -LLD := lld
>>>> +USE_LD := $(shell command -v $(LLD))
>>>
>>> Does this cause the build to fail if the toolchain directory is not in the
>>> system PATH?
>>>
>>> If a user explicitly specifies the compiler using an absolute path like
>>> CLANG=/path/to/clang, but does not add the toolchain directory to their PATH,
>>> the command -v utility will return an empty string.
>>
>> If they don't use LLVM= and use CLANG= with an absolute path then they need
>> to set all the tools with an absolute path. That's the problem that setting
>> the path with a prefix for all tools with LLVM= solved.
>>
>> I suppose this could _technically_ be a regression, but so could adding any
>> new build rule that added a new tool that wouldn't be set in someones
>> existing build command. Being that strict would mean we can't ever add new
>> tools so I don't think this is an issue. And I don't think there's a way to
>> solve this and also respect the documented rules of LLVM=
> 
> Hey, this touches several tools and libs, did you get acks besides the
> one from Ian?
> 
> - Arnaldo

Only the build bpf failure report from Ihor on V1 but nothing from 
anyone on V2. Ihor did you get a chance to look at this version?

Thanks
James


^ permalink raw reply

* Re: [PATCH] HID: logitech-hidpp: Add support for HID++ Multi-Platform feature (0x4531)
From: dev exalt @ 2026-05-21  9:27 UTC (permalink / raw)
  To: Bastien Nocera
  Cc: jikos, bentiss, lains, linux-input, linux-kernel, sari.kreitem,
	hbarnor
In-Reply-To: <CAJaUH_-aPMopA90xthPMVhktyyL6WHJPgn748yQ95UAVgaaaFQ@mail.gmail.com>

Hi,

Following up on this thread regarding HID++ Multi-Platform feature
(0x4531) support.
Please let us know if you’d like us to proceed with a V2 patch based
on the discussed approach.

Thanks,
Baraa

On Sun, May 10, 2026 at 9:36 AM dev exalt <exalt.dev.team@gmail.com> wrote:
>
> Hi Bastien,
>
> Just following up on this thread in case you had a chance to review
> our latest response.
>
> Would you be OK with us proceeding with the implementation and
> preparing a v2 patch based on it?
>
> Thanks,
> Baraa
>
> On Thu, Mar 19, 2026 at 12:05 PM dev exalt <exalt.dev.team@gmail.com> wrote:
> >
> > Hi Bastien,
> >
> > Thanks for the review. Please see our responses inline below.
> >
> > On Mon, Mar 9, 2026 at 11:53 AM Bastien Nocera <hadess@hadess.net> wrote:
> > >
> > > Hey,
> > >
> > > Sorry for not looking at this earlier, it slipped through the cracks as
> > > it arrived on the mailing-list as I was away.
> > >
> > > On Mon, 2025-12-15 at 14:53 +0200, DevExalt wrote:
> > > > From: "Baraa Atta (Dev Exalt)" <exalt.dev.team@gmail.com>
> > > >
> > > > Add support in the Logitech HID++ driver for the HID++ Multi-Platform
> > > > feature (0x4531), which enables HID++ devices to adjust their
> > > > behavior
> > > > based on the host operating system (Linux, ChromeOS, Android).
> > >
> > > Can you please explain what the feature actually does ? (the Logitech
> > > docs say "Set the right keyboard layout for your computer operating
> > > system" and mention that some multimedia keys are inoperable unless a
> > > compatible OS is set).
> >
> > The HID++ Multi-Platform feature (0x4531) allows a device to select a
> > platform profile that determines how the device firmware behaves for a
> > specific operating system.
> >
> > In practice, this affects how certain keys and functions are exposed
> > to the host. Depending on the selected platform, the device may emit
> > different HID usages or key combinations for the same physical key.
> >
> > For example, on the Logitech MX Keys S keyboard a specific key
> > produces different events depending on the configured platform. When
> > the platform is set to Linux the key generates the combination Shift +
> > Ctrl + Alt + Meta + Space, while when the platform is set to Chrome
> > the same key generates a dedicated Emoji key event (HID usage code
> > 585).
> >
> > The exact behavioral differences are device-specific and defined by
> > the device firmware.
> >
> > >
> > > >
> > > > This patch:
> > > >  * Adds device IDs for MX Keys S (046d:b378) and Casa Keys
> > > > (046d:b371).
> > > >  * Introduces the module parameter "hidpp_platform" to allow
> > > > selecting a
> > > >    target platform.
> > > >  * Detects whether a device implements feature 0x4531.
> > > >  * Validates that the requested platform is supported by the device.
> > > >  * Applies the platform index when valid, otherwise leaves the device
> > > >    unchanged.
> > > >  * Keeps default behavior when "hidpp_platform" is unset or invalid.
> > >
> > > Can you explain the benefits of setting this module parameter, compared
> > > to using the keyboard shortcuts to switch to a specific OS
> > > configuration?
> >
> > The distribution can configure the parameter and have the OS configure
> > the device automatically without user interaction. Devices will just
> > work as expected out of the box. Users can still override it using the
> > keyboard shortcut.
> >
> > >
> > > What happens when 2 Logitech devices with different supported OSes are
> > > used?
> >
> > If a device does not support the platform specified through the module
> > parameter, the driver does not modify that device and its default
> > platform configuration remains unchanged.
> >
> > During initialization, the driver queries the device for the list of
> > supported platform descriptors exposed by the HID++ Multi-Platform
> > feature (0x4531). The requested platform is only applied if the device
> > advertises support for a compatible descriptor.
> >
> > For example, if two devices are connected and the module parameter is
> > set to linux, a device that supports the Linux platform descriptor
> > will have its platform updated accordingly, causing its firmware
> > behavior to switch to the Linux profile. A device that does not
> > advertise support for the Linux platform will not be modified and will
> > continue operating on its default configuration.
> >
> > >
> > > > Supported values for hidpp_platform:
> > > >    Android, Linux, Chrome
> > >
> > > Any reason why there aren't more supported OSes?
> > >
> > > The Logitech docs[1] lists:
> > > WebOS iOS MacOS Android Chrome Linux WinEmb Windows Tizen
> > > as possible values.
> > >
> > > [1]:
> > > https://drive.google.com/file/d/1KyiBA5m_5V1s6jQ9eQrgRJN0SbbbI9_I/view
> > >
> > > I recently got a K980 which has this functionality, it only documents
> > > Windows, macOS and Chrome, but Solaar also lists Linux as an option.
> > >
> > > So my questions would be:
> > > - why not support the whole range of possible OSes in this option?
> >
> > Our initial focus during development was primarily on Android, and we
> > subsequently added Linux and Chrome. However, if it is preferred for
> > completeness, we are happy to add the rest of supported OS platforms
> > listed in the documentation.
> >
> > > - why is it a module option instead of, say, a sysfs attribute that
> > > could be changed per device?
> > > - why not implement this in user-space through a udev callout?
> >
> > The module parameter was initially chosen to provide a simple,
> > system-wide default that distributions can configure at boot. This
> > ensures the devices work out of the box without relying on user
> > interaction.
> >
> > Following your question, we can also add a per-device sysfs attribute
> > alongside the module parameter. This hybrid approach accommodates
> > compatibility with all systems, since udev is not supported by all
> > distributions.
> >
> > >
> > > Cheers
> > >
> > > >
> > > > TEST=Pair MX Keys S and Casa Keys over Bluetooth and verify:
> > > >      * Feature 0x4531 is detected.
> > > >      * Valid platform values are accepted and applied.
> > > >      * Invalid platform values result in no update.
> > > >      * Devices without 0x4531 retain default behavior.
> > > >      * Platform-specific key behavior is observed once applied.
> > > >
> > > > Signed-off-by: Baraa Atta (Dev Exalt) <exalt.dev.team@gmail.com>
> > > > ---
> > > >  drivers/hid/hid-ids.h            |   2 +
> > > >  drivers/hid/hid-logitech-hidpp.c | 280
> > > > +++++++++++++++++++++++++++++++
> > > >  drivers/hid/hid-quirks.c         |   2 +
> > > >  3 files changed, 284 insertions(+)
> > > >
> > > > diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
> > > > index d31711f1aaec..12de1194d7fa 100644
> > > > --- a/drivers/hid/hid-ids.h
> > > > +++ b/drivers/hid/hid-ids.h
> > > > @@ -866,6 +866,8 @@
> > > >  #define USB_DEVICE_ID_LOGITECH_T651  0xb00c
> > > >  #define USB_DEVICE_ID_LOGITECH_DINOVO_EDGE_KBD       0xb309
> > > >  #define USB_DEVICE_ID_LOGITECH_CASA_TOUCHPAD 0xbb00
> > > > +#define USB_DEVICE_ID_LOGITECH_CASA_KEYS_KEYBOARD    0xb371
> > > > +#define USB_DEVICE_ID_LOGITECH_MX_KEYS_S_KEYBOARD    0xb378
> > > >  #define USB_DEVICE_ID_LOGITECH_C007  0xc007
> > > >  #define USB_DEVICE_ID_LOGITECH_C077  0xc077
> > > >  #define USB_DEVICE_ID_LOGITECH_RECEIVER      0xc101
> > > > diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-
> > > > logitech-hidpp.c
> > > > index d5011a5d0890..e94daed31981 100644
> > > > --- a/drivers/hid/hid-logitech-hidpp.c
> > > > +++ b/drivers/hid/hid-logitech-hidpp.c
> > > > @@ -4373,6 +4373,280 @@ static bool hidpp_application_equals(struct
> > > > hid_device *hdev,
> > > >       return report && report->application == application;
> > > >  }
> > > >
> > > > +/* -----------------------------------------------------------------
> > > > --------- */
> > > > +/* 0x4531: Multi-Platform
> > > > Support                                             */
> > > > +/* -----------------------------------------------------------------
> > > > --------- */
> > > > +
> > > > +/*
> > > > + * Some Logitech devices expose the HID++ feature 0x4531 (Multi-
> > > > Platform) allowing
> > > > + * the host to specify which operating system platform to use on the
> > > > device. Changing device's
> > > > + * platform may alter the behavior of the device to match the
> > > > specified platform.
> > > > + */
> > > > +
> > > > +static char *hidpp_platform;
> > > > +module_param(hidpp_platform, charp, 0644);
> > > > +MODULE_PARM_DESC(hidpp_platform, "Select host platform type for
> > > > Logitech HID++ Multi-Platform feature "
> > > > +              "0x4531, valid values: (linux|chrome|android).  If
> > > > unset, no "
> > > > +              "change is applied.");
> > > > +
> > > > +#define HIDPP_MULTIPLATFORM_FEAT_ID                  0x4531
> > > > +#define HIDPP_MULTIPLATFORM_GET_FEATURE_INFO         0x0F
> > > > +#define HIDPP_MULTIPLATFORM_GET_PLATFORM_DESCRIPTOR  0x1F
> > > > +#define HIDPP_MULTIPLATFORM_SET_CURRENT_PLATFORM     0x3F
> > > > +
> > > > +#define
> > > > HIDPP_MULTIPLATFORM_PLATFORM_MASK_LINUX               BIT(10)
> > > > +#define HIDPP_MULTIPLATFORM_PLATFORM_MASK_CHROME     BIT(11)
> > > > +#define HIDPP_MULTIPLATFORM_PLATFORM_MASK_ANDROID    BIT(12)
> > > > +
> > > > +struct hidpp_platform_desc {
> > > > +     u8 plat_idx;
> > > > +     u8 desc_idx;
> > > > +     u16 plat_mask;
> > > > +};
> > > > +
> > > > +/**
> > > > + * hidpp_multiplatform_mask_from_str() - Convert platform name to an
> > > > HID++ platform mask
> > > > + * @pname: Platform name string
> > > > + *
> > > > + * Converts a platform name string to its corresponding HID++
> > > > platform mask based on
> > > > + * the Multi-Platform feature specification.
> > > > + *
> > > > + * Return: Platform mask corresponding to @pname on success,
> > > > + * or 0 if @pname is NULL or unsupported.
> > > > + */
> > > > +static u16 hidpp_multiplatform_mask_from_str(const char *pname)
> > > > +{
> > > > +     if (!pname)
> > > > +             return 0;
> > > > +
> > > > +     if (!strcasecmp(pname, "linux"))
> > > > +             return HIDPP_MULTIPLATFORM_PLATFORM_MASK_LINUX;
> > > > +     if (!strcasecmp(pname, "chrome"))
> > > > +             return HIDPP_MULTIPLATFORM_PLATFORM_MASK_CHROME;
> > > > +     if (!strcasecmp(pname, "android"))
> > > > +             return HIDPP_MULTIPLATFORM_PLATFORM_MASK_ANDROID;
> > > > +
> > > > +     return 0;
> > > > +}
> > > > +
> > > > +/**
> > > > + * hidpp_multiplatform_get_num_pdesc() - Retrieve number of platform
> > > > descriptors
> > > > + * @hidpp: Pointer to the hidpp_device instance
> > > > + * @feat_index: Feature index of the Multi-Platform feature
> > > > + * @num_desc: Pointer to store the number of platform descriptors
> > > > + *
> > > > + * Retrieves the number of platform descriptors supported by the
> > > > device through
> > > > + * the Multi-Platform feature and stores it in @num_desc.
> > > > + *
> > > > + * Return: 0 on success, or non-zero on failure.
> > > > + */
> > > > +static int hidpp_multiplatform_get_num_pdesc(struct hidpp_device
> > > > *hidpp,
> > > > +                                          u8 feat_index, u8
> > > > *num_desc)
> > > > +{
> > > > +     int ret;
> > > > +     struct hidpp_report response;
> > > > +     struct hid_device *hdev = hidpp->hid_dev;
> > > > +
> > > > +     ret = hidpp_send_fap_command_sync(hidpp, feat_index,
> > > > +
> > > > HIDPP_MULTIPLATFORM_GET_FEATURE_INFO,
> > > > +                                       NULL, 0, &response);
> > > > +     if (ret) {
> > > > +             hid_warn(hdev, "Multiplatform: GET_FEATURE_INFO
> > > > failed (err=%d)", ret);
> > > > +             return ret;
> > > > +     }
> > > > +
> > > > +     *num_desc = response.fap.params[3];
> > > > +     hid_dbg(hdev, "Multiplatform: Device supports %d platform
> > > > descriptors", *num_desc);
> > > > +
> > > > +     return 0;
> > > > +}
> > > > +
> > > > +/**
> > > > + * hidpp_multiplatform_get_platform_desc() - Retrieve a platform
> > > > descriptor entry
> > > > + * @hidpp: Pointer to the hidpp_device instance
> > > > + * @feat_index: Feature index of the Multi-Platform feature
> > > > + * @platform_idx: Index of the platform descriptor to retrieve
> > > > + * @pdesc: Pointer to store the retrieved platform descriptor
> > > > + *
> > > > + * Retrieves a single platform descriptor identified by
> > > > @platform_idx from the
> > > > + * device and stores the parsed descriptor fields in @pdesc.
> > > > + *
> > > > + * Return: 0 on success, or non-zero on failure.
> > > > + */
> > > > +static int hidpp_multiplatform_get_platform_desc(struct hidpp_device
> > > > *hidpp, u8 feat_index,
> > > > +                                              u8 platform_idx,
> > > > struct hidpp_platform_desc *pdesc)
> > > > +{
> > > > +     int ret;
> > > > +     struct hidpp_report response;
> > > > +     u8 params[1] = { platform_idx };
> > > > +     struct hid_device *hdev = hidpp->hid_dev;
> > > > +
> > > > +     ret = hidpp_send_fap_command_sync(hidpp, feat_index,
> > > > +
> > > > HIDPP_MULTIPLATFORM_GET_PLATFORM_DESCRIPTOR,
> > > > +                                       params, sizeof(params),
> > > > &response);
> > > > +
> > > > +     if (ret) {
> > > > +             hid_warn(hdev,
> > > > +                      "Multiplatform: GET_PLATFORM_DESCRIPTOR
> > > > failed for index %d (err=%d)",
> > > > +                      platform_idx, ret);
> > > > +             return ret;
> > > > +     }
> > > > +
> > > > +     pdesc->plat_idx = response.fap.params[0];
> > > > +     pdesc->desc_idx = response.fap.params[1];
> > > > +     pdesc->plat_mask =
> > > > get_unaligned_be16(&response.fap.params[2]);
> > > > +
> > > > +     hid_dbg(hdev,
> > > > +             "Multiplatform: descriptor %d: plat_idx=%d,
> > > > desc_idx=%d, plat_mask=0x%04x",
> > > > +             platform_idx, pdesc->plat_idx, pdesc->desc_idx,
> > > > pdesc->plat_mask);
> > > > +
> > > > +     return 0;
> > > > +}
> > > > +
> > > > +/**
> > > > + * hidpp_multiplatform_get_platform_index() - Find platform index
> > > > for a mask
> > > > + * @hidpp: Pointer to the hidpp_device instance
> > > > + * @feat_index: Feature index of the Multi-Platform feature
> > > > + * @plat_mask: Platform mask to search for
> > > > + * @plat_index: Pointer to store the matched platform index
> > > > + *
> > > > + * Iterates through all platform descriptors exposed by the device
> > > > via the
> > > > + * Multi-Platform feature, retrieving each descriptor and comparing
> > > > its
> > > > + * platform mask to @plat_mask. A descriptor matches if its mask
> > > > overlaps with
> > > > + * the requested @plat_mask (i.e. (pdesc.plat_mask & plat_mask) is
> > > > non-zero).
> > > > + *
> > > > + * When a matching descriptor is found, its platform index
> > > > (plat_idx) is
> > > > + * written to @plat_index and the function returns success.
> > > > + *
> > > > + * If no descriptor matches, -ENOENT is returned.
> > > > + *
> > > > + * Return: 0 on success; -ENOENT if no matching descriptor exists;
> > > > + *         or non-zero on failure.
> > > > + */
> > > > +static int hidpp_multiplatform_get_platform_index(struct
> > > > hidpp_device *hidpp,
> > > > +                                               u8 feat_index, u16
> > > > plat_mask,
> > > > +                                               u8 *plat_index)
> > > > +{
> > > > +     int i;
> > > > +     int ret;
> > > > +     u8 num_desc;
> > > > +     struct hidpp_platform_desc pdesc;
> > > > +     struct hid_device *hdev = hidpp->hid_dev;
> > > > +
> > > > +     ret = hidpp_multiplatform_get_num_pdesc(hidpp, feat_index,
> > > > &num_desc);
> > > > +     if (ret)
> > > > +             return ret;
> > > > +
> > > > +     for (i = 0; i < num_desc; i++) {
> > > > +             ret = hidpp_multiplatform_get_platform_desc(hidpp,
> > > > feat_index, i, &pdesc);
> > > > +             if (ret)
> > > > +                     return ret;
> > > > +
> > > > +             if (pdesc.plat_mask & plat_mask) {
> > > > +                     *plat_index = pdesc.plat_idx;
> > > > +                     hid_dbg(hdev,
> > > > +                             "Multiplatform: Selected platform
> > > > index %d for platform '%s'",
> > > > +                             *plat_index, hidpp_platform);
> > > > +                     return 0;
> > > > +             }
> > > > +     }
> > > > +
> > > > +     hid_dbg(hdev,
> > > > +             "Multiplatform: No matching platform descriptor
> > > > found for platform '%s'",
> > > > +             hidpp_platform);
> > > > +     return -ENOENT;
> > > > +}
> > > > +
> > > > +/**
> > > > + * hidpp_multiplatform_update_device_platform() - Update the device
> > > > platform
> > > > + * @hidpp: Pointer to the hidpp_device instance
> > > > + * @feat_index: Feature index of the Multi-Platform feature
> > > > + * @plat_index: Platform index to set on the device
> > > > + *
> > > > + * Sends the HID++ Multi-Platform 'SET_CURRENT_PLATFORM' command to
> > > > the device to
> > > > + * update its platform index to @plat_index.
> > > > + *
> > > > + * Return: 0 on success, or non-zero on failure.
> > > > + */
> > > > +static int hidpp_multiplatform_update_device_platform(struct
> > > > hidpp_device *hidpp,
> > > > +                                                   u8 feat_index,
> > > > u8 plat_index)
> > > > +{
> > > > +     int ret;
> > > > +     struct hidpp_report response;
> > > > +     /* Byte 0 (hostIndex): 0xFF selects the current host. */
> > > > +     u8 params[2] = { 0xFF, plat_index };
> > > > +
> > > > +     ret = hidpp_send_fap_command_sync(hidpp, feat_index,
> > > > +
> > > > HIDPP_MULTIPLATFORM_SET_CURRENT_PLATFORM,
> > > > +                                       params, sizeof(params),
> > > > &response);
> > > > +
> > > > +     if (ret)
> > > > +             hid_warn(hidpp->hid_dev,
> > > > +                      "Multiplatform: SET_CURRENT_PLATFORM failed
> > > > for index %d (err=%d)",
> > > > +                      plat_index, ret);
> > > > +
> > > > +     return ret;
> > > > +}
> > > > +
> > > > +/**
> > > > + * hidpp_multiplatform_init() - Apply the HID++ Multi-Platform
> > > > (0x4531) feature
> > > > + * @hidpp: Pointer to the hidpp_device instance
> > > > + *
> > > > + * Initializes the Multi-Platform feature by selecting the device
> > > > platform
> > > > + * corresponding to the module parameter @hidpp_platform, if
> > > > provided.
> > > > + *
> > > > + * The function performs the following steps:
> > > > + *   1. Convert the @hidpp_platform string into a platform mask.
> > > > + *   2. Check whether the device supports the Multi-Platform feature
> > > > (0x4531).
> > > > + *   3. Look up the device's platform index whose mask matches the
> > > > host
> > > > + *      platform mask.
> > > > + *   4. Apply that platform index to the device via
> > > > 'SET_CURRENT_PLATFORM'.
> > > > + *
> > > > + * If the module parameter is unset or invalid, or the device does
> > > > not support
> > > > + * the feature, or no matching platform descriptor is found, the
> > > > function exits
> > > > + * silently without modifying the device state.
> > > > + *
> > > > + * On success, the device's platform configuration is updated.
> > > > + */
> > > > +static void hidpp_multiplatform_init(struct hidpp_device *hidpp)
> > > > +{
> > > > +     int ret;
> > > > +     u8 feat_index;
> > > > +     u8 plat_index;
> > > > +     u16 host_plat_mask;
> > > > +     struct hid_device *hdev = hidpp->hid_dev;
> > > > +
> > > > +     if (!hidpp_platform)
> > > > +             return;
> > > > +
> > > > +     host_plat_mask =
> > > > hidpp_multiplatform_mask_from_str(hidpp_platform);
> > > > +     if (!host_plat_mask) {
> > > > +             hid_warn(hdev,
> > > > +                      "Multiplatform: Invalid or unsupported
> > > > platform name '%s'",
> > > > +                      hidpp_platform);
> > > > +             return;
> > > > +     }
> > > > +
> > > > +     ret = hidpp_root_get_feature(hidpp,
> > > > HIDPP_MULTIPLATFORM_FEAT_ID, &feat_index);
> > > > +     if (ret) {
> > > > +             hid_warn(hdev,
> > > > +                      "Multiplatform: Failed to get the HID++
> > > > multiplatform feature 0x4531");
> > > > +             return;
> > > > +     }
> > > > +
> > > > +     ret = hidpp_multiplatform_get_platform_index(hidpp,
> > > > feat_index, host_plat_mask,
> > > > +                                                  &plat_index);
> > > > +     if (ret)
> > > > +             return;
> > > > +
> > > > +     ret = hidpp_multiplatform_update_device_platform(hidpp,
> > > > feat_index, plat_index);
> > > > +     if (ret)
> > > > +             return;
> > > > +
> > > > +     hid_info(hdev,
> > > > +              "Multiplatform: Device platform successfully set to
> > > > '%s'", hidpp_platform);
> > > > +}
> > > > +
> > > >  static int hidpp_probe(struct hid_device *hdev, const struct
> > > > hid_device_id *id)
> > > >  {
> > > >       struct hidpp_device *hidpp;
> > > > @@ -4467,6 +4741,8 @@ static int hidpp_probe(struct hid_device *hdev,
> > > > const struct hid_device_id *id)
> > > >       if (hidpp->quirks & HIDPP_QUIRK_DELAYED_INIT)
> > > >               connect_mask &= ~HID_CONNECT_HIDINPUT;
> > > >
> > > > +     hidpp_multiplatform_init(hidpp);
> > > > +
> > > >       /* Now export the actual inputs and hidraw nodes to the
> > > > world */
> > > >       hid_device_io_stop(hdev);
> > > >       ret = hid_connect(hdev, connect_mask);
> > > > @@ -4664,6 +4940,10 @@ static const struct hid_device_id
> > > > hidpp_devices[] = {
> > > >         HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb034) },
> > > >       { /* MX Anywhere 3SB mouse over Bluetooth */
> > > >         HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH, 0xb038) },
> > > > +     { /* Casa Keys keyboard over Bluetooth */
> > > > +       HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH,
> > > > USB_DEVICE_ID_LOGITECH_CASA_KEYS_KEYBOARD) },
> > > > +     { /* MX Keys S keyboard over Bluetooth */
> > > > +       HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH,
> > > > USB_DEVICE_ID_LOGITECH_MX_KEYS_S_KEYBOARD) },
> > > >       {}
> > > >  };
> > > >
> > > > diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c
> > > > index c89a015686c0..99ca04b61bda 100644
> > > > --- a/drivers/hid/hid-quirks.c
> > > > +++ b/drivers/hid/hid-quirks.c
> > > > @@ -520,6 +520,8 @@ static const struct hid_device_id
> > > > hid_have_special_driver[] = {
> > > >  #endif
> > > >  #if IS_ENABLED(CONFIG_HID_LOGITECH_HIDPP)
> > > >       { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
> > > > USB_DEVICE_ID_LOGITECH_G920_WHEEL) },
> > > > +     { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH,
> > > > USB_DEVICE_ID_LOGITECH_CASA_KEYS_KEYBOARD) },
> > > > +     { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_LOGITECH,
> > > > USB_DEVICE_ID_LOGITECH_MX_KEYS_S_KEYBOARD) },
> > > >  #endif
> > > >  #if IS_ENABLED(CONFIG_HID_MAGICMOUSE)
> > > >       { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_APPLE,
> > > > USB_DEVICE_ID_APPLE_MAGICMOUSE) },
> >
> > Thanks,
> >
> > Baraa Atta (Dev Exalt) <exalt.dev.team@gmail.com>

^ permalink raw reply

* Re: [PATCH 07/10] [v6 net-next] dt-bindings: net: add st,stlc4560/p54spi binding
From: Bartosz Golaszewski @ 2026-05-21  9:04 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: linux-kernel, Arnd Bergmann, Christian Lamparter, Johannes Berg,
	Aaro Koskinen, Andreas Kemnade, Kevin Hilman, Roger Quadros,
	Tony Lindgren, Thomas Bogendoerfer, John Paul Adrian Glaubitz,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
	H. Peter Anvin, Linus Walleij, Bartosz Golaszewski,
	Dmitry Torokhov, Lee Jones, Pavel Machek, Matti Vaittinen,
	Florian Fainelli, Jonas Gorski, Andrew Lunn, Vladimir Oltean,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	linux-wireless, linux-omap, linux-arm-kernel, linux-mips,
	linux-sh, linux-input, linux-leds, netdev, Christian Lamparter,
	Conor Dooley, linux-gpio
In-Reply-To: <20260520183815.2510387-8-arnd@kernel.org>

On Wed, 20 May 2026 20:38:12 +0200, Arnd Bergmann <arnd@kernel.org> said:
> From: Arnd Bergmann <arnd@arndb.de>
>
> The SPI version of Prism54 was sold under a couple of different
> names and supported by the Linux p54spi driver, but there was
> never a DT binding for it.
>
> Document the four known names of this device and the properties
> that are sufficient for its use on the Nokia N8x0 tablet.
>
> As I don't have this hardware or documentation for it, this is
> purely based on existing usage in the driver.
>
> Link: https://lore.kernel.org/all/e8dc9acb-6f85-e0a9-a145-d101ca6da201@gmail.com/
> Acked-by: Christian Lamparter <chunkeey@gmail.com>
>
> Acked-by: Conor Dooley <conor.dooley@microchip.com>
> Signed-off-by: Arnd Bergmann <arnd@arndb.de>
> ---

Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>

^ permalink raw reply

* Re: [PATCH 02/10] [v3] input: gpio-keys: make legacy gpiolib optional
From: Bartosz Golaszewski @ 2026-05-21  9:03 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: linux-kernel, Arnd Bergmann, Christian Lamparter, Johannes Berg,
	Aaro Koskinen, Andreas Kemnade, Kevin Hilman, Roger Quadros,
	Tony Lindgren, Thomas Bogendoerfer, John Paul Adrian Glaubitz,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
	H. Peter Anvin, Linus Walleij, Bartosz Golaszewski,
	Dmitry Torokhov, Lee Jones, Pavel Machek, Matti Vaittinen,
	Florian Fainelli, Jonas Gorski, Andrew Lunn, Vladimir Oltean,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	linux-wireless, linux-omap, linux-arm-kernel, linux-mips,
	linux-sh, linux-input, linux-leds, netdev, linux-gpio
In-Reply-To: <20260520183815.2510387-3-arnd@kernel.org>

On Wed, 20 May 2026 20:38:07 +0200, Arnd Bergmann <arnd@kernel.org> said:
> From: Arnd Bergmann <arnd@arndb.de>
>
> Most users of gpio-keys and gpio-keys-polled use modern gpiolib
> interfaces, but there are still number of ancient sh, arm32 and x86
> machines that have never been converted.
>
> Add an #ifdef block for the parts of the driver that are only used on
> those legacy machines.
>
> The two Rohm PMIC drivers use a gpio-keys device without an actual GPIO,
> passing an IRQ number instead. In order to keep this working both with
> and with CONFIG_GPIOLIB_LEGACY, change the gpio-keys driver to ignore
> the gpio number if an IRQ is passed.
>
> Link: https://lore.kernel.org/all/b3c94552-c104-42e3-be15-7e8362e8039e@gmail.com/
> Link: https://lore.kernel.org/all/afJXG4_rtaj3l2Dk@google.com/
> Signed-off-by: Arnd Bergmann <arnd@arndb.de>
> ---

Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>

^ permalink raw reply

* Re: [PATCH v3] HID: Expose LattePanda IOTA UPS as a power_supply device
From: Benjamin Tissoires @ 2026-05-21  6:54 UTC (permalink / raw)
  To: Andrew Maney; +Cc: jikos, linux-kernel, linux-input
In-Reply-To: <20260521031750.498110-1-andrewmaney05@gmail.com>

On May 20 2026, Andrew Maney wrote:
> This driver exposes the DFRobot LattePanda IOTA UPS board as a standard
> power_supply device, allowing desktop environments and power management
> tools such as UPower and systemd-logind to display battery status,
> remaining capacity, and charging status without any special
> configuration. It also enables automatic suspend or shutdown on low
> battery and power profile configuration via any tool that supports the
> standard power_supply interface.
> 
> The UPS presents itself as an Arduino Leonardo HID device running custom
> firmware (VID 0x2341, PID 0x8036). It reports status and capacity via
> HID reports 0x07 and 0x0C respectively.

As sashiko detected, you are using a standard Arduino Leonardo and not
making any specific detections. So I was thinking that maybe we should
implement that feature as a HID-BPF program on top of the generic HID
handling.

However, the handling of battery supplies in the HID generic core is not
entirely filling all of the requirements here, but that's something I
wanted to do for a couple of month but I have been swamped with other
projects.

Anyway, I wanted to understand why this product was using a generic
Leonardo PID, and: https://wiki.dfrobot.com/dfr1247#tech_specs

"it leverages the standard HID-UPS protocol to be natively recognized by
Windows as a battery-powered device"

So. Instead of working on a custom driver, why not simply implement (or
finish the generic implementation) of HID-UPS in hid-input.c?

Cheers,
Benjamin

> 
> The charge limit (80% or 100%) is configured via a physical DIP switch
> on the UPS board and cannot be detected automatically. Userspace can
> inform the driver of the configured limit via
> charge_control_end_threshold.
> 
> ---
> 
> Changes in v3:
> - Deferred power_supply registration to workqueue to avoid blocking probe
> - Fixed kernel panic when instantiated via uhid by checking hid_is_usb()
>   before dereferencing USB-specific structures
> 
> - Fixed ERR_PTR dereference in raw_event by only assigning ups->psu on
>   successful registration
> 
> - Fixed data race on ups->charge_limit using spin_lock_irqsave()
> - Removed TIME_TO_EMPTY_NOW and TIME_TO_FULL_NOW properties to avoid
> spurious shutdowns
> 
> - Changed plugged-in but not charging state from FULL to NOT_CHARGING
> - Used devm_kasprintf() for a unique sysfs name in order to support
> multiple devices
> 
> - Added POWER_SUPPLY and HIDRAW dependencies to Kconfig
> - Used %pe for more human-readable error messages
> 
> Changes in v2:
> - Rebased on top of the current tree
> - Moved vendor and device IDs to drivers/hid/hid-ids.h
> - Added Kconfig entry under HID bus support -> Special HID drivers
> - Added build rule to drivers/hid/Makefile
> 
> Signed-off-by: Andrew Maney <andrewmaney05@gmail.com>
> ---
>  MAINTAINERS                           |   6 +
>  drivers/hid/Kconfig                   |  10 +
>  drivers/hid/Makefile                  |   1 +
>  drivers/hid/hid-ids.h                 |   3 +
>  drivers/hid/hid-lattepanda-iota-ups.c | 409 ++++++++++++++++++++++++++
>  5 files changed, 429 insertions(+)
>  create mode 100644 drivers/hid/hid-lattepanda-iota-ups.c
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 10e825318..d80721c2c 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -11416,6 +11416,12 @@ F:	include/uapi/linux/hid*
>  F:	samples/hid/
>  F:	tools/testing/selftests/hid/
>  
> +HID LATTEPANDA IOTA UPS DRIVER
> +M:	Andrew Maney <andrewmaney05@gmail.com>
> +L:	linux-input@vger.kernel.org
> +S:	Maintained
> +F:	drivers/hid/hid-lattepanda-iota-ups.c
> +
>  HID LOGITECH DRIVERS
>  R:	Filipe Laíns <lains@riseup.net>
>  L:	linux-input@vger.kernel.org
> diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
> index ff2f580b6..21ffc2fd0 100644
> --- a/drivers/hid/Kconfig
> +++ b/drivers/hid/Kconfig
> @@ -510,6 +510,16 @@ config HID_KYSONA
>  	Say Y here if you have a Kysona M600 mouse
>  	and want to be able to read its battery capacity.
>  
> +config HID_LATTEPANDA_IOTA_UPS
> +	tristate "LattePanda IOTA UPS"
> +	depends on USB_HID && USB_HIDDEV && X86 && POWER_SUPPLY
> +	help
> +	Support for the LattePanda IOTA UPS (DFRobot, VID 0x2341 PID 0x8036).
> +	Exposes the battery status and capacity via the power_supply interface.
> +
> +	To compile as a module, choose M here: the module will be
> +	called hid-lattepanda-iota-ups.
> +
>  config HID_UCLOGIC
>  	tristate "UC-Logic"
>  	depends on USB_HID
> diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
> index 0597fd6a4..d7ad3fc8f 100644
> --- a/drivers/hid/Makefile
> +++ b/drivers/hid/Makefile
> @@ -74,6 +74,7 @@ obj-$(CONFIG_HID_KENSINGTON)	+= hid-kensington.o
>  obj-$(CONFIG_HID_KEYTOUCH)	+= hid-keytouch.o
>  obj-$(CONFIG_HID_KYE)		+= hid-kye.o
>  obj-$(CONFIG_HID_KYSONA)	+= hid-kysona.o
> +obj-$(CONFIG_HID_LATTEPANDA_IOTA_UPS)	+= hid-lattepanda-iota-ups.o
>  obj-$(CONFIG_HID_LCPOWER)	+= hid-lcpower.o
>  obj-$(CONFIG_HID_LENOVO)	+= hid-lenovo.o
>  obj-$(CONFIG_HID_LENOVO_GO)	+= hid-lenovo-go.o
> diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
> index 4657d96fb..6ded2c943 100644
> --- a/drivers/hid/hid-ids.h
> +++ b/drivers/hid/hid-ids.h
> @@ -859,6 +859,9 @@
>  #define USB_DEVICE_ID_LD_HYBRID		0x2090
>  #define USB_DEVICE_ID_LD_HEATCONTROL	0x20A0
>  
> +#define USB_VENDOR_ID_LATTEPANDA_IOTA	0x2341
> +#define USB_DEVICE_ID_LATTEPANDA_IOTA_UPS	0x8036
> +
>  #define USB_VENDOR_ID_LENOVO		0x17ef
>  #define USB_DEVICE_ID_LENOVO_TPKBD	0x6009
>  #define USB_DEVICE_ID_LENOVO_CUSBKBD	0x6047
> diff --git a/drivers/hid/hid-lattepanda-iota-ups.c b/drivers/hid/hid-lattepanda-iota-ups.c
> new file mode 100644
> index 000000000..f5d522695
> --- /dev/null
> +++ b/drivers/hid/hid-lattepanda-iota-ups.c
> @@ -0,0 +1,409 @@
> +// SPDX-License-Identifier: GPL-2.0
> +#include <linux/power_supply.h>
> +#include <linux/completion.h>
> +#include <linux/workqueue.h>
> +#include <linux/module.h>
> +#include <linux/spinlock.h>
> +#include <linux/hid.h>
> +#include <linux/usb.h>
> +#include "hid-ids.h"
> +
> +#define REPORT_ID_CAPACITY	0x0C
> +#define REPORT_ID_STATUS	0x07
> +
> +#define STATUS_DISCHARGING	BIT(1)
> +#define STATUS_PLUGGED_IN	BIT(0)
> +#define STATUS_CHARGING		BIT(2)
> +
> +MODULE_AUTHOR("Andrew Maney");
> +MODULE_DESCRIPTION("LattePanda IOTA UPS power supply driver");
> +MODULE_LICENSE("GPL");
> +
> +struct iota_ups {
> +	struct power_supply_desc psu_desc;
> +	struct power_supply *psu;
> +	struct hid_device *hiddev;
> +	spinlock_t lock; /* Protects cached HID report values */
> +
> +	/* Cached values that are updated from HID reports */
> +	bool plugged_in;
> +	char serial[64];
> +	int charge_limit;
> +	int psu_status;
> +	int capacity;
> +
> +	/*
> +	 * Wait for both status and capacity reports before registering
> +	 * with the power_supply core, so initial values are correct and
> +	 * not erroneous.
> +	 */
> +	struct completion got_initial_data;
> +	struct work_struct register_work;
> +	bool got_capacity;
> +	bool data_ready;
> +	bool got_status;
> +};
> +
> +static enum power_supply_property iota_ups_properties[] = {
> +	POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD,
> +	POWER_SUPPLY_PROP_SERIAL_NUMBER,
> +	POWER_SUPPLY_PROP_MANUFACTURER,
> +	POWER_SUPPLY_PROP_MODEL_NAME,
> +	POWER_SUPPLY_PROP_TECHNOLOGY,
> +	POWER_SUPPLY_PROP_CAPACITY,
> +	POWER_SUPPLY_PROP_PRESENT,
> +	POWER_SUPPLY_PROP_ONLINE,
> +	POWER_SUPPLY_PROP_STATUS,
> +	POWER_SUPPLY_PROP_SCOPE,
> +};
> +
> +static const struct hid_device_id iota_ups_devices[] = {
> +	{ HID_USB_DEVICE(USB_VENDOR_ID_LATTEPANDA_IOTA,
> +		USB_DEVICE_ID_LATTEPANDA_IOTA_UPS) },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(hid, iota_ups_devices);
> +
> +static int iota_ups_get_property(struct power_supply *supply,
> +				 enum power_supply_property psp,
> +				 union power_supply_propval *val)
> +{
> +	struct iota_ups *ups = power_supply_get_drvdata(supply);
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&ups->lock, flags);
> +
> +	switch (psp) {
> +	case POWER_SUPPLY_PROP_STATUS:
> +		val->intval = ups->psu_status;
> +		break;
> +
> +	/* Remaining capacity as a percentage from 0 to 100 */
> +	case POWER_SUPPLY_PROP_CAPACITY:
> +		val->intval = ups->capacity;
> +		break;
> +
> +	/* The UPS is always present if the driver is loaded */
> +	case POWER_SUPPLY_PROP_PRESENT:
> +		val->intval = 1;
> +		break;
> +
> +	/* Whether mains power is connected */
> +	case POWER_SUPPLY_PROP_ONLINE:
> +		val->intval = ups->plugged_in ? 1 : 0;
> +		break;
> +
> +	/*
> +	 * The UPS board supplies power to the IOTA and any
> +	 * peripherals connected to it, therefore its scope
> +	 * is system-wide.
> +	 */
> +	case POWER_SUPPLY_PROP_SCOPE:
> +		val->intval = POWER_SUPPLY_SCOPE_SYSTEM;
> +		break;
> +
> +	/* V1.0 only accepts 18650 Li-ion cells */
> +	case POWER_SUPPLY_PROP_TECHNOLOGY:
> +		val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
> +		break;
> +
> +	/* 80% or 100%, configured via a DIP switch on the UPS board */
> +	case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD:
> +		val->intval = ups->charge_limit;
> +		break;
> +
> +	case POWER_SUPPLY_PROP_MANUFACTURER:
> +		val->strval = "DFRobot";
> +		break;
> +
> +	case POWER_SUPPLY_PROP_MODEL_NAME:
> +		val->strval = "LattePanda IOTA UPS";
> +		break;
> +
> +	/* Retrieved from the USB descriptor */
> +	case POWER_SUPPLY_PROP_SERIAL_NUMBER:
> +		val->strval = ups->serial;
> +		break;
> +
> +	default:
> +		spin_unlock_irqrestore(&ups->lock, flags);
> +		return -EINVAL;
> +	}
> +
> +	spin_unlock_irqrestore(&ups->lock, flags);
> +	return 0;
> +}
> +
> +static int iota_ups_set_property(struct power_supply *supply,
> +				 enum power_supply_property psp,
> +				 const union power_supply_propval *val)
> +{
> +	struct iota_ups *ups = power_supply_get_drvdata(supply);
> +
> +	if (psp == POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD) {
> +		unsigned long flags;
> +
> +		/*
> +		 * V1.0 supports 80% and 100% charge limits only, which is
> +		 * set via a DIP switch on the board. This property allows
> +		 * userspace to inform the driver which limit is configured.
> +		 */
> +		if (val->intval != 80 && val->intval != 100)
> +			return -EINVAL;
> +
> +		spin_lock_irqsave(&ups->lock, flags);
> +		ups->charge_limit = val->intval;
> +		spin_unlock_irqrestore(&ups->lock, flags);
> +		return 0;
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +static int iota_ups_property_is_writable(struct power_supply *supply,
> +					 enum power_supply_property psp)
> +{
> +	return psp == POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD;
> +}
> +
> +static int iota_ups_raw_event(struct hid_device *hdev,
> +			      struct hid_report *report,
> +			      u8 *data, int size)
> +{
> +	struct iota_ups *ups = hid_get_drvdata(hdev);
> +	unsigned long flags;
> +	bool changed = false;
> +
> +	/* All of the UPS's reports are at least 2 bytes */
> +	if (size < 2)
> +		return 0;
> +
> +	spin_lock_irqsave(&ups->lock, flags);
> +
> +	switch (data[0]) {
> +	case REPORT_ID_STATUS: {
> +		u8 status = data[1];
> +		int new_status;
> +		bool plugged_in = !!(status & STATUS_PLUGGED_IN);
> +
> +		/*
> +		 * The UPS status is determined as follows:
> +		 * Battery full:
> +		 *	UPS is plugged in
> +		 *	Battery is at full capacity
> +		 *
> +		 * Battery charging:
> +		 *	UPS is plugged in
> +		 *	Battery is not at full capacity
> +		 *
> +		 * Battery discharging:
> +		 *	UPS is not plugged in
> +		 *
> +		 * Battery not charging:
> +		 *	UPS is plugged in
> +		 *	UPS has halted charging for some reason
> +		 *
> +		 * Unknown:
> +		 *	None of the above conditions are met
> +		 */
> +		if (status & STATUS_CHARGING) {
> +			if (ups->capacity >= ups->charge_limit)
> +				new_status = POWER_SUPPLY_STATUS_FULL;
> +			else
> +				new_status = POWER_SUPPLY_STATUS_CHARGING;
> +
> +		} else if (status & STATUS_DISCHARGING) {
> +			new_status = POWER_SUPPLY_STATUS_DISCHARGING;
> +
> +		} else if (plugged_in) {
> +			new_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
> +
> +		} else {
> +			new_status = POWER_SUPPLY_STATUS_UNKNOWN;
> +		}
> +
> +		if (new_status != ups->psu_status ||
> +		    plugged_in != ups->plugged_in) {
> +			ups->plugged_in = plugged_in;
> +			ups->psu_status = new_status;
> +			changed = true;
> +		}
> +
> +		ups->got_status = true;
> +		break;
> +	}
> +
> +	case REPORT_ID_CAPACITY: {
> +		int new_cap = clamp((int)data[1], 0, 100);
> +
> +		if (new_cap != ups->capacity) {
> +			ups->capacity = new_cap;
> +			changed = true;
> +		}
> +
> +		ups->got_capacity = true;
> +		break;
> +	}
> +	}
> +
> +	/*
> +	 * Signal that the UPS is ready to be registered because we have
> +	 * received both capacity and status reports.
> +	 */
> +	if (!ups->data_ready && ups->got_status && ups->got_capacity) {
> +		ups->data_ready = true;
> +		complete(&ups->got_initial_data);
> +	}
> +
> +	spin_unlock_irqrestore(&ups->lock, flags);
> +
> +	/*
> +	 * Notify the power_supply core outside the spinlock to avoid
> +	 * a deadlock; power_supply_changed() may call back into
> +	 * get_property() which acquires the same lock.
> +	 */
> +	if (changed && ups->psu)
> +		power_supply_changed(ups->psu);
> +
> +	return 0;
> +}
> +
> +static void iota_ups_register_work(struct work_struct *work)
> +{
> +	struct iota_ups *ups = container_of(work, struct iota_ups, register_work);
> +	struct power_supply_config psu_config = {};
> +	struct power_supply *psu;
> +
> +	/*
> +	 * Wait for both status and capacity reports before registering.
> +	 * The device sends reports every ~1 second, so 3 seconds is safe.
> +	 * We wait here in order to prevent registration in an unknown
> +	 * state, since this could cause emergency shutdowns or other
> +	 * undesired effects.
> +	 */
> +	wait_for_completion_timeout(&ups->got_initial_data,
> +				    msecs_to_jiffies(3000));
> +
> +	/* Configure the UPS's power supply properties */
> +	ups->psu_desc.name = devm_kasprintf(&ups->hiddev->dev, GFP_KERNEL,
> +					    "lattepanda-iota-ups.%s",
> +					    dev_name(&ups->hiddev->dev));
> +
> +	if (!ups->psu_desc.name) {
> +		hid_err(ups->hiddev, "failed to allocate power supply name\n");
> +		return;
> +	}
> +
> +	ups->psu_desc.property_is_writeable = iota_ups_property_is_writable;
> +	ups->psu_desc.num_properties	    = ARRAY_SIZE(iota_ups_properties);
> +	ups->psu_desc.get_property	    = iota_ups_get_property;
> +	ups->psu_desc.set_property	    = iota_ups_set_property;
> +	ups->psu_desc.properties	    = iota_ups_properties;
> +	ups->psu_desc.type		    = POWER_SUPPLY_TYPE_BATTERY;
> +	psu_config.drv_data		    = ups;
> +
> +	/* Register the UPS as a power_supply device */
> +	psu = devm_power_supply_register(&ups->hiddev->dev, &ups->psu_desc, &psu_config);
> +	if (IS_ERR(psu)) {
> +		hid_err(ups->hiddev, "power supply registration failed: %pe\n", psu);
> +		return;
> +	}
> +
> +	/*
> +	 * Finally, notify the power_supply core so userspace reads the correct
> +	 * initial state immediately after registration.
> +	 */
> +	ups->psu = psu;
> +	power_supply_changed(ups->psu);
> +	hid_info(ups->hiddev, "LattePanda IOTA UPS registered as a power_supply device\n");
> +}
> +
> +static int iota_ups_probe(struct hid_device *hdev,
> +			  const struct hid_device_id *id)
> +{
> +	struct iota_ups *ups;
> +	int ret;
> +
> +	ups = devm_kzalloc(&hdev->dev, sizeof(*ups), GFP_KERNEL);
> +	if (!ups)
> +		return -ENOMEM;
> +
> +	ups->hiddev = hdev;
> +	ups->psu_status = POWER_SUPPLY_STATUS_UNKNOWN;
> +
> +	/* 50% is a safe default if wait_for_completion_timeout() times out. */
> +	ups->capacity = 50;
> +
> +	/*
> +	 * Default to 100% to prevent unexpected shutdowns.
> +	 * Userspace can update this via charge_control_end_threshold.
> +	 */
> +	ups->charge_limit = 100;
> +
> +	init_completion(&ups->got_initial_data);
> +	spin_lock_init(&ups->lock);
> +	hid_set_drvdata(hdev, ups);
> +
> +	/*
> +	 * Retrieve the UPS's serial number from the USB descriptor. If the device is not
> +	 * a USB device, we can use the unique device identifier as the serial number.
> +	 */
> +	if (hid_is_usb(hdev)) {
> +		struct usb_device *udev = to_usb_device(hdev->dev.parent->parent);
> +
> +		if (udev->serial)
> +			strscpy(ups->serial, udev->serial, sizeof(ups->serial));
> +		else
> +			strscpy(ups->serial, "Unknown", sizeof(ups->serial));
> +	} else {
> +		if (*hdev->uniq)
> +			strscpy(ups->serial, hdev->uniq, sizeof(ups->serial));
> +		else
> +			strscpy(ups->serial, "Unknown", sizeof(ups->serial));
> +	}
> +
> +	ret = hid_parse(hdev);
> +	if (ret) {
> +		hid_err(hdev, "HID parse failed: %pe\n", ERR_PTR(ret));
> +		return ret;
> +	}
> +
> +	ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
> +	if (ret) {
> +		hid_err(hdev, "HID hw start failed: %pe\n", ERR_PTR(ret));
> +		return ret;
> +	}
> +
> +	ret = hid_hw_open(hdev);
> +	if (ret) {
> +		hid_err(hdev, "HID hw open failed: %pe\n", ERR_PTR(ret));
> +		goto err_stop;
> +	}
> +
> +	/* Probe for the UPS in a worker queue so we don't halt the enumeration thread */
> +	INIT_WORK(&ups->register_work, iota_ups_register_work);
> +	schedule_work(&ups->register_work);
> +	return 0;
> +
> +err_stop:
> +	hid_hw_stop(hdev);
> +	return ret;
> +}
> +
> +static void iota_ups_remove(struct hid_device *hdev)
> +{
> +	struct iota_ups *ups = hid_get_drvdata(hdev);
> +
> +	cancel_work_sync(&ups->register_work);
> +	hid_hw_close(hdev);
> +	hid_hw_stop(hdev);
> +}
> +
> +static struct hid_driver iota_ups_driver = {
> +	.name = "lattepanda-iota-ups",
> +	.id_table = iota_ups_devices,
> +	.probe = iota_ups_probe,
> +	.remove = iota_ups_remove,
> +	.raw_event = iota_ups_raw_event,
> +};
> +module_hid_driver(iota_ups_driver);
> -- 
> 2.54.0
> 
> 

^ permalink raw reply

* Re: [PATCH 04/10] [v2] sh: select legacy gpiolib interface
From: John Paul Adrian Glaubitz @ 2026-05-21  6:49 UTC (permalink / raw)
  To: Arnd Bergmann, linux-gpio
  Cc: linux-kernel, Arnd Bergmann, Christian Lamparter, Johannes Berg,
	Aaro Koskinen, Andreas Kemnade, Kevin Hilman, Roger Quadros,
	Tony Lindgren, Thomas Bogendoerfer, Thomas Gleixner, Ingo Molnar,
	Borislav Petkov, Dave Hansen, x86, H. Peter Anvin, Linus Walleij,
	Bartosz Golaszewski, Dmitry Torokhov, Lee Jones, Pavel Machek,
	Matti Vaittinen, Florian Fainelli, Jonas Gorski, Andrew Lunn,
	Vladimir Oltean, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, linux-wireless, linux-omap, linux-arm-kernel,
	linux-mips, linux-sh, linux-input, linux-leds, netdev
In-Reply-To: <20260520183815.2510387-5-arnd@kernel.org>

Hi Arnd,

On Wed, 2026-05-20 at 20:38 +0200, Arnd Bergmann wrote:
> From: Arnd Bergmann <arnd@arndb.de>
> 
> Many board files on sh reference the legacy gpiolib interfaces that
> are becoming optional. To ensure the boards can keep building, select
> CONFIG_GPIOLIB_LEGACY on each of the boards that have one of the
> hardcoded calls.
> 
> Cc: John Paul Adrian Glaubitz <glaubitz@physik.fu-berlin.de>
> Signed-off-by: Arnd Bergmann <arnd@arndb.de>
> ---
> v2: no changes. Adrian said he'll pick it up for 7.2, but so
>     far the patch is not in linux-next yet, so I'm including it
>     for completeness here.

Sorry, I hadn't gotten around to pick the changes for v7.2 yet. I can
pick it up this weekend as I was planning to review and merge some
patches this weekend.

I have received quite a lot of patches for SH recently, so it will take
some time to dig myself through the queue.

Adrian

-- 
 .''`.  John Paul Adrian Glaubitz
: :' :  Debian Developer
`. `'   Physicist
  `-    GPG: 62FF 8A75 84E0 2956 9546  0006 7426 3B37 F5B5 F913

^ permalink raw reply

* Re: [PATCH v3] HID: Expose LattePanda IOTA UPS as a power_supply device
From: sashiko-bot @ 2026-05-21  3:45 UTC (permalink / raw)
  To: Andrew Maney; +Cc: linux-input, dmitry.torokhov
In-Reply-To: <20260521031750.498110-1-andrewmaney05@gmail.com>

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] The driver unconditionally binds to the generic Arduino Leonardo VID/PID, breaking standard HID input functionality for all such devices.
- [Medium] Device removal can block the USB disconnect process for up to 3 seconds if the device is unplugged during initialization.
- [Medium] The Kconfig dependency incorrectly requires `USB_HIDDEV`, preventing the driver from being built on modern systems.
- [Low] Unsynchronized access to the `ups->psu` pointer causes a C data race.
--

commit 9862889f837997957a0680fd4f3667a6d06c2828
Author: Andrew Maney <andrewmaney05@gmail.com>

HID: Expose LattePanda IOTA UPS as a power_supply device

This commit introduces a new HID driver to expose the LattePanda IOTA UPS
as a power_supply device. It reports battery status and capacity via HID
reports from a custom Arduino Leonardo firmware.

> diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
> index ff2f580b660ba..21ffc2fd06096 100644
> --- a/drivers/hid/Kconfig
> +++ b/drivers/hid/Kconfig
> @@ -510,6 +510,16 @@ config HID_KYSONA
>  	Say Y here if you have a Kysona M600 mouse
>  	and want to be able to read its battery capacity.
>  
> +config HID_LATTEPANDA_IOTA_UPS
> +	tristate "LattePanda IOTA UPS"
> +	depends on USB_HID && USB_HIDDEV && X86 && POWER_SUPPLY

[Severity: Medium]
Does this driver actually require the legacy /dev/usb/hiddevX interface?

Since the driver connects using HID_CONNECT_HIDRAW, depending on USB_HIDDEV
could unnecessarily prevent the driver from being enabled or compiled on
distributions that have disabled legacy hiddev support.

> +	help
> +	Support for the LattePanda IOTA UPS (DFRobot, VID 0x2341 PID 0x8036).
> +	Exposes the battery status and capacity via the power_supply interface.

[ ... ]

> diff --git a/drivers/hid/hid-lattepanda-iota-ups.c b/drivers/hid/hid-lattepanda-iota-ups.c
> new file mode 100644
> index 0000000000000..f5d522695081e
> --- /dev/null
> +++ b/drivers/hid/hid-lattepanda-iota-ups.c
> @@ -0,0 +1,409 @@
[ ... ]
> +static const struct hid_device_id iota_ups_devices[] = {
> +	{ HID_USB_DEVICE(USB_VENDOR_ID_LATTEPANDA_IOTA,
> +		USB_DEVICE_ID_LATTEPANDA_IOTA_UPS) },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(hid, iota_ups_devices);

[Severity: High]
Does this driver unconditionally bind to the generic Arduino Leonardo VID/PID
(0x2341/0x8036)?

Since it matches the standard development board ID and uses HID_CONNECT_HIDRAW
without HID_CONNECT_HIDINPUT, will this prevent any Arduino Leonardo acting as
a standard keyboard, mouse, or gamepad from functioning properly?

The driver might need to inspect the HID report descriptor during probe()
(e.g., checking for specific report IDs or usages) to confirm it is actually
communicating with the custom UPS firmware before returning success.

[ ... ]

> +static int iota_ups_raw_event(struct hid_device *hdev,
> +			      struct hid_report *report,
> +			      u8 *data, int size)
> +{
[ ... ]
> +	/*
> +	 * Notify the power_supply core outside the spinlock to avoid
> +	 * a deadlock; power_supply_changed() may call back into
> +	 * get_property() which acquires the same lock.
> +	 */
> +	if (changed && ups->psu)
> +		power_supply_changed(ups->psu);

[Severity: Low]
Could there be a data race when accessing ups->psu here?

In iota_ups_register_work(), ups->psu is assigned without synchronization.
Concurrently, iota_ups_raw_event() reads ups->psu outside of the spinlock.

While x86 aligned pointer writes are atomic (preventing partial pointer reads),
this is undefined behavior in C. Using WRITE_ONCE() and READ_ONCE(), or safely
reading the pointer inside the existing ups->lock critical section, would
prevent this.

> +
> +	return 0;
> +}

[ ... ]

> +static void iota_ups_remove(struct hid_device *hdev)
> +{
> +	struct iota_ups *ups = hid_get_drvdata(hdev);
> +
> +	cancel_work_sync(&ups->register_work);

[Severity: Medium]
Will device removal block the USB disconnect process for up to 3 seconds if
the device is unplugged during initialization?

If the device is abruptly disconnected while iota_ups_register_work() is still
waiting in wait_for_completion_timeout(), cancel_work_sync() will block here.

Since the physically removed device cannot send reports to trigger complete(),
the wait will always take the full 3 seconds. The driver likely needs to signal
completion proactively during removal to abort the wait before calling
cancel_work_sync().

> +	hid_hw_close(hdev);
> +	hid_hw_stop(hdev);
> +}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260521031750.498110-1-andrewmaney05@gmail.com?part=1

^ permalink raw reply

* [PATCH v3] HID: Expose LattePanda IOTA UPS as a power_supply device
From: Andrew Maney @ 2026-05-21  3:17 UTC (permalink / raw)
  To: jikos; +Cc: bentiss, linux-kernel, linux-input, Andrew Maney

This driver exposes the DFRobot LattePanda IOTA UPS board as a standard
power_supply device, allowing desktop environments and power management
tools such as UPower and systemd-logind to display battery status,
remaining capacity, and charging status without any special
configuration. It also enables automatic suspend or shutdown on low
battery and power profile configuration via any tool that supports the
standard power_supply interface.

The UPS presents itself as an Arduino Leonardo HID device running custom
firmware (VID 0x2341, PID 0x8036). It reports status and capacity via
HID reports 0x07 and 0x0C respectively.

The charge limit (80% or 100%) is configured via a physical DIP switch
on the UPS board and cannot be detected automatically. Userspace can
inform the driver of the configured limit via
charge_control_end_threshold.

---

Changes in v3:
- Deferred power_supply registration to workqueue to avoid blocking probe
- Fixed kernel panic when instantiated via uhid by checking hid_is_usb()
  before dereferencing USB-specific structures

- Fixed ERR_PTR dereference in raw_event by only assigning ups->psu on
  successful registration

- Fixed data race on ups->charge_limit using spin_lock_irqsave()
- Removed TIME_TO_EMPTY_NOW and TIME_TO_FULL_NOW properties to avoid
spurious shutdowns

- Changed plugged-in but not charging state from FULL to NOT_CHARGING
- Used devm_kasprintf() for a unique sysfs name in order to support
multiple devices

- Added POWER_SUPPLY and HIDRAW dependencies to Kconfig
- Used %pe for more human-readable error messages

Changes in v2:
- Rebased on top of the current tree
- Moved vendor and device IDs to drivers/hid/hid-ids.h
- Added Kconfig entry under HID bus support -> Special HID drivers
- Added build rule to drivers/hid/Makefile

Signed-off-by: Andrew Maney <andrewmaney05@gmail.com>
---
 MAINTAINERS                           |   6 +
 drivers/hid/Kconfig                   |  10 +
 drivers/hid/Makefile                  |   1 +
 drivers/hid/hid-ids.h                 |   3 +
 drivers/hid/hid-lattepanda-iota-ups.c | 409 ++++++++++++++++++++++++++
 5 files changed, 429 insertions(+)
 create mode 100644 drivers/hid/hid-lattepanda-iota-ups.c

diff --git a/MAINTAINERS b/MAINTAINERS
index 10e825318..d80721c2c 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -11416,6 +11416,12 @@ F:	include/uapi/linux/hid*
 F:	samples/hid/
 F:	tools/testing/selftests/hid/
 
+HID LATTEPANDA IOTA UPS DRIVER
+M:	Andrew Maney <andrewmaney05@gmail.com>
+L:	linux-input@vger.kernel.org
+S:	Maintained
+F:	drivers/hid/hid-lattepanda-iota-ups.c
+
 HID LOGITECH DRIVERS
 R:	Filipe Laíns <lains@riseup.net>
 L:	linux-input@vger.kernel.org
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index ff2f580b6..21ffc2fd0 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -510,6 +510,16 @@ config HID_KYSONA
 	Say Y here if you have a Kysona M600 mouse
 	and want to be able to read its battery capacity.
 
+config HID_LATTEPANDA_IOTA_UPS
+	tristate "LattePanda IOTA UPS"
+	depends on USB_HID && USB_HIDDEV && X86 && POWER_SUPPLY
+	help
+	Support for the LattePanda IOTA UPS (DFRobot, VID 0x2341 PID 0x8036).
+	Exposes the battery status and capacity via the power_supply interface.
+
+	To compile as a module, choose M here: the module will be
+	called hid-lattepanda-iota-ups.
+
 config HID_UCLOGIC
 	tristate "UC-Logic"
 	depends on USB_HID
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index 0597fd6a4..d7ad3fc8f 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -74,6 +74,7 @@ obj-$(CONFIG_HID_KENSINGTON)	+= hid-kensington.o
 obj-$(CONFIG_HID_KEYTOUCH)	+= hid-keytouch.o
 obj-$(CONFIG_HID_KYE)		+= hid-kye.o
 obj-$(CONFIG_HID_KYSONA)	+= hid-kysona.o
+obj-$(CONFIG_HID_LATTEPANDA_IOTA_UPS)	+= hid-lattepanda-iota-ups.o
 obj-$(CONFIG_HID_LCPOWER)	+= hid-lcpower.o
 obj-$(CONFIG_HID_LENOVO)	+= hid-lenovo.o
 obj-$(CONFIG_HID_LENOVO_GO)	+= hid-lenovo-go.o
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 4657d96fb..6ded2c943 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -859,6 +859,9 @@
 #define USB_DEVICE_ID_LD_HYBRID		0x2090
 #define USB_DEVICE_ID_LD_HEATCONTROL	0x20A0
 
+#define USB_VENDOR_ID_LATTEPANDA_IOTA	0x2341
+#define USB_DEVICE_ID_LATTEPANDA_IOTA_UPS	0x8036
+
 #define USB_VENDOR_ID_LENOVO		0x17ef
 #define USB_DEVICE_ID_LENOVO_TPKBD	0x6009
 #define USB_DEVICE_ID_LENOVO_CUSBKBD	0x6047
diff --git a/drivers/hid/hid-lattepanda-iota-ups.c b/drivers/hid/hid-lattepanda-iota-ups.c
new file mode 100644
index 000000000..f5d522695
--- /dev/null
+++ b/drivers/hid/hid-lattepanda-iota-ups.c
@@ -0,0 +1,409 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/power_supply.h>
+#include <linux/completion.h>
+#include <linux/workqueue.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/hid.h>
+#include <linux/usb.h>
+#include "hid-ids.h"
+
+#define REPORT_ID_CAPACITY	0x0C
+#define REPORT_ID_STATUS	0x07
+
+#define STATUS_DISCHARGING	BIT(1)
+#define STATUS_PLUGGED_IN	BIT(0)
+#define STATUS_CHARGING		BIT(2)
+
+MODULE_AUTHOR("Andrew Maney");
+MODULE_DESCRIPTION("LattePanda IOTA UPS power supply driver");
+MODULE_LICENSE("GPL");
+
+struct iota_ups {
+	struct power_supply_desc psu_desc;
+	struct power_supply *psu;
+	struct hid_device *hiddev;
+	spinlock_t lock; /* Protects cached HID report values */
+
+	/* Cached values that are updated from HID reports */
+	bool plugged_in;
+	char serial[64];
+	int charge_limit;
+	int psu_status;
+	int capacity;
+
+	/*
+	 * Wait for both status and capacity reports before registering
+	 * with the power_supply core, so initial values are correct and
+	 * not erroneous.
+	 */
+	struct completion got_initial_data;
+	struct work_struct register_work;
+	bool got_capacity;
+	bool data_ready;
+	bool got_status;
+};
+
+static enum power_supply_property iota_ups_properties[] = {
+	POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD,
+	POWER_SUPPLY_PROP_SERIAL_NUMBER,
+	POWER_SUPPLY_PROP_MANUFACTURER,
+	POWER_SUPPLY_PROP_MODEL_NAME,
+	POWER_SUPPLY_PROP_TECHNOLOGY,
+	POWER_SUPPLY_PROP_CAPACITY,
+	POWER_SUPPLY_PROP_PRESENT,
+	POWER_SUPPLY_PROP_ONLINE,
+	POWER_SUPPLY_PROP_STATUS,
+	POWER_SUPPLY_PROP_SCOPE,
+};
+
+static const struct hid_device_id iota_ups_devices[] = {
+	{ HID_USB_DEVICE(USB_VENDOR_ID_LATTEPANDA_IOTA,
+		USB_DEVICE_ID_LATTEPANDA_IOTA_UPS) },
+	{ }
+};
+MODULE_DEVICE_TABLE(hid, iota_ups_devices);
+
+static int iota_ups_get_property(struct power_supply *supply,
+				 enum power_supply_property psp,
+				 union power_supply_propval *val)
+{
+	struct iota_ups *ups = power_supply_get_drvdata(supply);
+	unsigned long flags;
+
+	spin_lock_irqsave(&ups->lock, flags);
+
+	switch (psp) {
+	case POWER_SUPPLY_PROP_STATUS:
+		val->intval = ups->psu_status;
+		break;
+
+	/* Remaining capacity as a percentage from 0 to 100 */
+	case POWER_SUPPLY_PROP_CAPACITY:
+		val->intval = ups->capacity;
+		break;
+
+	/* The UPS is always present if the driver is loaded */
+	case POWER_SUPPLY_PROP_PRESENT:
+		val->intval = 1;
+		break;
+
+	/* Whether mains power is connected */
+	case POWER_SUPPLY_PROP_ONLINE:
+		val->intval = ups->plugged_in ? 1 : 0;
+		break;
+
+	/*
+	 * The UPS board supplies power to the IOTA and any
+	 * peripherals connected to it, therefore its scope
+	 * is system-wide.
+	 */
+	case POWER_SUPPLY_PROP_SCOPE:
+		val->intval = POWER_SUPPLY_SCOPE_SYSTEM;
+		break;
+
+	/* V1.0 only accepts 18650 Li-ion cells */
+	case POWER_SUPPLY_PROP_TECHNOLOGY:
+		val->intval = POWER_SUPPLY_TECHNOLOGY_LION;
+		break;
+
+	/* 80% or 100%, configured via a DIP switch on the UPS board */
+	case POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD:
+		val->intval = ups->charge_limit;
+		break;
+
+	case POWER_SUPPLY_PROP_MANUFACTURER:
+		val->strval = "DFRobot";
+		break;
+
+	case POWER_SUPPLY_PROP_MODEL_NAME:
+		val->strval = "LattePanda IOTA UPS";
+		break;
+
+	/* Retrieved from the USB descriptor */
+	case POWER_SUPPLY_PROP_SERIAL_NUMBER:
+		val->strval = ups->serial;
+		break;
+
+	default:
+		spin_unlock_irqrestore(&ups->lock, flags);
+		return -EINVAL;
+	}
+
+	spin_unlock_irqrestore(&ups->lock, flags);
+	return 0;
+}
+
+static int iota_ups_set_property(struct power_supply *supply,
+				 enum power_supply_property psp,
+				 const union power_supply_propval *val)
+{
+	struct iota_ups *ups = power_supply_get_drvdata(supply);
+
+	if (psp == POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD) {
+		unsigned long flags;
+
+		/*
+		 * V1.0 supports 80% and 100% charge limits only, which is
+		 * set via a DIP switch on the board. This property allows
+		 * userspace to inform the driver which limit is configured.
+		 */
+		if (val->intval != 80 && val->intval != 100)
+			return -EINVAL;
+
+		spin_lock_irqsave(&ups->lock, flags);
+		ups->charge_limit = val->intval;
+		spin_unlock_irqrestore(&ups->lock, flags);
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static int iota_ups_property_is_writable(struct power_supply *supply,
+					 enum power_supply_property psp)
+{
+	return psp == POWER_SUPPLY_PROP_CHARGE_CONTROL_END_THRESHOLD;
+}
+
+static int iota_ups_raw_event(struct hid_device *hdev,
+			      struct hid_report *report,
+			      u8 *data, int size)
+{
+	struct iota_ups *ups = hid_get_drvdata(hdev);
+	unsigned long flags;
+	bool changed = false;
+
+	/* All of the UPS's reports are at least 2 bytes */
+	if (size < 2)
+		return 0;
+
+	spin_lock_irqsave(&ups->lock, flags);
+
+	switch (data[0]) {
+	case REPORT_ID_STATUS: {
+		u8 status = data[1];
+		int new_status;
+		bool plugged_in = !!(status & STATUS_PLUGGED_IN);
+
+		/*
+		 * The UPS status is determined as follows:
+		 * Battery full:
+		 *	UPS is plugged in
+		 *	Battery is at full capacity
+		 *
+		 * Battery charging:
+		 *	UPS is plugged in
+		 *	Battery is not at full capacity
+		 *
+		 * Battery discharging:
+		 *	UPS is not plugged in
+		 *
+		 * Battery not charging:
+		 *	UPS is plugged in
+		 *	UPS has halted charging for some reason
+		 *
+		 * Unknown:
+		 *	None of the above conditions are met
+		 */
+		if (status & STATUS_CHARGING) {
+			if (ups->capacity >= ups->charge_limit)
+				new_status = POWER_SUPPLY_STATUS_FULL;
+			else
+				new_status = POWER_SUPPLY_STATUS_CHARGING;
+
+		} else if (status & STATUS_DISCHARGING) {
+			new_status = POWER_SUPPLY_STATUS_DISCHARGING;
+
+		} else if (plugged_in) {
+			new_status = POWER_SUPPLY_STATUS_NOT_CHARGING;
+
+		} else {
+			new_status = POWER_SUPPLY_STATUS_UNKNOWN;
+		}
+
+		if (new_status != ups->psu_status ||
+		    plugged_in != ups->plugged_in) {
+			ups->plugged_in = plugged_in;
+			ups->psu_status = new_status;
+			changed = true;
+		}
+
+		ups->got_status = true;
+		break;
+	}
+
+	case REPORT_ID_CAPACITY: {
+		int new_cap = clamp((int)data[1], 0, 100);
+
+		if (new_cap != ups->capacity) {
+			ups->capacity = new_cap;
+			changed = true;
+		}
+
+		ups->got_capacity = true;
+		break;
+	}
+	}
+
+	/*
+	 * Signal that the UPS is ready to be registered because we have
+	 * received both capacity and status reports.
+	 */
+	if (!ups->data_ready && ups->got_status && ups->got_capacity) {
+		ups->data_ready = true;
+		complete(&ups->got_initial_data);
+	}
+
+	spin_unlock_irqrestore(&ups->lock, flags);
+
+	/*
+	 * Notify the power_supply core outside the spinlock to avoid
+	 * a deadlock; power_supply_changed() may call back into
+	 * get_property() which acquires the same lock.
+	 */
+	if (changed && ups->psu)
+		power_supply_changed(ups->psu);
+
+	return 0;
+}
+
+static void iota_ups_register_work(struct work_struct *work)
+{
+	struct iota_ups *ups = container_of(work, struct iota_ups, register_work);
+	struct power_supply_config psu_config = {};
+	struct power_supply *psu;
+
+	/*
+	 * Wait for both status and capacity reports before registering.
+	 * The device sends reports every ~1 second, so 3 seconds is safe.
+	 * We wait here in order to prevent registration in an unknown
+	 * state, since this could cause emergency shutdowns or other
+	 * undesired effects.
+	 */
+	wait_for_completion_timeout(&ups->got_initial_data,
+				    msecs_to_jiffies(3000));
+
+	/* Configure the UPS's power supply properties */
+	ups->psu_desc.name = devm_kasprintf(&ups->hiddev->dev, GFP_KERNEL,
+					    "lattepanda-iota-ups.%s",
+					    dev_name(&ups->hiddev->dev));
+
+	if (!ups->psu_desc.name) {
+		hid_err(ups->hiddev, "failed to allocate power supply name\n");
+		return;
+	}
+
+	ups->psu_desc.property_is_writeable = iota_ups_property_is_writable;
+	ups->psu_desc.num_properties	    = ARRAY_SIZE(iota_ups_properties);
+	ups->psu_desc.get_property	    = iota_ups_get_property;
+	ups->psu_desc.set_property	    = iota_ups_set_property;
+	ups->psu_desc.properties	    = iota_ups_properties;
+	ups->psu_desc.type		    = POWER_SUPPLY_TYPE_BATTERY;
+	psu_config.drv_data		    = ups;
+
+	/* Register the UPS as a power_supply device */
+	psu = devm_power_supply_register(&ups->hiddev->dev, &ups->psu_desc, &psu_config);
+	if (IS_ERR(psu)) {
+		hid_err(ups->hiddev, "power supply registration failed: %pe\n", psu);
+		return;
+	}
+
+	/*
+	 * Finally, notify the power_supply core so userspace reads the correct
+	 * initial state immediately after registration.
+	 */
+	ups->psu = psu;
+	power_supply_changed(ups->psu);
+	hid_info(ups->hiddev, "LattePanda IOTA UPS registered as a power_supply device\n");
+}
+
+static int iota_ups_probe(struct hid_device *hdev,
+			  const struct hid_device_id *id)
+{
+	struct iota_ups *ups;
+	int ret;
+
+	ups = devm_kzalloc(&hdev->dev, sizeof(*ups), GFP_KERNEL);
+	if (!ups)
+		return -ENOMEM;
+
+	ups->hiddev = hdev;
+	ups->psu_status = POWER_SUPPLY_STATUS_UNKNOWN;
+
+	/* 50% is a safe default if wait_for_completion_timeout() times out. */
+	ups->capacity = 50;
+
+	/*
+	 * Default to 100% to prevent unexpected shutdowns.
+	 * Userspace can update this via charge_control_end_threshold.
+	 */
+	ups->charge_limit = 100;
+
+	init_completion(&ups->got_initial_data);
+	spin_lock_init(&ups->lock);
+	hid_set_drvdata(hdev, ups);
+
+	/*
+	 * Retrieve the UPS's serial number from the USB descriptor. If the device is not
+	 * a USB device, we can use the unique device identifier as the serial number.
+	 */
+	if (hid_is_usb(hdev)) {
+		struct usb_device *udev = to_usb_device(hdev->dev.parent->parent);
+
+		if (udev->serial)
+			strscpy(ups->serial, udev->serial, sizeof(ups->serial));
+		else
+			strscpy(ups->serial, "Unknown", sizeof(ups->serial));
+	} else {
+		if (*hdev->uniq)
+			strscpy(ups->serial, hdev->uniq, sizeof(ups->serial));
+		else
+			strscpy(ups->serial, "Unknown", sizeof(ups->serial));
+	}
+
+	ret = hid_parse(hdev);
+	if (ret) {
+		hid_err(hdev, "HID parse failed: %pe\n", ERR_PTR(ret));
+		return ret;
+	}
+
+	ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW);
+	if (ret) {
+		hid_err(hdev, "HID hw start failed: %pe\n", ERR_PTR(ret));
+		return ret;
+	}
+
+	ret = hid_hw_open(hdev);
+	if (ret) {
+		hid_err(hdev, "HID hw open failed: %pe\n", ERR_PTR(ret));
+		goto err_stop;
+	}
+
+	/* Probe for the UPS in a worker queue so we don't halt the enumeration thread */
+	INIT_WORK(&ups->register_work, iota_ups_register_work);
+	schedule_work(&ups->register_work);
+	return 0;
+
+err_stop:
+	hid_hw_stop(hdev);
+	return ret;
+}
+
+static void iota_ups_remove(struct hid_device *hdev)
+{
+	struct iota_ups *ups = hid_get_drvdata(hdev);
+
+	cancel_work_sync(&ups->register_work);
+	hid_hw_close(hdev);
+	hid_hw_stop(hdev);
+}
+
+static struct hid_driver iota_ups_driver = {
+	.name = "lattepanda-iota-ups",
+	.id_table = iota_ups_devices,
+	.probe = iota_ups_probe,
+	.remove = iota_ups_remove,
+	.raw_event = iota_ups_raw_event,
+};
+module_hid_driver(iota_ups_driver);
-- 
2.54.0


^ permalink raw reply related

* Re: [syzbot] [input?] possible deadlock in tasklet_action_common (2)
From: Hillf Danton @ 2026-05-21  1:52 UTC (permalink / raw)
  To: syzbot
  Cc: dmitry.torokhov, linux-input, Sebastian Andrzej Siewior,
	Tetsuo Handa, linux-kernel, syzkaller-bugs
In-Reply-To: <6a0de9e2.050a0220.18b3cb.000d.GAE@google.com>

> Date: Wed, 20 May 2026 10:05:38 -0700	[thread overview]
> Hello,
> 
> syzbot found the following issue on:
> 
> HEAD commit:    e98d21c170b0 Add linux-next specific files for 20260508
> git tree:       linux-next
> console output: https://syzkaller.appspot.com/x/log.txt?x=1354bfce580000
> kernel config:  https://syzkaller.appspot.com/x/.config?x=59b98218d9b2edf4
> dashboard link: https://syzkaller.appspot.com/bug?extid=b5d7ab56d43de3fd5aac
> compiler:       Debian clang version 21.1.8 (++20251221033036+2078da43e25a-1~exp1~20251221153213.50), Debian LLD 21.1.8
> 
> Unfortunately, I don't have any reproducer for this issue yet.
> 
> Downloadable assets:
> disk image: https://storage.googleapis.com/syzbot-assets/66f2a00ee290/disk-e98d21c1.raw.xz
> vmlinux: https://storage.googleapis.com/syzbot-assets/6b982257ce9e/vmlinux-e98d21c1.xz
> kernel image: https://storage.googleapis.com/syzbot-assets/a73fbea43e1a/bzImage-e98d21c1.xz
> 
> IMPORTANT: if you fix the issue, please add the following tag to the commit:
> Reported-by: syzbot+b5d7ab56d43de3fd5aac@syzkaller.appspotmail.com
> 
> ======================================================
> WARNING: possible circular locking dependency detected
> syzkaller #0 Tainted: G             L     
> ------------------------------------------------------
> syz.2.4328/32663 is trying to acquire lock:
> ffff8880b8724168 (tasklet_sync_callback.cb_lock){+...}-{3:3}, at: spin_lock include/linux/spinlock_rt.h:45 [inline]
> ffff8880b8724168 (tasklet_sync_callback.cb_lock){+...}-{3:3}, at: tasklet_lock_callback kernel/softirq.c:881 [inline]
> ffff8880b8724168 (tasklet_sync_callback.cb_lock){+...}-{3:3}, at: tasklet_action_common+0xc5/0x610 kernel/softirq.c:931
> 
> but task is already holding lock:
> ffff888058598270 (&dev->event_lock#2){+.+.}-{3:3}, at: spin_lock include/linux/spinlock_rt.h:45 [inline]
> ffff888058598270 (&dev->event_lock#2){+.+.}-{3:3}, at: class_spinlock_irqsave_constructor include/linux/spinlock.h:619 [inline]
> ffff888058598270 (&dev->event_lock#2){+.+.}-{3:3}, at: input_inject_event+0xa4/0x320 drivers/input/input.c:419
> 
> which lock already depends on the new lock.
> 
> 
> the existing dependency chain (in reverse order) is:
> 
> -> #1 (&dev->event_lock#2){+.+.}-{3:3}:
>        rt_spin_lock+0x83/0x400 kernel/locking/spinlock_rt.c:56
>        spin_lock include/linux/spinlock_rt.h:45 [inline]
>        class_spinlock_irqsave_constructor include/linux/spinlock.h:619 [inline]
>        input_inject_event+0xa4/0x320 drivers/input/input.c:419
>        led_trigger_event+0x13b/0x220 drivers/leds/led-triggers.c:420
>        kbd_propagate_led_state drivers/tty/vt/keyboard.c:1118 [inline]
>        kbd_bh+0x1b4/0x2c0 drivers/tty/vt/keyboard.c:1297
>        tasklet_action_common+0x31c/0x610 kernel/softirq.c:942
>        handle_softirqs+0x1de/0x6d0 kernel/softirq.c:626
>        __do_softirq kernel/softirq.c:660 [inline]
>        run_ktimerd+0x69/0x100 kernel/softirq.c:1155
>        smpboot_thread_fn+0x541/0xa50 kernel/smpboot.c:160
>        kthread+0x388/0x470 kernel/kthread.c:436
>        ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158
>        ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
> 
Deadlock if the timer thread is preempted

	timer thread
	tasklet_sync_callback.cb_lock

	// preempted
		&dev->event_lock
		do softirq
		acquire tasklet_sync_callback.cb_lock

	acquire &dev->event_lock


> -> #0 (tasklet_sync_callback.cb_lock){+...}-{3:3}:
>        check_prev_add kernel/locking/lockdep.c:3167 [inline]
>        check_prevs_add kernel/locking/lockdep.c:3286 [inline]
>        validate_chain kernel/locking/lockdep.c:3910 [inline]
>        __lock_acquire+0x15a5/0x2d10 kernel/locking/lockdep.c:5239
>        lock_acquire+0x106/0x350 kernel/locking/lockdep.c:5870
>        rt_spin_lock+0x83/0x400 kernel/locking/spinlock_rt.c:56
>        spin_lock include/linux/spinlock_rt.h:45 [inline]
>        tasklet_lock_callback kernel/softirq.c:881 [inline]
>        tasklet_action_common+0xc5/0x610 kernel/softirq.c:931
>        handle_softirqs+0x1de/0x6d0 kernel/softirq.c:626
>        __do_softirq kernel/softirq.c:660 [inline]
>        __local_bh_enable_ip+0x170/0x2b0 kernel/softirq.c:302
>        local_bh_enable include/linux/bottom_half.h:33 [inline]
>        __alloc_skb+0x1aa/0x7d0 net/core/skbuff.c:697
>        alloc_skb include/linux/skbuff.h:1381 [inline]
>        hidp_send_message+0xb5/0x230 net/bluetooth/hidp/core.c:111
>        hidp_send_intr_message net/bluetooth/hidp/core.c:143 [inline]
>        hidp_input_event+0x2a4/0x380 net/bluetooth/hidp/core.c:175
>        input_event_dispose+0x80/0x6b0 drivers/input/input.c:322
>        input_inject_event+0x1d7/0x320 drivers/input/input.c:424
>        kbd_led_trigger_activate+0xbc/0x100 drivers/tty/vt/keyboard.c:1074
>        led_trigger_set+0x53b/0x960 drivers/leds/led-triggers.c:220
>        led_match_default_trigger drivers/leds/led-triggers.c:277 [inline]
>        led_trigger_set_default+0x266/0x2a0 drivers/leds/led-triggers.c:300
>        led_classdev_register_ext+0x787/0x9c0 drivers/leds/led-class.c:581
>        led_classdev_register include/linux/leds.h:274 [inline]
>        input_leds_connect+0x517/0x790 drivers/input/input-leds.c:145
>        input_attach_handler drivers/input/input.c:1011 [inline]
>        input_register_device+0xce0/0x1140 drivers/input/input.c:2395
>        hidp_session_dev_add net/bluetooth/hidp/core.c:861 [inline]
>        hidp_session_probe+0x1a5/0x8a0 net/bluetooth/hidp/core.c:1139
>        l2cap_register_user+0xc2/0x1d0 net/bluetooth/l2cap_core.c:1725
>        hidp_connection_add+0x158b/0x1a20 net/bluetooth/hidp/core.c:1411
>        do_hidp_sock_ioctl net/bluetooth/hidp/sock.c:81 [inline]
>        hidp_sock_ioctl+0x403/0x650 net/bluetooth/hidp/sock.c:128
>        sock_do_ioctl+0x101/0x320 net/socket.c:1328
>        sock_ioctl+0x5c9/0x7f0 net/socket.c:1449
>        vfs_ioctl fs/ioctl.c:51 [inline]
>        __do_sys_ioctl fs/ioctl.c:597 [inline]
>        __se_sys_ioctl+0xff/0x170 fs/ioctl.c:583
>        do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
>        do_syscall_64+0x15f/0xf80 arch/x86/entry/syscall_64.c:94
>        entry_SYSCALL_64_after_hwframe+0x77/0x7f
> 
> other info that might help us debug this:
> 
>  Possible unsafe locking scenario:
> 
>        CPU0                    CPU1
>        ----                    ----
>   lock(&dev->event_lock#2);
>                                lock(tasklet_sync_callback.cb_lock);
>                                lock(&dev->event_lock#2);
>   lock(tasklet_sync_callback.cb_lock);
> 
>  *** DEADLOCK ***
> 
> 10 locks held by syz.2.4328/32663:
>  #0: ffff88805ebb3370 (&conn->lock#2){+.+.}-{4:4}, at: l2cap_register_user+0x35/0x1d0 net/bluetooth/l2cap_core.c:1712
>  #1: ffffffff8f73d960 (hidp_session_sem){++++}-{4:4}, at: hidp_session_probe+0x98/0x8a0 net/bluetooth/hidp/core.c:1129
>  #2: ffffffff8f006e58 (input_mutex){+.+.}-{4:4}, at: class_mutex_intr_constructor include/linux/mutex.h:255 [inline]
>  #2: ffffffff8f006e58 (input_mutex){+.+.}-{4:4}, at: input_register_device+0xa56/0x1140 drivers/input/input.c:2391
>  #3: ffff8880125ea860 (&led_cdev->led_access){+.+.}-{4:4}, at: led_classdev_register_ext+0x484/0x9c0 drivers/leds/led-class.c:539
>  #4: ffffffff8e9c4400 (triggers_list_lock){++++}-{4:4}, at: led_trigger_set_default+0x77/0x2a0 drivers/leds/led-triggers.c:297
>  #5: ffff8880125ea788 (&led_cdev->trigger_lock){+.+.}-{4:4}, at: led_trigger_set_default+0x87/0x2a0 drivers/leds/led-triggers.c:298
>  #6: ffff888058598270 (&dev->event_lock#2){+.+.}-{3:3}, at: spin_lock include/linux/spinlock_rt.h:45 [inline]
>  #6: ffff888058598270 (&dev->event_lock#2){+.+.}-{3:3}, at: class_spinlock_irqsave_constructor include/linux/spinlock.h:619 [inline]
>  #6: ffff888058598270 (&dev->event_lock#2){+.+.}-{3:3}, at: input_inject_event+0xa4/0x320 drivers/input/input.c:419
>  #7: ffffffff8e1c8240 (rcu_read_lock){....}-{1:3}, at: rcu_lock_acquire include/linux/rcupdate.h:300 [inline]
>  #7: ffffffff8e1c8240 (rcu_read_lock){....}-{1:3}, at: rcu_read_lock include/linux/rcupdate.h:838 [inline]
>  #7: ffffffff8e1c8240 (rcu_read_lock){....}-{1:3}, at: __rt_spin_lock kernel/locking/spinlock_rt.c:50 [inline]
>  #7: ffffffff8e1c8240 (rcu_read_lock){....}-{1:3}, at: rt_spin_lock+0x1e0/0x400 kernel/locking/spinlock_rt.c:57
>  #8: ffffffff8e1c8240 (rcu_read_lock){....}-{1:3}, at: rcu_lock_acquire include/linux/rcupdate.h:300 [inline]
>  #8: ffffffff8e1c8240 (rcu_read_lock){....}-{1:3}, at: rcu_read_lock include/linux/rcupdate.h:838 [inline]
>  #8: ffffffff8e1c8240 (rcu_read_lock){....}-{1:3}, at: class_rcu_constructor include/linux/rcupdate.h:1181 [inline]
>  #8: ffffffff8e1c8240 (rcu_read_lock){....}-{1:3}, at: input_inject_event+0xb0/0x320 drivers/input/input.c:420
>  #9: ffffffff8e1c8240 (rcu_read_lock){....}-{1:3}, at: __local_bh_disable_ip+0x3c/0x420 kernel/softirq.c:163
> 
> stack backtrace:
> CPU: 1 UID: 0 PID: 32663 Comm: syz.2.4328 Tainted: G             L      syzkaller #0 PREEMPT_{RT,(full)} 
> Tainted: [L]=SOFTLOCKUP
> Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 04/18/2026
> Call Trace:
>  <TASK>
>  dump_stack_lvl+0xe8/0x150 lib/dump_stack.c:120
>  print_circular_bug+0x2e1/0x300 kernel/locking/lockdep.c:2045
>  check_noncircular+0x12e/0x150 kernel/locking/lockdep.c:2177
>  check_prev_add kernel/locking/lockdep.c:3167 [inline]
>  check_prevs_add kernel/locking/lockdep.c:3286 [inline]
>  validate_chain kernel/locking/lockdep.c:3910 [inline]
>  __lock_acquire+0x15a5/0x2d10 kernel/locking/lockdep.c:5239
>  lock_acquire+0x106/0x350 kernel/locking/lockdep.c:5870
>  rt_spin_lock+0x83/0x400 kernel/locking/spinlock_rt.c:56
>  spin_lock include/linux/spinlock_rt.h:45 [inline]
>  tasklet_lock_callback kernel/softirq.c:881 [inline]
>  tasklet_action_common+0xc5/0x610 kernel/softirq.c:931
>  handle_softirqs+0x1de/0x6d0 kernel/softirq.c:626
>  __do_softirq kernel/softirq.c:660 [inline]
>  __local_bh_enable_ip+0x170/0x2b0 kernel/softirq.c:302
>  local_bh_enable include/linux/bottom_half.h:33 [inline]
>  __alloc_skb+0x1aa/0x7d0 net/core/skbuff.c:697
>  alloc_skb include/linux/skbuff.h:1381 [inline]
>  hidp_send_message+0xb5/0x230 net/bluetooth/hidp/core.c:111
>  hidp_send_intr_message net/bluetooth/hidp/core.c:143 [inline]
>  hidp_input_event+0x2a4/0x380 net/bluetooth/hidp/core.c:175
>  input_event_dispose+0x80/0x6b0 drivers/input/input.c:322
>  input_inject_event+0x1d7/0x320 drivers/input/input.c:424
>  kbd_led_trigger_activate+0xbc/0x100 drivers/tty/vt/keyboard.c:1074
>  led_trigger_set+0x53b/0x960 drivers/leds/led-triggers.c:220
>  led_match_default_trigger drivers/leds/led-triggers.c:277 [inline]
>  led_trigger_set_default+0x266/0x2a0 drivers/leds/led-triggers.c:300
>  led_classdev_register_ext+0x787/0x9c0 drivers/leds/led-class.c:581
>  led_classdev_register include/linux/leds.h:274 [inline]
>  input_leds_connect+0x517/0x790 drivers/input/input-leds.c:145
>  input_attach_handler drivers/input/input.c:1011 [inline]
>  input_register_device+0xce0/0x1140 drivers/input/input.c:2395
>  hidp_session_dev_add net/bluetooth/hidp/core.c:861 [inline]
>  hidp_session_probe+0x1a5/0x8a0 net/bluetooth/hidp/core.c:1139
>  l2cap_register_user+0xc2/0x1d0 net/bluetooth/l2cap_core.c:1725
>  hidp_connection_add+0x158b/0x1a20 net/bluetooth/hidp/core.c:1411
>  do_hidp_sock_ioctl net/bluetooth/hidp/sock.c:81 [inline]
>  hidp_sock_ioctl+0x403/0x650 net/bluetooth/hidp/sock.c:128
>  sock_do_ioctl+0x101/0x320 net/socket.c:1328
>  sock_ioctl+0x5c9/0x7f0 net/socket.c:1449
>  vfs_ioctl fs/ioctl.c:51 [inline]
>  __do_sys_ioctl fs/ioctl.c:597 [inline]
>  __se_sys_ioctl+0xff/0x170 fs/ioctl.c:583
>  do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
>  do_syscall_64+0x15f/0xf80 arch/x86/entry/syscall_64.c:94
>  entry_SYSCALL_64_after_hwframe+0x77/0x7f
> RIP: 0033:0x7fcb5e6cce59
> Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 e8 ff ff ff f7 d8 64 89 01 48
> RSP: 002b:00007fcb5c905028 EFLAGS: 00000246 ORIG_RAX: 0000000000000010
> RAX: ffffffffffffffda RBX: 00007fcb5e946090 RCX: 00007fcb5e6cce59
> RDX: 00002000000000c0 RSI: 00000000400448c8 RDI: 0000000000000009
> RBP: 00007fcb5e762d6f R08: 0000000000000000 R09: 0000000000000000
> R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
> R13: 00007fcb5e946128 R14: 00007fcb5e946090 R15: 00007ffddb8adae8
>  </TASK>

^ permalink raw reply

* Re: [PATCH 09/10] [v6 omap] ARM: dts: omap2: add stlc4560 spi-wireless node
From: Andreas Kemnade @ 2026-05-20 21:46 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: linux-gpio, linux-kernel, Arnd Bergmann, Christian Lamparter,
	Johannes Berg, Aaro Koskinen, Kevin Hilman, Roger Quadros,
	Tony Lindgren, Thomas Bogendoerfer, John Paul Adrian Glaubitz,
	Thomas Gleixner, Ingo Molnar, Borislav Petkov, Dave Hansen, x86,
	H. Peter Anvin, Linus Walleij, Bartosz Golaszewski,
	Dmitry Torokhov, Lee Jones, Pavel Machek, Matti Vaittinen,
	Florian Fainelli, Jonas Gorski, Andrew Lunn, Vladimir Oltean,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
	linux-wireless, linux-omap, linux-arm-kernel, linux-mips,
	linux-sh, linux-input, linux-leds, netdev, Krzysztof Kozlowski
In-Reply-To: <20260520183815.2510387-10-arnd@kernel.org>

On Wed, 20 May 2026 20:38:14 +0200
Arnd Bergmann <arnd@kernel.org> wrote:

> From: Arnd Bergmann <arnd@arndb.de>
> 
> Converted from the platform_device creation in board-n8x0.c.
> 
> Link: https://lore.kernel.org/all/20230314163201.955689-1-arnd@kernel.org/
> Reviewed-by: Krzysztof Kozlowski <krzk@kernel.org>
> Reviewed-by: Linus Walleij <linusw@kernel.org>
> Signed-off-by: Arnd Bergmann <arnd@arndb.de>
> ---
> v6: no changes
> v1 through v5: adaptations that correspond to the binding updates
> ---
>  arch/arm/boot/dts/ti/omap/omap2.dtsi                |  4 ++++
>  arch/arm/boot/dts/ti/omap/omap2420-n8x0-common.dtsi | 12 ++++++++++++
>  2 files changed, 16 insertions(+)
> 
Reviewed-by: Andreas Kemnade <andreas@kemnade.info>

^ permalink raw reply

* Re: [PATCH 09/10] [v6 omap] ARM: dts: omap2: add stlc4560 spi-wireless node
From: Johannes Berg @ 2026-05-20 21:39 UTC (permalink / raw)
  To: Arnd Bergmann, linux-gpio
  Cc: linux-kernel, Arnd Bergmann, Christian Lamparter, Aaro Koskinen,
	Andreas Kemnade, Kevin Hilman, Roger Quadros, Tony Lindgren,
	Thomas Bogendoerfer, John Paul Adrian Glaubitz, Thomas Gleixner,
	Ingo Molnar, Borislav Petkov, Dave Hansen, x86, H. Peter Anvin,
	Linus Walleij, Bartosz Golaszewski, Dmitry Torokhov, Lee Jones,
	Pavel Machek, Matti Vaittinen, Florian Fainelli, Jonas Gorski,
	Andrew Lunn, Vladimir Oltean, David S. Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, linux-wireless, linux-omap,
	linux-arm-kernel, linux-mips, linux-sh, linux-input, linux-leds,
	netdev, Krzysztof Kozlowski
In-Reply-To: <20260520183815.2510387-10-arnd@kernel.org>

On Wed, 2026-05-20 at 20:38 +0200, Arnd Bergmann wrote:
> 
> v1 through v5: adaptations that correspond to the binding updates

FWIW, I had just applied v5 of these three patches today, but didn't
send out a pull request yet. I'll do that tomorrow morning.

johannes

^ permalink raw reply

* Re: [PATCH v6 2/6] dt-bindings: input: cpcap-pwrbutton: convert to DT schema
From: Rob Herring (Arm) @ 2026-05-20 20:13 UTC (permalink / raw)
  To: Svyatoslav Ryhel
  Cc: Pavel Machek, linux-leds, Dmitry Torokhov, Lee Jones,
	Conor Dooley, linux-kernel, Krzysztof Kozlowski, linux-input,
	devicetree
In-Reply-To: <20260520182124.117863-3-clamor95@gmail.com>


On Wed, 20 May 2026 21:21:20 +0300, Svyatoslav Ryhel wrote:
> Convert power button devicetree bindings for the Motorola CPCAP MFD from
> TXT to YAML format. This patch does not change any functionality; the
> bindings remain the same.
> 
> Acked-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
> Signed-off-by: Svyatoslav Ryhel <clamor95@gmail.com>
> Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
> ---
>  .../bindings/input/cpcap-pwrbutton.txt        | 20 ------------
>  .../input/motorola,cpcap-pwrbutton.yaml       | 32 +++++++++++++++++++
>  2 files changed, 32 insertions(+), 20 deletions(-)
>  delete mode 100644 Documentation/devicetree/bindings/input/cpcap-pwrbutton.txt
>  create mode 100644 Documentation/devicetree/bindings/input/motorola,cpcap-pwrbutton.yaml
> 

My bot found errors running 'make dt_binding_check' on your patch:

yamllint warnings/errors:

dtschema/dtc warnings/errors:


doc reference errors (make refcheckdocs):
Warning: Documentation/devicetree/bindings/input/motorola,cpcap-pwrbutton.yaml references a file that doesn't exist: Documentation/devicetree/bindings/mfd/motorola,cpcap.yaml
Warning: Documentation/devicetree/bindings/mfd/motorola-cpcap.txt references a file that doesn't exist: Documentation/devicetree/bindings/input/cpcap-pwrbutton.txt
Documentation/devicetree/bindings/input/motorola,cpcap-pwrbutton.yaml: Documentation/devicetree/bindings/mfd/motorola,cpcap.yaml
Documentation/devicetree/bindings/mfd/motorola-cpcap.txt: Documentation/devicetree/bindings/input/cpcap-pwrbutton.txt

See https://patchwork.kernel.org/project/devicetree/patch/20260520182124.117863-3-clamor95@gmail.com

The base for the series is generally the latest rc1. A different dependency
should be noted in *this* patch.

If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure 'yamllint' is installed and dt-schema is up to
date:

pip3 install dtschema --upgrade

Please check and re-submit after running the above command yourself. Note
that DT_SCHEMA_FILES can be set to your schema file to speed up checking
your schema. However, it must be unset to test all examples with your schema.


^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox