* [PATCH 0/2] Input: ilitek_ts: add stylus support for 0x0c reports
@ 2026-06-26 21:42 Kristian Mide
2026-06-26 21:42 ` [PATCH 1/2] Input: ilitek_ts: add stylus input support Kristian Mide
` (2 more replies)
0 siblings, 3 replies; 12+ messages in thread
From: Kristian Mide @ 2026-06-26 21:42 UTC (permalink / raw)
To: dmitry.torokhov; +Cc: linux-input, linux-kernel, Kristian Mide
Hi,
I bought a CHUWI Hi10 Max and was annoyed to find that the built-in
stylus was not usable with the upstream ilitek_ts driver.
This small series adds support for the pen report format emitted by the
device, exposing the stylus as a separate input device with hover,
pressure, and side-button support. The pen device is created lazily on
first pen report so touchscreen-only hardware does not advertise stylus
capabilities up front.
The packet format was reverse engineered on this device. In particular,
report ID 0x0c appears to carry pen events, pressure comes from
buf[6..7] shifted right by one, and the side buttons map to two bits in
buf[1].
The second patch is unrelated to the pen support itself, but keeps the
existing wrong-report logging from flooding dmesg when unexpected packets
repeat quickly.
On the tested CHUWI Hi10 Max this makes the pen usable in GNOME/Krita,
including pressure and side buttons.
Thanks,
Kristian
Kristian Mide (2):
Input: ilitek_ts: add stylus input support
Input: ilitek_ts: ratelimit unexpected report logging
drivers/input/touchscreen/ilitek_ts_i2c.c | 134 +++++++++++++++++++++-
1 file changed, 133 insertions(+), 1 deletion(-)
--
2.54.0
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH 1/2] Input: ilitek_ts: add stylus input support
2026-06-26 21:42 [PATCH 0/2] Input: ilitek_ts: add stylus support for 0x0c reports Kristian Mide
@ 2026-06-26 21:42 ` Kristian Mide
2026-06-26 21:56 ` sashiko-bot
2026-06-26 21:42 ` [PATCH 2/2] Input: ilitek_ts: ratelimit unexpected report logging Kristian Mide
2026-07-09 18:25 ` [PATCH v2 0/3] Input: ilitek_ts: add stylus support for 0x0c reports Kristian Mide
2 siblings, 1 reply; 12+ messages in thread
From: Kristian Mide @ 2026-06-26 21:42 UTC (permalink / raw)
To: dmitry.torokhov; +Cc: linux-input, linux-kernel, Kristian Mide
Add a separate stylus input device for report ID 0x0c packets,
with pressure, hover, and side-button support.
The pen device is created lazily on first pen report so
touchscreen-only hardware does not expose stylus capabilities up
front.
The packet format is reverse engineered from a tested CHUWI
Hi10 Max. Pressure is reported from buf[6..7] shifted right by
one, matching the observed 1024 pressure levels on the tested
device.
Pen coordinates are reported through touchscreen_report_pos()
so the same axis inversion and swapping properties used by the
touch path apply to the stylus as well.
---
drivers/input/touchscreen/ilitek_ts_i2c.c | 130 ++++++++++++++++++++++
1 file changed, 130 insertions(+)
diff --git a/drivers/input/touchscreen/ilitek_ts_i2c.c b/drivers/input/touchscreen/ilitek_ts_i2c.c
index 3de0fbf8d..f0721af02 100644
--- a/drivers/input/touchscreen/ilitek_ts_i2c.c
+++ b/drivers/input/touchscreen/ilitek_ts_i2c.c
@@ -18,6 +18,7 @@
#include <linux/gpio/consumer.h>
#include <linux/errno.h>
#include <linux/acpi.h>
+#include <linux/device/devres.h>
#include <linux/input/touchscreen.h>
#include <linux/unaligned.h>
@@ -37,6 +38,18 @@
#define ILITEK_TP_CMD_GET_IC_MODE 0xC0
#define ILITEK_TP_I2C_REPORT_ID 0x48
+/* Reverse engineered stylus report on a tested CHUWI Hi10 Max device. */
+#define ILITEK_PEN_I2C_REPORT_ID 0x0C
+#define ILITEK_PEN_PRESSURE_MAX 1023
+#define ILITEK_PEN_DISTANCE_MAX 2
+/* Userspace expects tablet axis resolution; with INPUT_PROP_DIRECT this is */
+/* mostly descriptive and does not materially affect event coordinates. */
+#define ILITEK_PEN_RESOLUTION 68
+
+#define ILITEK_PEN_FLAG_TOUCH 0x01
+#define ILITEK_PEN_FLAG_STYLUS2 0x02
+#define ILITEK_PEN_FLAG_STYLUS 0x08
+#define ILITEK_PEN_FLAG_PROX 0x10
#define REPORT_COUNT_ADDRESS 61
#define ILITEK_SUPPORT_MAX_POINT 40
@@ -50,6 +63,7 @@ struct ilitek_ts_data {
struct i2c_client *client;
struct gpio_desc *reset_gpio;
struct input_dev *input_dev;
+ struct input_dev *pen_input_dev;
struct touchscreen_properties prop;
const struct ilitek_protocol_map *ptl_cb_func;
@@ -89,6 +103,9 @@ enum ilitek_cmds {
MAX_CMD_CNT
};
+static int ilitek_pen_input_dev_init(struct device *dev,
+ struct ilitek_ts_data *ts);
+
/* ILITEK I2C R/W APIs */
static int ilitek_i2c_write_and_read(struct ilitek_ts_data *ts,
u8 *cmd, int write_len, int delay,
@@ -146,6 +163,62 @@ static void ilitek_touch_down(struct ilitek_ts_data *ts, unsigned int id,
touchscreen_report_pos(input, &ts->prop, x, y, true);
}
+static void ilitek_unregister_pen_input(void *data)
+{
+ struct ilitek_ts_data *ts = data;
+
+ input_unregister_device(ts->pen_input_dev);
+ ts->pen_input_dev = NULL;
+}
+
+/*
+ * buf[1] carries prox/touch/side-button state and buf[6..7] carries
+ * pressure. A right shift by one matches the observed 1024 pressure levels.
+ */
+static int ilitek_process_pen_report(struct ilitek_ts_data *ts, u8 *buf)
+{
+ struct device *dev = &ts->client->dev;
+ struct input_dev *input = ts->pen_input_dev;
+ unsigned int x, y, z, distance;
+ bool prox, touch, stylus, stylus2;
+ int error;
+
+ if (!input) {
+ error = ilitek_pen_input_dev_init(dev, ts);
+ if (error) {
+ dev_err_ratelimited(dev,
+ "failed to register pen input device: %d\n",
+ error);
+ return 0;
+ }
+ input = ts->pen_input_dev;
+ }
+
+ x = get_unaligned_le16(buf + 2);
+ y = get_unaligned_le16(buf + 4);
+ z = get_unaligned_le16(buf + 6) >> 1;
+ prox = !!(buf[1] & ILITEK_PEN_FLAG_PROX);
+ touch = !!(buf[1] & ILITEK_PEN_FLAG_TOUCH);
+ stylus = !!(buf[1] & ILITEK_PEN_FLAG_STYLUS);
+ stylus2 = !!(buf[1] & ILITEK_PEN_FLAG_STYLUS2);
+ distance = prox ? (touch ? 0 : 1) : ILITEK_PEN_DISTANCE_MAX;
+ if (!touch)
+ z = 0;
+ else if (z > ILITEK_PEN_PRESSURE_MAX)
+ z = ILITEK_PEN_PRESSURE_MAX;
+
+ input_report_key(input, BTN_TOOL_PEN, prox || touch);
+ input_report_key(input, BTN_TOUCH, touch);
+ input_report_key(input, BTN_STYLUS, stylus);
+ input_report_key(input, BTN_STYLUS2, stylus2);
+ touchscreen_report_pos(input, &ts->prop, x, y, false);
+ input_report_abs(input, ABS_PRESSURE, z);
+ input_report_abs(input, ABS_DISTANCE, distance);
+ input_sync(input);
+
+ return 0;
+}
+
static int ilitek_process_and_report_v6(struct ilitek_ts_data *ts)
{
int error = 0;
@@ -164,6 +237,9 @@ static int ilitek_process_and_report_v6(struct ilitek_ts_data *ts)
return error;
}
+ if (buf[0] == ILITEK_PEN_I2C_REPORT_ID)
+ return ilitek_process_pen_report(ts, buf);
+
if (buf[0] != ILITEK_TP_I2C_REPORT_ID) {
dev_err(dev, "get touch info failed. Wrong id: 0x%02X\n", buf[0]);
return -EINVAL;
@@ -459,6 +535,60 @@ static int ilitek_read_tp_info(struct ilitek_ts_data *ts, bool boot)
return 0;
}
+static int ilitek_pen_input_dev_init(struct device *dev, struct ilitek_ts_data *ts)
+{
+ struct input_dev *pen_input;
+ int error;
+
+ if (ts->pen_input_dev)
+ return 0;
+
+ /* No explicit pen capability probe is known; create on first pen report. */
+
+ pen_input = input_allocate_device();
+ if (!pen_input)
+ return -ENOMEM;
+
+ ts->pen_input_dev = pen_input;
+ pen_input->dev.parent = dev;
+ pen_input->name = "ilitek_ts_pen";
+ pen_input->id.bustype = BUS_I2C;
+
+ __set_bit(INPUT_PROP_DIRECT, pen_input->propbit);
+ __set_bit(EV_KEY, pen_input->evbit);
+ __set_bit(EV_ABS, pen_input->evbit);
+ __set_bit(BTN_TOUCH, pen_input->keybit);
+ __set_bit(BTN_TOOL_PEN, pen_input->keybit);
+ __set_bit(BTN_STYLUS, pen_input->keybit);
+ __set_bit(BTN_STYLUS2, pen_input->keybit);
+
+ input_set_abs_params(pen_input, ABS_X,
+ ts->screen_min_x, ts->screen_max_x, 0, 0);
+ input_set_abs_params(pen_input, ABS_Y,
+ ts->screen_min_y, ts->screen_max_y, 0, 0);
+ input_set_abs_params(pen_input, ABS_PRESSURE, 0,
+ ILITEK_PEN_PRESSURE_MAX, 0, 0);
+ input_set_abs_params(pen_input, ABS_DISTANCE, 0,
+ ILITEK_PEN_DISTANCE_MAX, 0, 0);
+ input_abs_set_res(pen_input, ABS_X, ILITEK_PEN_RESOLUTION);
+ input_abs_set_res(pen_input, ABS_Y, ILITEK_PEN_RESOLUTION);
+
+ error = input_register_device(pen_input);
+ if (error)
+ goto err_free_pen_input;
+
+ error = devm_add_action_or_reset(dev, ilitek_unregister_pen_input, ts);
+ if (error)
+ return error;
+
+ return 0;
+
+err_free_pen_input:
+ ts->pen_input_dev = NULL;
+ input_free_device(pen_input);
+ return error;
+}
+
static int ilitek_input_dev_init(struct device *dev, struct ilitek_ts_data *ts)
{
int error;
--
2.54.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH 2/2] Input: ilitek_ts: ratelimit unexpected report logging
2026-06-26 21:42 [PATCH 0/2] Input: ilitek_ts: add stylus support for 0x0c reports Kristian Mide
2026-06-26 21:42 ` [PATCH 1/2] Input: ilitek_ts: add stylus input support Kristian Mide
@ 2026-06-26 21:42 ` Kristian Mide
2026-06-26 21:56 ` sashiko-bot
2026-07-09 18:25 ` [PATCH v2 0/3] Input: ilitek_ts: add stylus support for 0x0c reports Kristian Mide
2 siblings, 1 reply; 12+ messages in thread
From: Kristian Mide @ 2026-06-26 21:42 UTC (permalink / raw)
To: dmitry.torokhov; +Cc: linux-input, linux-kernel, Kristian Mide
Unexpected report IDs can repeat quickly when the controller
emits unknown packets.
Use dev_err_ratelimited() and include the first 16 bytes of the
raw packet to keep dmesg usable while preserving enough context
for debugging.
---
drivers/input/touchscreen/ilitek_ts_i2c.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/input/touchscreen/ilitek_ts_i2c.c b/drivers/input/touchscreen/ilitek_ts_i2c.c
index f0721af02..8886bd39b 100644
--- a/drivers/input/touchscreen/ilitek_ts_i2c.c
+++ b/drivers/input/touchscreen/ilitek_ts_i2c.c
@@ -241,7 +241,9 @@ static int ilitek_process_and_report_v6(struct ilitek_ts_data *ts)
return ilitek_process_pen_report(ts, buf);
if (buf[0] != ILITEK_TP_I2C_REPORT_ID) {
- dev_err(dev, "get touch info failed. Wrong id: 0x%02X\n", buf[0]);
+ dev_err_ratelimited(dev,
+ "get touch info failed. Wrong id: 0x%02X raw[0:16]=%*ph\n",
+ buf[0], 16, buf);
return -EINVAL;
}
--
2.54.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH 2/2] Input: ilitek_ts: ratelimit unexpected report logging
2026-06-26 21:42 ` [PATCH 2/2] Input: ilitek_ts: ratelimit unexpected report logging Kristian Mide
@ 2026-06-26 21:56 ` sashiko-bot
0 siblings, 0 replies; 12+ messages in thread
From: sashiko-bot @ 2026-06-26 21:56 UTC (permalink / raw)
To: Kristian Mide; +Cc: dmitry.torokhov, linux-input
Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
New issues:
- [High] The I2C read/write wrapper does not properly check for short transfers, and this patch leaks the resulting uninitialized stack memory to dmesg.
Pre-existing issues:
- [High] The patch fails to prevent dmesg flooding and causes the interrupt handler to incorrectly signal a spurious interrupt, which can permanently disable the touchscreen.
- [High] Dynamically adding a devres action in a threaded IRQ handler creates an inverted teardown order during device unbind, leading to a memory leak and Use-After-Free.
- [High] If the I2C sleep command fails during suspend, the function returns an error without re-enabling the IRQ, rendering the touchscreen permanently unresponsive.
- [High] The driver passes stack-allocated arrays to i2c_transfer(), violating DMA mapping rules and risking memory corruption.
--
commit ec332d9cd1c7923c04e4eec0684513387a92348d
Author: Kristian Mide <kristian@mide.dk>
Input: ilitek_ts: ratelimit unexpected report logging
Unexpected report IDs can repeat quickly when the controller emits unknown
packets. This commit uses dev_err_ratelimited() and includes the first 16
bytes of the raw packet for context.
> diff --git a/drivers/input/touchscreen/ilitek_ts_i2c.c b/drivers/input/touchscreen/ilitek_ts_i2c.c
> index f0721af02adf2..8886bd39bc0b6 100644
> --- a/drivers/input/touchscreen/ilitek_ts_i2c.c
> +++ b/drivers/input/touchscreen/ilitek_ts_i2c.c
> @@ -241,7 +241,9 @@ static int ilitek_process_and_report_v6(struct ilitek_ts_data *ts)
[Severity: High]
This is a pre-existing issue, but does ilitek_process_and_report_v6() pass a
stack-allocated array to i2c_transfer()?
drivers/input/touchscreen/ilitek_ts_i2c.c:ilitek_process_and_report_v6() {
...
u8 buf[512];
...
error = ilitek_i2c_write_and_read(ts, NULL, 0, 0, buf, 64);
If the underlying I2C host controller driver maps the buffer for DMA, it might
attempt to map a stack address. On modern kernels where VMAP_STACK is enabled,
can this cause an immediate panic because stack memory is not physically
contiguous, or lead to cacheline sharing corruption otherwise?
> return ilitek_process_pen_report(ts, buf);
[Severity: High]
This is a pre-existing issue, but does dynamically adding a devres action
in the threaded IRQ handler create an inverted teardown order?
The IRQ is registered via devm_request_threaded_irq() in probe. Later, the
ISR dynamically calls devm_add_action_or_reset() to register the pen cleanup:
drivers/input/touchscreen/ilitek_ts_i2c.c:ilitek_process_pen_report() {
...
if (!input) {
error = ilitek_pen_input_dev_init(dev, ts);
which calls devm_add_action_or_reset(dev, ilitek_unregister_pen_input, ts).
Because devm is a LIFO stack, the pen cleanup is placed above the IRQ release
action. During device unbind, devres_release_all() will run the pen cleanup
first, freeing the device, while the IRQ remains active. If an interrupt fires
before the IRQ is finally freed, could the ISR access the freed device and
cause a use-after-free, or attempt to append a new devres action while the
stack is being torn down?
>
> if (buf[0] != ILITEK_TP_I2C_REPORT_ID) {
> - dev_err(dev, "get touch info failed. Wrong id: 0x%02X\n", buf[0]);
> + dev_err_ratelimited(dev,
> + "get touch info failed. Wrong id: 0x%02X raw[0:16]=%*ph\n",
> + buf[0], 16, buf);
[Severity: High]
Does this new logging leak uninitialized kernel stack memory to dmesg?
Looking at ilitek_i2c_write_and_read(), it checks if error < 0, but
i2c_transfer() returns the number of successfully transferred messages:
drivers/input/touchscreen/ilitek_ts_i2c.c:ilitek_i2c_write_and_read() {
...
error = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
if (error < 0)
return error;
If a short transfer occurs, this evaluates to false, so the wrapper returns 0
(success) despite the read buffer not being populated. When that happens,
won't this new log message directly print 16 bytes of uninitialized kernel
stack memory to the dmesg buffer?
> return -EINVAL;
[Severity: High]
This is a pre-existing issue, but does returning -EINVAL here cause the
interrupt handler to incorrectly signal a spurious interrupt?
When an unknown packet is received, ilitek_process_and_report_v6() returns
-EINVAL to the ISR, which then handles the error like this:
drivers/input/touchscreen/ilitek_ts_i2c.c:ilitek_i2c_isr() {
...
error = ilitek_process_and_report_v6(ts);
if (error < 0) {
dev_err(&ts->client->dev, "[%s] err:%d\n", __func__, error);
return IRQ_NONE;
}
First, the ISR unconditionally logs the error, which defeats the ratelimiting
added in this patch. Second, could repeatedly returning IRQ_NONE when the
hardware interrupt was actually serviced (cleared by the I2C read) cause the
kernel's spurious interrupt detection to eventually disable the IRQ line
permanently?
> }
>
[ ... ]
[Severity: High]
This is a pre-existing issue, but does ilitek_suspend() return an error
without re-enabling the previously disabled IRQ?
drivers/input/touchscreen/ilitek_ts_i2c.c:ilitek_suspend() {
...
disable_irq(client->irq);
if (!device_may_wakeup(dev)) {
error = api_protocol_set_cmd(ts, SET_IC_SLEEP, NULL, NULL);
if (error)
return error;
}
If the I2C sleep command fails during suspend, the function returns an error
while the device IRQ is disabled. The PM core will abort the suspend
transition and resume previously suspended devices, but it will not call the
resume callback for the device that returned the error. Could this render the
touchscreen permanently unresponsive until reboot or module reload?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260626214248.5563-1-kristian@mide.dk?part=2
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH 1/2] Input: ilitek_ts: add stylus input support
2026-06-26 21:42 ` [PATCH 1/2] Input: ilitek_ts: add stylus input support Kristian Mide
@ 2026-06-26 21:56 ` sashiko-bot
0 siblings, 0 replies; 12+ messages in thread
From: sashiko-bot @ 2026-06-26 21:56 UTC (permalink / raw)
To: Kristian Mide; +Cc: dmitry.torokhov, linux-input
Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
New issues:
- [High] Lazy initialization of the pen input device from within the IRQ handler causes a devres LIFO ordering inversion, leading to a memory leak and Use-After-Free during device unbind.
- [High] The pen input device axis limits are not synced with Device Tree properties, leading to mismatched axis boundaries and massive coordinate underflow values.
- [Low] Direct bit manipulation is used instead of the preferred `input_set_capability()` helper.
Pre-existing issues:
- [High] I2C transfers are performed using buffers allocated on the stack, which is unsafe for DMA.
--
commit 0912736a7a818d72cc6868aba6ad50c6f06afd78
Author: Kristian Mide <kristian@mide.dk>
Input: ilitek_ts: add stylus input support
Add a separate stylus input device for report ID 0x0c packets,
with pressure, hover, and side-button support. The pen device is
created lazily on first pen report so touchscreen-only hardware does
not expose stylus capabilities up front.
> diff --git a/drivers/input/touchscreen/ilitek_ts_i2c.c b/drivers/input/touchscreen/ilitek_ts_i2c.c
> index 3de0fbf8da386..f0721af02adf2 100644
> --- a/drivers/input/touchscreen/ilitek_ts_i2c.c
> +++ b/drivers/input/touchscreen/ilitek_ts_i2c.c
[ ... ]
> @@ -164,6 +237,9 @@ static int ilitek_process_and_report_v6(struct ilitek_ts_data *ts)
> return error;
> }
>
> + if (buf[0] == ILITEK_PEN_I2C_REPORT_ID)
> + return ilitek_process_pen_report(ts, buf);
[Severity: High]
This is a pre-existing issue and was not introduced by the patch under
review, but since buf is passed here, is it safe to allocate buf as a
512-byte array on the stack in ilitek_process_and_report_v6() and pass it to
I2C transfer functions?
When CONFIG_VMAP_STACK is enabled, stack addresses cannot be safely mapped
for DMA. If the underlying I2C host controller uses DMA, could attempting
to map this stack buffer lead to memory corruption or kernel panics?
> +
> if (buf[0] != ILITEK_TP_I2C_REPORT_ID) {
> dev_err(dev, "get touch info failed. Wrong id: 0x%02X\n", buf[0]);
> return -EINVAL;
[ ... ]
> @@ -459,6 +535,60 @@ static int ilitek_read_tp_info(struct ilitek_ts_data *ts, bool boot)
> return 0;
> }
>
> +static int ilitek_pen_input_dev_init(struct device *dev, struct ilitek_ts_data *ts)
> +{
> + struct input_dev *pen_input;
> + int error;
> +
> + if (ts->pen_input_dev)
> + return 0;
> +
> + /* No explicit pen capability probe is known; create on first pen report. */
> +
> + pen_input = input_allocate_device();
> + if (!pen_input)
> + return -ENOMEM;
> +
> + ts->pen_input_dev = pen_input;
> + pen_input->dev.parent = dev;
> + pen_input->name = "ilitek_ts_pen";
> + pen_input->id.bustype = BUS_I2C;
> +
> + __set_bit(INPUT_PROP_DIRECT, pen_input->propbit);
> + __set_bit(EV_KEY, pen_input->evbit);
> + __set_bit(EV_ABS, pen_input->evbit);
> + __set_bit(BTN_TOUCH, pen_input->keybit);
> + __set_bit(BTN_TOOL_PEN, pen_input->keybit);
> + __set_bit(BTN_STYLUS, pen_input->keybit);
> + __set_bit(BTN_STYLUS2, pen_input->keybit);
[Severity: Low]
This isn't a bug, but could we use the input_set_capability() helper here
instead of direct bit manipulation?
> +
> + input_set_abs_params(pen_input, ABS_X,
> + ts->screen_min_x, ts->screen_max_x, 0, 0);
> + input_set_abs_params(pen_input, ABS_Y,
> + ts->screen_min_y, ts->screen_max_y, 0, 0);
[Severity: High]
Should the touchscreen properties (DT properties) be applied to these
absolute parameters?
The touchscreen_report_pos() function called during event processing will
apply inversion and swapping based on Device Tree properties to the
coordinates.
If the limits advertised here are strictly from the raw hardware bounds and
are unaware of those DT properties, could this cause the device to report
out-of-bounds coordinates or result in unsigned underflows when an axis is
inverted?
> + input_set_abs_params(pen_input, ABS_PRESSURE, 0,
> + ILITEK_PEN_PRESSURE_MAX, 0, 0);
> + input_set_abs_params(pen_input, ABS_DISTANCE, 0,
> + ILITEK_PEN_DISTANCE_MAX, 0, 0);
> + input_abs_set_res(pen_input, ABS_X, ILITEK_PEN_RESOLUTION);
> + input_abs_set_res(pen_input, ABS_Y, ILITEK_PEN_RESOLUTION);
> +
> + error = input_register_device(pen_input);
> + if (error)
> + goto err_free_pen_input;
> +
> + error = devm_add_action_or_reset(dev, ilitek_unregister_pen_input, ts);
[Severity: High]
Since this function is called lazily from the threaded IRQ handler after
probe has completed, does adding a devm action here break the LIFO teardown
ordering?
During device unbind, devres will release resources in LIFO order. This
action would run first and set ts->pen_input_dev = NULL, but the IRQ
handler would still be active.
If a hardware interrupt fires during unbind after this has been
unregistered, would the IRQ handler observe the NULL pointer, allocate a
new pen device, and add a new devres action that devres_release_all()
will never process, resulting in a memory leak and a potential
use-after-free?
> + if (error)
> + return error;
> +
> + return 0;
> +
> +err_free_pen_input:
> + ts->pen_input_dev = NULL;
> + input_free_device(pen_input);
> + return error;
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260626214248.5563-1-kristian@mide.dk?part=1
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v2 0/3] Input: ilitek_ts: add stylus support for 0x0c reports
2026-06-26 21:42 [PATCH 0/2] Input: ilitek_ts: add stylus support for 0x0c reports Kristian Mide
2026-06-26 21:42 ` [PATCH 1/2] Input: ilitek_ts: add stylus input support Kristian Mide
2026-06-26 21:42 ` [PATCH 2/2] Input: ilitek_ts: ratelimit unexpected report logging Kristian Mide
@ 2026-07-09 18:25 ` Kristian Mide
2026-07-09 18:26 ` [PATCH v2 1/3] Input: ilitek_ts: add stylus input support Kristian Mide
` (2 more replies)
2 siblings, 3 replies; 12+ messages in thread
From: Kristian Mide @ 2026-07-09 18:25 UTC (permalink / raw)
To: dmitry.torokhov; +Cc: linux-input, linux-kernel, Kristian Mide
Hi,
I bought a CHUWI Hi10 Max and was annoyed to find that the built-in
stylus was not usable with the upstream ilitek_ts driver.
This series adds support for the pen report format emitted by the
device, exposing the stylus as a separate input device with hover,
pressure, and side-button support. The pen device is created lazily on
first pen report so touchscreen-only hardware does not advertise stylus
capabilities up front.
The packet format was reverse engineered on this device. In particular,
report ID 0x0c appears to carry pen events, pressure comes from
buf[6..7] shifted right by one, and the side buttons map to two bits in
buf[1].
Changes in v2:
- manage the lazy pen device lifetime explicitly and tear down the IRQ
before unregistering the pen device
- sync pen axis limits with the DT-adjusted touch axis limits
- reject short I2C transfers in ilitek_i2c_write_and_read()
- split the short-transfer fix from the ratelimited logging change
On the tested CHUWI Hi10 Max this makes the pen usable in GNOME/Krita,
including pressure and side buttons.
Thanks,
Kristian
Kristian Mide (3):
Input: ilitek_ts: add stylus input support
Input: ilitek_ts: handle short I2C transfers
Input: ilitek_ts: ratelimit unexpected report logging
drivers/input/touchscreen/ilitek_ts_i2c.c | 149 +++++++++++++++++++++-
1 file changed, 145 insertions(+), 4 deletions(-)
--
2.54.0
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v2 1/3] Input: ilitek_ts: add stylus input support
2026-07-09 18:25 ` [PATCH v2 0/3] Input: ilitek_ts: add stylus support for 0x0c reports Kristian Mide
@ 2026-07-09 18:26 ` Kristian Mide
2026-07-09 18:36 ` sashiko-bot
2026-07-09 18:26 ` [PATCH v2 2/3] Input: ilitek_ts: handle short I2C transfers Kristian Mide
2026-07-09 18:26 ` [PATCH v2 3/3] Input: ilitek_ts: ratelimit unexpected report logging Kristian Mide
2 siblings, 1 reply; 12+ messages in thread
From: Kristian Mide @ 2026-07-09 18:26 UTC (permalink / raw)
To: dmitry.torokhov; +Cc: linux-input, linux-kernel, Kristian Mide
Add a separate stylus input device for report ID 0x0c packets,
with pressure, hover, and side-button support.
The pen device is created lazily on first pen report so
touchscreen-only hardware does not expose stylus capabilities up
front.
The packet format is reverse engineered from a tested CHUWI
Hi10 Max. Pressure is reported from buf[6..7] shifted right by
one, matching the observed 1024 pressure levels on the tested
device.
Pen coordinates are reported through touchscreen_report_pos()
so the same axis inversion and swapping properties used by the
touch path apply to the stylus as well.
The pen device lifetime is managed explicitly so the IRQ is
removed before the lazy pen input device is unregistered during
driver teardown.
---
drivers/input/touchscreen/ilitek_ts_i2c.c | 139 +++++++++++++++++++++-
1 file changed, 136 insertions(+), 3 deletions(-)
diff --git a/drivers/input/touchscreen/ilitek_ts_i2c.c b/drivers/input/touchscreen/ilitek_ts_i2c.c
index 3de0fbf8d..54e39721f 100644
--- a/drivers/input/touchscreen/ilitek_ts_i2c.c
+++ b/drivers/input/touchscreen/ilitek_ts_i2c.c
@@ -37,6 +37,18 @@
#define ILITEK_TP_CMD_GET_IC_MODE 0xC0
#define ILITEK_TP_I2C_REPORT_ID 0x48
+/* Reverse engineered stylus report on a tested CHUWI Hi10 Max device. */
+#define ILITEK_PEN_I2C_REPORT_ID 0x0C
+#define ILITEK_PEN_PRESSURE_MAX 1023
+#define ILITEK_PEN_DISTANCE_MAX 2
+/* Userspace expects tablet axis resolution; with INPUT_PROP_DIRECT this is */
+/* mostly descriptive and does not materially affect event coordinates. */
+#define ILITEK_PEN_RESOLUTION 68
+
+#define ILITEK_PEN_FLAG_TOUCH 0x01
+#define ILITEK_PEN_FLAG_STYLUS2 0x02
+#define ILITEK_PEN_FLAG_STYLUS 0x08
+#define ILITEK_PEN_FLAG_PROX 0x10
#define REPORT_COUNT_ADDRESS 61
#define ILITEK_SUPPORT_MAX_POINT 40
@@ -50,6 +62,7 @@ struct ilitek_ts_data {
struct i2c_client *client;
struct gpio_desc *reset_gpio;
struct input_dev *input_dev;
+ struct input_dev *pen_input_dev;
struct touchscreen_properties prop;
const struct ilitek_protocol_map *ptl_cb_func;
@@ -89,6 +102,9 @@ enum ilitek_cmds {
MAX_CMD_CNT
};
+static int ilitek_pen_input_dev_init(struct device *dev,
+ struct ilitek_ts_data *ts);
+
/* ILITEK I2C R/W APIs */
static int ilitek_i2c_write_and_read(struct ilitek_ts_data *ts,
u8 *cmd, int write_len, int delay,
@@ -146,6 +162,54 @@ static void ilitek_touch_down(struct ilitek_ts_data *ts, unsigned int id,
touchscreen_report_pos(input, &ts->prop, x, y, true);
}
+/*
+ * buf[1] carries prox/touch/side-button state and buf[6..7] carries
+ * pressure. A right shift by one matches the observed 1024 pressure levels.
+ */
+static int ilitek_process_pen_report(struct ilitek_ts_data *ts, u8 *buf)
+{
+ struct device *dev = &ts->client->dev;
+ struct input_dev *input = ts->pen_input_dev;
+ unsigned int x, y, z, distance;
+ bool prox, touch, stylus, stylus2;
+ int error;
+
+ if (!input) {
+ error = ilitek_pen_input_dev_init(dev, ts);
+ if (error) {
+ dev_err_ratelimited(dev,
+ "failed to register pen input device: %d\n",
+ error);
+ return 0;
+ }
+ input = ts->pen_input_dev;
+ }
+
+ x = get_unaligned_le16(buf + 2);
+ y = get_unaligned_le16(buf + 4);
+ z = get_unaligned_le16(buf + 6) >> 1;
+ prox = !!(buf[1] & ILITEK_PEN_FLAG_PROX);
+ touch = !!(buf[1] & ILITEK_PEN_FLAG_TOUCH);
+ stylus = !!(buf[1] & ILITEK_PEN_FLAG_STYLUS);
+ stylus2 = !!(buf[1] & ILITEK_PEN_FLAG_STYLUS2);
+ distance = prox ? (touch ? 0 : 1) : ILITEK_PEN_DISTANCE_MAX;
+ if (!touch)
+ z = 0;
+ else if (z > ILITEK_PEN_PRESSURE_MAX)
+ z = ILITEK_PEN_PRESSURE_MAX;
+
+ input_report_key(input, BTN_TOOL_PEN, prox || touch);
+ input_report_key(input, BTN_TOUCH, touch);
+ input_report_key(input, BTN_STYLUS, stylus);
+ input_report_key(input, BTN_STYLUS2, stylus2);
+ touchscreen_report_pos(input, &ts->prop, x, y, false);
+ input_report_abs(input, ABS_PRESSURE, z);
+ input_report_abs(input, ABS_DISTANCE, distance);
+ input_sync(input);
+
+ return 0;
+}
+
static int ilitek_process_and_report_v6(struct ilitek_ts_data *ts)
{
int error = 0;
@@ -164,6 +228,9 @@ static int ilitek_process_and_report_v6(struct ilitek_ts_data *ts)
return error;
}
+ if (buf[0] == ILITEK_PEN_I2C_REPORT_ID)
+ return ilitek_process_pen_report(ts, buf);
+
if (buf[0] != ILITEK_TP_I2C_REPORT_ID) {
dev_err(dev, "get touch info failed. Wrong id: 0x%02X\n", buf[0]);
return -EINVAL;
@@ -459,6 +526,60 @@ static int ilitek_read_tp_info(struct ilitek_ts_data *ts, bool boot)
return 0;
}
+static int ilitek_pen_input_dev_init(struct device *dev, struct ilitek_ts_data *ts)
+{
+ struct input_dev *pen_input;
+ int error;
+
+ if (ts->pen_input_dev)
+ return 0;
+
+ /* No explicit pen capability probe is known; create on first pen report. */
+
+ pen_input = input_allocate_device();
+ if (!pen_input)
+ return -ENOMEM;
+
+ ts->pen_input_dev = pen_input;
+ pen_input->dev.parent = dev;
+ pen_input->name = "ilitek_ts_pen";
+ pen_input->id.bustype = BUS_I2C;
+
+ __set_bit(INPUT_PROP_DIRECT, pen_input->propbit);
+ __set_bit(EV_KEY, pen_input->evbit);
+ __set_bit(EV_ABS, pen_input->evbit);
+ __set_bit(BTN_TOUCH, pen_input->keybit);
+ __set_bit(BTN_TOOL_PEN, pen_input->keybit);
+ __set_bit(BTN_STYLUS, pen_input->keybit);
+ __set_bit(BTN_STYLUS2, pen_input->keybit);
+
+ input_set_abs_params(pen_input, ABS_X,
+ input_abs_get_min(ts->input_dev, ABS_MT_POSITION_X),
+ input_abs_get_max(ts->input_dev, ABS_MT_POSITION_X),
+ 0, 0);
+ input_set_abs_params(pen_input, ABS_Y,
+ input_abs_get_min(ts->input_dev, ABS_MT_POSITION_Y),
+ input_abs_get_max(ts->input_dev, ABS_MT_POSITION_Y),
+ 0, 0);
+ input_set_abs_params(pen_input, ABS_PRESSURE, 0,
+ ILITEK_PEN_PRESSURE_MAX, 0, 0);
+ input_set_abs_params(pen_input, ABS_DISTANCE, 0,
+ ILITEK_PEN_DISTANCE_MAX, 0, 0);
+ input_abs_set_res(pen_input, ABS_X, ILITEK_PEN_RESOLUTION);
+ input_abs_set_res(pen_input, ABS_Y, ILITEK_PEN_RESOLUTION);
+
+ error = input_register_device(pen_input);
+ if (error)
+ goto err_free_pen_input;
+
+ return 0;
+
+err_free_pen_input:
+ ts->pen_input_dev = NULL;
+ input_free_device(pen_input);
+ return error;
+}
+
static int ilitek_input_dev_init(struct device *dev, struct ilitek_ts_data *ts)
{
int error;
@@ -589,9 +710,8 @@ static int ilitek_ts_i2c_probe(struct i2c_client *client)
return error;
}
- error = devm_request_threaded_irq(dev, ts->client->irq,
- NULL, ilitek_i2c_isr, IRQF_ONESHOT,
- "ilitek_touch_irq", ts);
+ error = request_threaded_irq(ts->client->irq, NULL, ilitek_i2c_isr,
+ IRQF_ONESHOT, "ilitek_touch_irq", ts);
if (error) {
dev_err(dev, "request threaded irq failed: %d\n", error);
return error;
@@ -600,6 +720,18 @@ static int ilitek_ts_i2c_probe(struct i2c_client *client)
return 0;
}
+static void ilitek_ts_i2c_remove(struct i2c_client *client)
+{
+ struct ilitek_ts_data *ts = i2c_get_clientdata(client);
+
+ free_irq(client->irq, ts);
+
+ if (ts->pen_input_dev) {
+ input_unregister_device(ts->pen_input_dev);
+ ts->pen_input_dev = NULL;
+ }
+}
+
static int ilitek_suspend(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
@@ -677,6 +809,7 @@ static struct i2c_driver ilitek_ts_i2c_driver = {
.acpi_match_table = ACPI_PTR(ilitekts_acpi_id),
},
.probe = ilitek_ts_i2c_probe,
+ .remove = ilitek_ts_i2c_remove,
.id_table = ilitek_ts_i2c_id,
};
module_i2c_driver(ilitek_ts_i2c_driver);
--
2.54.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v2 2/3] Input: ilitek_ts: handle short I2C transfers
2026-07-09 18:25 ` [PATCH v2 0/3] Input: ilitek_ts: add stylus support for 0x0c reports Kristian Mide
2026-07-09 18:26 ` [PATCH v2 1/3] Input: ilitek_ts: add stylus input support Kristian Mide
@ 2026-07-09 18:26 ` Kristian Mide
2026-07-09 18:37 ` sashiko-bot
2026-07-09 18:26 ` [PATCH v2 3/3] Input: ilitek_ts: ratelimit unexpected report logging Kristian Mide
2 siblings, 1 reply; 12+ messages in thread
From: Kristian Mide @ 2026-07-09 18:26 UTC (permalink / raw)
To: dmitry.torokhov; +Cc: linux-input, linux-kernel, Kristian Mide
Check that i2c_transfer() returns the expected number of
messages in ilitek_i2c_write_and_read().
Short transfers were previously treated as success, allowing
callers to continue with partially filled buffers. In the report
path that can expose uninitialized stack data through debugging
output and can also confuse packet parsing.
---
drivers/input/touchscreen/ilitek_ts_i2c.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/drivers/input/touchscreen/ilitek_ts_i2c.c b/drivers/input/touchscreen/ilitek_ts_i2c.c
index 54e39721f..6146baa2b 100644
--- a/drivers/input/touchscreen/ilitek_ts_i2c.c
+++ b/drivers/input/touchscreen/ilitek_ts_i2c.c
@@ -131,11 +131,15 @@ static int ilitek_i2c_write_and_read(struct ilitek_ts_data *ts,
error = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
if (error < 0)
return error;
+ if (error != ARRAY_SIZE(msgs))
+ return -EIO;
} else {
if (write_len > 0) {
error = i2c_transfer(client->adapter, msgs, 1);
if (error < 0)
return error;
+ if (error != 1)
+ return -EIO;
}
if (delay > 0)
fsleep(delay * 1000);
@@ -144,6 +148,8 @@ static int ilitek_i2c_write_and_read(struct ilitek_ts_data *ts,
error = i2c_transfer(client->adapter, msgs + 1, 1);
if (error < 0)
return error;
+ if (error != 1)
+ return -EIO;
}
}
--
2.54.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v2 3/3] Input: ilitek_ts: ratelimit unexpected report logging
2026-07-09 18:25 ` [PATCH v2 0/3] Input: ilitek_ts: add stylus support for 0x0c reports Kristian Mide
2026-07-09 18:26 ` [PATCH v2 1/3] Input: ilitek_ts: add stylus input support Kristian Mide
2026-07-09 18:26 ` [PATCH v2 2/3] Input: ilitek_ts: handle short I2C transfers Kristian Mide
@ 2026-07-09 18:26 ` Kristian Mide
2026-07-09 18:40 ` sashiko-bot
2 siblings, 1 reply; 12+ messages in thread
From: Kristian Mide @ 2026-07-09 18:26 UTC (permalink / raw)
To: dmitry.torokhov; +Cc: linux-input, linux-kernel, Kristian Mide
Unexpected report IDs can repeat quickly when the controller
emits unknown packets.
Use dev_err_ratelimited() and include the first 16 bytes of the
raw packet to keep dmesg usable while preserving enough context
for debugging.
---
drivers/input/touchscreen/ilitek_ts_i2c.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/input/touchscreen/ilitek_ts_i2c.c b/drivers/input/touchscreen/ilitek_ts_i2c.c
index 6146baa2b..e68bc0522 100644
--- a/drivers/input/touchscreen/ilitek_ts_i2c.c
+++ b/drivers/input/touchscreen/ilitek_ts_i2c.c
@@ -238,7 +238,9 @@ static int ilitek_process_and_report_v6(struct ilitek_ts_data *ts)
return ilitek_process_pen_report(ts, buf);
if (buf[0] != ILITEK_TP_I2C_REPORT_ID) {
- dev_err(dev, "get touch info failed. Wrong id: 0x%02X\n", buf[0]);
+ dev_err_ratelimited(dev,
+ "get touch info failed. Wrong id: 0x%02X raw[0:16]=%*ph\n",
+ buf[0], 16, buf);
return -EINVAL;
}
--
2.54.0
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH v2 1/3] Input: ilitek_ts: add stylus input support
2026-07-09 18:26 ` [PATCH v2 1/3] Input: ilitek_ts: add stylus input support Kristian Mide
@ 2026-07-09 18:36 ` sashiko-bot
0 siblings, 0 replies; 12+ messages in thread
From: sashiko-bot @ 2026-07-09 18:36 UTC (permalink / raw)
To: Kristian Mide; +Cc: linux-input, dmitry.torokhov
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] If `api_protocol_set_cmd` fails during `ilitek_suspend`, the function returns an error without re-enabling the IRQ.
--
--- Patch [1]: [PATCH v2 1/3] Input: ilitek_ts: add stylus input support ---
commit 93b3664194ad6413b8e1020fb08120b8f1d2a174
Author: Kristian Mide <kristian@mide.dk>
Input: ilitek_ts: add stylus input support
Add a separate stylus input device for report ID 0x0c packets,
with pressure, hover, and side-button support.
> diff --git a/drivers/input/touchscreen/ilitek_ts_i2c.c b/drivers/input/touchscreen/ilitek_ts_i2c.c
> index 3de0fbf8da386..54e39721f2473 100644
> --- a/drivers/input/touchscreen/ilitek_ts_i2c.c
> +++ b/drivers/input/touchscreen/ilitek_ts_i2c.c
[ ... ]
> @@ -600,6 +720,18 @@ static int ilitek_ts_i2c_probe(struct i2c_client *client)
> return 0;
> }
>
> +static void ilitek_ts_i2c_remove(struct i2c_client *client)
> +{
[ ... ]
> +}
> +
> static int ilitek_suspend(struct device *dev)
> {
> struct i2c_client *client = to_i2c_client(dev);
[Severity: High]
This isn't a bug introduced by this patch, but looking at ilitek_suspend():
drivers/input/touchscreen/ilitek_ts_i2c.c:ilitek_suspend() {
...
disable_irq(client->irq);
if (!device_may_wakeup(dev)) {
error = api_protocol_set_cmd(ts, SET_IC_SLEEP, NULL, NULL);
if (error)
return error;
...
}
If api_protocol_set_cmd() fails, does returning the error directly leave
the IRQ permanently disabled?
If an error is returned during suspend, the PM core aborts the system suspend
and will not invoke the resume callback for this device. As a result,
enable_irq() would never be called, which could leave the touchscreen
unresponsive. Should this error path re-enable the IRQ before returning?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260709182602.7308-1-kristian@mide.dk?part=1
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v2 2/3] Input: ilitek_ts: handle short I2C transfers
2026-07-09 18:26 ` [PATCH v2 2/3] Input: ilitek_ts: handle short I2C transfers Kristian Mide
@ 2026-07-09 18:37 ` sashiko-bot
0 siblings, 0 replies; 12+ messages in thread
From: sashiko-bot @ 2026-07-09 18:37 UTC (permalink / raw)
To: Kristian Mide; +Cc: dmitry.torokhov, linux-input
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
Pre-existing issues:
- [High] Missing `enable_irq()` on error paths in suspend and resume functions.
- [High] Incorrect touch packet parsing offset across 64-byte chunks for points > 10.
--
--- Patch [2]: [PATCH v2 2/3] Input: ilitek_ts: handle short I2C transfers ---
commit fea6e8240af99ced183b54d3a3d2c69a63ac6791
Author: Kristian Mide <kristian@mide.dk>
Input: ilitek_ts: handle short I2C transfers
Check that i2c_transfer() returns the expected number of
messages in ilitek_i2c_write_and_read().
> diff --git a/drivers/input/touchscreen/ilitek_ts_i2c.c b/drivers/input/touchscreen/ilitek_ts_i2c.c
> index 54e39721f2473..6146baa2b6062 100644
> --- a/drivers/input/touchscreen/ilitek_ts_i2c.c
> +++ b/drivers/input/touchscreen/ilitek_ts_i2c.c
> @@ -131,11 +131,15 @@ static int ilitek_i2c_write_and_read(struct ilitek_ts_data *ts,
> error = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
> if (error < 0)
> return error;
> + if (error != ARRAY_SIZE(msgs))
> + return -EIO;
> } else {
> if (write_len > 0) {
> error = i2c_transfer(client->adapter, msgs, 1);
> if (error < 0)
> return error;
> + if (error != 1)
> + return -EIO;
> }
> if (delay > 0)
> fsleep(delay * 1000);
> @@ -144,6 +148,8 @@ static int ilitek_i2c_write_and_read(struct ilitek_ts_data *ts,
> error = i2c_transfer(client->adapter, msgs + 1, 1);
> if (error < 0)
> return error;
> + if (error != 1)
> + return -EIO;
> }
> }
drivers/input/touchscreen/ilitek_ts_i2c.c:ilitek_suspend() {
...
error = api_protocol_set_cmd(ts, SET_IC_SLEEP, NULL, NULL);
if (error)
return error;
...
}
[Severity: High]
This isn't a bug introduced by this patch, but does this error path return
early without re-enabling the IRQ that was disabled at the start of the
function?
If api_protocol_set_cmd() fails during system suspend or resume, the PM core
will abort the suspend process. Because enable_irq() is skipped, won't
ilitek_resume() not be called for this device, leaving the IRQ permanently
disabled and the device unresponsive?
drivers/input/touchscreen/ilitek_ts_i2c.c:ilitek_resume() {
...
error = api_protocol_set_cmd(ts, SET_IC_WAKE, NULL, NULL);
if (error)
return error;
...
}
[Severity: High]
This is a pre-existing issue, but does this error path also return early
before the enable_irq() call is reached?
drivers/input/touchscreen/ilitek_ts_i2c.c:ilitek_process_and_report_v6() {
...
status = buf[i * packet_len + 1] & 0x40;
if (!status)
continue;
id = buf[i * packet_len + 1] & 0x3F;
x = get_unaligned_le16(buf + i * packet_len + 2);
y = get_unaligned_le16(buf + i * packet_len + 4);
...
}
[Severity: High]
This isn't a bug introduced by this patch, but does this purely linear
index calculation i * packet_len cause out-of-bounds offsets into padding
areas and conflict with metadata offsets for indices >= 10?
The driver reads touch points into buf in 64-byte chunks, each holding up
to 10 touch points, and advances the buffer pointer by 64 bytes for each
chunk.
Because each chunk only uses 50 bytes of data (leaving 14 bytes of padding),
won't the linear formula fail to account for the 64-byte chunk boundaries
instead of correctly tracking them?
For example, for the 11th point (i = 10), the driver should read from
buf[64], but it incorrectly reads from buf[50] (which is inside the first
chunk's padding). For i = 12, the offset buf[12 * 5 + 1] resolves to 61,
colliding exactly with REPORT_COUNT_ADDRESS, resulting in corrupted
coordinates and statuses for points > 10.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260709182602.7308-1-kristian@mide.dk?part=2
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v2 3/3] Input: ilitek_ts: ratelimit unexpected report logging
2026-07-09 18:26 ` [PATCH v2 3/3] Input: ilitek_ts: ratelimit unexpected report logging Kristian Mide
@ 2026-07-09 18:40 ` sashiko-bot
0 siblings, 0 replies; 12+ messages in thread
From: sashiko-bot @ 2026-07-09 18:40 UTC (permalink / raw)
To: Kristian Mide; +Cc: linux-input, dmitry.torokhov
Thank you for your contribution! Sashiko AI review found 6 potential issue(s) to consider:
New issues:
- [Medium] The patch fails to prevent dmesg spam because the caller logs an un-ratelimited error for any negative return value.
- [Low] Direct bit manipulation is used to set single device capabilities instead of the standard helper function.
Pre-existing issues:
- [High] Unbalanced disable_irq() in suspend and resume error paths leaves the device interrupt permanently disabled.
- [High] Multi-touch data parsing accesses incorrect buffer offsets for reports with more than 10 active touch points, parsing garbage data.
- [High] Returning IRQ_NONE for a serviced hardware interrupt can cause the kernel to permanently disable the IRQ line.
- [High] I2C transfer buffers are allocated on the stack, which can cause arbitrary physical memory corruption when DMA is used with vmapped stacks.
--
--- Patch [3]: [PATCH v2 3/3] Input: ilitek_ts: ratelimit unexpected report logging ---
commit bf03da99321dcdb2c6e97a5d9bbe75d687cccec8
Author: Kristian Mide <kristian@mide.dk>
Input: ilitek_ts: ratelimit unexpected report logging
Unexpected report IDs can repeat quickly when the controller
emits unknown packets.
Use dev_err_ratelimited() and include the first 16 bytes of the
raw packet to keep dmesg usable while preserving enough context
for debugging.
> diff --git a/drivers/input/touchscreen/ilitek_ts_i2c.c b/drivers/input/touchscreen/ilitek_ts_i2c.c
> index 6146baa2b6062..e68bc052248ef 100644
> --- a/drivers/input/touchscreen/ilitek_ts_i2c.c
> +++ b/drivers/input/touchscreen/ilitek_ts_i2c.c
> @@ -238,7 +238,9 @@ static int ilitek_process_and_report_v6(struct ilitek_ts_data *ts)
> return ilitek_process_pen_report(ts, buf);
>
> if (buf[0] != ILITEK_TP_I2C_REPORT_ID) {
> - dev_err(dev, "get touch info failed. Wrong id: 0x%02X\n", buf[0]);
> + dev_err_ratelimited(dev,
> + "get touch info failed. Wrong id: 0x%02X raw[0:16]=%*ph\n",
> + buf[0], 16, buf);
> return -EINVAL;
> }
[Severity: Medium]
Does this actually prevent dmesg spam? The caller ilitek_i2c_isr() observes
the -EINVAL return value and unconditionally logs an un-ratelimited error
for any negative return value:
drivers/input/touchscreen/ilitek_ts_i2c.c:ilitek_i2c_isr() {
...
error = ilitek_process_and_report_v6(ts);
if (error < 0) {
dev_err(&ts->client->dev, "[%s] err:%d\n", __func__, error);
return IRQ_NONE;
}
...
}
[Severity: High]
This is a pre-existing issue, but returning IRQ_NONE from ilitek_i2c_isr()
after successfully executing I2C reads and clearing the hardware interrupt
can trigger the kernel's spurious interrupt detector.
If the hardware emits a stream of unexpected report IDs, will repeatedly
returning IRQ_NONE permanently disable the IRQ line?
[Severity: High]
This is a pre-existing issue, but does ilitek_process_and_report_v6()
incorrectly parse multi-touch data for reports with more than 10 active
touch points?
For 11+ points, the data is split into multiple 64-byte reads, but the loop
calculates a linear byte offset that completely disregards the 64-byte chunk
boundaries:
drivers/input/touchscreen/ilitek_ts_i2c.c:ilitek_process_and_report_v6() {
...
for (i = 0; i < report_max_point; i++) {
status = buf[i * packet_len + 1] & 0x40;
...
}
For the 11th point (index 10), this offset calculation (10 * 5 + 1 = 51)
reads from the padding of the first 64-byte chunk instead of advancing to
the next chunk. Does this result in parsing garbage data?
[Severity: High]
This is a pre-existing issue, but does allocating I2C transfer buffers on the
stack cause arbitrary physical memory corruption when DMA is used?
drivers/input/touchscreen/ilitek_ts_i2c.c:ilitek_process_and_report_v6() {
...
u8 buf[512];
...
error = ilitek_i2c_write_and_read(ts, NULL, 0, 0, buf, 64);
...
}
If CONFIG_VMAP_STACK is enabled, the stack may not be physically contiguous.
Can a DMA transfer crossing a physical page boundary silently overwrite
unrelated physical memory? (This also happens in ilitek_read_tp_info()
with a 256-byte stack buffer).
[Severity: Low]
Does ilitek_pen_input_dev_init() use direct bit manipulation to set single
device capabilities instead of the standard input_set_capability() helper?
drivers/input/touchscreen/ilitek_ts_i2c.c:ilitek_pen_input_dev_init() {
...
__set_bit(INPUT_PROP_DIRECT, pen_input->propbit);
__set_bit(EV_KEY, pen_input->evbit);
__set_bit(EV_ABS, pen_input->evbit);
...
}
[Severity: High]
This is a pre-existing issue, but do the suspend and resume error paths leave
the device interrupt permanently disabled?
drivers/input/touchscreen/ilitek_ts_i2c.c:ilitek_suspend() {
...
disable_irq(client->irq);
if (!device_may_wakeup(dev)) {
error = api_protocol_set_cmd(ts, SET_IC_SLEEP, NULL, NULL);
if (error)
return error;
}
...
}
If api_protocol_set_cmd() fails, it bails out before enable_irq() can be
called to balance the disable_irq(). A similar issue exists in
ilitek_resume(). Does a failure during suspend/resume permanently kill
touchscreen functionality?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260709182602.7308-1-kristian@mide.dk?part=3
^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2026-07-09 18:40 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-26 21:42 [PATCH 0/2] Input: ilitek_ts: add stylus support for 0x0c reports Kristian Mide
2026-06-26 21:42 ` [PATCH 1/2] Input: ilitek_ts: add stylus input support Kristian Mide
2026-06-26 21:56 ` sashiko-bot
2026-06-26 21:42 ` [PATCH 2/2] Input: ilitek_ts: ratelimit unexpected report logging Kristian Mide
2026-06-26 21:56 ` sashiko-bot
2026-07-09 18:25 ` [PATCH v2 0/3] Input: ilitek_ts: add stylus support for 0x0c reports Kristian Mide
2026-07-09 18:26 ` [PATCH v2 1/3] Input: ilitek_ts: add stylus input support Kristian Mide
2026-07-09 18:36 ` sashiko-bot
2026-07-09 18:26 ` [PATCH v2 2/3] Input: ilitek_ts: handle short I2C transfers Kristian Mide
2026-07-09 18:37 ` sashiko-bot
2026-07-09 18:26 ` [PATCH v2 3/3] Input: ilitek_ts: ratelimit unexpected report logging Kristian Mide
2026-07-09 18:40 ` sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox