* [PATCH v2 1/5] Input: zinitix - check all available fingers for every touch event
2026-07-30 19:48 [PATCH v2 0/5] Support zinitix touch modes 0 and 1, dt changes to Galaxy J6 (j6lte) Kaustabh Chakraborty
@ 2026-07-30 19:48 ` Kaustabh Chakraborty
2026-07-30 19:59 ` sashiko-bot
2026-07-30 19:48 ` [PATCH v2 2/5] Input: zinitix - do not ignore non-moving fingers Kaustabh Chakraborty
` (3 subsequent siblings)
4 siblings, 1 reply; 11+ messages in thread
From: Kaustabh Chakraborty @ 2026-07-30 19:48 UTC (permalink / raw)
To: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Michael Srba, Linus Walleij, Peter Griffin, Alim Akhtar
Cc: linux-input, linux-kernel, devicetree, linux-arm-kernel,
linux-samsung-soc, Kaustabh Chakraborty
When this initial driver was first added to tree, that is, in commit
26822652c85e ("Input: add zinitix touchscreen driver"), the touch_event
struct had a field called finger_cnt. It was supposed to report how many
fingers are touching the screen.
But then, in commit e941dc13fd37 ("Input: zinitix - do not report shadow
fingers"), some touchscreens reportedly exposed a bit mask for the
fingers, instead of the count. So the code was changed to bitwise
iteration.
With my testing on the ZT7548 touchscreen of the Galaxy J6, I find the
former to be true. This shows that there's two valid methods depending
on what hardware the driver is made to work on.
One solution is to implement both methods, and use some flag to select
between the two. However, this introduces more implementation overhead,
and a possibility of regression on devices the driver is expected to work.
Instead, unconditionally check all fingers. The finger_mask field is now
left unused, thus serving as padding bytes in the struct. For each
finger, zinitix_report_finger() is called if the status reports the
SUB_BIT_EXIST bit, so phantom fingers are not going to be a thing.
Moreover, the android driver [1] does exactly that, so it's a tried
method of implementation.
Link: https://android.googlesource.com/kernel/bcm/+/23d376ef33aa4c500a5ea24a290f029d5f8e2de3/drivers/input/touchscreen/zinitix_touch.c#1942 [1]
Signed-off-by: Kaustabh Chakraborty <kauschluss@disroot.org>
---
drivers/input/touchscreen/zinitix.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/drivers/input/touchscreen/zinitix.c b/drivers/input/touchscreen/zinitix.c
index 0c36765bd79f..3421b8ffb19b 100644
--- a/drivers/input/touchscreen/zinitix.c
+++ b/drivers/input/touchscreen/zinitix.c
@@ -445,7 +445,6 @@ static irqreturn_t zinitix_ts_irq_handler(int irq, void *bt541_handler)
struct bt541_ts_data *bt541 = bt541_handler;
struct i2c_client *client = bt541->client;
struct touch_event touch_event;
- unsigned long finger_mask;
__le16 icon_events;
int error;
int i;
@@ -470,8 +469,7 @@ static irqreturn_t zinitix_ts_irq_handler(int irq, void *bt541_handler)
zinitix_report_keys(bt541, le16_to_cpu(icon_events));
}
- finger_mask = touch_event.finger_mask;
- for_each_set_bit(i, &finger_mask, MAX_SUPPORTED_FINGER_NUM) {
+ for (i = 0; i < MAX_SUPPORTED_FINGER_NUM; i++) {
const struct point_coord *p = &touch_event.point_coord[i];
/* Only process contacts that are actually reported */
--
2.54.0
^ permalink raw reply related [flat|nested] 11+ messages in thread* Re: [PATCH v2 1/5] Input: zinitix - check all available fingers for every touch event
2026-07-30 19:48 ` [PATCH v2 1/5] Input: zinitix - check all available fingers for every touch event Kaustabh Chakraborty
@ 2026-07-30 19:59 ` sashiko-bot
0 siblings, 0 replies; 11+ messages in thread
From: sashiko-bot @ 2026-07-30 19:59 UTC (permalink / raw)
To: Kaustabh Chakraborty
Cc: robh, conor+dt, linux-input, dmitry.torokhov, devicetree
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
New issues:
- [High] Unconditionally iterating over all slots reverts a previous bugfix and reintroduces phantom touches on BT404 controllers.
Pre-existing issues:
- [High] I2C message buffers are allocated on the stack, violating DMA API rules.
- [High] Input device is registered before all driver private data is fully initialized, allowing a Use-After-Free if probe fails.
--
--- Patch [1]: [PATCH v2 1/5] Input: zinitix - check all available fingers for every touch event ---
commit 035227fe3b93a4e9baec0dee6f309a653f790ded
Author: Kaustabh Chakraborty <kauschluss@disroot.org>
Input: zinitix - check all available fingers for every touch event
This commit modifies the Zinitix touchscreen driver to unconditionally
iterate over all possible fingers during a touch event, removing the
use of the finger_mask field.
Link: https://android.googlesource.com/kernel/bcm/+/23d376ef33aa4c500a5ea24a290f029d5f8e2de3/drivers/input/touchscreen/zinitix_touch.c#1942 [1]
> diff --git a/drivers/input/touchscreen/zinitix.c b/drivers/input/touchscreen/zinitix.c
> index 0c36765bd79f8..3421b8ffb19b2 100644
> --- a/drivers/input/touchscreen/zinitix.c
> +++ b/drivers/input/touchscreen/zinitix.c
> @@ -445,7 +445,6 @@ static irqreturn_t zinitix_ts_irq_handler(int irq, void *bt541_handler)
> struct bt541_ts_data *bt541 = bt541_handler;
> struct i2c_client *client = bt541->client;
> struct touch_event touch_event;
[Severity: High]
This is a pre-existing issue, but does passing this stack-allocated variable
to the I2C read functions violate DMA API rules?
During a touch event, zinitix_ts_irq_handler() passes the stack-allocated
touch_event down to zinitix_read_data(), which eventually reaches
i2c_master_recv(). If the I2C master driver uses DMA, mapping this stack
memory could lead to memory corruption or a kernel panic on architectures
with VMAP_STACK and DMA API debugging enabled.
[ ... ]
> @@ -470,8 +469,7 @@ static irqreturn_t zinitix_ts_irq_handler(int irq, void *bt541_handler)
> zinitix_report_keys(bt541, le16_to_cpu(icon_events));
> }
>
> - finger_mask = touch_event.finger_mask;
> - for_each_set_bit(i, &finger_mask, MAX_SUPPORTED_FINGER_NUM) {
> + for (i = 0; i < MAX_SUPPORTED_FINGER_NUM; i++) {
> const struct point_coord *p = &touch_event.point_coord[i];
>
> /* Only process contacts that are actually reported */
[Severity: High]
Could unconditionally iterating over all slots revert a previous bugfix for
BT404 controllers?
When a touch occurs on a BT404 controller, the firmware incorrectly leaves
SUB_BIT_EXIST set for shadow fingers. By dropping the finger_mask filter
here, it looks like zinitix_report_finger() will be called for those
shadow fingers, potentially causing multiple phantom inputs for every
physical touch.
[Severity: High]
This is a pre-existing issue, but looking at zinitix_ts_probe(), is there a
use-after-free window if probe fails?
The zinitix,mode property is parsed and validated after the input device is
already registered by zinitix_init_input_dev():
zinitix_ts_probe() {
...
error = zinitix_init_input_dev(bt541);
if (error) { ... }
error = device_property_read_u32(&client->dev, "zinitix,mode",
&bt541->zinitix_mode);
if (error < 0) { ... }
...
}
If parsing fails, probe returns an error and devres frees the bt541
structure. If userspace opens the device during this window, the input_dev
survives the probe failure. When userspace subsequently closes it, wouldn't
zinitix_input_close() dereference the freed bt541 pointer?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260731-zinitix-modes-v2-0-ffe2ec9a9031@disroot.org?part=1
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v2 2/5] Input: zinitix - do not ignore non-moving fingers
2026-07-30 19:48 [PATCH v2 0/5] Support zinitix touch modes 0 and 1, dt changes to Galaxy J6 (j6lte) Kaustabh Chakraborty
2026-07-30 19:48 ` [PATCH v2 1/5] Input: zinitix - check all available fingers for every touch event Kaustabh Chakraborty
@ 2026-07-30 19:48 ` Kaustabh Chakraborty
2026-07-30 19:58 ` sashiko-bot
2026-07-30 19:48 ` [PATCH v2 3/5] Input: zinitix - add support for modes 0 and 1 Kaustabh Chakraborty
` (2 subsequent siblings)
4 siblings, 1 reply; 11+ messages in thread
From: Kaustabh Chakraborty @ 2026-07-30 19:48 UTC (permalink / raw)
To: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Michael Srba, Linus Walleij, Peter Griffin, Alim Akhtar
Cc: linux-input, linux-kernel, devicetree, linux-arm-kernel,
linux-samsung-soc, Kaustabh Chakraborty
With the ZT7548 touchscreen present in the Galaxy J6, multitouch does not
work reliably. This is due to the fact that the driver reports fingers
only when their state is changed, so it's either placed against the
screen, moved, or drawn away from the screen.
The function which is responsible for this is zinitix_report_finger().
This function is called from the IRQ handler, under the following
condition:
if (p->sub_status & SUB_BIT_EXIST)
zinitix_report_finger(bt541, i, p);
This implies and ensures that every valid finger must have the
SUB_BIT_EXIST flag.
However, at the beginning of the function, it refuses to recognize any
finger if it has none of SUB_BIT_UP | SUB_BIT_DOWN | SUB_BIT_MOVE. This
excludes fingers in reports which do not move from the position since
the previous interrupt. Add SUB_BIT_EXIST to the list of valid bits.
Signed-off-by: Kaustabh Chakraborty <kauschluss@disroot.org>
---
drivers/input/touchscreen/zinitix.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/input/touchscreen/zinitix.c b/drivers/input/touchscreen/zinitix.c
index 3421b8ffb19b..a0a89d25e25b 100644
--- a/drivers/input/touchscreen/zinitix.c
+++ b/drivers/input/touchscreen/zinitix.c
@@ -406,7 +406,7 @@ static void zinitix_report_finger(struct bt541_ts_data *bt541, int slot,
u16 x, y;
if (unlikely(!(p->sub_status &
- (SUB_BIT_UP | SUB_BIT_DOWN | SUB_BIT_MOVE)))) {
+ (SUB_BIT_EXIST | SUB_BIT_UP | SUB_BIT_DOWN | SUB_BIT_MOVE)))) {
dev_dbg(&bt541->client->dev, "unknown finger event %#02x\n",
p->sub_status);
return;
@@ -473,7 +473,7 @@ static irqreturn_t zinitix_ts_irq_handler(int irq, void *bt541_handler)
const struct point_coord *p = &touch_event.point_coord[i];
/* Only process contacts that are actually reported */
- if (p->sub_status & SUB_BIT_EXIST)
+ if (p->sub_status)
zinitix_report_finger(bt541, i, p);
}
--
2.54.0
^ permalink raw reply related [flat|nested] 11+ messages in thread* Re: [PATCH v2 2/5] Input: zinitix - do not ignore non-moving fingers
2026-07-30 19:48 ` [PATCH v2 2/5] Input: zinitix - do not ignore non-moving fingers Kaustabh Chakraborty
@ 2026-07-30 19:58 ` sashiko-bot
0 siblings, 0 replies; 11+ messages in thread
From: sashiko-bot @ 2026-07-30 19:58 UTC (permalink / raw)
To: Kaustabh Chakraborty
Cc: conor+dt, devicetree, linux-input, robh, dmitry.torokhov
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
Pre-existing issues:
- [High] Device properties are read and validated after the input device has been registered, creating a race condition with userspace.
- [High] The driver allocates I2C transfer buffers on the stack, which violates DMA mapping rules and can cause memory corruption or panics.
--
--- Patch [2]: [PATCH v2 2/5] Input: zinitix - do not ignore non-moving fingers ---
commit 79b05b71b81ce17697cba617a5a5c777d1fe07a7
Author: Kaustabh Chakraborty <kauschluss@disroot.org>
Subject: Input: zinitix - do not ignore non-moving fingers
This commit fixes an issue where multitouch does not work reliably on the
ZT7548 touchscreen by no longer ignoring fingers that lack movement flags.
It ensures contacts with the SUB_BIT_EXIST flag are correctly processed in
zinitix_report_finger().
> diff --git a/drivers/input/touchscreen/zinitix.c b/drivers/input/touchscreen/zinitix.c
> index 3421b8ffb19b2..a0a89d25e25bb 100644
> --- a/drivers/input/touchscreen/zinitix.c
> +++ b/drivers/input/touchscreen/zinitix.c
[ ... ]
> @@ -473,7 +473,7 @@ static irqreturn_t zinitix_ts_irq_handler(int irq, void *bt541_handler)
> const struct point_coord *p = &touch_event.point_coord[i];
>
> /* Only process contacts that are actually reported */
> - if (p->sub_status & SUB_BIT_EXIST)
> + if (p->sub_status)
> zinitix_report_finger(bt541, i, p);
> }
>
[Severity: High]
This isn't a bug introduced by this patch, but does the interrupt handler
allocate I2C transfer buffers on the stack, potentially violating DMA
mapping constraints?
In zinitix_ts_irq_handler(), touch_event is declared on the stack and passed
directly down to the I2C subsystem:
drivers/input/touchscreen/zinitix.c:zinitix_ts_irq_handler() {
struct touch_event touch_event;
...
error = zinitix_read_data(bt541->client, ZINITIX_POINT_STATUS_REG,
&touch_event, sizeof(struct touch_event));
The helpers zinitix_read_data() and zinitix_write_u16() also allocate buffers
on the stack before passing them to i2c_master_send():
drivers/input/touchscreen/zinitix.c:zinitix_read_data() {
__le16 reg_le = cpu_to_le16(reg);
...
ret = i2c_master_send(client, (u8 *)®_le, sizeof(reg_le));
Could this cause memory corruption or silent DMA failures if the underlying
I2C adapter attempts to DMA-map these stack pointers?
[Severity: High]
This isn't a bug introduced by this patch, but is there a race condition
with userspace during the driver's probe?
The driver initializes and registers the input device, exposing it to
userspace, before finishing its property validation:
drivers/input/touchscreen/zinitix.c:zinitix_ts_probe() {
...
error = zinitix_init_input_dev(bt541);
// device is now registered and visible to userspace
...
error = device_property_read_u32(&client->dev, "zinitix,mode",
&bt541->zinitix_mode);
...
if (bt541->zinitix_mode != 2) {
dev_err(&client->dev,
"Malformed zinitix,mode property, must be 2 ...\n",
bt541->zinitix_mode);
return -EINVAL;
}
If userspace opens the device right after registration, zinitix_start()
could run with an uninitialized bt541->zinitix_mode, writing garbage to the
hardware.
Furthermore, if the probe fails due to a malformed mode property, the
already-exposed device will be abruptly destroyed while potentially in use.
Could the device properties be read and validated before calling
input_register_device() to ensure safe initialization?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260731-zinitix-modes-v2-0-ffe2ec9a9031@disroot.org?part=2
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v2 3/5] Input: zinitix - add support for modes 0 and 1
2026-07-30 19:48 [PATCH v2 0/5] Support zinitix touch modes 0 and 1, dt changes to Galaxy J6 (j6lte) Kaustabh Chakraborty
2026-07-30 19:48 ` [PATCH v2 1/5] Input: zinitix - check all available fingers for every touch event Kaustabh Chakraborty
2026-07-30 19:48 ` [PATCH v2 2/5] Input: zinitix - do not ignore non-moving fingers Kaustabh Chakraborty
@ 2026-07-30 19:48 ` Kaustabh Chakraborty
2026-07-30 20:00 ` sashiko-bot
2026-07-30 19:48 ` [PATCH v2 4/5] dt-bindings: input/ts/zinitix: document mode 0 Kaustabh Chakraborty
2026-07-30 19:48 ` [PATCH v2 5/5] arm64: dts: exynos7870-j6lte: set mode 0 as default for zinitix touchscreen Kaustabh Chakraborty
4 siblings, 1 reply; 11+ messages in thread
From: Kaustabh Chakraborty @ 2026-07-30 19:48 UTC (permalink / raw)
To: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Michael Srba, Linus Walleij, Peter Griffin, Alim Akhtar
Cc: linux-input, linux-kernel, devicetree, linux-arm-kernel,
linux-samsung-soc, Kaustabh Chakraborty
Zinitix touchscreens have three modes, numbered 0 to 2. The driver
implements mode 2, and leaves out modes 0 and 1. The difference in the
touchscreen modes is the schema of the event data.
Implement modes 0 and 1 in the driver, along with their event structs.
Maintain a common, canonical event struct which is to be the superset of
the mode-specific structs. With that, introduce functions to convert the
mode event info to the canonical format.
Signed-off-by: Kaustabh Chakraborty <kauschluss@disroot.org>
---
drivers/input/touchscreen/zinitix.c | 133 +++++++++++++++++++++++++++++++++---
1 file changed, 125 insertions(+), 8 deletions(-)
diff --git a/drivers/input/touchscreen/zinitix.c b/drivers/input/touchscreen/zinitix.c
index a0a89d25e25b..aafd0b7d8edb 100644
--- a/drivers/input/touchscreen/zinitix.c
+++ b/drivers/input/touchscreen/zinitix.c
@@ -145,7 +145,37 @@ struct point_coord {
u8 angle;
};
+struct point_coord_mode0 {
+ __le16 x;
+ __le16 y;
+ u8 width;
+ u8 sub_status;
+};
+
struct touch_event {
+ __le16 status;
+ __le16 event_flag;
+ u8 finger_mask;
+ u8 time_stamp;
+ struct point_coord point_coord[MAX_SUPPORTED_FINGER_NUM];
+};
+
+struct touch_event_mode0 {
+ __le16 status;
+ u8 finger_mask;
+ u8 time_stamp;
+ struct point_coord_mode0 point_coord[MAX_SUPPORTED_FINGER_NUM];
+};
+
+struct touch_event_mode1 {
+ __le16 status;
+ __le16 event_flag;
+ u8 finger_mask;
+ u8 time_stamp;
+ struct point_coord_mode0 point_coord[MAX_SUPPORTED_FINGER_NUM];
+};
+
+struct touch_event_mode2 {
__le16 status;
u8 finger_mask;
u8 time_stamp;
@@ -165,6 +195,7 @@ struct bt541_ts_data {
u16 firmware_version;
u16 regdata_version;
u16 icon_status_reg;
+ int (*read_point_status)(struct bt541_ts_data *bt541, struct touch_event *event);
};
static int zinitix_read_data(struct i2c_client *client,
@@ -440,6 +471,87 @@ static void zinitix_report_keys(struct bt541_ts_data *bt541, u16 icon_events)
bt541->keycodes[i], icon_events & BIT(i));
}
+static int zinitix_read_point_status_mode0(struct bt541_ts_data *bt541,
+ struct touch_event *event)
+{
+ struct touch_event_mode0 event_mode0;
+ int ret;
+ int i;
+
+ ret = zinitix_read_data(bt541->client, ZINITIX_POINT_STATUS_REG,
+ &event_mode0, sizeof(struct touch_event_mode0));
+ if (ret)
+ return ret;
+
+ event->status = event_mode0.status;
+ event->finger_mask = event_mode0.finger_mask;
+ event->time_stamp = event_mode0.time_stamp;
+
+ for (i = 0; i < ARRAY_SIZE(event_mode0.point_coord); i++) {
+ event->point_coord[i].x = event_mode0.point_coord[i].x;
+ event->point_coord[i].y = event_mode0.point_coord[i].y;
+ event->point_coord[i].width = event_mode0.point_coord[i].width;
+ event->point_coord[i].sub_status = event_mode0.point_coord[i].sub_status;
+ }
+
+ return 0;
+}
+
+static int zinitix_read_point_status_mode1(struct bt541_ts_data *bt541,
+ struct touch_event *event)
+{
+ struct touch_event_mode1 event_mode1;
+ int ret;
+ int i;
+
+ ret = zinitix_read_data(bt541->client, ZINITIX_POINT_STATUS_REG,
+ &event_mode1, sizeof(struct touch_event_mode1));
+ if (ret)
+ return ret;
+
+ event->status = event_mode1.status;
+ event->event_flag = event_mode1.event_flag;
+ event->finger_mask = event_mode1.finger_mask;
+ event->time_stamp = event_mode1.time_stamp;
+
+ for (i = 0; i < ARRAY_SIZE(event_mode1.point_coord); i++) {
+ event->point_coord[i].x = event_mode1.point_coord[i].x;
+ event->point_coord[i].y = event_mode1.point_coord[i].y;
+ event->point_coord[i].width = event_mode1.point_coord[i].width;
+ event->point_coord[i].sub_status = event_mode1.point_coord[i].sub_status;
+ }
+
+ return 0;
+}
+
+static int zinitix_read_point_status_mode2(struct bt541_ts_data *bt541,
+ struct touch_event *event)
+{
+ struct touch_event_mode2 event_mode2;
+ int ret;
+ int i;
+
+ ret = zinitix_read_data(bt541->client, ZINITIX_POINT_STATUS_REG,
+ &event_mode2, sizeof(struct touch_event_mode2));
+ if (ret)
+ return ret;
+
+ event->status = event_mode2.status;
+ event->finger_mask = event_mode2.finger_mask;
+ event->time_stamp = event_mode2.time_stamp;
+
+ for (i = 0; i < ARRAY_SIZE(event_mode2.point_coord); i++) {
+ event->point_coord[i].x = event_mode2.point_coord[i].x;
+ event->point_coord[i].y = event_mode2.point_coord[i].y;
+ event->point_coord[i].width = event_mode2.point_coord[i].width;
+ event->point_coord[i].sub_status = event_mode2.point_coord[i].sub_status;
+ event->point_coord[i].minor_width = event_mode2.point_coord[i].minor_width;
+ event->point_coord[i].angle = event_mode2.point_coord[i].angle;
+ }
+
+ return 0;
+}
+
static irqreturn_t zinitix_ts_irq_handler(int irq, void *bt541_handler)
{
struct bt541_ts_data *bt541 = bt541_handler;
@@ -451,8 +563,7 @@ static irqreturn_t zinitix_ts_irq_handler(int irq, void *bt541_handler)
memset(&touch_event, 0, sizeof(struct touch_event));
- error = zinitix_read_data(bt541->client, ZINITIX_POINT_STATUS_REG,
- &touch_event, sizeof(struct touch_event));
+ error = bt541->read_point_status(bt541, &touch_event);
if (error) {
dev_err(&client->dev, "Failed to read in touchpoint struct\n");
goto out;
@@ -682,13 +793,19 @@ static int zinitix_ts_probe(struct i2c_client *client)
bt541->zinitix_mode = DEFAULT_TOUCH_POINT_MODE;
}
- if (bt541->zinitix_mode != 2) {
- /*
- * If there are devices that don't support mode 2, support
- * for other modes (0, 1) will be needed.
- */
+ switch (bt541->zinitix_mode) {
+ case 0:
+ bt541->read_point_status = zinitix_read_point_status_mode0;
+ break;
+ case 1:
+ bt541->read_point_status = zinitix_read_point_status_mode1;
+ break;
+ case 2:
+ bt541->read_point_status = zinitix_read_point_status_mode2;
+ break;
+ default:
dev_err(&client->dev,
- "Malformed zinitix,mode property, must be 2 (supplied: %d)\n",
+ "Malformed zinitix,mode property, must be 0, 1, or 2 (supplied: %d)\n",
bt541->zinitix_mode);
return -EINVAL;
}
--
2.54.0
^ permalink raw reply related [flat|nested] 11+ messages in thread* Re: [PATCH v2 3/5] Input: zinitix - add support for modes 0 and 1
2026-07-30 19:48 ` [PATCH v2 3/5] Input: zinitix - add support for modes 0 and 1 Kaustabh Chakraborty
@ 2026-07-30 20:00 ` sashiko-bot
0 siblings, 0 replies; 11+ messages in thread
From: sashiko-bot @ 2026-07-30 20:00 UTC (permalink / raw)
To: Kaustabh Chakraborty
Cc: linux-input, robh, conor+dt, devicetree, dmitry.torokhov
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
New issues:
- [High] The input device is registered and exposed to userspace before its private `read_point_status` function pointer is initialized, creating a race condition that leads to a NULL pointer dereference in the IRQ handler.
Pre-existing issues:
- [High] The driver passes stack-allocated structs to the I2C subsystem for receiving data, which can cause memory corruption or kernel panics if the I2C controller driver maps the buffers for DMA.
--
--- Patch [3]: [PATCH v2 3/5] Input: zinitix - add support for modes 0 and 1 ---
commit 2caca9eabecb85f6226ee1af1cfdde1aeb2f1bc2
Author: Kaustabh Chakraborty <kauschluss@disroot.org>
Input: zinitix - add support for modes 0 and 1
Zinitix touchscreens have three modes, numbered 0 to 2. The driver
implements mode 2, and leaves out modes 0 and 1. Implement modes 0 and 1
in the driver, along with their event structs.
> diff --git a/drivers/input/touchscreen/zinitix.c b/drivers/input/touchscreen/zinitix.c
> index a0a89d25e25bb..aafd0b7d8edbd 100644
> --- a/drivers/input/touchscreen/zinitix.c
> +++ b/drivers/input/touchscreen/zinitix.c
[ ... ]
> @@ -440,6 +471,87 @@ static void zinitix_report_keys(struct bt541_ts_data *bt541, u16 icon_events)
> bt541->keycodes[i], icon_events & BIT(i));
> }
>
> +static int zinitix_read_point_status_mode0(struct bt541_ts_data *bt541,
> + struct touch_event *event)
> +{
> + struct touch_event_mode0 event_mode0;
> + int ret;
> + int i;
> +
> + ret = zinitix_read_data(bt541->client, ZINITIX_POINT_STATUS_REG,
> + &event_mode0, sizeof(struct touch_event_mode0));
[Severity: High]
This is a pre-existing issue, but does passing this stack-allocated
event_mode0 struct to the I2C subsystem cause memory corruption or kernel
panics if the underlying I2C host controller driver maps it for DMA?
The same pattern is also present in the new zinitix_read_point_status_mode1()
and zinitix_read_point_status_mode2() functions.
[ ... ]
> @@ -682,13 +793,19 @@ static int zinitix_ts_probe(struct i2c_client *client)
> bt541->zinitix_mode = DEFAULT_TOUCH_POINT_MODE;
> }
>
> - if (bt541->zinitix_mode != 2) {
> - /*
> - * If there are devices that don't support mode 2, support
> - * for other modes (0, 1) will be needed.
> - */
> + switch (bt541->zinitix_mode) {
> + case 0:
> + bt541->read_point_status = zinitix_read_point_status_mode0;
[Severity: High]
Can a fast userspace trigger a NULL pointer dereference here?
Since zinitix_init_input_dev() is called earlier in zinitix_ts_probe(),
the device is registered and exposed to userspace before
bt541->read_point_status is initialized:
zinitix_ts_probe()
zinitix_init_input_dev()
input_register_device()
...
switch (bt541->zinitix_mode) {
case 0:
bt541->read_point_status = zinitix_read_point_status_mode0;
If userspace opens the device and an interrupt triggers before the switch
statement, zinitix_ts_irq_handler() will dereference the uninitialized
read_point_status pointer:
zinitix_ts_irq_handler()
bt541->read_point_status(bt541, &touch_event);
> + break;
> + case 1:
> + bt541->read_point_status = zinitix_read_point_status_mode1;
> + break;
> + case 2:
> + bt541->read_point_status = zinitix_read_point_status_mode2;
> + break;
[ ... ]
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260731-zinitix-modes-v2-0-ffe2ec9a9031@disroot.org?part=3
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v2 4/5] dt-bindings: input/ts/zinitix: document mode 0
2026-07-30 19:48 [PATCH v2 0/5] Support zinitix touch modes 0 and 1, dt changes to Galaxy J6 (j6lte) Kaustabh Chakraborty
` (2 preceding siblings ...)
2026-07-30 19:48 ` [PATCH v2 3/5] Input: zinitix - add support for modes 0 and 1 Kaustabh Chakraborty
@ 2026-07-30 19:48 ` Kaustabh Chakraborty
2026-07-30 19:56 ` sashiko-bot
2026-07-30 19:48 ` [PATCH v2 5/5] arm64: dts: exynos7870-j6lte: set mode 0 as default for zinitix touchscreen Kaustabh Chakraborty
4 siblings, 1 reply; 11+ messages in thread
From: Kaustabh Chakraborty @ 2026-07-30 19:48 UTC (permalink / raw)
To: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Michael Srba, Linus Walleij, Peter Griffin, Alim Akhtar
Cc: linux-input, linux-kernel, devicetree, linux-arm-kernel,
linux-samsung-soc, Kaustabh Chakraborty
Zinitix touchscreens support three modes, numbered 0 to 2. These modes
differ based on their event report schema.
The devicetree schema does not mention mode 0, thus does not consider it
as a valid state. Add it.
Signed-off-by: Kaustabh Chakraborty <kauschluss@disroot.org>
---
.../devicetree/bindings/input/touchscreen/zinitix,bt400.yaml | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/Documentation/devicetree/bindings/input/touchscreen/zinitix,bt400.yaml b/Documentation/devicetree/bindings/input/touchscreen/zinitix,bt400.yaml
index f1ce837b16df..9b51212ea9b9 100644
--- a/Documentation/devicetree/bindings/input/touchscreen/zinitix,bt400.yaml
+++ b/Documentation/devicetree/bindings/input/touchscreen/zinitix,bt400.yaml
@@ -76,9 +76,9 @@ properties:
zinitix,mode:
description: Mode of reporting touch points. Some modes may not work
with a particular ts firmware for unknown reasons. Available modes are
- 1 and 2. Mode 2 is the default and preferred.
+ 0, 1, and 2. Mode 2 is the default and preferred.
$ref: /schemas/types.yaml#/definitions/uint32
- enum: [1, 2]
+ enum: [0, 1, 2]
linux,keycodes:
description:
--
2.54.0
^ permalink raw reply related [flat|nested] 11+ messages in thread* Re: [PATCH v2 4/5] dt-bindings: input/ts/zinitix: document mode 0
2026-07-30 19:48 ` [PATCH v2 4/5] dt-bindings: input/ts/zinitix: document mode 0 Kaustabh Chakraborty
@ 2026-07-30 19:56 ` sashiko-bot
0 siblings, 0 replies; 11+ messages in thread
From: sashiko-bot @ 2026-07-30 19:56 UTC (permalink / raw)
To: Kaustabh Chakraborty
Cc: linux-input, conor+dt, dmitry.torokhov, robh, devicetree
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [High] Race condition during probe where the input device is registered before device properties are fully read, leading to a state mismatch and a NULL pointer dereference crash.
--
--- Patch [4]: [PATCH v2 4/5] dt-bindings: input/ts/zinitix: document mode 0 ---
commit 68cae6b2efa127f7ca443fe7f4819ab411c887fb
Author: Kaustabh Chakraborty <kauschluss@disroot.org>
dt-bindings: input/ts/zinitix: document mode 0
Zinitix touchscreens support three modes, numbered 0 to 2. This commit
adds mode 0 to the devicetree schema as it was previously unmentioned.
> diff --git a/Documentation/devicetree/bindings/input/touchscreen/zinitix,bt400.yaml b/Documentation/devicetree/bindings/input/touchscreen/zinitix,bt400.yaml
> index f1ce837b16dfa..9b51212ea9b97 100644
> --- a/Documentation/devicetree/bindings/input/touchscreen/zinitix,bt400.yaml
> +++ b/Documentation/devicetree/bindings/input/touchscreen/zinitix,bt400.yaml
> @@ -76,9 +76,9 @@ properties:
> zinitix,mode:
> description: Mode of reporting touch points. Some modes may not work
> with a particular ts firmware for unknown reasons. Available modes are
> - 1 and 2. Mode 2 is the default and preferred.
> + 0, 1, and 2. Mode 2 is the default and preferred.
> $ref: /schemas/types.yaml#/definitions/uint32
> - enum: [1, 2]
> + enum: [0, 1, 2]
[Severity: High]
This is a pre-existing issue, but does adding mode 0 highlight a race
condition in the driver's initialization sequence?
Looking at drivers/input/touchscreen/zinitix.c, the input device is
registered before the properties are read:
drivers/input/touchscreen/zinitix.c:zinitix_ts_probe() {
error = zinitix_init_input_dev(bt541);
if (error) {
...
}
error = device_property_read_u32(&client->dev, "zinitix,mode",
&bt541->zinitix_mode);
...
switch (bt541->zinitix_mode) {
case 0:
bt541->read_point_status = zinitix_read_point_status_mode0;
break;
...
}
}
If userspace rapidly opens the device (e.g., via udevd) right after
zinitix_init_input_dev() is called, zinitix_start() will run and call
enable_irq().
If an interrupt fires before probe() reaches the switch statement and assigns
the bt541->read_point_status function pointer, could this result in a NULL
pointer dereference in the interrupt handler?
drivers/input/touchscreen/zinitix.c:zinitix_ts_irq_handler() {
...
error = bt541->read_point_status(bt541, &touch_event);
...
}
Should the property reading and function pointer initialization be moved
before the input device is registered to prevent this?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260731-zinitix-modes-v2-0-ffe2ec9a9031@disroot.org?part=4
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH v2 5/5] arm64: dts: exynos7870-j6lte: set mode 0 as default for zinitix touchscreen
2026-07-30 19:48 [PATCH v2 0/5] Support zinitix touch modes 0 and 1, dt changes to Galaxy J6 (j6lte) Kaustabh Chakraborty
` (3 preceding siblings ...)
2026-07-30 19:48 ` [PATCH v2 4/5] dt-bindings: input/ts/zinitix: document mode 0 Kaustabh Chakraborty
@ 2026-07-30 19:48 ` Kaustabh Chakraborty
2026-07-30 20:01 ` sashiko-bot
4 siblings, 1 reply; 11+ messages in thread
From: Kaustabh Chakraborty @ 2026-07-30 19:48 UTC (permalink / raw)
To: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Michael Srba, Linus Walleij, Peter Griffin, Alim Akhtar
Cc: linux-input, linux-kernel, devicetree, linux-arm-kernel,
linux-samsung-soc, Kaustabh Chakraborty
The ZT7548 chip in the Galaxy J6 is actually able to support mode 2 (the
recommended mode) just fine.
However, the downstream kernel driver utilizes mode 0. This creates a
problem in the aftermarket world where fake touch digitizers only
implement the bare minimum functionality, as used by the downstream
driver, to compromise on cost, which leads to such chips implementing
mode 0 only. To ensure touch works on those devices, set the default
mode to 0.
Signed-off-by: Kaustabh Chakraborty <kauschluss@disroot.org>
---
arch/arm64/boot/dts/exynos/exynos7870-j6lte.dts | 2 ++
1 file changed, 2 insertions(+)
diff --git a/arch/arm64/boot/dts/exynos/exynos7870-j6lte.dts b/arch/arm64/boot/dts/exynos/exynos7870-j6lte.dts
index de30d0970336..bd8f798c9810 100644
--- a/arch/arm64/boot/dts/exynos/exynos7870-j6lte.dts
+++ b/arch/arm64/boot/dts/exynos/exynos7870-j6lte.dts
@@ -424,6 +424,8 @@ touchscreen@20 {
touchscreen-size-y = <1480>;
vdd-supply = <&vdd_ldo34>;
+
+ zinitix,mode = <0>;
};
};
--
2.54.0
^ permalink raw reply related [flat|nested] 11+ messages in thread* Re: [PATCH v2 5/5] arm64: dts: exynos7870-j6lte: set mode 0 as default for zinitix touchscreen
2026-07-30 19:48 ` [PATCH v2 5/5] arm64: dts: exynos7870-j6lte: set mode 0 as default for zinitix touchscreen Kaustabh Chakraborty
@ 2026-07-30 20:01 ` sashiko-bot
0 siblings, 0 replies; 11+ messages in thread
From: sashiko-bot @ 2026-07-30 20:01 UTC (permalink / raw)
To: Kaustabh Chakraborty
Cc: dmitry.torokhov, robh, devicetree, conor+dt, linux-input
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] NULL pointer dereference in IRQ handler due to race condition with input device registration.
- [Medium] Commit message and code logic mismatch regarding finger event filtering.
--
--- Patch [5]: [PATCH v2 5/5] arm64: dts: exynos7870-j6lte: set mode 0 as default for zinitix touchscreen ---
commit 8857c310eb5f5f4f8a5e742e862931240c3227fa
Author: Kaustabh Chakraborty <kauschluss@disroot.org>
arm64: dts: exynos7870-j6lte: set mode 0 as default for zinitix touchscreen
This commit sets the default mode for the ZT7548 touchscreen chip to 0 in the
Galaxy J6 device tree. It ensures that fake aftermarket digitizers, which only
implement mode 0, function correctly since the downstream kernel driver also
utilizes mode 0.
> arm64: dts: exynos7870-j6lte: set mode 0 as default for zinitix touchscreen
[Severity: Medium]
Is there a mismatch between the commit message and code logic regarding finger
event filtering?
The commit "Input: zinitix - do not ignore non-moving fingers" states in its
message: "...under the following condition: if (p->sub_status & SUB_BIT_EXIST)
zinitix_report_finger(bt541, i, p); This implies and ensures that every valid
finger must have the SUB_BIT_EXIST flag."
However, the condition simply checks if (p->sub_status):
drivers/input/touchscreen/zinitix.c:zinitix_ts_irq_handler() {
...
/* Only process contacts that are actually reported */
if (p->sub_status)
zinitix_report_finger(bt541, i, p);
...
}
Does this regression allow events without SUB_BIT_EXIST to bypass the filter,
invalidating the stated premise?
> diff --git a/arch/arm64/boot/dts/exynos/exynos7870-j6lte.dts b/arch/arm64/boot/dts/exynos/exynos7870-j6lte.dts
> index de30d0970336f..bd8f798c9810b 100644
> --- a/arch/arm64/boot/dts/exynos/exynos7870-j6lte.dts
> +++ b/arch/arm64/boot/dts/exynos/exynos7870-j6lte.dts
> @@ -424,6 +424,8 @@ touchscreen@20 {
> touchscreen-size-y = <1480>;
>
> vdd-supply = <&vdd_ldo34>;
> +
> + zinitix,mode = <0>;
[Severity: High]
Could setting the mode here introduce a regression where a NULL pointer
dereference occurs in the IRQ handler due to a race condition with input
device registration?
Looking at drivers/input/touchscreen/zinitix.c:zinitix_ts_probe(), it calls
zinitix_init_input_dev() which registers the input device:
drivers/input/touchscreen/zinitix.c:zinitix_init_input_dev() {
...
error = input_register_device(input_dev);
...
}
This immediately exposes the evdev node to userspace. If userspace (e.g., udev)
opens the device instantly, zinitix_input_open() invokes zinitix_start(),
which enables the hardware IRQ.
If an interrupt fires at this exact moment, zinitix_ts_irq_handler() runs and
dereferences the callback:
drivers/input/touchscreen/zinitix.c:zinitix_ts_irq_handler() {
...
error = bt541->read_point_status(bt541, &touch_event);
...
}
However, the probe thread has not yet assigned this function pointer, because
it happens later via the mode switch:
drivers/input/touchscreen/zinitix.c:zinitix_ts_probe() {
...
switch (bt541->zinitix_mode) {
case 0:
bt541->read_point_status = zinitix_read_point_status_mode0;
...
}
Since the function pointer is uninitialized when the IRQ fires, would this
regression cause a kernel panic?
> };
> };
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260731-zinitix-modes-v2-0-ffe2ec9a9031@disroot.org?part=5
^ permalink raw reply [flat|nested] 11+ messages in thread