* [PATCH 1/3] Input: wacom_w8001 - fix teardown order and device lifetime
@ 2026-10-09 19:07 Dmitry Torokhov
2026-10-09 19:07 ` [PATCH 2/3] Input: wacom_w8001 - assign distinct phys paths to pen and touch devices Dmitry Torokhov
` (2 more replies)
0 siblings, 3 replies; 8+ messages in thread
From: Dmitry Torokhov @ 2026-10-09 19:07 UTC (permalink / raw)
To: linux-input
Cc: Ping Cheng, Jason Gerecke, Peter Hutterer, linux-kernel,
sashiko-bot
In w8001_disconnect(), serio_close() was called before unregistering
the input devices. When input_unregister_device() calls w8001_close()
to shut down the hardware, w8001_command() fails because the serio
port is already closed, preventing W8001_CMD_STOP from reaching the
controller.
Additionally, w8001_connect() pre-allocated both pen_dev and touch_dev
upfront before serio_open() so w8001_interrupt() could dereference
them unconditionally, and then freed unused input devices mid-probe
without synchronizing with w8001_interrupt() when pen or touch was
absent. That created a race and use-after-free hazard with
w8001_interrupt(), as well as a double-free on the fail4 error path.
Fix these issues by guarding event reporting in w8001_interrupt()
against NULL device pointers, allocating and registering input devices
on demand after querying capabilities, synchronizing device pointer
publication and teardown with serio_pause_rx, and unregistering input
devices before closing the serio port in both disconnect and probe
error unwind paths.
Fixes: 66fd9385ee9c ("Input: wacom_w8001 - simplify w8001_remove")
Fixes: e0361b70175f ("Input: wacom_w8001 - split the touch and pen devices into two devices")
Reported-by: sashiko-bot@kernel.org
Assisted-by: LLM
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
---
drivers/input/touchscreen/wacom_w8001.c | 341 +++++++++++++-----------
1 file changed, 186 insertions(+), 155 deletions(-)
diff --git a/drivers/input/touchscreen/wacom_w8001.c b/drivers/input/touchscreen/wacom_w8001.c
index d8d1cdc3f09e..1c48e2775214 100644
--- a/drivers/input/touchscreen/wacom_w8001.c
+++ b/drivers/input/touchscreen/wacom_w8001.c
@@ -302,7 +302,8 @@ static irqreturn_t w8001_interrupt(struct serio *serio,
if (w8001->pktlen == w8001->idx) {
w8001->idx = 0;
- if (w8001->type != BTN_TOOL_PEN &&
+ if (w8001->touch_dev &&
+ w8001->type != BTN_TOOL_PEN &&
w8001->type != BTN_TOOL_RUBBER) {
parse_single_touch(w8001->data, &coord);
report_single_touch(w8001, &coord);
@@ -321,8 +322,10 @@ static irqreturn_t w8001_interrupt(struct serio *serio,
break;
w8001->idx = 0;
- parse_pen_data(w8001->data, &coord);
- report_pen_events(w8001, &coord);
+ if (w8001->pen_dev) {
+ parse_pen_data(w8001->data, &coord);
+ report_pen_events(w8001, &coord);
+ }
break;
/* control packet */
@@ -340,7 +343,8 @@ static irqreturn_t w8001_interrupt(struct serio *serio,
/* 2 finger touch packet */
case W8001_PKTLEN_TOUCH2FG - 1:
w8001->idx = 0;
- parse_multi_touch(w8001);
+ if (w8001->touch_dev)
+ parse_multi_touch(w8001);
break;
default:
@@ -349,7 +353,9 @@ static irqreturn_t w8001_interrupt(struct serio *serio,
* sends invalid data packets that are larger than
* W8001_PKTLEN_TPCPEN. Let's start over again.
*/
- if (!w8001->touch_dev && w8001->idx > W8001_PKTLEN_TPCPEN - 1)
+ if (!w8001->touch_dev &&
+ (w8001->data[0] & W8001_TAB_MASK) != W8001_TAB_BYTE &&
+ w8001->idx > W8001_PKTLEN_TPCPEN - 1)
w8001->idx = 0;
}
@@ -404,19 +410,6 @@ static void w8001_close(struct input_dev *dev)
w8001_command(w8001, W8001_CMD_STOP, false);
}
-static int w8001_detect(struct w8001 *w8001)
-{
- int error;
-
- error = w8001_command(w8001, W8001_CMD_STOP, false);
- if (error)
- return error;
-
- msleep(250); /* wait 250ms before querying the device */
-
- return 0;
-}
-
static void w8001_append_suffix(char *dest, const char *suffix, size_t dest_sz)
{
size_t used = strnlen(dest, dest_sz);
@@ -424,88 +417,54 @@ static void w8001_append_suffix(char *dest, const char *suffix, size_t dest_sz)
strscpy(dest + used, suffix, dest_sz - used);
}
-static int w8001_setup_pen(struct w8001 *w8001, char *basename,
- size_t basename_sz)
+static bool w8001_detect_pen(struct w8001 *w8001,
+ char *basename, size_t basename_sz,
+ struct w8001_coord *coord)
{
- struct input_dev *dev = w8001->pen_dev;
- struct w8001_coord coord;
int error;
- /* penabled? */
error = w8001_command(w8001, W8001_CMD_QUERY, true);
if (error)
- return error;
-
- __set_bit(EV_KEY, dev->evbit);
- __set_bit(EV_ABS, dev->evbit);
- __set_bit(BTN_TOUCH, dev->keybit);
- __set_bit(BTN_TOOL_PEN, dev->keybit);
- __set_bit(BTN_TOOL_RUBBER, dev->keybit);
- __set_bit(BTN_STYLUS, dev->keybit);
- __set_bit(BTN_STYLUS2, dev->keybit);
- __set_bit(INPUT_PROP_DIRECT, dev->propbit);
-
- parse_pen_data(w8001->response, &coord);
- w8001->max_pen_x = coord.x;
- w8001->max_pen_y = coord.y;
-
- input_set_abs_params(dev, ABS_X, 0, coord.x, 0, 0);
- input_set_abs_params(dev, ABS_Y, 0, coord.y, 0, 0);
- input_abs_set_res(dev, ABS_X, W8001_PEN_RESOLUTION);
- input_abs_set_res(dev, ABS_Y, W8001_PEN_RESOLUTION);
- input_set_abs_params(dev, ABS_PRESSURE, 0, coord.pen_pressure, 0, 0);
- if (coord.tilt_x && coord.tilt_y) {
- input_set_abs_params(dev, ABS_TILT_X, 0, coord.tilt_x, 0, 0);
- input_set_abs_params(dev, ABS_TILT_Y, 0, coord.tilt_y, 0, 0);
- }
+ return false;
+ parse_pen_data(w8001->response, coord);
+ w8001->max_pen_x = coord->x;
+ w8001->max_pen_y = coord->y;
w8001->id = 0x90;
w8001_append_suffix(basename, " Penabled", basename_sz);
- return 0;
+ return true;
}
-static int w8001_setup_touch(struct w8001 *w8001, char *basename,
- size_t basename_sz)
+static bool w8001_detect_touch(struct w8001 *w8001,
+ char *basename, size_t basename_sz,
+ struct w8001_touch_query *touch)
{
- struct input_dev *dev = w8001->touch_dev;
- struct w8001_touch_query touch;
int error;
-
- /* Touch enabled? */
error = w8001_command(w8001, W8001_CMD_TOUCHQUERY, true);
if (error)
- return error;
+ return false;
+
/*
* Some non-touch devices may reply to the touch query. But their
* second byte is empty, which indicates touch is not supported.
*/
if (!w8001->response[1])
- return -ENXIO;
+ return false;
- __set_bit(EV_KEY, dev->evbit);
- __set_bit(EV_ABS, dev->evbit);
- __set_bit(BTN_TOUCH, dev->keybit);
- __set_bit(INPUT_PROP_DIRECT, dev->propbit);
-
- parse_touchquery(w8001->response, &touch);
- w8001->max_touch_x = touch.x;
- w8001->max_touch_y = touch.y;
+ parse_touchquery(w8001->response, touch);
+ w8001->max_touch_x = touch->x;
+ w8001->max_touch_y = touch->y;
if (w8001->max_pen_x && w8001->max_pen_y) {
/* if pen is supported scale to pen maximum */
- touch.x = w8001->max_pen_x;
- touch.y = w8001->max_pen_y;
- touch.panel_res = W8001_PEN_RESOLUTION;
+ touch->x = w8001->max_pen_x;
+ touch->y = w8001->max_pen_y;
+ touch->panel_res = W8001_PEN_RESOLUTION;
}
- input_set_abs_params(dev, ABS_X, 0, touch.x, 0, 0);
- input_set_abs_params(dev, ABS_Y, 0, touch.y, 0, 0);
- input_abs_set_res(dev, ABS_X, touch.panel_res);
- input_abs_set_res(dev, ABS_Y, touch.panel_res);
-
- switch (touch.sensor_id) {
+ switch (touch->sensor_id) {
case 0:
case 2:
w8001->pktlen = W8001_PKTLEN_TOUCH93;
@@ -523,24 +482,6 @@ static int w8001_setup_touch(struct w8001 *w8001, char *basename,
case 5:
w8001->pktlen = W8001_PKTLEN_TOUCH2FG;
-
- __set_bit(BTN_TOOL_DOUBLETAP, dev->keybit);
- error = input_mt_init_slots(dev, 2, 0);
- if (error) {
- dev_err(&w8001->serio->dev,
- "failed to initialize MT slots: %d\n", error);
- return error;
- }
-
- input_set_abs_params(dev, ABS_MT_POSITION_X,
- 0, touch.x, 0, 0);
- input_set_abs_params(dev, ABS_MT_POSITION_Y,
- 0, touch.y, 0, 0);
- input_set_abs_params(dev, ABS_MT_TOOL_TYPE,
- 0, MT_TOOL_MAX, 0, 0);
- input_abs_set_res(dev, ABS_MT_POSITION_X, touch.panel_res);
- input_abs_set_res(dev, ABS_MT_POSITION_Y, touch.panel_res);
-
w8001_append_suffix(basename, " 2FG", basename_sz);
if (w8001->max_pen_x && w8001->max_pen_y)
w8001->id = 0xE3;
@@ -550,13 +491,19 @@ static int w8001_setup_touch(struct w8001 *w8001, char *basename,
}
w8001_append_suffix(basename, " Touchscreen", basename_sz);
-
- return 0;
+ return true;
}
-static void w8001_set_devdata(struct input_dev *dev, struct w8001 *w8001,
- struct serio *serio)
+static struct input_dev *w8001_allocate_device(struct w8001 *w8001,
+ const char *name)
{
+ struct input_dev *dev;
+
+ dev = input_allocate_device();
+ if (!dev)
+ return NULL;
+
+ dev->name = name;
dev->phys = w8001->phys;
dev->id.bustype = BUS_RS232;
dev->id.product = w8001->id;
@@ -565,28 +512,139 @@ static void w8001_set_devdata(struct input_dev *dev, struct w8001 *w8001,
dev->open = w8001_open;
dev->close = w8001_close;
- dev->dev.parent = &serio->dev;
+ dev->dev.parent = &w8001->serio->dev;
+
+ __set_bit(INPUT_PROP_DIRECT, dev->propbit);
+ input_set_capability(dev, EV_KEY, BTN_TOUCH);
input_set_drvdata(dev, w8001);
+
+ return dev;
+}
+
+static int w8001_setup_pen(struct w8001 *w8001, const char *basename,
+ const struct w8001_coord *coord)
+{
+ struct input_dev *dev;
+ int error;
+
+ snprintf(w8001->pen_name, sizeof(w8001->pen_name), "%s Pen", basename);
+
+ dev = w8001_allocate_device(w8001, w8001->pen_name);
+ if (!dev)
+ return -ENOMEM;
+
+ __set_bit(BTN_TOOL_PEN, dev->keybit);
+ __set_bit(BTN_TOOL_RUBBER, dev->keybit);
+ __set_bit(BTN_STYLUS, dev->keybit);
+ __set_bit(BTN_STYLUS2, dev->keybit);
+
+ input_set_abs_params(dev, ABS_X, 0, coord->x, 0, 0);
+ input_set_abs_params(dev, ABS_Y, 0, coord->y, 0, 0);
+ input_abs_set_res(dev, ABS_X, W8001_PEN_RESOLUTION);
+ input_abs_set_res(dev, ABS_Y, W8001_PEN_RESOLUTION);
+ input_set_abs_params(dev, ABS_PRESSURE, 0, coord->pen_pressure, 0, 0);
+ if (coord->tilt_x && coord->tilt_y) {
+ input_set_abs_params(dev, ABS_TILT_X, 0, coord->tilt_x, 0, 0);
+ input_set_abs_params(dev, ABS_TILT_Y, 0, coord->tilt_y, 0, 0);
+ }
+
+ scoped_guard(serio_pause_rx, w8001->serio)
+ w8001->pen_dev = dev;
+
+ error = input_register_device(dev);
+ if (error)
+ goto err_unpublish_dev;
+
+ return 0;
+
+err_unpublish_dev:
+ scoped_guard(serio_pause_rx, w8001->serio)
+ w8001->pen_dev = NULL;
+ input_free_device(dev);
+ return error;
+}
+
+static int w8001_setup_touch(struct w8001 *w8001, const char *basename,
+ const struct w8001_touch_query *touch)
+{
+ struct input_dev *dev;
+ int error;
+
+ snprintf(w8001->touch_name, sizeof(w8001->touch_name), "%s Finger", basename);
+
+ dev = w8001_allocate_device(w8001, w8001->touch_name);
+ if (!dev)
+ return -ENOMEM;
+
+ input_set_abs_params(dev, ABS_X, 0, touch->x, 0, 0);
+ input_set_abs_params(dev, ABS_Y, 0, touch->y, 0, 0);
+ input_abs_set_res(dev, ABS_X, touch->panel_res);
+ input_abs_set_res(dev, ABS_Y, touch->panel_res);
+
+ if (touch->sensor_id == 5) {
+ __set_bit(BTN_TOOL_DOUBLETAP, dev->keybit);
+ error = input_mt_init_slots(dev, 2, 0);
+ if (error) {
+ dev_err(&w8001->serio->dev,
+ "failed to initialize MT slots: %d\n", error);
+ goto err_free_dev;
+ }
+
+ input_set_abs_params(dev, ABS_MT_POSITION_X, 0, touch->x, 0, 0);
+ input_set_abs_params(dev, ABS_MT_POSITION_Y, 0, touch->y, 0, 0);
+ input_set_abs_params(dev, ABS_MT_TOOL_TYPE, 0, MT_TOOL_MAX, 0, 0);
+ input_abs_set_res(dev, ABS_MT_POSITION_X, touch->panel_res);
+ input_abs_set_res(dev, ABS_MT_POSITION_Y, touch->panel_res);
+ }
+
+ scoped_guard(serio_pause_rx, w8001->serio)
+ w8001->touch_dev = dev;
+
+ error = input_register_device(dev);
+ if (error)
+ goto err_unpublish_dev;
+
+ return 0;
+
+err_unpublish_dev:
+ scoped_guard(serio_pause_rx, w8001->serio)
+ w8001->touch_dev = NULL;
+err_free_dev:
+ input_free_device(dev);
+ return error;
}
/*
* w8001_disconnect() is the opposite of w8001_connect()
*/
+static void w8001_unregister_devices(struct w8001 *w8001)
+{
+ struct input_dev *pen_dev = NULL;
+ struct input_dev *touch_dev = NULL;
+
+ scoped_guard(serio_pause_rx, w8001->serio) {
+ swap(pen_dev, w8001->pen_dev);
+ swap(touch_dev, w8001->touch_dev);
+ }
+
+ if (pen_dev)
+ input_unregister_device(pen_dev);
+ if (touch_dev)
+ input_unregister_device(touch_dev);
+}
+
static void w8001_disconnect(struct serio *serio)
{
struct w8001 *w8001 = serio_get_drvdata(serio);
+ w8001_unregister_devices(w8001);
+
serio_close(serio);
+ serio_set_drvdata(serio, NULL);
- if (w8001->pen_dev)
- input_unregister_device(w8001->pen_dev);
- if (w8001->touch_dev)
- input_unregister_device(w8001->touch_dev);
kfree(w8001);
-
- serio_set_drvdata(serio, NULL);
}
/*
@@ -598,22 +656,17 @@ static void w8001_disconnect(struct serio *serio)
static int w8001_connect(struct serio *serio, struct serio_driver *drv)
{
struct w8001 *w8001;
- struct input_dev *input_dev_pen;
- struct input_dev *input_dev_touch;
+ struct w8001_coord coord;
+ struct w8001_touch_query touch;
char basename[64] = "Wacom Serial";
- int err, err_pen, err_touch;
+ bool has_pen, has_touch;
+ int err;
w8001 = kzalloc_obj(*w8001);
- input_dev_pen = input_allocate_device();
- input_dev_touch = input_allocate_device();
- if (!w8001 || !input_dev_pen || !input_dev_touch) {
- err = -ENOMEM;
- goto fail1;
- }
+ if (!w8001)
+ return -ENOMEM;
w8001->serio = serio;
- w8001->pen_dev = input_dev_pen;
- w8001->touch_dev = input_dev_touch;
mutex_init(&w8001->mutex);
init_completion(&w8001->cmd_done);
snprintf(w8001->phys, sizeof(w8001->phys), "%s/input0", serio->phys);
@@ -621,66 +674,44 @@ static int w8001_connect(struct serio *serio, struct serio_driver *drv)
serio_set_drvdata(serio, w8001);
err = serio_open(serio, drv);
if (err)
- goto fail2;
+ goto err_free_w8001;
- err = w8001_detect(w8001);
+ err = w8001_command(w8001, W8001_CMD_STOP, false);
if (err)
- goto fail3;
+ goto err_close_serio;
+
+ msleep(250); /* wait 250ms before querying the device */
- /* For backwards-compatibility we compose the basename based on
+ /*
+ * For backwards-compatibility we compose the basename based on
* capabilities and then just append the tool type
*/
- err_pen = w8001_setup_pen(w8001, basename, sizeof(basename));
- err_touch = w8001_setup_touch(w8001, basename, sizeof(basename));
- if (err_pen && err_touch) {
+ has_pen = w8001_detect_pen(w8001, basename, sizeof(basename), &coord);
+ has_touch = w8001_detect_touch(w8001, basename, sizeof(basename), &touch);
+ if (!has_pen && !has_touch) {
err = -ENXIO;
- goto fail3;
+ goto err_close_serio;
}
- if (!err_pen) {
- snprintf(w8001->pen_name, sizeof(w8001->pen_name),
- "%s Pen", basename);
- input_dev_pen->name = w8001->pen_name;
-
- w8001_set_devdata(input_dev_pen, w8001, serio);
-
- err = input_register_device(w8001->pen_dev);
+ if (has_pen) {
+ err = w8001_setup_pen(w8001, basename, &coord);
if (err)
- goto fail3;
- } else {
- input_free_device(input_dev_pen);
- input_dev_pen = NULL;
- w8001->pen_dev = NULL;
+ goto err_close_serio;
}
- if (!err_touch) {
- snprintf(w8001->touch_name, sizeof(w8001->touch_name),
- "%s Finger", basename);
- input_dev_touch->name = w8001->touch_name;
-
- w8001_set_devdata(input_dev_touch, w8001, serio);
-
- err = input_register_device(w8001->touch_dev);
+ if (has_touch) {
+ err = w8001_setup_touch(w8001, basename, &touch);
if (err)
- goto fail4;
- } else {
- input_free_device(input_dev_touch);
- input_dev_touch = NULL;
- w8001->touch_dev = NULL;
+ goto err_close_serio;
}
return 0;
-fail4:
- if (w8001->pen_dev)
- input_unregister_device(w8001->pen_dev);
-fail3:
+err_close_serio:
+ w8001_unregister_devices(w8001);
serio_close(serio);
-fail2:
+err_free_w8001:
serio_set_drvdata(serio, NULL);
-fail1:
- input_free_device(input_dev_pen);
- input_free_device(input_dev_touch);
kfree(w8001);
return err;
}
--
2.56.0.385.gd3acb90ef8-goog
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH 2/3] Input: wacom_w8001 - assign distinct phys paths to pen and touch devices 2026-10-09 19:07 [PATCH 1/3] Input: wacom_w8001 - fix teardown order and device lifetime Dmitry Torokhov @ 2026-10-09 19:07 ` Dmitry Torokhov 2026-10-13 22:42 ` Jason Gerecke 2026-10-09 19:07 ` [PATCH 3/3] Input: wacom_w8001 - use ACQUIRE() for interruptible mutex in w8001_open() Dmitry Torokhov 2026-10-13 22:41 ` [PATCH 1/3] Input: wacom_w8001 - fix teardown order and device lifetime Jason Gerecke 2 siblings, 1 reply; 8+ messages in thread From: Dmitry Torokhov @ 2026-10-09 19:07 UTC (permalink / raw) To: linux-input; +Cc: Ping Cheng, Jason Gerecke, Peter Hutterer, linux-kernel Previously, both pen and touch input devices used the same phys path "%s/input0", which made it impossible for userspace or udev rules to distinguish the devices by physical topology. Assign distinct phys paths by introducing separate buffers pen_phys ("%s/input0") and touch_phys ("%s/input1") in struct w8001. Pass the corresponding phys path to w8001_allocate_device() during pen and touch setup, and drop the shared phys buffer from struct w8001. Fixes: e0361b70175f ("Input: wacom_w8001 - split the touch and pen devices into two devices") Assisted-by: LLM Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com> --- drivers/input/touchscreen/wacom_w8001.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/drivers/input/touchscreen/wacom_w8001.c b/drivers/input/touchscreen/wacom_w8001.c index 1c48e2775214..e967f2413706 100644 --- a/drivers/input/touchscreen/wacom_w8001.c +++ b/drivers/input/touchscreen/wacom_w8001.c @@ -91,7 +91,8 @@ struct w8001 { unsigned char response_type; unsigned char response[W8001_MAX_LENGTH]; unsigned char data[W8001_MAX_LENGTH]; - char phys[W8001_MAX_PHYS]; + char pen_phys[W8001_MAX_PHYS]; + char touch_phys[W8001_MAX_PHYS]; int type; unsigned int pktlen; u16 max_touch_x; @@ -495,7 +496,8 @@ static bool w8001_detect_touch(struct w8001 *w8001, } static struct input_dev *w8001_allocate_device(struct w8001 *w8001, - const char *name) + const char *name, + const char *phys) { struct input_dev *dev; @@ -504,7 +506,7 @@ static struct input_dev *w8001_allocate_device(struct w8001 *w8001, return NULL; dev->name = name; - dev->phys = w8001->phys; + dev->phys = phys; dev->id.bustype = BUS_RS232; dev->id.product = w8001->id; dev->id.vendor = 0x056a; @@ -529,8 +531,10 @@ static int w8001_setup_pen(struct w8001 *w8001, const char *basename, int error; snprintf(w8001->pen_name, sizeof(w8001->pen_name), "%s Pen", basename); + snprintf(w8001->pen_phys, sizeof(w8001->pen_phys), "%s/input0", + w8001->serio->phys); - dev = w8001_allocate_device(w8001, w8001->pen_name); + dev = w8001_allocate_device(w8001, w8001->pen_name, w8001->pen_phys); if (!dev) return -ENOMEM; @@ -572,8 +576,10 @@ static int w8001_setup_touch(struct w8001 *w8001, const char *basename, int error; snprintf(w8001->touch_name, sizeof(w8001->touch_name), "%s Finger", basename); + snprintf(w8001->touch_phys, sizeof(w8001->touch_phys), "%s/input1", + w8001->serio->phys); - dev = w8001_allocate_device(w8001, w8001->touch_name); + dev = w8001_allocate_device(w8001, w8001->touch_name, w8001->touch_phys); if (!dev) return -ENOMEM; @@ -669,7 +675,6 @@ static int w8001_connect(struct serio *serio, struct serio_driver *drv) w8001->serio = serio; mutex_init(&w8001->mutex); init_completion(&w8001->cmd_done); - snprintf(w8001->phys, sizeof(w8001->phys), "%s/input0", serio->phys); serio_set_drvdata(serio, w8001); err = serio_open(serio, drv); -- 2.56.0.385.gd3acb90ef8-goog ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH 2/3] Input: wacom_w8001 - assign distinct phys paths to pen and touch devices 2026-10-09 19:07 ` [PATCH 2/3] Input: wacom_w8001 - assign distinct phys paths to pen and touch devices Dmitry Torokhov @ 2026-10-13 22:42 ` Jason Gerecke 2026-10-13 23:53 ` Dmitry Torokhov 0 siblings, 1 reply; 8+ messages in thread From: Jason Gerecke @ 2026-10-13 22:42 UTC (permalink / raw) To: Dmitry Torokhov Cc: linux-input, Ping Cheng, Jason Gerecke, Peter Hutterer, linux-kernel On Fri, Oct 9, 2026 at 12:28 PM Dmitry Torokhov <dmitry.torokhov@gmail.com> wrote: > > Previously, both pen and touch input devices used the same phys path > "%s/input0", which made it impossible for userspace or udev rules to > distinguish the devices by physical topology. > > Assign distinct phys paths by introducing separate buffers pen_phys > ("%s/input0") and touch_phys ("%s/input1") in struct w8001. Pass the > corresponding phys path to w8001_allocate_device() during pen and touch > setup, and drop the shared phys buffer from struct w8001. > > Fixes: e0361b70175f ("Input: wacom_w8001 - split the touch and pen devices into two devices") Does this really deserve a "Fixes" tag? This does not seem to be as much of a "bug" as it is a limitation. Userspace and udev rules are (currently) able to reliably distinguish the devices by name. Obviously that's not ideal, but I don't see much benefit to this patch getting backported to the stable trees -- especially when /existing/ udev rules aren't necessarily going to know what to do with this new "input1" phys. > Assisted-by: LLM Same Assisted-by comments from Patch 1/3 apply. Jason (she/they) --- Now instead of four in the eights place / you’ve got three, ‘Cause you added one / (That is to say, eight) to the two, / But you can’t take seven from three, / So you look at the sixty-fours.... > Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com> > --- > drivers/input/touchscreen/wacom_w8001.c | 17 +++++++++++------ > 1 file changed, 11 insertions(+), 6 deletions(-) > > diff --git a/drivers/input/touchscreen/wacom_w8001.c b/drivers/input/touchscreen/wacom_w8001.c > index 1c48e2775214..e967f2413706 100644 > --- a/drivers/input/touchscreen/wacom_w8001.c > +++ b/drivers/input/touchscreen/wacom_w8001.c > @@ -91,7 +91,8 @@ struct w8001 { > unsigned char response_type; > unsigned char response[W8001_MAX_LENGTH]; > unsigned char data[W8001_MAX_LENGTH]; > - char phys[W8001_MAX_PHYS]; > + char pen_phys[W8001_MAX_PHYS]; > + char touch_phys[W8001_MAX_PHYS]; > int type; > unsigned int pktlen; > u16 max_touch_x; > @@ -495,7 +496,8 @@ static bool w8001_detect_touch(struct w8001 *w8001, > } > > static struct input_dev *w8001_allocate_device(struct w8001 *w8001, > - const char *name) > + const char *name, > + const char *phys) > { > struct input_dev *dev; > > @@ -504,7 +506,7 @@ static struct input_dev *w8001_allocate_device(struct w8001 *w8001, > return NULL; > > dev->name = name; > - dev->phys = w8001->phys; > + dev->phys = phys; > dev->id.bustype = BUS_RS232; > dev->id.product = w8001->id; > dev->id.vendor = 0x056a; > @@ -529,8 +531,10 @@ static int w8001_setup_pen(struct w8001 *w8001, const char *basename, > int error; > > snprintf(w8001->pen_name, sizeof(w8001->pen_name), "%s Pen", basename); > + snprintf(w8001->pen_phys, sizeof(w8001->pen_phys), "%s/input0", > + w8001->serio->phys); > > - dev = w8001_allocate_device(w8001, w8001->pen_name); > + dev = w8001_allocate_device(w8001, w8001->pen_name, w8001->pen_phys); > if (!dev) > return -ENOMEM; > > @@ -572,8 +576,10 @@ static int w8001_setup_touch(struct w8001 *w8001, const char *basename, > int error; > > snprintf(w8001->touch_name, sizeof(w8001->touch_name), "%s Finger", basename); > + snprintf(w8001->touch_phys, sizeof(w8001->touch_phys), "%s/input1", > + w8001->serio->phys); > > - dev = w8001_allocate_device(w8001, w8001->touch_name); > + dev = w8001_allocate_device(w8001, w8001->touch_name, w8001->touch_phys); > if (!dev) > return -ENOMEM; > > @@ -669,7 +675,6 @@ static int w8001_connect(struct serio *serio, struct serio_driver *drv) > w8001->serio = serio; > mutex_init(&w8001->mutex); > init_completion(&w8001->cmd_done); > - snprintf(w8001->phys, sizeof(w8001->phys), "%s/input0", serio->phys); > > serio_set_drvdata(serio, w8001); > err = serio_open(serio, drv); > -- > 2.56.0.385.gd3acb90ef8-goog > > ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 2/3] Input: wacom_w8001 - assign distinct phys paths to pen and touch devices 2026-10-13 22:42 ` Jason Gerecke @ 2026-10-13 23:53 ` Dmitry Torokhov 0 siblings, 0 replies; 8+ messages in thread From: Dmitry Torokhov @ 2026-10-13 23:53 UTC (permalink / raw) To: Jason Gerecke Cc: linux-input, Ping Cheng, Jason Gerecke, Peter Hutterer, linux-kernel On Tue, Oct 13, 2026 at 03:42:10PM -0700, Jason Gerecke wrote: > On Fri, Oct 9, 2026 at 12:28 PM Dmitry Torokhov > <dmitry.torokhov@gmail.com> wrote: > > > > Previously, both pen and touch input devices used the same phys path > > "%s/input0", which made it impossible for userspace or udev rules to > > distinguish the devices by physical topology. > > > > Assign distinct phys paths by introducing separate buffers pen_phys > > ("%s/input0") and touch_phys ("%s/input1") in struct w8001. Pass the > > corresponding phys path to w8001_allocate_device() during pen and touch > > setup, and drop the shared phys buffer from struct w8001. > > > > Fixes: e0361b70175f ("Input: wacom_w8001 - split the touch and pen devices into two devices") > > Does this really deserve a "Fixes" tag? This does not seem to be as > much of a "bug" as it is a limitation. Userspace and udev rules are > (currently) able to reliably distinguish the devices by name. It is a bug as far as input subsystem is concerned and it was introduced in the above referenced commit, so it has this tag. > Obviously that's not ideal, but I don't see much benefit to this patch > getting backported to the stable trees -- especially when /existing/ > udev rules aren't necessarily going to know what to do with this new > "input1" phys. Right. For input subsystem only commits that are explicitly marked as Cc: stable@ will be backported to stable trees, so it shoudl not be a concern. Thanks. -- Dmitry ^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH 3/3] Input: wacom_w8001 - use ACQUIRE() for interruptible mutex in w8001_open() 2026-10-09 19:07 [PATCH 1/3] Input: wacom_w8001 - fix teardown order and device lifetime Dmitry Torokhov 2026-10-09 19:07 ` [PATCH 2/3] Input: wacom_w8001 - assign distinct phys paths to pen and touch devices Dmitry Torokhov @ 2026-10-09 19:07 ` Dmitry Torokhov 2026-10-13 22:42 ` Jason Gerecke 2026-10-13 22:41 ` [PATCH 1/3] Input: wacom_w8001 - fix teardown order and device lifetime Jason Gerecke 2 siblings, 1 reply; 8+ messages in thread From: Dmitry Torokhov @ 2026-10-09 19:07 UTC (permalink / raw) To: linux-input; +Cc: Ping Cheng, Jason Gerecke, Peter Hutterer, linux-kernel Switch from scoped_guard(mutex_intr, ...) with an error fallback of -EINTR to ACQUIRE(mutex_intr, lock) and ACQUIRE_ERR(mutex_intr, &lock). This flattens the function logic and propagates the exact return value from mutex_lock_interruptible(). Assisted-by: LLM Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com> --- drivers/input/touchscreen/wacom_w8001.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/drivers/input/touchscreen/wacom_w8001.c b/drivers/input/touchscreen/wacom_w8001.c index e967f2413706..b34fcac44f63 100644 --- a/drivers/input/touchscreen/wacom_w8001.c +++ b/drivers/input/touchscreen/wacom_w8001.c @@ -387,18 +387,20 @@ static int w8001_open(struct input_dev *dev) struct w8001 *w8001 = input_get_drvdata(dev); int err; - scoped_guard(mutex_intr, &w8001->mutex) { - if (w8001->open_count == 0) { - err = w8001_command(w8001, W8001_CMD_START, false); - if (err) - return err; - } + ACQUIRE(mutex_intr, lock)(&w8001->mutex); + err = ACQUIRE_ERR(mutex_intr, &lock); + if (err) + return err; - w8001->open_count++; - return 0; + if (w8001->open_count == 0) { + err = w8001_command(w8001, W8001_CMD_START, false); + if (err) + return err; } - return -EINTR; + w8001->open_count++; + + return 0; } static void w8001_close(struct input_dev *dev) -- 2.56.0.385.gd3acb90ef8-goog ^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH 3/3] Input: wacom_w8001 - use ACQUIRE() for interruptible mutex in w8001_open() 2026-10-09 19:07 ` [PATCH 3/3] Input: wacom_w8001 - use ACQUIRE() for interruptible mutex in w8001_open() Dmitry Torokhov @ 2026-10-13 22:42 ` Jason Gerecke 0 siblings, 0 replies; 8+ messages in thread From: Jason Gerecke @ 2026-10-13 22:42 UTC (permalink / raw) To: Dmitry Torokhov Cc: linux-input, Ping Cheng, Jason Gerecke, Peter Hutterer, linux-kernel On Fri, Oct 9, 2026 at 12:28 PM Dmitry Torokhov <dmitry.torokhov@gmail.com> wrote: > > Switch from scoped_guard(mutex_intr, ...) with an error fallback of > -EINTR to ACQUIRE(mutex_intr, lock) and ACQUIRE_ERR(mutex_intr, &lock). > This flattens the function logic and propagates the exact return value > from mutex_lock_interruptible(). > > Assisted-by: LLM Aside from the Assisted-by comment in Patch 1/3, this LGTM. Reviewed-by: Jason Gerecke <jason.gerecke@wacom.com> Jason (she/they) --- Now instead of four in the eights place / you’ve got three, ‘Cause you added one / (That is to say, eight) to the two, / But you can’t take seven from three, / So you look at the sixty-fours.... > Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com> > --- > drivers/input/touchscreen/wacom_w8001.c | 20 +++++++++++--------- > 1 file changed, 11 insertions(+), 9 deletions(-) > > diff --git a/drivers/input/touchscreen/wacom_w8001.c b/drivers/input/touchscreen/wacom_w8001.c > index e967f2413706..b34fcac44f63 100644 > --- a/drivers/input/touchscreen/wacom_w8001.c > +++ b/drivers/input/touchscreen/wacom_w8001.c > @@ -387,18 +387,20 @@ static int w8001_open(struct input_dev *dev) > struct w8001 *w8001 = input_get_drvdata(dev); > int err; > > - scoped_guard(mutex_intr, &w8001->mutex) { > - if (w8001->open_count == 0) { > - err = w8001_command(w8001, W8001_CMD_START, false); > - if (err) > - return err; > - } > + ACQUIRE(mutex_intr, lock)(&w8001->mutex); > + err = ACQUIRE_ERR(mutex_intr, &lock); > + if (err) > + return err; > > - w8001->open_count++; > - return 0; > + if (w8001->open_count == 0) { > + err = w8001_command(w8001, W8001_CMD_START, false); > + if (err) > + return err; > } > > - return -EINTR; > + w8001->open_count++; > + > + return 0; > } > > static void w8001_close(struct input_dev *dev) > -- > 2.56.0.385.gd3acb90ef8-goog > > ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 1/3] Input: wacom_w8001 - fix teardown order and device lifetime 2026-10-09 19:07 [PATCH 1/3] Input: wacom_w8001 - fix teardown order and device lifetime Dmitry Torokhov 2026-10-09 19:07 ` [PATCH 2/3] Input: wacom_w8001 - assign distinct phys paths to pen and touch devices Dmitry Torokhov 2026-10-09 19:07 ` [PATCH 3/3] Input: wacom_w8001 - use ACQUIRE() for interruptible mutex in w8001_open() Dmitry Torokhov @ 2026-10-13 22:41 ` Jason Gerecke 2026-10-13 23:50 ` Dmitry Torokhov 2 siblings, 1 reply; 8+ messages in thread From: Jason Gerecke @ 2026-10-13 22:41 UTC (permalink / raw) To: Dmitry Torokhov Cc: linux-input, Ping Cheng, Jason Gerecke, Peter Hutterer, linux-kernel, sashiko-bot On Fri, Oct 9, 2026 at 12:25 PM Dmitry Torokhov <dmitry.torokhov@gmail.com> wrote: > > In w8001_disconnect(), serio_close() was called before unregistering > the input devices. When input_unregister_device() calls w8001_close() > to shut down the hardware, w8001_command() fails because the serio > port is already closed, preventing W8001_CMD_STOP from reaching the > controller. > > Additionally, w8001_connect() pre-allocated both pen_dev and touch_dev > upfront before serio_open() so w8001_interrupt() could dereference > them unconditionally, and then freed unused input devices mid-probe > without synchronizing with w8001_interrupt() when pen or touch was > absent. That created a race and use-after-free hazard with > w8001_interrupt(), as well as a double-free on the fail4 error path. > > Fix these issues by guarding event reporting in w8001_interrupt() > against NULL device pointers, allocating and registering input devices > on demand after querying capabilities, synchronizing device pointer > publication and teardown with serio_pause_rx, and unregistering input > devices before closing the serio port in both disconnect and probe > error unwind paths. > This patch seems to be pretty complicated for a 'Fixes' and I'd feel a lot more comfortable reviewing it if it could be broken into smaller chunks. I know that might not be possible, but it's pretty hard to audit all the changes here. There's nothing that sets off loud alarms, but I think at this level of complexity the best I could do is Ack the patch... > Fixes: 66fd9385ee9c ("Input: wacom_w8001 - simplify w8001_remove") > Fixes: e0361b70175f ("Input: wacom_w8001 - split the touch and pen devices into two devices") > Reported-by: sashiko-bot@kernel.org > Assisted-by: LLM Just a note that current recommendations are to include the agent name and model version in Assisted-by lines: https://elixir.bootlin.com/linux/v7.2.9/source/Documentation/process/coding-assistants.rst#L39 > Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com> > --- > drivers/input/touchscreen/wacom_w8001.c | 341 +++++++++++++----------- > 1 file changed, 186 insertions(+), 155 deletions(-) > > diff --git a/drivers/input/touchscreen/wacom_w8001.c b/drivers/input/touchscreen/wacom_w8001.c > index d8d1cdc3f09e..1c48e2775214 100644 > --- a/drivers/input/touchscreen/wacom_w8001.c > +++ b/drivers/input/touchscreen/wacom_w8001.c > @@ -302,7 +302,8 @@ static irqreturn_t w8001_interrupt(struct serio *serio, > > if (w8001->pktlen == w8001->idx) { > w8001->idx = 0; > - if (w8001->type != BTN_TOOL_PEN && > + if (w8001->touch_dev && > + w8001->type != BTN_TOOL_PEN && > w8001->type != BTN_TOOL_RUBBER) { > parse_single_touch(w8001->data, &coord); > report_single_touch(w8001, &coord); > @@ -321,8 +322,10 @@ static irqreturn_t w8001_interrupt(struct serio *serio, > break; > > w8001->idx = 0; > - parse_pen_data(w8001->data, &coord); > - report_pen_events(w8001, &coord); > + if (w8001->pen_dev) { > + parse_pen_data(w8001->data, &coord); > + report_pen_events(w8001, &coord); > + } > break; > > /* control packet */ > @@ -340,7 +343,8 @@ static irqreturn_t w8001_interrupt(struct serio *serio, > /* 2 finger touch packet */ > case W8001_PKTLEN_TOUCH2FG - 1: > w8001->idx = 0; > - parse_multi_touch(w8001); > + if (w8001->touch_dev) > + parse_multi_touch(w8001); > break; > > default: > @@ -349,7 +353,9 @@ static irqreturn_t w8001_interrupt(struct serio *serio, > * sends invalid data packets that are larger than > * W8001_PKTLEN_TPCPEN. Let's start over again. > */ > - if (!w8001->touch_dev && w8001->idx > W8001_PKTLEN_TPCPEN - 1) > + if (!w8001->touch_dev && > + (w8001->data[0] & W8001_TAB_MASK) != W8001_TAB_BYTE && > + w8001->idx > W8001_PKTLEN_TPCPEN - 1) I'm not an expert in the w8001 protocol and don't have docs handy at the moment. Can you explain the purpose of adding this new W8001_TAB_BYTE check? I have a vague idea that it might have something to do with allowing the query response packets to be processed prior to pen/touch being initialized? Jason (she/they) --- Now instead of four in the eights place / you’ve got three, ‘Cause you added one / (That is to say, eight) to the two, / But you can’t take seven from three, / So you look at the sixty-fours.... > w8001->idx = 0; > } > > @@ -404,19 +410,6 @@ static void w8001_close(struct input_dev *dev) > w8001_command(w8001, W8001_CMD_STOP, false); > } > > -static int w8001_detect(struct w8001 *w8001) > -{ > - int error; > - > - error = w8001_command(w8001, W8001_CMD_STOP, false); > - if (error) > - return error; > - > - msleep(250); /* wait 250ms before querying the device */ > - > - return 0; > -} > - > static void w8001_append_suffix(char *dest, const char *suffix, size_t dest_sz) > { > size_t used = strnlen(dest, dest_sz); > @@ -424,88 +417,54 @@ static void w8001_append_suffix(char *dest, const char *suffix, size_t dest_sz) > strscpy(dest + used, suffix, dest_sz - used); > } > > -static int w8001_setup_pen(struct w8001 *w8001, char *basename, > - size_t basename_sz) > +static bool w8001_detect_pen(struct w8001 *w8001, > + char *basename, size_t basename_sz, > + struct w8001_coord *coord) > { > - struct input_dev *dev = w8001->pen_dev; > - struct w8001_coord coord; > int error; > > - /* penabled? */ > error = w8001_command(w8001, W8001_CMD_QUERY, true); > if (error) > - return error; > - > - __set_bit(EV_KEY, dev->evbit); > - __set_bit(EV_ABS, dev->evbit); > - __set_bit(BTN_TOUCH, dev->keybit); > - __set_bit(BTN_TOOL_PEN, dev->keybit); > - __set_bit(BTN_TOOL_RUBBER, dev->keybit); > - __set_bit(BTN_STYLUS, dev->keybit); > - __set_bit(BTN_STYLUS2, dev->keybit); > - __set_bit(INPUT_PROP_DIRECT, dev->propbit); > - > - parse_pen_data(w8001->response, &coord); > - w8001->max_pen_x = coord.x; > - w8001->max_pen_y = coord.y; > - > - input_set_abs_params(dev, ABS_X, 0, coord.x, 0, 0); > - input_set_abs_params(dev, ABS_Y, 0, coord.y, 0, 0); > - input_abs_set_res(dev, ABS_X, W8001_PEN_RESOLUTION); > - input_abs_set_res(dev, ABS_Y, W8001_PEN_RESOLUTION); > - input_set_abs_params(dev, ABS_PRESSURE, 0, coord.pen_pressure, 0, 0); > - if (coord.tilt_x && coord.tilt_y) { > - input_set_abs_params(dev, ABS_TILT_X, 0, coord.tilt_x, 0, 0); > - input_set_abs_params(dev, ABS_TILT_Y, 0, coord.tilt_y, 0, 0); > - } > + return false; > > + parse_pen_data(w8001->response, coord); > + w8001->max_pen_x = coord->x; > + w8001->max_pen_y = coord->y; > w8001->id = 0x90; > w8001_append_suffix(basename, " Penabled", basename_sz); > > - return 0; > + return true; > } > > -static int w8001_setup_touch(struct w8001 *w8001, char *basename, > - size_t basename_sz) > +static bool w8001_detect_touch(struct w8001 *w8001, > + char *basename, size_t basename_sz, > + struct w8001_touch_query *touch) > { > - struct input_dev *dev = w8001->touch_dev; > - struct w8001_touch_query touch; > int error; > > - > - /* Touch enabled? */ > error = w8001_command(w8001, W8001_CMD_TOUCHQUERY, true); > if (error) > - return error; > + return false; > + > /* > * Some non-touch devices may reply to the touch query. But their > * second byte is empty, which indicates touch is not supported. > */ > if (!w8001->response[1]) > - return -ENXIO; > + return false; > > - __set_bit(EV_KEY, dev->evbit); > - __set_bit(EV_ABS, dev->evbit); > - __set_bit(BTN_TOUCH, dev->keybit); > - __set_bit(INPUT_PROP_DIRECT, dev->propbit); > - > - parse_touchquery(w8001->response, &touch); > - w8001->max_touch_x = touch.x; > - w8001->max_touch_y = touch.y; > + parse_touchquery(w8001->response, touch); > + w8001->max_touch_x = touch->x; > + w8001->max_touch_y = touch->y; > > if (w8001->max_pen_x && w8001->max_pen_y) { > /* if pen is supported scale to pen maximum */ > - touch.x = w8001->max_pen_x; > - touch.y = w8001->max_pen_y; > - touch.panel_res = W8001_PEN_RESOLUTION; > + touch->x = w8001->max_pen_x; > + touch->y = w8001->max_pen_y; > + touch->panel_res = W8001_PEN_RESOLUTION; > } > > - input_set_abs_params(dev, ABS_X, 0, touch.x, 0, 0); > - input_set_abs_params(dev, ABS_Y, 0, touch.y, 0, 0); > - input_abs_set_res(dev, ABS_X, touch.panel_res); > - input_abs_set_res(dev, ABS_Y, touch.panel_res); > - > - switch (touch.sensor_id) { > + switch (touch->sensor_id) { > case 0: > case 2: > w8001->pktlen = W8001_PKTLEN_TOUCH93; > @@ -523,24 +482,6 @@ static int w8001_setup_touch(struct w8001 *w8001, char *basename, > > case 5: > w8001->pktlen = W8001_PKTLEN_TOUCH2FG; > - > - __set_bit(BTN_TOOL_DOUBLETAP, dev->keybit); > - error = input_mt_init_slots(dev, 2, 0); > - if (error) { > - dev_err(&w8001->serio->dev, > - "failed to initialize MT slots: %d\n", error); > - return error; > - } > - > - input_set_abs_params(dev, ABS_MT_POSITION_X, > - 0, touch.x, 0, 0); > - input_set_abs_params(dev, ABS_MT_POSITION_Y, > - 0, touch.y, 0, 0); > - input_set_abs_params(dev, ABS_MT_TOOL_TYPE, > - 0, MT_TOOL_MAX, 0, 0); > - input_abs_set_res(dev, ABS_MT_POSITION_X, touch.panel_res); > - input_abs_set_res(dev, ABS_MT_POSITION_Y, touch.panel_res); > - > w8001_append_suffix(basename, " 2FG", basename_sz); > if (w8001->max_pen_x && w8001->max_pen_y) > w8001->id = 0xE3; > @@ -550,13 +491,19 @@ static int w8001_setup_touch(struct w8001 *w8001, char *basename, > } > > w8001_append_suffix(basename, " Touchscreen", basename_sz); > - > - return 0; > + return true; > } > > -static void w8001_set_devdata(struct input_dev *dev, struct w8001 *w8001, > - struct serio *serio) > +static struct input_dev *w8001_allocate_device(struct w8001 *w8001, > + const char *name) > { > + struct input_dev *dev; > + > + dev = input_allocate_device(); > + if (!dev) > + return NULL; > + > + dev->name = name; > dev->phys = w8001->phys; > dev->id.bustype = BUS_RS232; > dev->id.product = w8001->id; > @@ -565,28 +512,139 @@ static void w8001_set_devdata(struct input_dev *dev, struct w8001 *w8001, > dev->open = w8001_open; > dev->close = w8001_close; > > - dev->dev.parent = &serio->dev; > + dev->dev.parent = &w8001->serio->dev; > + > + __set_bit(INPUT_PROP_DIRECT, dev->propbit); > + input_set_capability(dev, EV_KEY, BTN_TOUCH); > > input_set_drvdata(dev, w8001); > + > + return dev; > +} > + > +static int w8001_setup_pen(struct w8001 *w8001, const char *basename, > + const struct w8001_coord *coord) > +{ > + struct input_dev *dev; > + int error; > + > + snprintf(w8001->pen_name, sizeof(w8001->pen_name), "%s Pen", basename); > + > + dev = w8001_allocate_device(w8001, w8001->pen_name); > + if (!dev) > + return -ENOMEM; > + > + __set_bit(BTN_TOOL_PEN, dev->keybit); > + __set_bit(BTN_TOOL_RUBBER, dev->keybit); > + __set_bit(BTN_STYLUS, dev->keybit); > + __set_bit(BTN_STYLUS2, dev->keybit); > + > + input_set_abs_params(dev, ABS_X, 0, coord->x, 0, 0); > + input_set_abs_params(dev, ABS_Y, 0, coord->y, 0, 0); > + input_abs_set_res(dev, ABS_X, W8001_PEN_RESOLUTION); > + input_abs_set_res(dev, ABS_Y, W8001_PEN_RESOLUTION); > + input_set_abs_params(dev, ABS_PRESSURE, 0, coord->pen_pressure, 0, 0); > + if (coord->tilt_x && coord->tilt_y) { > + input_set_abs_params(dev, ABS_TILT_X, 0, coord->tilt_x, 0, 0); > + input_set_abs_params(dev, ABS_TILT_Y, 0, coord->tilt_y, 0, 0); > + } > + > + scoped_guard(serio_pause_rx, w8001->serio) > + w8001->pen_dev = dev; > + > + error = input_register_device(dev); > + if (error) > + goto err_unpublish_dev; > + > + return 0; > + > +err_unpublish_dev: > + scoped_guard(serio_pause_rx, w8001->serio) > + w8001->pen_dev = NULL; > + input_free_device(dev); > + return error; > +} > + > +static int w8001_setup_touch(struct w8001 *w8001, const char *basename, > + const struct w8001_touch_query *touch) > +{ > + struct input_dev *dev; > + int error; > + > + snprintf(w8001->touch_name, sizeof(w8001->touch_name), "%s Finger", basename); > + > + dev = w8001_allocate_device(w8001, w8001->touch_name); > + if (!dev) > + return -ENOMEM; > + > + input_set_abs_params(dev, ABS_X, 0, touch->x, 0, 0); > + input_set_abs_params(dev, ABS_Y, 0, touch->y, 0, 0); > + input_abs_set_res(dev, ABS_X, touch->panel_res); > + input_abs_set_res(dev, ABS_Y, touch->panel_res); > + > + if (touch->sensor_id == 5) { > + __set_bit(BTN_TOOL_DOUBLETAP, dev->keybit); > + error = input_mt_init_slots(dev, 2, 0); > + if (error) { > + dev_err(&w8001->serio->dev, > + "failed to initialize MT slots: %d\n", error); > + goto err_free_dev; > + } > + > + input_set_abs_params(dev, ABS_MT_POSITION_X, 0, touch->x, 0, 0); > + input_set_abs_params(dev, ABS_MT_POSITION_Y, 0, touch->y, 0, 0); > + input_set_abs_params(dev, ABS_MT_TOOL_TYPE, 0, MT_TOOL_MAX, 0, 0); > + input_abs_set_res(dev, ABS_MT_POSITION_X, touch->panel_res); > + input_abs_set_res(dev, ABS_MT_POSITION_Y, touch->panel_res); > + } > + > + scoped_guard(serio_pause_rx, w8001->serio) > + w8001->touch_dev = dev; > + > + error = input_register_device(dev); > + if (error) > + goto err_unpublish_dev; > + > + return 0; > + > +err_unpublish_dev: > + scoped_guard(serio_pause_rx, w8001->serio) > + w8001->touch_dev = NULL; > +err_free_dev: > + input_free_device(dev); > + return error; > } > > /* > * w8001_disconnect() is the opposite of w8001_connect() > */ > > +static void w8001_unregister_devices(struct w8001 *w8001) > +{ > + struct input_dev *pen_dev = NULL; > + struct input_dev *touch_dev = NULL; > + > + scoped_guard(serio_pause_rx, w8001->serio) { > + swap(pen_dev, w8001->pen_dev); > + swap(touch_dev, w8001->touch_dev); > + } > + > + if (pen_dev) > + input_unregister_device(pen_dev); > + if (touch_dev) > + input_unregister_device(touch_dev); > +} > + > static void w8001_disconnect(struct serio *serio) > { > struct w8001 *w8001 = serio_get_drvdata(serio); > > + w8001_unregister_devices(w8001); > + > serio_close(serio); > + serio_set_drvdata(serio, NULL); > > - if (w8001->pen_dev) > - input_unregister_device(w8001->pen_dev); > - if (w8001->touch_dev) > - input_unregister_device(w8001->touch_dev); > kfree(w8001); > - > - serio_set_drvdata(serio, NULL); > } > > /* > @@ -598,22 +656,17 @@ static void w8001_disconnect(struct serio *serio) > static int w8001_connect(struct serio *serio, struct serio_driver *drv) > { > struct w8001 *w8001; > - struct input_dev *input_dev_pen; > - struct input_dev *input_dev_touch; > + struct w8001_coord coord; > + struct w8001_touch_query touch; > char basename[64] = "Wacom Serial"; > - int err, err_pen, err_touch; > + bool has_pen, has_touch; > + int err; > > w8001 = kzalloc_obj(*w8001); > - input_dev_pen = input_allocate_device(); > - input_dev_touch = input_allocate_device(); > - if (!w8001 || !input_dev_pen || !input_dev_touch) { > - err = -ENOMEM; > - goto fail1; > - } > + if (!w8001) > + return -ENOMEM; > > w8001->serio = serio; > - w8001->pen_dev = input_dev_pen; > - w8001->touch_dev = input_dev_touch; > mutex_init(&w8001->mutex); > init_completion(&w8001->cmd_done); > snprintf(w8001->phys, sizeof(w8001->phys), "%s/input0", serio->phys); > @@ -621,66 +674,44 @@ static int w8001_connect(struct serio *serio, struct serio_driver *drv) > serio_set_drvdata(serio, w8001); > err = serio_open(serio, drv); > if (err) > - goto fail2; > + goto err_free_w8001; > > - err = w8001_detect(w8001); > + err = w8001_command(w8001, W8001_CMD_STOP, false); > if (err) > - goto fail3; > + goto err_close_serio; > + > + msleep(250); /* wait 250ms before querying the device */ > > - /* For backwards-compatibility we compose the basename based on > + /* > + * For backwards-compatibility we compose the basename based on > * capabilities and then just append the tool type > */ > - err_pen = w8001_setup_pen(w8001, basename, sizeof(basename)); > - err_touch = w8001_setup_touch(w8001, basename, sizeof(basename)); > - if (err_pen && err_touch) { > + has_pen = w8001_detect_pen(w8001, basename, sizeof(basename), &coord); > + has_touch = w8001_detect_touch(w8001, basename, sizeof(basename), &touch); > + if (!has_pen && !has_touch) { > err = -ENXIO; > - goto fail3; > + goto err_close_serio; > } > > - if (!err_pen) { > - snprintf(w8001->pen_name, sizeof(w8001->pen_name), > - "%s Pen", basename); > - input_dev_pen->name = w8001->pen_name; > - > - w8001_set_devdata(input_dev_pen, w8001, serio); > - > - err = input_register_device(w8001->pen_dev); > + if (has_pen) { > + err = w8001_setup_pen(w8001, basename, &coord); > if (err) > - goto fail3; > - } else { > - input_free_device(input_dev_pen); > - input_dev_pen = NULL; > - w8001->pen_dev = NULL; > + goto err_close_serio; > } > > - if (!err_touch) { > - snprintf(w8001->touch_name, sizeof(w8001->touch_name), > - "%s Finger", basename); > - input_dev_touch->name = w8001->touch_name; > - > - w8001_set_devdata(input_dev_touch, w8001, serio); > - > - err = input_register_device(w8001->touch_dev); > + if (has_touch) { > + err = w8001_setup_touch(w8001, basename, &touch); > if (err) > - goto fail4; > - } else { > - input_free_device(input_dev_touch); > - input_dev_touch = NULL; > - w8001->touch_dev = NULL; > + goto err_close_serio; > } > > return 0; > > -fail4: > - if (w8001->pen_dev) > - input_unregister_device(w8001->pen_dev); > -fail3: > +err_close_serio: > + w8001_unregister_devices(w8001); > serio_close(serio); > -fail2: > +err_free_w8001: > serio_set_drvdata(serio, NULL); > -fail1: > - input_free_device(input_dev_pen); > - input_free_device(input_dev_touch); > kfree(w8001); > return err; > } > -- > 2.56.0.385.gd3acb90ef8-goog > > ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH 1/3] Input: wacom_w8001 - fix teardown order and device lifetime 2026-10-13 22:41 ` [PATCH 1/3] Input: wacom_w8001 - fix teardown order and device lifetime Jason Gerecke @ 2026-10-13 23:50 ` Dmitry Torokhov 0 siblings, 0 replies; 8+ messages in thread From: Dmitry Torokhov @ 2026-10-13 23:50 UTC (permalink / raw) To: Jason Gerecke Cc: linux-input, Ping Cheng, Jason Gerecke, Peter Hutterer, linux-kernel, sashiko-bot Hi Jason, On Tue, Oct 13, 2026 at 03:41:53PM -0700, Jason Gerecke wrote: > On Fri, Oct 9, 2026 at 12:25 PM Dmitry Torokhov > <dmitry.torokhov@gmail.com> wrote: > > > > In w8001_disconnect(), serio_close() was called before unregistering > > the input devices. When input_unregister_device() calls w8001_close() > > to shut down the hardware, w8001_command() fails because the serio > > port is already closed, preventing W8001_CMD_STOP from reaching the > > controller. > > > > Additionally, w8001_connect() pre-allocated both pen_dev and touch_dev > > upfront before serio_open() so w8001_interrupt() could dereference > > them unconditionally, and then freed unused input devices mid-probe > > without synchronizing with w8001_interrupt() when pen or touch was > > absent. That created a race and use-after-free hazard with > > w8001_interrupt(), as well as a double-free on the fail4 error path. > > > > Fix these issues by guarding event reporting in w8001_interrupt() > > against NULL device pointers, allocating and registering input devices > > on demand after querying capabilities, synchronizing device pointer > > publication and teardown with serio_pause_rx, and unregistering input > > devices before closing the serio port in both disconnect and probe > > error unwind paths. > > > > This patch seems to be pretty complicated for a 'Fixes' and I'd feel a > lot more comfortable reviewing it if it could be broken into smaller > chunks. I know that might not be possible, but it's pretty hard to > audit all the changes here. There's nothing that sets off loud alarms, > but I think at this level of complexity the best I could do is Ack the > patch... Sorry, I do not think it makes sense to split it further... > > > Fixes: 66fd9385ee9c ("Input: wacom_w8001 - simplify w8001_remove") > > Fixes: e0361b70175f ("Input: wacom_w8001 - split the touch and pen devices into two devices") > > Reported-by: sashiko-bot@kernel.org > > Assisted-by: LLM > > Just a note that current recommendations are to include the agent name > and model version in Assisted-by lines: > > https://elixir.bootlin.com/linux/v7.2.9/source/Documentation/process/coding-assistants.rst#L39 Not anymore since 816d9992d9ed ("coding-assistants: simplify attribution") The current format is: Assisted-by: LLM [TOOL1] [TOOL2] as in Assisted-by: LLM coccinelle sparse > > @@ -349,7 +353,9 @@ static irqreturn_t w8001_interrupt(struct serio *serio, > > * sends invalid data packets that are larger than > > * W8001_PKTLEN_TPCPEN. Let's start over again. > > */ > > - if (!w8001->touch_dev && w8001->idx > W8001_PKTLEN_TPCPEN - 1) > > + if (!w8001->touch_dev && > > + (w8001->data[0] & W8001_TAB_MASK) != W8001_TAB_BYTE && > > + w8001->idx > W8001_PKTLEN_TPCPEN - 1) > > I'm not an expert in the w8001 protocol and don't have docs handy at > the moment. Can you explain the purpose of adding this new > W8001_TAB_BYTE check? I have a vague idea that it might have something > to do with allowing the query response packets to be processed prior > to pen/touch being initialized? Yes, that's right. Previously, w8001->touch_dev was pre-allocated before querying the hardware, so !w8001->touch_dev was false during probing. Now that touch_dev is allocated on demand after the queries complete, w8001->touch_dev is NULL during probing, and without the W8001_TAB_BYTE check we'd be junking query responses (which are longer than W8001_PKTLEN_TPCPEN). Thanks. -- Dmitry ^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-10-13 23:53 UTC | newest] Thread overview: 8+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-10-09 19:07 [PATCH 1/3] Input: wacom_w8001 - fix teardown order and device lifetime Dmitry Torokhov 2026-10-09 19:07 ` [PATCH 2/3] Input: wacom_w8001 - assign distinct phys paths to pen and touch devices Dmitry Torokhov 2026-10-13 22:42 ` Jason Gerecke 2026-10-13 23:53 ` Dmitry Torokhov 2026-10-09 19:07 ` [PATCH 3/3] Input: wacom_w8001 - use ACQUIRE() for interruptible mutex in w8001_open() Dmitry Torokhov 2026-10-13 22:42 ` Jason Gerecke 2026-10-13 22:41 ` [PATCH 1/3] Input: wacom_w8001 - fix teardown order and device lifetime Jason Gerecke 2026-10-13 23:50 ` Dmitry Torokhov
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.