* [PATCH 3/4] Input: defer handler's start() until device is opened
2026-06-24 5:50 [PATCH 1/4] Input: fix poller start order on uninhibit Dmitry Torokhov
2026-06-24 5:50 ` [PATCH 2/4] Input: call handler->start() when uninhibiting device Dmitry Torokhov
@ 2026-06-24 5:50 ` Dmitry Torokhov
2026-06-24 5:50 ` [PATCH 4/4] Input: ensure device is ready before delivering events Dmitry Torokhov
2026-06-24 6:00 ` [PATCH 1/4] Input: fix poller start order on uninhibit sashiko-bot
3 siblings, 0 replies; 6+ messages in thread
From: Dmitry Torokhov @ 2026-06-24 5:50 UTC (permalink / raw)
To: linux-input, Jiri Kosina, Benjamin Tissoires; +Cc: linux-kernel
When registering an input handle, handler->start() is currently called
immediately. However, the input device might not be fully opened or
ready to process events at this stage, meaning any state synchronization
events (like setting LED states) injected by the handler's start method
might be dropped.
Move the handler->start() invocation to input_open_device(). If it is
the first handle opening the device, start() is called after the driver's
open() method has successfully completed and the device is fully prepared.
To facilitate this, factor out the device startup logic (calling driver's
open and starting polling) into input_start_device().
For passive observer handlers, their start() method is also deferred
until the handle is opened. Since opening a passive observer handle does
not start the underlying hardware device, their start() method is called
immediately upon opening, regardless of whether the device is active.
Fixes: c7e8dc6ee6d5 ("Input: add start() method to input handlers")
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
---
drivers/input/input.c | 45 +++++++++++++++++++++++++------------------
include/linux/input.h | 5 +++--
2 files changed, 29 insertions(+), 21 deletions(-)
diff --git a/drivers/input/input.c b/drivers/input/input.c
index c2a038d31beb..0a95cbdc467e 100644
--- a/drivers/input/input.c
+++ b/drivers/input/input.c
@@ -568,6 +568,28 @@ void input_release_device(struct input_handle *handle)
}
EXPORT_SYMBOL(input_release_device);
+static int input_start_device(struct input_dev *dev)
+{
+ int error;
+
+ lockdep_assert_held(&dev->mutex);
+
+ if (dev->users++ == 0 && !dev->inhibited) {
+ if (dev->open) {
+ error = dev->open(dev);
+ if (error) {
+ dev->users--;
+ return error;
+ }
+ }
+
+ if (dev->poller)
+ input_dev_poller_start(dev->poller);
+ }
+
+ return 0;
+}
+
/**
* input_open_device - open input device
* @handle: handle through which device is being accessed
@@ -586,21 +608,9 @@ int input_open_device(struct input_handle *handle)
handle->open++;
- if (handle->handler->passive_observer)
- return 0;
-
- if (dev->users++ || dev->inhibited) {
- /*
- * Device is already opened and/or inhibited,
- * so we can exit immediately and report success.
- */
- return 0;
- }
-
- if (dev->open) {
- error = dev->open(dev);
+ if (!handle->handler->passive_observer) {
+ error = input_start_device(dev);
if (error) {
- dev->users--;
handle->open--;
/*
* Make sure we are not delivering any more
@@ -611,8 +621,8 @@ int input_open_device(struct input_handle *handle)
}
}
- if (dev->poller)
- input_dev_poller_start(dev->poller);
+ if (handle->open == 1 && handle->handler->start)
+ handle->handler->start(handle);
}
return 0;
@@ -2662,9 +2672,6 @@ int input_register_handle(struct input_handle *handle)
*/
list_add_tail_rcu(&handle->h_node, &handler->h_list);
- if (handler->start)
- handler->start(handle);
-
return 0;
}
EXPORT_SYMBOL(input_register_handle);
diff --git a/include/linux/input.h b/include/linux/input.h
index 3022bb730898..f7a2cfad5448 100644
--- a/include/linux/input.h
+++ b/include/linux/input.h
@@ -284,8 +284,9 @@ struct input_handle;
* @connect: called when attaching a handler to an input device
* @disconnect: disconnects a handler from input device
* @start: starts handler for given handle. This function is called by
- * input core right after connect() method and also when a process
- * that "grabbed" a device releases it
+ * input core when device is open and ready to process events,
+ * and also when device is uninhibited or when a process that "grabbed"
+ * a device releases it
* @passive_observer: set to %true by drivers only interested in observing
* data stream from devices if there are other users present. Such
* drivers will not result in starting underlying hardware device
--
2.55.0.rc0.799.gd6f94ed593-goog
^ permalink raw reply related [flat|nested] 6+ messages in thread* [PATCH 4/4] Input: ensure device is ready before delivering events
2026-06-24 5:50 [PATCH 1/4] Input: fix poller start order on uninhibit Dmitry Torokhov
2026-06-24 5:50 ` [PATCH 2/4] Input: call handler->start() when uninhibiting device Dmitry Torokhov
2026-06-24 5:50 ` [PATCH 3/4] Input: defer handler's start() until device is opened Dmitry Torokhov
@ 2026-06-24 5:50 ` Dmitry Torokhov
2026-06-24 6:02 ` sashiko-bot
2026-06-24 6:00 ` [PATCH 1/4] Input: fix poller start order on uninhibit sashiko-bot
3 siblings, 1 reply; 6+ messages in thread
From: Dmitry Torokhov @ 2026-06-24 5:50 UTC (permalink / raw)
To: linux-input, Jiri Kosina, Benjamin Tissoires; +Cc: linux-kernel
When a device is opened via input_open_device(), the driver's open()
callback is invoked. Some drivers, like cm109, submit URBs or perform
other hardware initialization in their open() callbacks.
However, the input core does not prevent dev->event() from being called
concurrently during the driver's open() execution. For instance, if a
console beep occurs, the kbd handler might inject an EV_SND event. This
can lead to double list_add BUGs if the driver submits the same URB in
both open() and event() paths without adequate synchronization.
To fix this, introduce a ready flag in the input_dev structure.
For complex devices (where dev->open is defined), this flag is set to true
only after the driver's open() method successfully completes. The core now
checks ready in input_event_dispose() and input_dev_toggle()
to prevent events from reaching the hardware before it is fully prepared.
For simple devices (no open callback), events are delivered immediately.
We also replay the logical state in input_open_device() by calling
input_dev_toggle() right after marking the device ready, ensuring no
events are permanently lost.
In the inhibit path, we ensure that physical feedback (LEDs/sounds) is
turned off before the device is closed, and we synchronize the inhibited
state transition under the event lock to prevent races with incoming events.
Assisted-by: Antigravity:gemini-3.5-flash
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
---
drivers/input/input.c | 107 ++++++++++++++++++++++++++----------------
include/linux/input.h | 12 +++--
2 files changed, 75 insertions(+), 44 deletions(-)
diff --git a/drivers/input/input.c b/drivers/input/input.c
index 0a95cbdc467e..724cc146fc09 100644
--- a/drivers/input/input.c
+++ b/drivers/input/input.c
@@ -318,7 +318,7 @@ static int input_get_disposition(struct input_dev *dev,
static void input_event_dispose(struct input_dev *dev, int disposition,
unsigned int type, unsigned int code, int value)
{
- if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
+ if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event && dev->ready)
dev->event(dev, type, code, value);
if (disposition & INPUT_PASS_TO_HANDLERS) {
@@ -568,6 +568,48 @@ void input_release_device(struct input_handle *handle)
}
EXPORT_SYMBOL(input_release_device);
+#define INPUT_DO_TOGGLE(dev, type, bits, on) \
+ do { \
+ int i; \
+ bool active; \
+ \
+ if (!test_bit(EV_##type, dev->evbit)) \
+ break; \
+ \
+ for_each_set_bit(i, dev->bits##bit, type##_CNT) { \
+ active = test_bit(i, dev->bits); \
+ if (!active && !on) \
+ continue; \
+ \
+ dev->event(dev, EV_##type, i, on ? active : 0); \
+ } \
+ } while (0)
+
+/*
+ * Iterate through the logical state of the input device (LEDs, sounds,
+ * auto-repeat) and explicitly push that state down to the hardware
+ * via dev->event() to match the current logical state (if activate is true),
+ * or forcibly turn off all feedback like LEDs and sounds during teardown
+ * or suspend (if activate is false).
+ *
+ * Primarily used as a state-replay mechanism after a device is opened
+ * or uninhibited, as events might have been dropped by the core while the
+ * hardware was not marked as ready.
+ */
+static void input_dev_toggle(struct input_dev *dev, bool activate)
+{
+ if (!dev->event || !dev->ready)
+ return;
+
+ INPUT_DO_TOGGLE(dev, LED, led, activate);
+ INPUT_DO_TOGGLE(dev, SND, snd, activate);
+
+ if (activate && test_bit(EV_REP, dev->evbit)) {
+ dev->event(dev, EV_REP, REP_PERIOD, dev->rep[REP_PERIOD]);
+ dev->event(dev, EV_REP, REP_DELAY, dev->rep[REP_DELAY]);
+ }
+}
+
static int input_start_device(struct input_dev *dev)
{
int error;
@@ -583,6 +625,11 @@ static int input_start_device(struct input_dev *dev)
}
}
+ scoped_guard(spinlock_irq, &dev->event_lock) {
+ dev->ready = true;
+ input_dev_toggle(dev, true);
+ }
+
if (dev->poller)
input_dev_poller_start(dev->poller);
}
@@ -661,6 +708,10 @@ void input_close_device(struct input_handle *handle)
if (!--dev->users && !dev->inhibited) {
if (dev->poller)
input_dev_poller_stop(dev->poller);
+
+ scoped_guard(spinlock_irq, &dev->event_lock)
+ dev->ready = false;
+
if (dev->close)
dev->close(dev);
}
@@ -1712,37 +1763,6 @@ static int input_dev_uevent(const struct device *device, struct kobj_uevent_env
return 0;
}
-#define INPUT_DO_TOGGLE(dev, type, bits, on) \
- do { \
- int i; \
- bool active; \
- \
- if (!test_bit(EV_##type, dev->evbit)) \
- break; \
- \
- for_each_set_bit(i, dev->bits##bit, type##_CNT) { \
- active = test_bit(i, dev->bits); \
- if (!active && !on) \
- continue; \
- \
- dev->event(dev, EV_##type, i, on ? active : 0); \
- } \
- } while (0)
-
-static void input_dev_toggle(struct input_dev *dev, bool activate)
-{
- if (!dev->event)
- return;
-
- INPUT_DO_TOGGLE(dev, LED, led, activate);
- INPUT_DO_TOGGLE(dev, SND, snd, activate);
-
- if (activate && test_bit(EV_REP, dev->evbit)) {
- dev->event(dev, EV_REP, REP_PERIOD, dev->rep[REP_PERIOD]);
- dev->event(dev, EV_REP, REP_DELAY, dev->rep[REP_DELAY]);
- }
-}
-
/**
* input_reset_device() - reset/restore the state of input device
* @dev: input device whose state needs to be reset
@@ -1770,21 +1790,25 @@ static int input_inhibit_device(struct input_dev *dev)
return 0;
if (dev->users) {
- if (dev->close)
- dev->close(dev);
if (dev->poller)
input_dev_poller_stop(dev->poller);
+
+ scoped_guard(spinlock_irq, &dev->event_lock) {
+ input_dev_toggle(dev, false);
+ dev->ready = false;
+ }
+
+ if (dev->close)
+ dev->close(dev);
}
scoped_guard(spinlock_irq, &dev->event_lock) {
input_mt_release_slots(dev);
input_dev_release_keys(dev);
input_handle_event(dev, EV_SYN, SYN_REPORT, 1);
- input_dev_toggle(dev, false);
+ dev->inhibited = true;
}
- dev->inhibited = true;
-
return 0;
}
@@ -1804,12 +1828,15 @@ static int input_uninhibit_device(struct input_dev *dev)
if (error)
return error;
}
- }
- dev->inhibited = false;
+ guard(spinlock_irq)(&dev->event_lock);
+ dev->ready = true;
+ }
- scoped_guard(spinlock_irq, &dev->event_lock)
+ scoped_guard(spinlock_irq, &dev->event_lock) {
+ dev->inhibited = false;
input_dev_toggle(dev, true);
+ }
if (dev->users && dev->poller)
input_dev_poller_start(dev->poller);
diff --git a/include/linux/input.h b/include/linux/input.h
index f7a2cfad5448..a307a3f6d5e2 100644
--- a/include/linux/input.h
+++ b/include/linux/input.h
@@ -128,11 +128,14 @@ enum input_clock_type {
* @devres_managed: indicates that devices is managed with devres framework
* and needs not be explicitly unregistered or freed.
* @timestamp: storage for a timestamp set by input_set_timestamp called
- * by a driver
+ * by a driver
* @inhibited: indicates that the input device is inhibited. If that is
- * the case then input core ignores any events generated by the device.
- * Device's close() is called when it is being inhibited and its open()
- * is called when it is being uninhibited.
+ * the case then input core ignores any events generated by the device.
+ * Device's close() is called when it is being inhibited and its open()
+ * is called when it is being uninhibited.
+ * @ready: indicates that the device has been successfully opened and is
+ * prepared to process events (like LEDs or sounds) sent from the
+ * input core.
*/
struct input_dev {
const char *name;
@@ -209,6 +212,7 @@ struct input_dev {
ktime_t timestamp[INPUT_CLK_MAX];
bool inhibited;
+ bool ready;
};
#define to_input_dev(d) container_of(d, struct input_dev, dev)
--
2.55.0.rc0.799.gd6f94ed593-goog
^ permalink raw reply related [flat|nested] 6+ messages in thread