Linux Input/HID development
 help / color / mirror / Atom feed
* Re: [PATCH v5 1/2] dt-bindings: Input: Add Wacom W9000-series penabled touchscreens
From: Conor Dooley @ 2026-03-19 17:09 UTC (permalink / raw)
  To: Hendrik Noack
  Cc: Dmitry Torokhov, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Ferass El Hafidi, linux-input, devicetree, linux-kernel
In-Reply-To: <20260319095303.19927-2-hendrik-noack@gmx.de>

[-- Attachment #1: Type: text/plain, Size: 2988 bytes --]

On Thu, Mar 19, 2026 at 10:53:02AM +0100, Hendrik Noack wrote:
> Add bindings for Wacom W9002 and two Wacom W9007 variants which can be
> found in tablets.
> 
> Co-developed-by: Ferass El Hafidi <funderscore@postmarketos.org>
> Signed-off-by: Ferass El Hafidi <funderscore@postmarketos.org>
> Signed-off-by: Hendrik Noack <hendrik-noack@gmx.de>
> ---
>  .../input/touchscreen/wacom,w9007a-lt03.yaml  | 73 +++++++++++++++++++
>  1 file changed, 73 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/input/touchscreen/wacom,w9007a-lt03.yaml
> 
> diff --git a/Documentation/devicetree/bindings/input/touchscreen/wacom,w9007a-lt03.yaml b/Documentation/devicetree/bindings/input/touchscreen/wacom,w9007a-lt03.yaml
> new file mode 100644
> index 000000000000..6d1da6a435d3
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/input/touchscreen/wacom,w9007a-lt03.yaml
> @@ -0,0 +1,73 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/input/touchscreen/wacom,w9007a-lt03.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Wacom W9000-series penabled I2C touchscreen
> +
> +maintainers:
> +  - Hendrik Noack <hendrik-noack@gmx.de>
> +
> +description: |
> +  The W9000-series are penabled touchscreen controllers by Wacom.
> +
> +  The firmware of controllers in different devices may differ. This can also
> +  affect the controller's behavior.
> +
> +allOf:
> +  - $ref: touchscreen.yaml#
> +
> +properties:
> +  compatible:
> +    enum:
> +      - wacom,w9002
> +      - wacom,w9007a-lt03
> +      - wacom,w9007a-v1

Please provide information in your commit message as to why these
devices are not compatible with one another.
With that,
Acked-by: Conor Dooley <conor.dooley@microchip.com>
pw-bot: changes-requested

Cheers,
Conor.


> +
> +  reg:
> +    maxItems: 1
> +
> +  interrupts:
> +    maxItems: 1
> +
> +  vdd-supply: true
> +
> +  flash-mode-gpios:
> +    maxItems: 1
> +
> +  reset-gpios:
> +    maxItems: 1
> +
> +required:
> +  - compatible
> +  - reg
> +  - interrupts
> +
> +unevaluatedProperties: false
> +
> +examples:
> +  - |
> +    #include <dt-bindings/gpio/gpio.h>
> +    #include <dt-bindings/interrupt-controller/irq.h>
> +
> +    i2c {
> +        #address-cells = <1>;
> +        #size-cells = <0>;
> +
> +        digitizer@56 {
> +            compatible = "wacom,w9007a-lt03";
> +            reg = <0x56>;
> +            interrupt-parent = <&gpd1>;
> +            interrupts = <1 IRQ_TYPE_EDGE_RISING>;
> +
> +            vdd-supply = <&stylus_reg>;
> +
> +            flash-mode-gpios = <&gpd1 3 GPIO_ACTIVE_HIGH>;
> +            reset-gpios = <&gpx0 1 GPIO_ACTIVE_LOW>;
> +
> +            touchscreen-x-mm = <216>;
> +            touchscreen-y-mm = <135>;
> +            touchscreen-inverted-x;
> +        };
> +    };
> -- 
> 2.43.0
> 

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply

* Re: [PATCH] HID: pulsar: add driver for Pulsar gaming mice
From: Nikolas Koesling @ 2026-03-19 19:27 UTC (permalink / raw)
  To: Jiri Kosina, Benjamin Tissoires, Leo; +Cc: linux-input, linux-kernel
In-Reply-To: <8590f544-9146-4e8e-9ea6-a3ed9ed9fa1f@managarm.org>

Hi Leo,

thanks for the review.

> While this driver looks fine at a glance, this does seem to use the
> same protocol as the hid-kysona driver. It might be more appropriate to
> extend that driver instead of rolling a new one? The HID identifiers in
> hid-ids.h already have a vendor constant for USB_VENDOR_ID_KYSONA,
> which takes the same value as this patch's USB_VENDOR_ID_PULSAR.

Same vendor ID, same protocol. I should have caught the overlap with
hid-kysona.

I don't think simply adding Pulsar IDs to hid-kysona as-is would be sufficient.
The current driver has some issues I addressed in my patch:
 
1. No locking: raw_event writes battery state concurrently with get_property
reads
2. raw_event always returns 0, so battery/online status responses are never
consumed by the driver. They leak through to userspace as spurious input
events. hid-pulsar uses pending_event + completion to match commands to
responses and consumes responses to driver requests.
3. the protocol includes a checksum, but hid-kysona never validates it on
incoming data.
4. hid-kysona does not react in any way to the power change event messages
from the device.

> For context, I was investigating exactly this battery reporting for
> another mouse, the ATK VXE R1 SE+. It does seem to use the exact same
> protocol; in wired mode, that takes the device ID 3554:F58F, while the
> wireless dongle has a device ID of 373B:1085. I plan on sending a patch
> to add these IDs after some testing.

With three vendors sharing the same protocol, a vendor-neutral name would make
sense. Any suggestions? Something like hid-paw3395-battery (sensor),
hid-nrf52840-mouse (SoC), or is there a better identifier for this protocol?

What would you prefer: rework and rename hid-kysona in place, or rename and
extend hid-pulsar while keeping hid-kysona as-is for compatibility?

Kind regards,
Nikolas




^ permalink raw reply

* Re: [PATCH] HID: wiimote: Fix table layout in comment
From: David Rheinsberg @ 2026-03-20  8:48 UTC (permalink / raw)
  To: j.ne, Jiri Kosina, Benjamin Tissoires; +Cc: linux-input, linux-kernel
In-Reply-To: <20260316-wiimod-table-v1-1-c1f91be05137@posteo.net>

Hi

On Mon, Mar 16, 2026, at 3:45 PM, J. Neuschäfer via B4 Relay wrote:
> From: "J. Neuschäfer" <j.ne@posteo.net>
>
> Some tab characters snuck into the data layout table for turntable
> extensions, which resulted in the table only looking right at a tabstop
> of 4, which is uncommon in the kernel. Change them to the equivalent
> amount of spaces, which should look correct in any editor.
>
> Signed-off-by: J. Neuschäfer <j.ne@posteo.net>

Reviewed-by: David Rheinsberg <david@readahead.eu>

This came in via 05086f3db530, which, looking at it, also left some trailing whitespace. Feel free to include those as well.

Thanks a lot!

> ---
>  drivers/hid/hid-wiimote-modules.c | 12 ++++++------
>  1 file changed, 6 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/hid/hid-wiimote-modules.c 
> b/drivers/hid/hid-wiimote-modules.c
> index dbccdfa6391672..d9e1b4313d6465 100644
> --- a/drivers/hid/hid-wiimote-modules.c
> +++ b/drivers/hid/hid-wiimote-modules.c
> @@ -2442,12 +2442,12 @@ static void wiimod_turntable_in_ext(struct 
> wiimote_data *wdata, const __u8 *ext)
>  	/* 
>  	 * Byte |  7   |  6  |  5  |  4  |  3  |  2   |  1   |  0     |
>  	 *------+------+-----+-----+-----+-----+------+------+--------+
> -	 *   0  | RTT<4:3>   | 		      	  SX <5:0> 			      |
> -	 *   1  | RTT<2:1>   |				  SY <5:0>			      |
> +	 *   0  | RTT<4:3>   |                SX <5:0>                |
> +	 *   1  | RTT<2:1>   |                SY <5:0>                |
>  	 *------+------+-----+-----+-----+-----+------+------+--------+
>  	 *   2  |RTT<0>|  ED<4:3>  |          CS<3:0>        | RTT<5> |
>  	 *------+------+-----+-----+-----+-----+------+------+--------+
> -	 *   3  |     ED<2:0> 	   | 			 LTT<4:0>			  |
> +	 *   3  |     ED<2:0>      |             LTT<4:0>             |
>  	 *------+------+-----+-----+-----+-----+------+------+--------+
>  	 *   4  |  0   |  0  | LBR |  B- |  0  |  B+  |  RBR | LTT<5> |
>  	 *------+------+-----+-----+-----+-----+------+------+--------+
> @@ -2458,12 +2458,12 @@ static void wiimod_turntable_in_ext(struct 
> wiimote_data *wdata, const __u8 *ext)
>  	 * With Motion+ enabled, it will look like this:
>  	 * Byte |  8   |  7  |  6  |  5  |  4  |  3   |  2   |  1     |
>  	 *------+------+-----+-----+-----+-----+------+------+--------+
> -	 *   1  | RTT<4:3>   | 		      	  SX <5:1> 		 |	  0   |
> -	 *   2  | RTT<2:1>   |				  SY <5:1>		 |	  0   |
> +	 *   1  | RTT<4:3>   |                SX <5:1>       |    0   |
> +	 *   2  | RTT<2:1>   |                SY <5:1>       |    0   |
>  	 *------+------+-----+-----+-----+-----+------+------+--------+
>  	 *   3  |RTT<0>|  ED<4:3>  |          CS<3:0>        | RTT<5> |
>  	 *------+------+-----+-----+-----+-----+------+------+--------+
> -	 *   4  |     ED<2:0> 	   | 			 LTT<4:0>			  |
> +	 *   4  |     ED<2:0>      |             LTT<4:0>             |
>  	 *------+------+-----+-----+-----+-----+------+------+--------+
>  	 *   5  |  0   |  0  | LBR |  B- |  0  |  B+  | RBR  |  XXXX  |
>  	 *------+------+-----+-----+-----+-----+------+------+--------+
>
> ---
> base-commit: f338e77383789c0cae23ca3d48adcc5e9e137e3c
> change-id: 20260316-wiimod-table-cd9abc0a2a98
>
> Best regards,
> -- 
> J. Neuschäfer <j.ne@posteo.net>

^ permalink raw reply

* Re: [BUG] HID: Apple Magic Mouse 2 (BT 0x004C:0x0323) reports wrong battery percentage -- hidinput_query_battery_capacity() reads buf[1] (status byte) instead of buf[2] (AbsoluteStateOfCharge) ---
From: Benjamin Tissoires @ 2026-03-20  9:29 UTC (permalink / raw)
  To: William MacKinnon; +Cc: linux-input, jikos
In-Reply-To: <CABWzDrqH8Z5qEjCHOAb5f68DUiTLUTjj-sdav7Y7+1+tFvnc4w@mail.gmail.com>

Hi William,

On Fri, Mar 20, 2026 at 12:04 AM William MacKinnon <wh6cyy@gmail.com> wrote:
>
>  Hi,
>
>  I am reporting a confirmed bug in the HID battery subsystem that causes the
>  Apple Magic Mouse 2 to permanently show 4% battery at boot when connected via
>  Bluetooth.  The root cause has been isolated to a hardcoded byte-index
>  assumption in hidinput_query_battery_capacity() (drivers/hid/hid-input.c)
>  that is incompatible with the Magic Mouse 2's multi-field report 0x90 structure.

Good finding. Indeed, hidinput_query_battery_capacity() makes overly
strong assumptions about the report's content (like why a report of
size 4 only?). :(

Good thing is that I was planning today to write HID battery tests and
then improve that code slightly (I want it to be aware of new HID
usages).

Anyway, I'll work on that over the next days/weeks if I'm not
preempted by something else.

Cheers,
Benjamin

>
>
>  --- System information ---
>
>    Kernel:   6.18.15-400.asahi.fc43.aarch64+16k (Asahi Linux on Apple Silicon)
>    Device:   Apple Magic Mouse 2, "William's Magic Mouse #2"
>    BT MAC:   D0:C0:50:CF:DF:F1
>    HID IDs:  Bus=0x0005 (Bluetooth), Vendor=0x004C (Apple BT),
>              Product=0x0323
>    Driver:   hid-magicmouse (BT path)
>
>
>  --- Observed vs. expected behaviour ---
>
>    /sys/class/power_supply/hid-d0:c0:50:cf:df:f1-battery/capacity
>    reports: 4
>
>    macOS reports 94% for the same mouse simultaneously (confirmed ground truth).
>    Occasional spontaneous correct readings (~95%) are observed at boot before
>    the first sysfs read (see Race Condition section below), confirming the
>    device transmits the correct value.
>
>    UPower sysfs snapshot:
>
>      POWER_SUPPLY_NAME=hid-d0:c0:50:cf:df:f1-battery
>      POWER_SUPPLY_TYPE=Battery
>      POWER_SUPPLY_STATUS=Discharging
>      POWER_SUPPLY_CAPACITY=4
>      POWER_SUPPLY_SCOPE=Device
>      POWER_SUPPLY_MODEL_NAME=William's Magic Mouse #2
>
>
>  --- HID Report Descriptor (battery collection) ---
>
>  Full raw descriptor (from hid-recorder):
>    R: 135 05 01 09 02 a1 01 85 12 05 09 19 01 29 02 15 00 25 01 95 02 75 01
>    81 02 95 01 75 06 81 03 05 01 09 01 a1 00 16 01 f8 26 ff 07 36 01 fb 46
>    ff 04 65 13 55 0d 09 30 09 31 75 10 95 02 81 06 75 08 95 02 81 01 c0 06
>    02 ff 09 55 85 55 15 00 26 ff 00 75 08 95 40 b1 a2 c0 06 00 ff 09 14 a1
>    01 85 90 05 84 75 01 95 03 15 00 25 01 09 61 05 85 09 44 09 46 81 02 95
>    05 81 01 75 08 95 01 15 00 26 ff 00 09 65 81 02 c0
>
>  Battery Application Collection (report 0x90):
>
>    85 90          -- Report ID 0x90
>    05 84          -- Usage Page: Power Device (0x84)
>    75 01 95 03    -- Report Size 1, Count 3
>    15 00 25 01    -- Logical Min 0, Max 1
>    09 61          -- Usage: PresentStatus (0x61)          [bit 0]
>    05 85          -- Usage Page: Battery System (0x85)
>    09 44          -- Usage: Charging (0x44)               [bit 1]
>    09 46          -- Usage: Discharging (0x46)            [bit 2]
>    81 02          -- Input (FIELD 0: 3 x 1-bit status)
>    95 05 81 01    -- 5-bit padding
>    75 08 95 01    -- Report Size 8, Count 1
>    15 00 26 ff 00 -- Logical Min 0, Max 255
>    09 65          -- Usage: AbsoluteStateOfCharge (0x65)
>    81 02          -- Input (FIELD 1: 8-bit charge)
>    c0             -- End Collection
>
>  Raw buffer layout for report 0x90 (as seen by the kernel):
>
>    buf[0] = 0x90  -- report ID
>    buf[1] = 0x04  -- FIELD 0: status byte (bit2=Discharging=1, rest=0)
>    buf[2] = 0x5F  -- FIELD 1: AbsoluteStateOfCharge = 95 decimal
>
>
>  --- Bluetooth traffic capture (btmon) ---
>
>  The kernel queries:  41 90  (GET_REPORT, INPUT type, report ID 0x90)
>
>  The mouse consistently responds:  a1 90 04 5f
>
>    a1 = BT HID DATA INPUT header
>    90 = report ID
>    04 = status byte = 0b00000100 (bit2=Discharging)
>    5f = AbsoluteStateOfCharge = 95 decimal  ← matches macOS 94%
>
>  The value 0x5f=95 is stable across every captured report.
>
>
>  --- Root cause ---
>
>  hidinput_query_battery_capacity() in drivers/hid/hid-input.c:
>
>    ret = hidinput_scale_battery_capacity(dev, buf[1]);   /* BUG */
>
>  It hardcodes buf[1] as the battery byte.  For reports where the battery
>  field is the first (and only) data field, buf[1] is correct.  For report
>  0x90 on the Magic Mouse 2, buf[1] is the status byte (0x04 = 4) and the
>  AbsoluteStateOfCharge is at buf[2] (0x5F = 95).
>
>  Arithmetic confirming the bug:
>
>    Observed: buf[1] = 0x04 = 4 → scale(4, 0, 100) = 4%  ✓ matches sysfs
>    Correct:  buf[2] = 0x5F = 95 → scale(95, 0, 100) = 95%  ✓ matches macOS
>
>  The correct byte offset is deterministic from the descriptor:
>    1 + (field->report_offset / 8) = 1 + (8/8) = 2  →  buf[2]
>
>  The leading 1 accounts for the report-ID byte prepended by hid_hw_raw_request().
>
>
>  --- Race condition / intermittent correct readings ---
>
>  hidinput_get_battery_property() has two paths:
>
>    A. Query path (buggy): when battery_status != HID_BATTERY_REPORTED, calls
>       hidinput_query_battery_capacity() → reads buf[1] = 4 → returns 4%.
>
>    B. Event-driven path (correct): after the first spontaneous HID INPUT
>       event for report 0x90 processes the AbsoluteStateOfCharge usage via
>       hid_input_var_field() (which correctly bit-extracts each usage's value
>       per the descriptor), hidinput_update_battery() caches 95 in
>       dev->battery_capacity and sets battery_status = HID_BATTERY_REPORTED.
>       Subsequent sysfs reads return 95%.
>
>  The race: if UPower reads sysfs before the first mouse input event (which is
>  typical at boot), path A fires → 4%.  If the user moves the mouse before
>  UPower reads → path B fires first → 95%.
>
>  This explains the "sometimes correct at boot" behaviour: it is a genuine
>  race between UPower's enumeration and the first spontaneous HID report.
>
>
>  --- Why magicmouse_fetch_battery() does not apply ---
>
>  magicmouse_fetch_battery() checks is_usb_magicmouse2() which tests:
>    vendor == USB_VENDOR_ID_APPLE  /* 0x05AC */
>
>  A BT-connected Magic Mouse 2 presents vendor=0x004C (Apple BT vendor ID),
>  not 0x05AC.  is_usb_magicmouse2() returns false; the battery timer is never
>  armed; all battery handling falls through to hid-input.c where the bug lives.
>
>
>  --- Proposed fix direction ---
>
>  Option A (general fix): Store the AbsoluteStateOfCharge field's byte offset
>  at hidinput_configure_usage() time (e.g., in a new hdev->battery_field_offset
>  member) and use it in hidinput_query_battery_capacity() instead of the
>  hardcoded 1.
>
>  Option B (device-specific): Extend is_usb_magicmouse2() to also accept
>  BT_VENDOR_ID_APPLE (0x004C), arming the existing battery timer for BT-path
>  devices so magicmouse_fetch_battery() runs and primes battery_status before
>  the first sysfs read, bypassing the hid-input.c query path entirely.
>
>  I do not have a tested patch yet.  I am happy to test patches.
>
>  Regards,
>  Will
>
>  --
>  Kernel: 6.18.15-400.asahi.fc43.aarch64+16k
>  Platform: Apple Silicon (Asahi Linux / Fedora 43)
>
>


^ permalink raw reply

* Re: [PATCH] HID: pulsar: add driver for Pulsar gaming mice
From: Leo @ 2026-03-20 13:53 UTC (permalink / raw)
  To: Nikolas Koesling, Jiri Kosina, Benjamin Tissoires
  Cc: linux-input, linux-kernel
In-Reply-To: <2344095.iZASKD2KPV@nk-eos>

Hi,

> With three vendors sharing the same protocol, a vendor-neutral name would make
> sense. Any suggestions? Something like hid-paw3395-battery (sensor),
> hid-nrf52840-mouse (SoC), or is there a better identifier for this protocol?

I did some digging into the protocol, and from what I can tell it is
not necessarily associated with the specific sensor, as multiple mice
with differing sensors speak the same protocol (PAW3950 to name one).
The nrf52840 also does not seem to be limited to this protocol. I can't
find a public name for the protocol. Given that and that I'm not sure
what the subsystem's policy on module naming is, but I would assume
that changing them after the fact is not ideal. Probably (?) the
optimal solution would be to integrate your patch (thank you!) with the
existing hid-kysona driver; maybe a maintainer should weigh in on that.

I added my mouse's ID (3554:F58F) to the list and tested the patch.
Everything behaved as expected, UPower output looks reasonable. If you
like, you can amend the device ID to your patch. The `model_id` for it
is reported as 0x0220, and the model format string should be
"ATK VXE R1 SE+ (%s)". My 2.4 GHz receiver seems to not report a model
ID; the web configuration utility uses a different command to identify
the mouse model. Given that it uses a different idVendor anyway, I'm
not worried about that for now.

Tested-by: Leo <leo@managarm.org>

Cheers,
Leo

^ permalink raw reply

* [PATCH] Input: gpio-keys - add full support of EV_REL and EV_ABS
From: Xiong Nandi @ 2026-03-20 14:52 UTC (permalink / raw)
  To: dmitry.torokhov
  Cc: linux-input, linux-kernel, Xiong Nandi, Gatien Chevallier,
	Ingo Molnar, Thomas Gleixner, Marco Crivellari, Fabrice Gasnier

gpio_keys_gpio_report_event() handled EV_ABS but silently ignored EV_REL,
while the polled driver supports both.  Extend the interrupt-driven driver
to fire an EV_REL event on button press.

For EV_ABS, use a shared atomic counter per (type, code) pair so that
a zero-value reset is sent only when the last active button on an axis
is released, avoiding premature axis resets when multiple buttons share
the same axis code.

Add gpio_keys_set_abs_params() to call input_set_abs_params() at setup
time, deriving the axis min/max from the configured button values.
Without this the input subsystem reports unbounded axis ranges.

Signed-off-by: Xiong Nandi <xndchn@gmail.com>
---
 drivers/input/keyboard/gpio_keys.c | 65 +++++++++++++++++++++++++++++-
 include/linux/gpio_keys.h          |  4 +-
 2 files changed, 65 insertions(+), 4 deletions(-)

diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c
index e19617485679..652a6932c52f 100644
--- a/drivers/input/keyboard/gpio_keys.c
+++ b/drivers/input/keyboard/gpio_keys.c
@@ -51,8 +51,10 @@ struct gpio_button_data {
 	spinlock_t lock;
 	bool disabled;
 	bool key_pressed;
+	bool axis_active;
 	bool suspended;
 	bool debounce_use_hrtimer;
+	atomic_t *axis_count;
 };
 
 struct gpio_keys_drvdata {
@@ -373,9 +375,16 @@ static void gpio_keys_gpio_report_event(struct gpio_button_data *bdata)
 		return;
 	}
 
-	if (type == EV_ABS) {
-		if (state)
+	if (type == EV_ABS || type == EV_REL) {
+		if (state && !bdata->axis_active) {
+			bdata->axis_active = true;
+			atomic_inc(bdata->axis_count);
 			input_event(input, type, button->code, button->value);
+		} else if (!state && bdata->axis_active) {
+			bdata->axis_active = false;
+			if (atomic_dec_and_test(bdata->axis_count))
+				input_event(input, type, button->code, 0);
+		}
 	} else {
 		input_event(input, type, *bdata->code, state);
 	}
@@ -493,6 +502,27 @@ static irqreturn_t gpio_keys_irq_isr(int irq, void *dev_id)
 	return IRQ_HANDLED;
 }
 
+static void gpio_keys_set_abs_params(struct input_dev *input,
+				     struct gpio_keys_drvdata *ddata,
+				     unsigned int code)
+{
+	int i, min = 0, max = 0;
+
+	for (i = 0; i < ddata->pdata->nbuttons; i++) {
+		const struct gpio_keys_button *button = &ddata->pdata->buttons[i];
+
+		if (button->type != EV_ABS || button->code != code)
+			continue;
+
+		if (button->value < min)
+			min = button->value;
+		if (button->value > max)
+			max = button->value;
+	}
+
+	input_set_abs_params(input, code, min, max, 0, 0);
+}
+
 static int gpio_keys_setup_key(struct platform_device *pdev,
 				struct input_dev *input,
 				struct gpio_keys_drvdata *ddata,
@@ -651,6 +681,8 @@ static int gpio_keys_setup_key(struct platform_device *pdev,
 	bdata->code = &ddata->keymap[idx];
 	*bdata->code = button->code;
 	input_set_capability(input, button->type ?: EV_KEY, *bdata->code);
+	if ((button->type ?: EV_KEY) == EV_ABS)
+		gpio_keys_set_abs_params(input, ddata, button->code);
 
 	/*
 	 * Install custom action to cancel release timer and
@@ -928,6 +960,35 @@ static int gpio_keys_probe(struct platform_device *pdev)
 
 	fwnode_handle_put(child);
 
+	/* Allocate shared axis counters for EV_ABS/EV_REL buttons */
+	for (i = 0; i < pdata->nbuttons; i++) {
+		struct gpio_button_data *bdata = &ddata->data[i];
+		unsigned int type = bdata->button->type ?: EV_KEY;
+		int j;
+
+		if (type != EV_ABS && type != EV_REL)
+			continue;
+
+		/* Reuse counter from an earlier button with same (type, code) */
+		for (j = 0; j < i; j++) {
+			struct gpio_button_data *prev = &ddata->data[j];
+			unsigned int prev_type = prev->button->type ?: EV_KEY;
+
+			if (prev_type == type &&
+			    prev->button->code == bdata->button->code) {
+				bdata->axis_count = prev->axis_count;
+				break;
+			}
+		}
+
+		if (!bdata->axis_count) {
+			bdata->axis_count = devm_kzalloc(dev,
+					sizeof(*bdata->axis_count), GFP_KERNEL);
+			if (!bdata->axis_count)
+				return -ENOMEM;
+		}
+	}
+
 	error = input_register_device(input);
 	if (error) {
 		dev_err(dev, "Unable to register input device, error: %d\n",
diff --git a/include/linux/gpio_keys.h b/include/linux/gpio_keys.h
index 80fa930b04c6..75a745a32fe1 100644
--- a/include/linux/gpio_keys.h
+++ b/include/linux/gpio_keys.h
@@ -13,13 +13,13 @@ struct device;
  * @active_low:		%true indicates that button is considered
  *			depressed when gpio is low
  * @desc:		label that will be attached to button's gpio
- * @type:		input event type (%EV_KEY, %EV_SW, %EV_ABS)
+ * @type:		input event type (%EV_KEY, %EV_SW, %EV_ABS, %EV_REL)
  * @wakeup:		configure the button as a wake-up source
  * @wakeup_event_action:	event action to trigger wakeup
  * @debounce_interval:	debounce ticks interval in msecs
  * @can_disable:	%true indicates that userspace is allowed to
  *			disable button via sysfs
- * @value:		axis value for %EV_ABS
+ * @value:		axis value for %EV_ABS/%EV_REL
  * @irq:		Irq number in case of interrupt keys
  * @wakeirq:		Optional dedicated wake-up interrupt
  */
-- 
2.25.1


^ permalink raw reply related

* [PATCH v7 1/7] dt-bindings: input: syna,rmi4: Document syna,rmi4-s3706b
From: David Heidelberg via B4 Relay @ 2026-03-20 16:44 UTC (permalink / raw)
  To: Kaustabh Chakraborty, Dmitry Torokhov, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Jason A. Donenfeld,
	Matthias Schiffer, Vincent Huang
  Cc: David Heidelberg, linux-input, devicetree, linux-kernel,
	Casey Connolly, phone-devel, Krzysztof Kozlowski
In-Reply-To: <20260320-synaptics-rmi4-v7-0-379360de18d0@ixit.cz>

From: David Heidelberg <david@ixit.cz>

Mostly irrelevant for authentic Synaptics touchscreens, but very important
for applying workarounds to cheap TS knockoffs.

These knockoffs work well with the downstream driver, and since the user
has no way to distinguish them, later in this patch set, we introduce
workarounds to ensure they function as well as possible.

Acked-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Signed-off-by: David Heidelberg <david@ixit.cz>
---
 Documentation/devicetree/bindings/input/syna,rmi4.yaml | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/Documentation/devicetree/bindings/input/syna,rmi4.yaml b/Documentation/devicetree/bindings/input/syna,rmi4.yaml
index 8685ef4481f4a..fb4804ac3544d 100644
--- a/Documentation/devicetree/bindings/input/syna,rmi4.yaml
+++ b/Documentation/devicetree/bindings/input/syna,rmi4.yaml
@@ -18,9 +18,14 @@ description: |
 
 properties:
   compatible:
-    enum:
-      - syna,rmi4-i2c
-      - syna,rmi4-spi
+    oneOf:
+      - enum:
+          - syna,rmi4-i2c
+          - syna,rmi4-spi
+      - items:
+          - enum:
+              - syna,rmi4-s3706b  # OnePlus 6/6T
+          - const: syna,rmi4-i2c
 
   reg:
     maxItems: 1

-- 
2.53.0



^ permalink raw reply related

* [PATCH v7 0/7] Input: synaptics-rmi4 - add quirks for third party touchscreen controllers
From: David Heidelberg via B4 Relay @ 2026-03-20 16:44 UTC (permalink / raw)
  To: Kaustabh Chakraborty, Dmitry Torokhov, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Jason A. Donenfeld,
	Matthias Schiffer, Vincent Huang
  Cc: David Heidelberg, linux-input, devicetree, linux-kernel,
	Casey Connolly, phone-devel, Krzysztof Kozlowski

With the growing popularity of running upstream Linux on mobile devices,
we're beginning to run into more and more edgecases. The OnePlus 6 is a
fairly well supported 2018 era smartphone, selling over a million units
in it's first 22 days. With this level of popularity, it's almost
inevitable that we get third party replacement displays, and as a
result, replacement touchscreen controllers.

The OnePlus 6 shipped with an extremely usecase specific touchscreen
driver, it implemented only the bare minimum parts of the highly generic
rmi4 protocol, instead hardcoding most of the register addresses.
  
As a result, the third party touchscreen controllers that are often
found in replacement screens, implement only the registers that the 
downstream driver reads from. They additionally have other restrictions
such as heavy penalties on unaligned reads.
 
This series attempts to implement the necessary workaround to support  
some of these chips with the rmi4 driver. Although it's worth noting
that at the time of writing there are other unofficial controllers in
the wild that don't work even with these patches.
 
We have been shipping these patches in postmarketOS for the last several
years, and they are known to not cause any regressions on the OnePlus
6/6T (with the official Synaptics controller), however I don't own any
other rmi4 hardware to further validate this.

The series is also available (until merged) at
  https://gitlab.com/sdm845/sdm845-next/-/commits/b4/synaptics-rmi4

---
Changes in v7:
- Rebased on top of next-20260320, no other changes.
- Link to v6: https://lore.kernel.org/r/20251113-synaptics-rmi4-v6-0-d9836afab801@ixit.cz

Changes in v6:
- Rebased on top of next-20251113.
- No other change since the Rob Herring comment.
- Link to v5: https://lore.kernel.org/r/20250410-synaptics-rmi4-v5-0-b41bb90f78b9@ixit.cz

Changes in v5:
- Removed -i2c suffix from rmi4-s3706b-i2c (Krzysztof).
- Link to v4: https://lore.kernel.org/r/20250402-synaptics-rmi4-v4-0-1bb95959e564@ixit.cz

Changes in v4:
- Replaced patch "dt-bindings: input: syna,rmi4: document syna,pdt-fallback-desc"
  with patch documenting specific touchscreen model used in OnePlus 6 and 6T.
- Fixed zero electrode return code (Dmitry).
- Switched the duplicate detection algo to bitmap (Dmitry).
- Optimized rmi_device_platform_data struct to avoid unnecessary
  padding.
- Changed fallback_size from int to unsigned int.
- Changed SoB from nickname and old address (methanal <baclofen@tuta.io>) to
  Kaustabh Chakraborty <kauschluss@disroot.org>.
  Verified ownership through the sdm845 chatroom on Matrix.
- Link to v3: https://lore.kernel.org/r/20250308-synaptics-rmi4-v3-0-215d3e7289a2@ixit.cz

Changes in v3:
- reworded dt-bindings property description
- fixed the rmi_driver_of_probe definition for non device-tree builds.
- fixed some indentation issues reported by checkpatch
- change rmi_pdt_entry_is_valid() variable to unsigned 
- Link to v2: https://lore.kernel.org/all/20230929-caleb-rmi4-quirks-v2-0-b227ac498d88@linaro.org

Changes in v2:
- Improve dt-bindings patch (thanks Rob)
- Add missing cast in patch 5 to fix the pointer arithmetic
- Link to v1: https://lore.kernel.org/r/20230929-caleb-rmi4-quirks-v1-0-cc3c703f022d@linaro.org

---
Casey Connolly (1):
      Input: synaptics-rmi4 - handle duplicate/unknown PDT entries

David Heidelberg (1):
      dt-bindings: input: syna,rmi4: Document syna,rmi4-s3706b

Kaustabh Chakraborty (5):
      Input: synaptics-rmi4 - f12: use hardcoded values for aftermarket touch ICs
      Input: synaptics-rmi4 - f55: handle zero electrode count
      Input: synaptics-rmi4 - don't do unaligned reads in IRQ context
      Input: synaptics-rmi4 - read product ID on aftermarket touch ICs
      Input: synaptics-rmi4 - support fallback values for PDT descriptor bytes

 .../devicetree/bindings/input/syna,rmi4.yaml       |  11 +-
 drivers/input/rmi4/rmi_driver.c                    | 124 +++++++++++++++++----
 drivers/input/rmi4/rmi_driver.h                    |  10 ++
 drivers/input/rmi4/rmi_f01.c                       |  14 +++
 drivers/input/rmi4/rmi_f12.c                       | 117 ++++++++++++++-----
 drivers/input/rmi4/rmi_f55.c                       |   5 +
 include/linux/rmi.h                                |   3 +
 7 files changed, 234 insertions(+), 50 deletions(-)
---
base-commit: 785f0eb2f85decbe7c1ef9ae922931f0194ffc2e
change-id: 20250308-synaptics-rmi4-c832b2f73ceb

Best regards,
-- 
David Heidelberg <david@ixit.cz>



^ permalink raw reply

* [PATCH v7 2/7] Input: synaptics-rmi4 - handle duplicate/unknown PDT entries
From: David Heidelberg via B4 Relay @ 2026-03-20 16:44 UTC (permalink / raw)
  To: Kaustabh Chakraborty, Dmitry Torokhov, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Jason A. Donenfeld,
	Matthias Schiffer, Vincent Huang
  Cc: David Heidelberg, linux-input, devicetree, linux-kernel,
	Casey Connolly, phone-devel
In-Reply-To: <20260320-synaptics-rmi4-v7-0-379360de18d0@ixit.cz>

From: Casey Connolly <casey.connolly@linaro.org>

Some third party rmi4-compatible ICs don't expose their PDT entries
very well. Add a few checks to skip duplicate entries as well as entries
for unsupported functions.

This is required to support some phones with third party displays.

Validated on a stock OnePlus 6T (original parts):
manufacturer: Synaptics, product: S3706B, fw id: 2852315

Co-developed-by: Kaustabh Chakraborty <kauschluss@disroot.org>
Signed-off-by: Kaustabh Chakraborty <kauschluss@disroot.org>
Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
Co-developed-by: David Heidelberg <david@ixit.cz>
Signed-off-by: David Heidelberg <david@ixit.cz>
---
 drivers/input/rmi4/rmi_driver.c | 42 +++++++++++++++++++++++++++++++++++------
 drivers/input/rmi4/rmi_driver.h |  8 ++++++++
 2 files changed, 44 insertions(+), 6 deletions(-)

diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c
index ccd9338a44dbe..c7d2f68e65487 100644
--- a/drivers/input/rmi4/rmi_driver.c
+++ b/drivers/input/rmi4/rmi_driver.c
@@ -494,12 +494,39 @@ static void rmi_driver_copy_pdt_to_fd(const struct pdt_entry *pdt,
 	fd->function_version = pdt->function_version;
 }
 
+static bool rmi_pdt_entry_is_valid(struct rmi_device *rmi_dev,
+				   struct pdt_scan_state *state, u8 fn)
+{
+	switch (fn) {
+	case 0x01:
+	case 0x03:
+	case 0x11:
+	case 0x12:
+	case 0x30:
+	case 0x34:
+	case 0x3a:
+	case 0x54:
+	case 0x55:
+		if (state->pdts[fn] == true)
+			return false;
+		break;
+	default:
+		rmi_dbg(RMI_DEBUG_CORE, &rmi_dev->dev,
+			"PDT has unknown function number %#02x\n", fn);
+		return false;
+	}
+
+	state->pdts[fn] = true;
+	state->pdt_count++;
+	return true;
+}
+
 #define RMI_SCAN_CONTINUE	0
 #define RMI_SCAN_DONE		1
 
 static int rmi_scan_pdt_page(struct rmi_device *rmi_dev,
 			     int page,
-			     int *empty_pages,
+			     struct pdt_scan_state *state,
 			     void *ctx,
 			     int (*callback)(struct rmi_device *rmi_dev,
 					     void *ctx,
@@ -522,6 +549,9 @@ static int rmi_scan_pdt_page(struct rmi_device *rmi_dev,
 		if (RMI4_END_OF_PDT(pdt_entry.function_number))
 			break;
 
+		if (!rmi_pdt_entry_is_valid(rmi_dev, state, pdt_entry.function_number))
+			continue;
+
 		retval = callback(rmi_dev, ctx, &pdt_entry);
 		if (retval != RMI_SCAN_CONTINUE)
 			return retval;
@@ -532,11 +562,11 @@ static int rmi_scan_pdt_page(struct rmi_device *rmi_dev,
 	 * or more is found, stop scanning.
 	 */
 	if (addr == pdt_start)
-		++*empty_pages;
+		++state->empty_pages;
 	else
-		*empty_pages = 0;
+		state->empty_pages = 0;
 
-	return (data->bootloader_mode || *empty_pages >= 2) ?
+	return (data->bootloader_mode || state->empty_pages >= 2) ?
 					RMI_SCAN_DONE : RMI_SCAN_CONTINUE;
 }
 
@@ -545,11 +575,11 @@ int rmi_scan_pdt(struct rmi_device *rmi_dev, void *ctx,
 		 void *ctx, const struct pdt_entry *entry))
 {
 	int page;
-	int empty_pages = 0;
+	struct pdt_scan_state state = {0, 0, {0}};
 	int retval = RMI_SCAN_DONE;
 
 	for (page = 0; page <= RMI4_MAX_PAGE; page++) {
-		retval = rmi_scan_pdt_page(rmi_dev, page, &empty_pages,
+		retval = rmi_scan_pdt_page(rmi_dev, page, &state,
 					   ctx, callback);
 		if (retval != RMI_SCAN_CONTINUE)
 			break;
diff --git a/drivers/input/rmi4/rmi_driver.h b/drivers/input/rmi4/rmi_driver.h
index e84495caab151..a4ae2af93ce3a 100644
--- a/drivers/input/rmi4/rmi_driver.h
+++ b/drivers/input/rmi4/rmi_driver.h
@@ -46,6 +46,14 @@ struct pdt_entry {
 	u8 function_number;
 };
 
+#define RMI_PDT_MAX 0x55
+
+struct pdt_scan_state {
+	u8 empty_pages;
+	u8 pdt_count;
+	bool pdts[RMI_PDT_MAX];
+};
+
 #define RMI_REG_DESC_PRESENSE_BITS	(32 * BITS_PER_BYTE)
 #define RMI_REG_DESC_SUBPACKET_BITS	(37 * BITS_PER_BYTE)
 

-- 
2.53.0



^ permalink raw reply related

* [PATCH v7 3/7] Input: synaptics-rmi4 - f12: use hardcoded values for aftermarket touch ICs
From: David Heidelberg via B4 Relay @ 2026-03-20 16:44 UTC (permalink / raw)
  To: Kaustabh Chakraborty, Dmitry Torokhov, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Jason A. Donenfeld,
	Matthias Schiffer, Vincent Huang
  Cc: David Heidelberg, linux-input, devicetree, linux-kernel,
	Casey Connolly, phone-devel
In-Reply-To: <20260320-synaptics-rmi4-v7-0-379360de18d0@ixit.cz>

From: Kaustabh Chakraborty <kauschluss@disroot.org>

Some replacement displays include third-party touch ICs which are
devoid of register descriptors. Create a fake data register descriptor
for such ICs and provide hardcoded default values.

It isn't possible to reliably determine if the touch IC is original or
not, so these fallback values are offered as an alternative to the error
path when register descriptors aren't available.

Signed-off-by: Kaustabh Chakraborty <kauschluss@disroot.org>
[changes for readability / codeflow, checkpatch fixes]
Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
Signed-off-by: David Heidelberg <david@ixit.cz>
---
 drivers/input/rmi4/rmi_f12.c | 117 +++++++++++++++++++++++++++++++++----------
 1 file changed, 91 insertions(+), 26 deletions(-)

diff --git a/drivers/input/rmi4/rmi_f12.c b/drivers/input/rmi4/rmi_f12.c
index 8246fe77114bb..1a103cc5f2235 100644
--- a/drivers/input/rmi4/rmi_f12.c
+++ b/drivers/input/rmi4/rmi_f12.c
@@ -218,6 +218,41 @@ static void rmi_f12_process_objects(struct f12_data *f12, u8 *data1, int size)
 		rmi_2d_sensor_abs_report(sensor, &sensor->objs[i], i);
 }
 
+static void rmi_f12_set_hardcoded_desc(struct rmi_function *fn, struct f12_data *f12)
+{
+	struct rmi_2d_sensor *sensor = &f12->sensor;
+	struct rmi_register_desc_item *reg_desc;
+
+	/* We have no f12->data_reg_desc, so the pkt_size is 0, override it with
+	 * a somewhat sensible default (this corresponds to 10 fingers).
+	 */
+	sensor->pkt_size = 88;
+
+	/*
+	 * There are no register descriptors to get these values from.
+	 * We set them to high values to either be overwritten by the clip
+	 * properties from devicetree, or to just not get in the way.
+	 */
+	sensor->max_x = 65535;
+	sensor->max_y = 65535;
+
+	/*
+	 * Create the Data1 register descriptor so that touch events
+	 * can work properly.
+	 */
+	reg_desc = devm_kcalloc(&fn->dev, 1,
+			sizeof(struct rmi_register_desc_item), GFP_KERNEL);
+	reg_desc->reg = 1;
+	reg_desc->reg_size = 80;
+	reg_desc->num_subpackets = 10;
+
+	f12->data1 = reg_desc;
+	f12->data1_offset = 0;
+	sensor->nbr_fingers = reg_desc->num_subpackets;
+	sensor->report_abs = 1;
+	sensor->attn_size += reg_desc->reg_size;
+}
+
 static irqreturn_t rmi_f12_attention(int irq, void *ctx)
 {
 	int retval;
@@ -338,6 +373,40 @@ static int rmi_f12_config(struct rmi_function *fn)
 	return 0;
 }
 
+static int rmi_f12_sensor_init(struct rmi_function *fn, struct f12_data *f12)
+{
+	struct rmi_2d_sensor *sensor = &f12->sensor;
+
+	sensor->fn = fn;
+	f12->data_addr = fn->fd.data_base_addr;
+
+	/* On quirky devices that don't have a data_reg_desc we hardcode the packet
+	 * in rmi_f12_set_hardcoded_desc(). Make sure not to set it to 0 here.
+	 */
+	if (!sensor->pkt_size)
+		sensor->pkt_size = rmi_register_desc_calc_size(&f12->data_reg_desc);
+
+	sensor->axis_align =
+		f12->sensor_pdata.axis_align;
+
+	sensor->x_mm = f12->sensor_pdata.x_mm;
+	sensor->y_mm = f12->sensor_pdata.y_mm;
+	sensor->dribble = f12->sensor_pdata.dribble;
+
+	if (sensor->sensor_type == rmi_sensor_default)
+		sensor->sensor_type =
+			f12->sensor_pdata.sensor_type;
+
+	rmi_dbg(RMI_DEBUG_FN, &fn->dev, "%s: data packet size: %d\n", __func__,
+		sensor->pkt_size);
+
+	sensor->data_pkt = devm_kzalloc(&fn->dev, sensor->pkt_size, GFP_KERNEL);
+	if (!sensor->data_pkt)
+		return -ENOMEM;
+
+	return 0;
+}
+
 static int rmi_f12_probe(struct rmi_function *fn)
 {
 	struct f12_data *f12;
@@ -351,6 +420,7 @@ static int rmi_f12_probe(struct rmi_function *fn)
 	struct rmi_driver_data *drvdata = dev_get_drvdata(&rmi_dev->dev);
 	u16 data_offset = 0;
 	int mask_size;
+	bool hardcoded_desc_quirk = false;
 
 	rmi_dbg(RMI_DEBUG_FN, &fn->dev, "%s\n", __func__);
 
@@ -365,9 +435,9 @@ static int rmi_f12_probe(struct rmi_function *fn)
 	++query_addr;
 
 	if (!(buf & BIT(0))) {
-		dev_err(&fn->dev,
-			"Behavior of F12 without register descriptors is undefined.\n");
-		return -ENODEV;
+		rmi_dbg(RMI_DEBUG_FN, &fn->dev,
+			"No register descriptors defined for F12, using fallback\n");
+		hardcoded_desc_quirk = true;
 	}
 
 	f12 = devm_kzalloc(&fn->dev, sizeof(struct f12_data) + mask_size * 2,
@@ -375,6 +445,8 @@ static int rmi_f12_probe(struct rmi_function *fn)
 	if (!f12)
 		return -ENOMEM;
 
+	dev_set_drvdata(&fn->dev, f12);
+
 	f12->abs_mask = (unsigned long *)((char *)f12
 			+ sizeof(struct f12_data));
 	f12->rel_mask = (unsigned long *)((char *)f12
@@ -393,6 +465,18 @@ static int rmi_f12_probe(struct rmi_function *fn)
 		f12->sensor_pdata = pdata->sensor_pdata;
 	}
 
+	sensor = &f12->sensor;
+
+	if (hardcoded_desc_quirk) {
+		rmi_f12_set_hardcoded_desc(fn, f12);
+
+		ret = rmi_f12_sensor_init(fn, f12);
+		if (ret)
+			return ret;
+
+		goto skip_register_desc;
+	}
+
 	ret = rmi_read_register_desc(rmi_dev, query_addr,
 					&f12->query_reg_desc);
 	if (ret) {
@@ -423,29 +507,9 @@ static int rmi_f12_probe(struct rmi_function *fn)
 	}
 	query_addr += 3;
 
-	sensor = &f12->sensor;
-	sensor->fn = fn;
-	f12->data_addr = fn->fd.data_base_addr;
-	sensor->pkt_size = rmi_register_desc_calc_size(&f12->data_reg_desc);
-
-	sensor->axis_align =
-		f12->sensor_pdata.axis_align;
-
-	sensor->x_mm = f12->sensor_pdata.x_mm;
-	sensor->y_mm = f12->sensor_pdata.y_mm;
-	sensor->dribble = f12->sensor_pdata.dribble;
-
-	if (sensor->sensor_type == rmi_sensor_default)
-		sensor->sensor_type =
-			f12->sensor_pdata.sensor_type;
-
-	rmi_dbg(RMI_DEBUG_FN, &fn->dev, "%s: data packet size: %d\n", __func__,
-		sensor->pkt_size);
-	sensor->data_pkt = devm_kzalloc(&fn->dev, sensor->pkt_size, GFP_KERNEL);
-	if (!sensor->data_pkt)
-		return -ENOMEM;
-
-	dev_set_drvdata(&fn->dev, f12);
+	ret = rmi_f12_sensor_init(fn, f12);
+	if (ret)
+		return ret;
 
 	ret = rmi_f12_read_sensor_tuning(f12);
 	if (ret)
@@ -543,6 +607,7 @@ static int rmi_f12_probe(struct rmi_function *fn)
 		data_offset += item->reg_size;
 	}
 
+skip_register_desc:
 	/* allocate the in-kernel tracking buffers */
 	sensor->tracking_pos = devm_kcalloc(&fn->dev,
 			sensor->nbr_fingers, sizeof(struct input_mt_pos),

-- 
2.53.0



^ permalink raw reply related

* [PATCH v7 4/7] Input: synaptics-rmi4 - f55: handle zero electrode count
From: David Heidelberg via B4 Relay @ 2026-03-20 16:44 UTC (permalink / raw)
  To: Kaustabh Chakraborty, Dmitry Torokhov, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Jason A. Donenfeld,
	Matthias Schiffer, Vincent Huang
  Cc: David Heidelberg, linux-input, devicetree, linux-kernel,
	Casey Connolly, phone-devel
In-Reply-To: <20260320-synaptics-rmi4-v7-0-379360de18d0@ixit.cz>

From: Kaustabh Chakraborty <kauschluss@disroot.org>

Some third party ICs claim to support f55 but report an electrode count
of 0. Catch this and bail out early so that we don't confuse the i2c bus
with 0 sized reads.

Signed-off-by: Kaustabh Chakraborty <kauschluss@disroot.org>
[simplify code, adjust wording]
Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
Signed-off-by: David Heidelberg <david@ixit.cz>
---
 drivers/input/rmi4/rmi_f55.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/drivers/input/rmi4/rmi_f55.c b/drivers/input/rmi4/rmi_f55.c
index 488adaca4dd00..776c915b82e72 100644
--- a/drivers/input/rmi4/rmi_f55.c
+++ b/drivers/input/rmi4/rmi_f55.c
@@ -52,6 +52,11 @@ static int rmi_f55_detect(struct rmi_function *fn)
 
 	f55->num_rx_electrodes = f55->qry[F55_NUM_RX_OFFSET];
 	f55->num_tx_electrodes = f55->qry[F55_NUM_TX_OFFSET];
+	if (!f55->num_rx_electrodes || !f55->num_tx_electrodes) {
+		dev_err(&fn->dev, "%s: F55 query returned no electrodes, giving up\n",
+			__func__);
+		return -EINVAL;
+	}
 
 	f55->cfg_num_rx_electrodes = f55->num_rx_electrodes;
 	f55->cfg_num_tx_electrodes = f55->num_rx_electrodes;

-- 
2.53.0



^ permalink raw reply related

* [PATCH v7 5/7] Input: synaptics-rmi4 - don't do unaligned reads in IRQ context
From: David Heidelberg via B4 Relay @ 2026-03-20 16:44 UTC (permalink / raw)
  To: Kaustabh Chakraborty, Dmitry Torokhov, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Jason A. Donenfeld,
	Matthias Schiffer, Vincent Huang
  Cc: David Heidelberg, linux-input, devicetree, linux-kernel,
	Casey Connolly, phone-devel
In-Reply-To: <20260320-synaptics-rmi4-v7-0-379360de18d0@ixit.cz>

From: Kaustabh Chakraborty <kauschluss@disroot.org>

Some replacement displays include third-party touch ICs which incur a
significant penalty (1-2 seconds) when doing certain unaligned reads.
This is enough to break functionality when it happens in the hot path,
so adjust the interrupt handler to not read from an unaligned address.

Signed-off-by: Kaustabh Chakraborty <kauschluss@disroot.org>
Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
Signed-off-by: David Heidelberg <david@ixit.cz>
---
 drivers/input/rmi4/rmi_driver.c | 20 +++++++++++++-------
 1 file changed, 13 insertions(+), 7 deletions(-)

diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c
index c7d2f68e65487..93a190e333c66 100644
--- a/drivers/input/rmi4/rmi_driver.c
+++ b/drivers/input/rmi4/rmi_driver.c
@@ -137,9 +137,14 @@ static int rmi_process_interrupt_requests(struct rmi_device *rmi_dev)
 		return 0;
 
 	if (!data->attn_data.data) {
+		/*
+		 * Read the device status register as well and ignore it.
+		 * Some aftermarket ICs have issues with interrupt requests
+		 * otherwise.
+		 */
 		error = rmi_read_block(rmi_dev,
-				data->f01_container->fd.data_base_addr + 1,
-				data->irq_status, data->num_of_irq_regs);
+				data->f01_container->fd.data_base_addr,
+				(u8 *)data->irq_status - 1, data->num_of_irq_regs + 1);
 		if (error < 0) {
 			dev_err(dev, "Failed to read irqs, code=%d\n", error);
 			return error;
@@ -1079,16 +1084,17 @@ int rmi_probe_interrupts(struct rmi_driver_data *data)
 	data->num_of_irq_regs = (data->irq_count + 7) / 8;
 
 	size = BITS_TO_LONGS(data->irq_count) * sizeof(unsigned long);
-	data->irq_memory = devm_kcalloc(dev, size, 4, GFP_KERNEL);
+	data->irq_memory = devm_kzalloc(dev, size * 4 + 1, GFP_KERNEL);
 	if (!data->irq_memory) {
 		dev_err(dev, "Failed to allocate memory for irq masks.\n");
 		return -ENOMEM;
 	}
 
-	data->irq_status	= data->irq_memory + size * 0;
-	data->fn_irq_bits	= data->irq_memory + size * 1;
-	data->current_irq_mask	= data->irq_memory + size * 2;
-	data->new_irq_mask	= data->irq_memory + size * 3;
+	/* The first byte is reserved for the device status register */
+	data->irq_status	= data->irq_memory + size * 0 + 1;
+	data->fn_irq_bits	= data->irq_memory + size * 1 + 1;
+	data->current_irq_mask	= data->irq_memory + size * 2 + 1;
+	data->new_irq_mask	= data->irq_memory + size * 3 + 1;
 
 	return retval;
 }

-- 
2.53.0



^ permalink raw reply related

* [PATCH v7 6/7] Input: synaptics-rmi4 - read product ID on aftermarket touch ICs
From: David Heidelberg via B4 Relay @ 2026-03-20 16:44 UTC (permalink / raw)
  To: Kaustabh Chakraborty, Dmitry Torokhov, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Jason A. Donenfeld,
	Matthias Schiffer, Vincent Huang
  Cc: David Heidelberg, linux-input, devicetree, linux-kernel,
	Casey Connolly, phone-devel
In-Reply-To: <20260320-synaptics-rmi4-v7-0-379360de18d0@ixit.cz>

From: Kaustabh Chakraborty <kauschluss@disroot.org>

Some replacement displays include third-party touch ICs which do not
report the product ID correctly unless we read directly from the
product ID register. Add a check and a fallback read to handle this.

Signed-off-by: Kaustabh Chakraborty <kauschluss@disroot.org>
Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
Signed-off-by: David Heidelberg <david@ixit.cz>
---
 drivers/input/rmi4/rmi_f01.c | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/drivers/input/rmi4/rmi_f01.c b/drivers/input/rmi4/rmi_f01.c
index 47be64284b25e..2278e9b6a9207 100644
--- a/drivers/input/rmi4/rmi_f01.c
+++ b/drivers/input/rmi4/rmi_f01.c
@@ -250,6 +250,20 @@ static int rmi_f01_read_properties(struct rmi_device *rmi_dev,
 		}
 	}
 
+	/*
+	 * Some aftermarket ICs put garbage into the product id field unless
+	 * we read directly from the product id register.
+	 */
+	if (props->product_id[0] < 0x20) {
+		ret = rmi_read_block(rmi_dev, query_base_addr + 11,
+				       props->product_id, RMI_PRODUCT_ID_LENGTH);
+		if (ret) {
+			dev_err(&rmi_dev->dev,
+				"Failed to read product id: %d\n", ret);
+			return ret;
+		}
+	}
+
 	return 0;
 }
 

-- 
2.53.0



^ permalink raw reply related

* [PATCH v7 7/7] Input: synaptics-rmi4 - support fallback values for PDT descriptor bytes
From: David Heidelberg via B4 Relay @ 2026-03-20 16:44 UTC (permalink / raw)
  To: Kaustabh Chakraborty, Dmitry Torokhov, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Jason A. Donenfeld,
	Matthias Schiffer, Vincent Huang
  Cc: David Heidelberg, linux-input, devicetree, linux-kernel,
	Casey Connolly, phone-devel
In-Reply-To: <20260320-synaptics-rmi4-v7-0-379360de18d0@ixit.cz>

From: Kaustabh Chakraborty <kauschluss@disroot.org>

Some replacement displays include third-party touch ICs which do not
expose the function number and the interrupt status in its PDT entries.

OnePlus 6 (original touch IC)
  rmi4_i2c 12-0020: read 6 bytes at 0x00e3: 0 (2b 22 0d 06 01 01)

OnePlus 6 (aftermarket touch IC)
  rmi4_i2c 12-0020: read 6 bytes at 0x00e3: 0 (2c 23 0d 06 00 00)

Signed-off-by: Kaustabh Chakraborty <kauschluss@disroot.org>
[codeflow adjustments, checkpatch fixes, wording]
Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
Co-developed-by: David Heidelberg <david@ixit.cz>
Signed-off-by: David Heidelberg <david@ixit.cz>
---
 drivers/input/rmi4/rmi_driver.c | 62 +++++++++++++++++++++++++++++++++++------
 drivers/input/rmi4/rmi_driver.h |  2 ++
 include/linux/rmi.h             |  3 ++
 3 files changed, 59 insertions(+), 8 deletions(-)

diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c
index 93a190e333c66..bb1db5bbb3abb 100644
--- a/drivers/input/rmi4/rmi_driver.c
+++ b/drivers/input/rmi4/rmi_driver.c
@@ -462,9 +462,10 @@ static int rmi_driver_reset_handler(struct rmi_device *rmi_dev)
 	return 0;
 }
 
-static int rmi_read_pdt_entry(struct rmi_device *rmi_dev,
-			      struct pdt_entry *entry, u16 pdt_address)
+static int rmi_read_pdt_entry(struct rmi_device *rmi_dev, struct pdt_entry *entry,
+			      struct pdt_scan_state *state, u16 pdt_address)
 {
+	const struct rmi_device_platform_data *pdata = rmi_get_platform_data(rmi_dev);
 	u8 buf[RMI_PDT_ENTRY_SIZE];
 	int error;
 
@@ -475,6 +476,21 @@ static int rmi_read_pdt_entry(struct rmi_device *rmi_dev,
 		return error;
 	}
 
+	if (pdata->pdt_fallback_size > state->pdt_count * RMI_OF_PDT_DESC_CELLS + 1) {
+		/* Use the description bytes from the driver */
+		buf[5] = pdata->pdt_fallback_desc[state->pdt_count * RMI_OF_PDT_DESC_CELLS];
+		buf[4] = pdata->pdt_fallback_desc[state->pdt_count * RMI_OF_PDT_DESC_CELLS + 1];
+
+		error = rmi_read_block(rmi_dev, pdt_address, buf,
+				RMI_PDT_ENTRY_SIZE - 2);
+		if (error) {
+			dev_err(&rmi_dev->dev,
+					"Read PDT entry at %#06x failed, code: %d.\n",
+					pdt_address, error);
+			return error;
+		}
+	}
+
 	entry->page_start = pdt_address & RMI4_PAGE_MASK;
 	entry->query_base_addr = buf[0];
 	entry->command_base_addr = buf[1];
@@ -547,7 +563,7 @@ static int rmi_scan_pdt_page(struct rmi_device *rmi_dev,
 	int retval;
 
 	for (addr = pdt_start; addr >= pdt_end; addr -= RMI_PDT_ENTRY_SIZE) {
-		error = rmi_read_pdt_entry(rmi_dev, &pdt_entry, addr);
+		error = rmi_read_pdt_entry(rmi_dev, &pdt_entry, state, addr);
 		if (error)
 			return error;
 
@@ -1024,9 +1040,13 @@ static int rmi_driver_remove(struct device *dev)
 }
 
 #ifdef CONFIG_OF
-static int rmi_driver_of_probe(struct device *dev,
-				struct rmi_device_platform_data *pdata)
+static const u8 rmi_s3706_fallback_pdt[] = {34, 41, 01, 01, 12, 01};
+
+static int rmi_driver_of_probe(struct rmi_device *rmi_dev,
+			       struct rmi_device_platform_data *pdata)
 {
+	struct device *dev = rmi_dev->xport->dev;
+	u8 buf[RMI_PDT_ENTRY_SIZE];
 	int retval;
 
 	retval = rmi_of_property_read_u32(dev, &pdata->reset_delay_ms,
@@ -1034,11 +1054,37 @@ static int rmi_driver_of_probe(struct device *dev,
 	if (retval)
 		return retval;
 
+	/*
+	 * In some aftermerket touch ICs, the first PDT entry is empty and
+	 * the function number register is 0. If so, the driver
+	 * may have provide backup PDT entries.
+	 */
+
+	retval = rmi_read_block(rmi_dev, PDT_START_SCAN_LOCATION,
+			buf, RMI_PDT_ENTRY_SIZE);
+	if (retval) {
+		dev_err(dev, "Read PDT entry at %#06x failed, code: %d.\n",
+			PDT_START_SCAN_LOCATION, retval);
+		return retval;
+	}
+
+	if (!RMI4_END_OF_PDT(buf[5]))
+		return 0;
+
+	/* List of known PDT entries per compatible. */
+	if (of_device_is_compatible(dev->of_node, "syna,rmi4-s3706b")) {
+		pdata->pdt_fallback_desc = rmi_s3706_fallback_pdt;
+		pdata->pdt_fallback_size = ARRAY_SIZE(rmi_s3706_fallback_pdt);
+	} else {
+		dev_err(dev, "First PDT entry is empty and no backup values provided.\n");
+		return -EINVAL;
+	}
+
 	return 0;
 }
 #else
-static inline int rmi_driver_of_probe(struct device *dev,
-					struct rmi_device_platform_data *pdata)
+static inline int rmi_driver_of_probe(struct rmi_device *rmi_dev,
+				      struct rmi_device_platform_data *pdata)
 {
 	return -ENODEV;
 }
@@ -1159,7 +1205,7 @@ static int rmi_driver_probe(struct device *dev)
 	pdata = rmi_get_platform_data(rmi_dev);
 
 	if (rmi_dev->xport->dev->of_node) {
-		retval = rmi_driver_of_probe(rmi_dev->xport->dev, pdata);
+		retval = rmi_driver_of_probe(rmi_dev, pdata);
 		if (retval)
 			return retval;
 	}
diff --git a/drivers/input/rmi4/rmi_driver.h b/drivers/input/rmi4/rmi_driver.h
index a4ae2af93ce3a..b931f428713bf 100644
--- a/drivers/input/rmi4/rmi_driver.h
+++ b/drivers/input/rmi4/rmi_driver.h
@@ -31,6 +31,8 @@
 #define RMI_PDT_FUNCTION_VERSION_MASK   0x60
 #define RMI_PDT_INT_SOURCE_COUNT_MASK   0x07
 
+#define RMI_OF_PDT_DESC_CELLS 2
+
 #define PDT_START_SCAN_LOCATION 0x00e9
 #define PDT_END_SCAN_LOCATION	0x0005
 #define RMI4_END_OF_PDT(id) ((id) == 0x00 || (id) == 0xff)
diff --git a/include/linux/rmi.h b/include/linux/rmi.h
index ab7eea01ab427..4ba2cefac8558 100644
--- a/include/linux/rmi.h
+++ b/include/linux/rmi.h
@@ -214,6 +214,9 @@ struct rmi_device_platform_data {
 	int reset_delay_ms;
 	int irq;
 
+	unsigned int pdt_fallback_size;
+	const u8 *pdt_fallback_desc;
+
 	struct rmi_device_platform_data_spi spi_data;
 
 	/* function handler pdata */

-- 
2.53.0



^ permalink raw reply related

* Re: [PATCH v7 2/7] Input: synaptics-rmi4 - handle duplicate/unknown PDT entries
From: Casey Connolly @ 2026-03-20 16:49 UTC (permalink / raw)
  To: david, Kaustabh Chakraborty, Dmitry Torokhov, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Jason A. Donenfeld,
	Matthias Schiffer, Vincent Huang
  Cc: linux-input, devicetree, linux-kernel, phone-devel
In-Reply-To: <20260320-synaptics-rmi4-v7-2-379360de18d0@ixit.cz>

Hi David,

Nice timing with the series, I hit an OOB access (found it when I
enabled UBSAN) with this patch the other day.

The pdt_scan_state->pdts array should actually be of size (RMI_PDT_MAX+1).

Additionally, I think rmi_pdt_entry_is_valid() is missing a bounds check.

Kind regards,

On 20/03/2026 17:44, David Heidelberg via B4 Relay wrote:
> From: Casey Connolly <casey.connolly@linaro.org>
> 
> Some third party rmi4-compatible ICs don't expose their PDT entries
> very well. Add a few checks to skip duplicate entries as well as entries
> for unsupported functions.
> 
> This is required to support some phones with third party displays.
> 
> Validated on a stock OnePlus 6T (original parts):
> manufacturer: Synaptics, product: S3706B, fw id: 2852315
> 
> Co-developed-by: Kaustabh Chakraborty <kauschluss@disroot.org>
> Signed-off-by: Kaustabh Chakraborty <kauschluss@disroot.org>
> Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
> Co-developed-by: David Heidelberg <david@ixit.cz>
> Signed-off-by: David Heidelberg <david@ixit.cz>
> ---
>  drivers/input/rmi4/rmi_driver.c | 42 +++++++++++++++++++++++++++++++++++------
>  drivers/input/rmi4/rmi_driver.h |  8 ++++++++
>  2 files changed, 44 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c
> index ccd9338a44dbe..c7d2f68e65487 100644
> --- a/drivers/input/rmi4/rmi_driver.c
> +++ b/drivers/input/rmi4/rmi_driver.c
> @@ -494,12 +494,39 @@ static void rmi_driver_copy_pdt_to_fd(const struct pdt_entry *pdt,
>  	fd->function_version = pdt->function_version;
>  }
>  
> +static bool rmi_pdt_entry_is_valid(struct rmi_device *rmi_dev,
> +				   struct pdt_scan_state *state, u8 fn)
> +{
> +	switch (fn) {
> +	case 0x01:
> +	case 0x03:
> +	case 0x11:
> +	case 0x12:
> +	case 0x30:
> +	case 0x34:
> +	case 0x3a:
> +	case 0x54:
> +	case 0x55:
> +		if (state->pdts[fn] == true)
> +			return false;
> +		break;
> +	default:
> +		rmi_dbg(RMI_DEBUG_CORE, &rmi_dev->dev,
> +			"PDT has unknown function number %#02x\n", fn);
> +		return false;
> +	}
> +
> +	state->pdts[fn] = true;
> +	state->pdt_count++;
> +	return true;
> +}
> +
>  #define RMI_SCAN_CONTINUE	0
>  #define RMI_SCAN_DONE		1
>  
>  static int rmi_scan_pdt_page(struct rmi_device *rmi_dev,
>  			     int page,
> -			     int *empty_pages,
> +			     struct pdt_scan_state *state,
>  			     void *ctx,
>  			     int (*callback)(struct rmi_device *rmi_dev,
>  					     void *ctx,
> @@ -522,6 +549,9 @@ static int rmi_scan_pdt_page(struct rmi_device *rmi_dev,
>  		if (RMI4_END_OF_PDT(pdt_entry.function_number))
>  			break;
>  
> +		if (!rmi_pdt_entry_is_valid(rmi_dev, state, pdt_entry.function_number))
> +			continue;
> +
>  		retval = callback(rmi_dev, ctx, &pdt_entry);
>  		if (retval != RMI_SCAN_CONTINUE)
>  			return retval;
> @@ -532,11 +562,11 @@ static int rmi_scan_pdt_page(struct rmi_device *rmi_dev,
>  	 * or more is found, stop scanning.
>  	 */
>  	if (addr == pdt_start)
> -		++*empty_pages;
> +		++state->empty_pages;
>  	else
> -		*empty_pages = 0;
> +		state->empty_pages = 0;
>  
> -	return (data->bootloader_mode || *empty_pages >= 2) ?
> +	return (data->bootloader_mode || state->empty_pages >= 2) ?
>  					RMI_SCAN_DONE : RMI_SCAN_CONTINUE;
>  }
>  
> @@ -545,11 +575,11 @@ int rmi_scan_pdt(struct rmi_device *rmi_dev, void *ctx,
>  		 void *ctx, const struct pdt_entry *entry))
>  {
>  	int page;
> -	int empty_pages = 0;
> +	struct pdt_scan_state state = {0, 0, {0}};
>  	int retval = RMI_SCAN_DONE;
>  
>  	for (page = 0; page <= RMI4_MAX_PAGE; page++) {
> -		retval = rmi_scan_pdt_page(rmi_dev, page, &empty_pages,
> +		retval = rmi_scan_pdt_page(rmi_dev, page, &state,
>  					   ctx, callback);
>  		if (retval != RMI_SCAN_CONTINUE)
>  			break;
> diff --git a/drivers/input/rmi4/rmi_driver.h b/drivers/input/rmi4/rmi_driver.h
> index e84495caab151..a4ae2af93ce3a 100644
> --- a/drivers/input/rmi4/rmi_driver.h
> +++ b/drivers/input/rmi4/rmi_driver.h
> @@ -46,6 +46,14 @@ struct pdt_entry {
>  	u8 function_number;
>  };
>  
> +#define RMI_PDT_MAX 0x55
> +
> +struct pdt_scan_state {
> +	u8 empty_pages;
> +	u8 pdt_count;
> +	bool pdts[RMI_PDT_MAX];
> +};
> +
>  #define RMI_REG_DESC_PRESENSE_BITS	(32 * BITS_PER_BYTE)
>  #define RMI_REG_DESC_SUBPACKET_BITS	(37 * BITS_PER_BYTE)
>  
> 

-- 
// Casey (she/her)


^ permalink raw reply

* Re: [PATCH v7 2/7] Input: synaptics-rmi4 - handle duplicate/unknown PDT entries
From: David Heidelberg @ 2026-03-20 16:54 UTC (permalink / raw)
  To: Casey Connolly, Kaustabh Chakraborty, Dmitry Torokhov,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Jason A. Donenfeld, Matthias Schiffer, Vincent Huang
  Cc: linux-input, devicetree, linux-kernel, phone-devel
In-Reply-To: <fba73d66-4300-4c4d-9bf6-4b38a4e847d7@postmarketos.org>

On 20/03/2026 17:49, Casey Connolly wrote:
> Hi David,
> 
> Nice timing with the series, I hit an OOB access (found it when I
> enabled UBSAN) with this patch the other day.
> 
> The pdt_scan_state->pdts array should actually be of size (RMI_PDT_MAX+1).
> 
> Additionally, I think rmi_pdt_entry_is_valid() is missing a bounds check.
> 
> Kind regards,


Thanks a lot for catching this and for the detailed notes — that’s very helpful.

Since you’re the original author of the commit, I’m completely fine with you 
taking over the b4 series if you’d prefer. Alternatively, if it’s easier, feel 
free to just send me a fixed patch and I can incorporate it.

Whichever works best for you.

David>
> On 20/03/2026 17:44, David Heidelberg via B4 Relay wrote:
>> From: Casey Connolly <casey.connolly@linaro.org>
>>
>> Some third party rmi4-compatible ICs don't expose their PDT entries
>> very well. Add a few checks to skip duplicate entries as well as entries
>> for unsupported functions.
>>
>> This is required to support some phones with third party displays.
>>
>> Validated on a stock OnePlus 6T (original parts):
>> manufacturer: Synaptics, product: S3706B, fw id: 2852315
>>
>> Co-developed-by: Kaustabh Chakraborty <kauschluss@disroot.org>
>> Signed-off-by: Kaustabh Chakraborty <kauschluss@disroot.org>
>> Signed-off-by: Casey Connolly <casey.connolly@linaro.org>
>> Co-developed-by: David Heidelberg <david@ixit.cz>
>> Signed-off-by: David Heidelberg <david@ixit.cz>

[...]

^ permalink raw reply

* Re: [PATCH v7 2/7] Input: synaptics-rmi4 - handle duplicate/unknown PDT entries
From: Casey Connolly @ 2026-03-20 17:03 UTC (permalink / raw)
  To: David Heidelberg, Kaustabh Chakraborty, Dmitry Torokhov,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Jason A. Donenfeld, Matthias Schiffer, Vincent Huang
  Cc: linux-input, devicetree, linux-kernel, phone-devel
In-Reply-To: <3dca0fc0-fe1c-4f84-b336-856f55a6e3da@ixit.cz>



On 20/03/2026 17:54, David Heidelberg wrote:
> On 20/03/2026 17:49, Casey Connolly wrote:
>> Hi David,
>>
>> Nice timing with the series, I hit an OOB access (found it when I
>> enabled UBSAN) with this patch the other day.
>>
>> The pdt_scan_state->pdts array should actually be of size
>> (RMI_PDT_MAX+1).
>>
>> Additionally, I think rmi_pdt_entry_is_valid() is missing a bounds check.
>>
>> Kind regards,
> 
> 
> Thanks a lot for catching this and for the detailed notes — that’s very
> helpful.
> 
> Since you’re the original author of the commit, I’m completely fine with
> you taking over the b4 series if you’d prefer. Alternatively, if it’s
> easier, feel free to just send me a fixed patch and I can incorporate it.
> 

Uh sure, not sure this will apply cleanly I just edited inline it's a
3-line delta. Also figured we can drop pdt_count since it's unused.

---

diff --git a/drivers/input/rmi4/rmi_driver.c
b/drivers/input/rmi4/rmi_driver.c
index ccd9338a44dbe..c7d2f68e65487 100644
--- a/drivers/input/rmi4/rmi_driver.c
+++ b/drivers/input/rmi4/rmi_driver.c
@@ -494,12 +494,39 @@ static void rmi_driver_copy_pdt_to_fd(const struct
pdt_entry *pdt,
 	fd->function_version = pdt->function_version;
 }

+static bool rmi_pdt_entry_is_valid(struct rmi_device *rmi_dev,
+				   struct pdt_scan_state *state, u8 fn)
+{
+	if (fn > RMI_PDT_MAX)
+		return false;
+
+	switch (fn) {
+	case 0x01:
+	case 0x03:
+	case 0x11:
+	case 0x12:
+	case 0x30:
+	case 0x34:
+	case 0x3a:
+	case 0x54:
+	case 0x55:
+		if (state->pdts[fn] == true)
+			return false;
+		break;
+	default:
+		rmi_dbg(RMI_DEBUG_CORE, &rmi_dev->dev,
+			"PDT has unknown function number %#02x\n", fn);
+		return false;
+	}
+
+	state->pdts[fn] = true;
+	return true;
+}
+
 #define RMI_SCAN_CONTINUE	0
 #define RMI_SCAN_DONE		1

 static int rmi_scan_pdt_page(struct rmi_device *rmi_dev,
 			     int page,
-			     int *empty_pages,
+			     struct pdt_scan_state *state,
 			     void *ctx,
 			     int (*callback)(struct rmi_device *rmi_dev,
 					     void *ctx,
@@ -522,6 +549,9 @@ static int rmi_scan_pdt_page(struct rmi_device *rmi_dev,
 		if (RMI4_END_OF_PDT(pdt_entry.function_number))
 			break;

+		if (!rmi_pdt_entry_is_valid(rmi_dev, state, pdt_entry.function_number))
+			continue;
+
 		retval = callback(rmi_dev, ctx, &pdt_entry);
 		if (retval != RMI_SCAN_CONTINUE)
 			return retval;
@@ -532,11 +562,11 @@ static int rmi_scan_pdt_page(struct rmi_device
*rmi_dev,
 	 * or more is found, stop scanning.
 	 */
 	if (addr == pdt_start)
-		++*empty_pages;
+		++state->empty_pages;
 	else
-		*empty_pages = 0;
+		state->empty_pages = 0;

-	return (data->bootloader_mode || *empty_pages >= 2) ?
+	return (data->bootloader_mode || state->empty_pages >= 2) ?
 					RMI_SCAN_DONE : RMI_SCAN_CONTINUE;
 }

@@ -545,11 +575,11 @@ int rmi_scan_pdt(struct rmi_device *rmi_dev, void
*ctx,
 		 void *ctx, const struct pdt_entry *entry))
 {
 	int page;
-	int empty_pages = 0;
+	struct pdt_scan_state state = {0, {0}};
 	int retval = RMI_SCAN_DONE;

 	for (page = 0; page <= RMI4_MAX_PAGE; page++) {
-		retval = rmi_scan_pdt_page(rmi_dev, page, &empty_pages,
+		retval = rmi_scan_pdt_page(rmi_dev, page, &state,
 					   ctx, callback);
 		if (retval != RMI_SCAN_CONTINUE)
 			break;
diff --git a/drivers/input/rmi4/rmi_driver.h
b/drivers/input/rmi4/rmi_driver.h
index e84495caab151..a4ae2af93ce3a 100644
--- a/drivers/input/rmi4/rmi_driver.h
+++ b/drivers/input/rmi4/rmi_driver.h
@@ -46,6 +46,14 @@ struct pdt_entry {
 	u8 function_number;
 };

+#define RMI_PDT_MAX 0x55
+
+struct pdt_scan_state {
+	u8 empty_pages;
+	bool pdts[RMI_PDT_MAX + 1];
+};
+
 #define RMI_REG_DESC_PRESENSE_BITS	(32 * BITS_PER_BYTE)
 #define RMI_REG_DESC_SUBPACKET_BITS	(37 * BITS_PER_BYTE)


-- 
2.53.0




-- 
// Casey (she/her)


^ permalink raw reply related

* Re: [PATCH v7 2/7] Input: synaptics-rmi4 - handle duplicate/unknown PDT entries
From: David Heidelberg @ 2026-03-20 17:12 UTC (permalink / raw)
  To: Casey Connolly, Kaustabh Chakraborty, Dmitry Torokhov,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Jason A. Donenfeld, Matthias Schiffer, Vincent Huang
  Cc: linux-input, devicetree, linux-kernel, phone-devel
In-Reply-To: <55dab1d8-87af-4285-9ab1-924bf392c78d@postmarketos.org>

On 20/03/2026 18:03, Casey Connolly wrote:
> 
> 
> On 20/03/2026 17:54, David Heidelberg wrote:
>> On 20/03/2026 17:49, Casey Connolly wrote:
>>> Hi David,
>>>
>>> Nice timing with the series, I hit an OOB access (found it when I
>>> enabled UBSAN) with this patch the other day.
>>>
>>> The pdt_scan_state->pdts array should actually be of size
>>> (RMI_PDT_MAX+1).
>>>
>>> Additionally, I think rmi_pdt_entry_is_valid() is missing a bounds check.
>>>
>>> Kind regards,
>>
>>
>> Thanks a lot for catching this and for the detailed notes — that’s very
>> helpful.
>>
>> Since you’re the original author of the commit, I’m completely fine with
>> you taking over the b4 series if you’d prefer. Alternatively, if it’s
>> easier, feel free to just send me a fixed patch and I can incorporate it.
>>
> 
> Uh sure, not sure this will apply cleanly I just edited inline it's a
> 3-line delta. Also figured we can drop pdt_count since it's unused.

The pdt_count is used in

Input: synaptics-rmi4 - support fallback values for PDT descriptor bytes

thus should be moved there I assume, but can be dropped here.

David

> 
> ---
> 
> diff --git a/drivers/input/rmi4/rmi_driver.c
> b/drivers/input/rmi4/rmi_driver.c
> index ccd9338a44dbe..c7d2f68e65487 100644
> --- a/drivers/input/rmi4/rmi_driver.c
> +++ b/drivers/input/rmi4/rmi_driver.c
> @@ -494,12 +494,39 @@ static void rmi_driver_copy_pdt_to_fd(const struct
> pdt_entry *pdt,
>   	fd->function_version = pdt->function_version;
>   }
> 
> +static bool rmi_pdt_entry_is_valid(struct rmi_device *rmi_dev,
> +				   struct pdt_scan_state *state, u8 fn)
> +{
> +	if (fn > RMI_PDT_MAX)
> +		return false;
> +
> +	switch (fn) {
> +	case 0x01:
> +	case 0x03:
> +	case 0x11:
> +	case 0x12:
> +	case 0x30:
> +	case 0x34:
> +	case 0x3a:
> +	case 0x54:
> +	case 0x55:
> +		if (state->pdts[fn] == true)
> +			return false;
> +		break;
> +	default:
> +		rmi_dbg(RMI_DEBUG_CORE, &rmi_dev->dev,
> +			"PDT has unknown function number %#02x\n", fn);
> +		return false;
> +	}
> +
> +	state->pdts[fn] = true;
> +	return true;
> +}
> +
>   #define RMI_SCAN_CONTINUE	0
>   #define RMI_SCAN_DONE		1
> 
>   static int rmi_scan_pdt_page(struct rmi_device *rmi_dev,
>   			     int page,
> -			     int *empty_pages,
> +			     struct pdt_scan_state *state,
>   			     void *ctx,
>   			     int (*callback)(struct rmi_device *rmi_dev,
>   					     void *ctx,
> @@ -522,6 +549,9 @@ static int rmi_scan_pdt_page(struct rmi_device *rmi_dev,
>   		if (RMI4_END_OF_PDT(pdt_entry.function_number))
>   			break;
> 
> +		if (!rmi_pdt_entry_is_valid(rmi_dev, state, pdt_entry.function_number))
> +			continue;
> +
>   		retval = callback(rmi_dev, ctx, &pdt_entry);
>   		if (retval != RMI_SCAN_CONTINUE)
>   			return retval;
> @@ -532,11 +562,11 @@ static int rmi_scan_pdt_page(struct rmi_device
> *rmi_dev,
>   	 * or more is found, stop scanning.
>   	 */
>   	if (addr == pdt_start)
> -		++*empty_pages;
> +		++state->empty_pages;
>   	else
> -		*empty_pages = 0;
> +		state->empty_pages = 0;
> 
> -	return (data->bootloader_mode || *empty_pages >= 2) ?
> +	return (data->bootloader_mode || state->empty_pages >= 2) ?
>   					RMI_SCAN_DONE : RMI_SCAN_CONTINUE;
>   }
> 
> @@ -545,11 +575,11 @@ int rmi_scan_pdt(struct rmi_device *rmi_dev, void
> *ctx,
>   		 void *ctx, const struct pdt_entry *entry))
>   {
>   	int page;
> -	int empty_pages = 0;
> +	struct pdt_scan_state state = {0, {0}};
>   	int retval = RMI_SCAN_DONE;
> 
>   	for (page = 0; page <= RMI4_MAX_PAGE; page++) {
> -		retval = rmi_scan_pdt_page(rmi_dev, page, &empty_pages,
> +		retval = rmi_scan_pdt_page(rmi_dev, page, &state,
>   					   ctx, callback);
>   		if (retval != RMI_SCAN_CONTINUE)
>   			break;
> diff --git a/drivers/input/rmi4/rmi_driver.h
> b/drivers/input/rmi4/rmi_driver.h
> index e84495caab151..a4ae2af93ce3a 100644
> --- a/drivers/input/rmi4/rmi_driver.h
> +++ b/drivers/input/rmi4/rmi_driver.h
> @@ -46,6 +46,14 @@ struct pdt_entry {
>   	u8 function_number;
>   };
> 
> +#define RMI_PDT_MAX 0x55
> +
> +struct pdt_scan_state {
> +	u8 empty_pages;
> +	bool pdts[RMI_PDT_MAX + 1];
> +};
> +
>   #define RMI_REG_DESC_PRESENSE_BITS	(32 * BITS_PER_BYTE)
>   #define RMI_REG_DESC_SUBPACKET_BITS	(37 * BITS_PER_BYTE)
> 
> 

-- 
David Heidelberg


^ permalink raw reply

* Re: [PATCH v7 2/7] Input: synaptics-rmi4 - handle duplicate/unknown PDT entries
From: David Heidelberg @ 2026-03-20 17:19 UTC (permalink / raw)
  To: Casey Connolly, Kaustabh Chakraborty, Dmitry Torokhov,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Jason A. Donenfeld, Matthias Schiffer, Vincent Huang
  Cc: linux-input, devicetree, linux-kernel, phone-devel
In-Reply-To: <f31d10ce-a3dc-4599-8201-8a78aa2fc65f@ixit.cz>

Pushed changes to

https://codeberg.org/sdm845/linux/commits/branch/b4/synaptics-rmi4

On 20/03/2026 18:12, David Heidelberg wrote:
> On 20/03/2026 18:03, Casey Connolly wrote:
>>
>>
>> On 20/03/2026 17:54, David Heidelberg wrote:
>>> On 20/03/2026 17:49, Casey Connolly wrote:
>>>> Hi David,
>>>>
>>>> Nice timing with the series, I hit an OOB access (found it when I
>>>> enabled UBSAN) with this patch the other day.
>>>>
>>>> The pdt_scan_state->pdts array should actually be of size
>>>> (RMI_PDT_MAX+1).
>>>>
>>>> Additionally, I think rmi_pdt_entry_is_valid() is missing a bounds check.
>>>>
>>>> Kind regards,
>>>
>>>
>>> Thanks a lot for catching this and for the detailed notes — that’s very
>>> helpful.
>>>
>>> Since you’re the original author of the commit, I’m completely fine with
>>> you taking over the b4 series if you’d prefer. Alternatively, if it’s
>>> easier, feel free to just send me a fixed patch and I can incorporate it.
>>>
>>
>> Uh sure, not sure this will apply cleanly I just edited inline it's a
>> 3-line delta. Also figured we can drop pdt_count since it's unused.
> 
> The pdt_count is used in
> 
> Input: synaptics-rmi4 - support fallback values for PDT descriptor bytes
> 
> thus should be moved there I assume, but can be dropped here.
> 
> David
> 
>>
>> ---
>>
>> diff --git a/drivers/input/rmi4/rmi_driver.c
>> b/drivers/input/rmi4/rmi_driver.c
>> index ccd9338a44dbe..c7d2f68e65487 100644
>> --- a/drivers/input/rmi4/rmi_driver.c
>> +++ b/drivers/input/rmi4/rmi_driver.c
>> @@ -494,12 +494,39 @@ static void rmi_driver_copy_pdt_to_fd(const struct
>> pdt_entry *pdt,
>>       fd->function_version = pdt->function_version;
>>   }
>>
>> +static bool rmi_pdt_entry_is_valid(struct rmi_device *rmi_dev,
>> +                   struct pdt_scan_state *state, u8 fn)
>> +{
>> +    if (fn > RMI_PDT_MAX)
>> +        return false;
>> +
>> +    switch (fn) {
>> +    case 0x01:
>> +    case 0x03:
>> +    case 0x11:
>> +    case 0x12:
>> +    case 0x30:
>> +    case 0x34:
>> +    case 0x3a:
>> +    case 0x54:
>> +    case 0x55:
>> +        if (state->pdts[fn] == true)
>> +            return false;
>> +        break;
>> +    default:
>> +        rmi_dbg(RMI_DEBUG_CORE, &rmi_dev->dev,
>> +            "PDT has unknown function number %#02x\n", fn);
>> +        return false;
>> +    }
>> +
>> +    state->pdts[fn] = true;
>> +    return true;
>> +}
>> +
>>   #define RMI_SCAN_CONTINUE    0
>>   #define RMI_SCAN_DONE        1
>>
>>   static int rmi_scan_pdt_page(struct rmi_device *rmi_dev,
>>                    int page,
>> -                 int *empty_pages,
>> +                 struct pdt_scan_state *state,
>>                    void *ctx,
>>                    int (*callback)(struct rmi_device *rmi_dev,
>>                            void *ctx,
>> @@ -522,6 +549,9 @@ static int rmi_scan_pdt_page(struct rmi_device *rmi_dev,
>>           if (RMI4_END_OF_PDT(pdt_entry.function_number))
>>               break;
>>
>> +        if (!rmi_pdt_entry_is_valid(rmi_dev, state, pdt_entry.function_number))
>> +            continue;
>> +
>>           retval = callback(rmi_dev, ctx, &pdt_entry);
>>           if (retval != RMI_SCAN_CONTINUE)
>>               return retval;
>> @@ -532,11 +562,11 @@ static int rmi_scan_pdt_page(struct rmi_device
>> *rmi_dev,
>>        * or more is found, stop scanning.
>>        */
>>       if (addr == pdt_start)
>> -        ++*empty_pages;
>> +        ++state->empty_pages;
>>       else
>> -        *empty_pages = 0;
>> +        state->empty_pages = 0;
>>
>> -    return (data->bootloader_mode || *empty_pages >= 2) ?
>> +    return (data->bootloader_mode || state->empty_pages >= 2) ?
>>                       RMI_SCAN_DONE : RMI_SCAN_CONTINUE;
>>   }
>>
>> @@ -545,11 +575,11 @@ int rmi_scan_pdt(struct rmi_device *rmi_dev, void
>> *ctx,
>>            void *ctx, const struct pdt_entry *entry))
>>   {
>>       int page;
>> -    int empty_pages = 0;
>> +    struct pdt_scan_state state = {0, {0}};
>>       int retval = RMI_SCAN_DONE;
>>
>>       for (page = 0; page <= RMI4_MAX_PAGE; page++) {
>> -        retval = rmi_scan_pdt_page(rmi_dev, page, &empty_pages,
>> +        retval = rmi_scan_pdt_page(rmi_dev, page, &state,
>>                          ctx, callback);
>>           if (retval != RMI_SCAN_CONTINUE)
>>               break;
>> diff --git a/drivers/input/rmi4/rmi_driver.h
>> b/drivers/input/rmi4/rmi_driver.h
>> index e84495caab151..a4ae2af93ce3a 100644
>> --- a/drivers/input/rmi4/rmi_driver.h
>> +++ b/drivers/input/rmi4/rmi_driver.h
>> @@ -46,6 +46,14 @@ struct pdt_entry {
>>       u8 function_number;
>>   };
>>
>> +#define RMI_PDT_MAX 0x55
>> +
>> +struct pdt_scan_state {
>> +    u8 empty_pages;
>> +    bool pdts[RMI_PDT_MAX + 1];
>> +};
>> +
>>   #define RMI_REG_DESC_PRESENSE_BITS    (32 * BITS_PER_BYTE)
>>   #define RMI_REG_DESC_SUBPACKET_BITS    (37 * BITS_PER_BYTE)
>>
>>
> 

-- 
David Heidelberg


^ permalink raw reply

* Re: [PATCH] Input: gpio-keys - add full support of EV_REL and EV_ABS
From: Dmitry Torokhov @ 2026-03-21  7:06 UTC (permalink / raw)
  To: Xiong Nandi
  Cc: linux-input, linux-kernel, Gatien Chevallier, Ingo Molnar,
	Thomas Gleixner, Marco Crivellari, Fabrice Gasnier
In-Reply-To: <20260320145217.9088-1-xndchn@gmail.com>

Hi Xiong,

On Fri, Mar 20, 2026 at 10:52:14PM +0800, Xiong Nandi wrote:
> gpio_keys_gpio_report_event() handled EV_ABS but silently ignored EV_REL,
> while the polled driver supports both.  Extend the interrupt-driven driver
> to fire an EV_REL event on button press.
> 
> For EV_ABS, use a shared atomic counter per (type, code) pair so that
> a zero-value reset is sent only when the last active button on an axis
> is released, avoiding premature axis resets when multiple buttons share
> the same axis code.
> 
> Add gpio_keys_set_abs_params() to call input_set_abs_params() at setup
> time, deriving the axis min/max from the configured button values.
> Without this the input subsystem reports unbounded axis ranges.

Could you please split this out? It looks like there are 2 or 3
logically separate changes.

Thanks.

-- 
Dmitry

^ permalink raw reply

* [PATCH] Input: goodix-berlin: report a resolution of 10 units/mm
From: Val Packett @ 2026-03-21  7:30 UTC (permalink / raw)
  To: Hans de Goede, Dmitry Torokhov, Henrik Rydberg
  Cc: Val Packett, Stanislav Zaikin, linux-input, phone-devel,
	~postmarketos/upstreaming, linux-kernel

Without a reported resolution, userspace was assuming 1 unit/mm which
is wildly wrong: a regular smartphone is clearly not 2.4 meters tall.
Most applications do not care much for this kind of raw mm value,
but Phosh's on-screen keyboard would accidentally trigger swipe-to-close
gestures due to misinterpreting small movements as huge ones.

Do what the older goodix.c driver does and set the resolution to 10
units/mm to make sure the numbers calculated by userspace are reasonable.

Signed-off-by: Val Packett <val@packett.cool>
---
 drivers/input/touchscreen/goodix_berlin_core.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/drivers/input/touchscreen/goodix_berlin_core.c b/drivers/input/touchscreen/goodix_berlin_core.c
index 83f28b870531..b0938a4f3fec 100644
--- a/drivers/input/touchscreen/goodix_berlin_core.c
+++ b/drivers/input/touchscreen/goodix_berlin_core.c
@@ -628,6 +628,14 @@ static int goodix_berlin_input_dev_config(struct goodix_berlin_core *cd,
 
 	touchscreen_parse_properties(cd->input_dev, true, &cd->props);
 
+	/*
+	 * The resolution of these touchscreens is about 10 units/mm, the actual
+	 * resolution does not matter much since we set INPUT_PROP_DIRECT.
+	 * Set it to 10 to ensure userspace isn't off by an order of magnitude.
+	 */
+	input_abs_set_res(cd->input_dev, ABS_MT_POSITION_X, 10);
+	input_abs_set_res(cd->input_dev, ABS_MT_POSITION_Y, 10);
+
 	error = input_mt_init_slots(cd->input_dev, GOODIX_BERLIN_MAX_TOUCH,
 				    INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
 	if (error)
-- 
2.53.0


^ permalink raw reply related

* [PATCH v2] HID: rakk: add support for Rakk Dasig X side buttons
From: Karl Cayme @ 2026-03-21 12:42 UTC (permalink / raw)
  To: jikos, bentiss; +Cc: linux-input, linux-kernel, linuxhid, Karl Cayme
In-Reply-To: <3284f00a-bea0-4141-a009-b367f527c466@cosmicgizmosystems.com>

The Rakk Dasig X gaming mouse has a faulty HID report descriptor that
declares USAGE_MAXIMUM=3 (buttons 1-3) while actually sending 5 button
bits (REPORT_COUNT=5). This causes the kernel to ignore side buttons
(buttons 4 and 5).

Fix by patching the descriptor to set USAGE_MAXIMUM=5 in the
report_fixup callback.

The mouse uses Telink vendor ID 0x248a with three product IDs for USB
direct (0xfb01), wireless dongle (0xfa02), and Bluetooth (0x8266)
connection modes. All three variants have the same bug at byte offset 17.

Suggested-by: Terry Junge <linuxhid@cosmicgizmosystems.com> 
Signed-off-by: Karl Cayme <kcayme@gmail.com>
---
Hi,

Thanks for the feedback. I updated the patch with your suggestion to
check PIDs.

Best,
Karl

v2 Changes:
  - included PID checking alongside descriptor size in report_fixup

 drivers/hid/Kconfig    |  9 +++++
 drivers/hid/Makefile   |  1 +
 drivers/hid/hid-ids.h  |  5 +++
 drivers/hid/hid-rakk.c | 75 ++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 90 insertions(+)
 create mode 100644 drivers/hid/hid-rakk.c

diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index c1d9f7c6a5f2..11c48cb1c6a6 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -755,6 +755,15 @@ config HID_MEGAWORLD_FF
 	Say Y here if you have a Mega World based game controller and want
 	to have force feedback support for it.
 
+config HID_RAKK
+	tristate "Rakk support"
+	help
+	  Support for Rakk gaming peripherals.
+
+	  Fixes the HID report descriptor of the Rakk Dasig X mouse,
+	  which declares USAGE_MAXIMUM=3 (buttons 1-3) while actually
+	  sending 5 button bits. This causes side buttons to be ignored.
+
 config HID_REDRAGON
 	tristate "Redragon keyboards"
 	default !EXPERT
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index e01838239ae6..7800613f5b2b 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -113,6 +113,7 @@ obj-$(CONFIG_HID_PLANTRONICS)	+= hid-plantronics.o
 obj-$(CONFIG_HID_PLAYSTATION)	+= hid-playstation.o
 obj-$(CONFIG_HID_PRIMAX)	+= hid-primax.o
 obj-$(CONFIG_HID_PXRC)		+= hid-pxrc.o
+obj-$(CONFIG_HID_RAKK)		+= hid-rakk.o
 obj-$(CONFIG_HID_RAPOO) += hid-rapoo.o
 obj-$(CONFIG_HID_RAZER)	+= hid-razer.o
 obj-$(CONFIG_HID_REDRAGON)	+= hid-redragon.o
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 3e299a30dcde..68fab837e8b3 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -1369,6 +1369,11 @@
 #define USB_DEVICE_ID_SYNAPTICS_ACER_SWITCH5_017	0x73f6
 #define USB_DEVICE_ID_SYNAPTICS_ACER_SWITCH5	0x81a7
 
+#define USB_VENDOR_ID_TELINK				0x248a
+#define USB_DEVICE_ID_TELINK_RAKK_DASIG_X		0xfb01
+#define USB_DEVICE_ID_TELINK_RAKK_DASIG_X_DONGLE	0xfa02
+#define USB_DEVICE_ID_TELINK_RAKK_DASIG_X_BT		0x8266
+
 #define USB_VENDOR_ID_TEXAS_INSTRUMENTS	0x2047
 #define USB_DEVICE_ID_TEXAS_INSTRUMENTS_LENOVO_YOGA	0x0855
 
diff --git a/drivers/hid/hid-rakk.c b/drivers/hid/hid-rakk.c
new file mode 100644
index 000000000000..c59ea47b8996
--- /dev/null
+++ b/drivers/hid/hid-rakk.c
@@ -0,0 +1,75 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *  HID driver for Rakk devices
+ *
+ *  Copyright (c) 2026 Karl Cayme
+ *
+ *  The Rakk Dasig X gaming mouse has a faulty HID report descriptor that
+ *  declares USAGE_MAXIMUM = 3 (buttons 1-3) while actually sending 5 button
+ *  bits (REPORT_COUNT = 5). This causes the kernel to ignore side buttons
+ *  (buttons 4 and 5). This driver fixes the descriptor so all 5 buttons
+ *  are properly recognized across 3 modes (wired, dongle, and Bluetooth).
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+#include "hid-ids.h"
+
+/*
+ * The faulty byte is at offset 17 in the report descriptor for all three
+ * connection modes (USB direct, wireless dongle, and Bluetooth).
+ *
+ * Bytes 16-17 are: 0x29 0x03 (USAGE_MAXIMUM = 3)
+ * The fix changes byte 17 to 0x05 (USAGE_MAXIMUM = 5).
+ *
+ * Original descriptor bytes 0-17:
+ *   05 01 09 02 a1 01 85 01 09 01 a1 00 05 09 19 01 29 03
+ *                                                       ^^
+ *   Should be 0x05 to declare 5 buttons instead of 3.
+ */
+#define RAKK_RDESC_USAGE_MAX_OFFSET	17
+#define RAKK_RDESC_USAGE_MAX_ORIG	0x03
+#define RAKK_RDESC_USAGE_MAX_FIXED	0x05
+#define RAKK_RDESC_USB_SIZE		193
+#define RAKK_RDESC_DONGLE_SIZE		150
+#define RAKK_RDESC_BT_SIZE		89
+
+static const __u8 *rakk_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+				     unsigned int *rsize)
+{
+	if (((*rsize == RAKK_RDESC_USB_SIZE &&
+	      hdev->product == USB_DEVICE_ID_TELINK_RAKK_DASIG_X) ||
+	     (*rsize == RAKK_RDESC_DONGLE_SIZE &&
+	      hdev->product == USB_DEVICE_ID_TELINK_RAKK_DASIG_X_DONGLE) ||
+	     (*rsize == RAKK_RDESC_BT_SIZE &&
+	      hdev->product == USB_DEVICE_ID_TELINK_RAKK_DASIG_X_BT)) &&
+	    rdesc[RAKK_RDESC_USAGE_MAX_OFFSET] == RAKK_RDESC_USAGE_MAX_ORIG) {
+		hid_info(hdev, "fixing Rakk Dasig X button count (3 -> 5)\n");
+		rdesc[RAKK_RDESC_USAGE_MAX_OFFSET] = RAKK_RDESC_USAGE_MAX_FIXED;
+	}
+
+	return rdesc;
+}
+
+static const struct hid_device_id rakk_devices[] = {
+	{ HID_USB_DEVICE(USB_VENDOR_ID_TELINK,
+			 USB_DEVICE_ID_TELINK_RAKK_DASIG_X) },
+	{ HID_USB_DEVICE(USB_VENDOR_ID_TELINK,
+			 USB_DEVICE_ID_TELINK_RAKK_DASIG_X_DONGLE) },
+	{ HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_TELINK,
+			       USB_DEVICE_ID_TELINK_RAKK_DASIG_X_BT) },
+	{ }
+};
+MODULE_DEVICE_TABLE(hid, rakk_devices);
+
+static struct hid_driver rakk_driver = {
+	.name = "rakk",
+	.id_table = rakk_devices,
+	.report_fixup = rakk_report_fixup,
+};
+module_hid_driver(rakk_driver);
+
+MODULE_DESCRIPTION("HID driver for Rakk Dasig X mouse - fix side button support");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Karl Cayme");
-- 
2.53.0


^ permalink raw reply related

* [PATCH] HID: drop 'default !EXPERT' from tristate symbols
From: Thomas Weißschuh @ 2026-03-21 13:15 UTC (permalink / raw)
  To: Jiri Kosina, Benjamin Tissoires
  Cc: linux-input, linux-kernel, Thomas Weißschuh

There is no reason to build random drivers for obscure hardware into the
core kernel by default.

The usages of 'default !EXPERT' for the HID_PICOLCD suboptions are kept,
as these make some sense, although they probably should use 'default y'.

Signed-off-by: Thomas Weißschuh <linux@weissschuh.net>
---
 drivers/hid/Kconfig | 13 -------------
 1 file changed, 13 deletions(-)

diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 10c12d8e6557..7b1d8d81bd65 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -107,7 +107,6 @@ menu "Special HID drivers"
 
 config HID_A4TECH
 	tristate "A4TECH mice"
-	default !EXPERT
 	help
 	Support for some A4TECH mice with two scroll wheels.
 
@@ -140,7 +139,6 @@ config HID_APPLE
 	tristate "Apple {i,Power,Mac}Books"
 	depends on LEDS_CLASS
 	depends on NEW_LEDS
-	default !EXPERT
 	help
 	Support for some Apple devices which less or more break
 	HID specification.
@@ -209,7 +207,6 @@ config HID_AUREAL
 
 config HID_BELKIN
 	tristate "Belkin Flip KVM and Wireless keyboard"
-	default !EXPERT
 	help
 	Support for Belkin Flip KVM and Wireless keyboard.
 
@@ -237,14 +234,12 @@ config HID_BIGBEN_FF
 
 config HID_CHERRY
 	tristate "Cherry Cymotion keyboard"
-	default !EXPERT
 	help
 	Support for Cherry Cymotion keyboard.
 
 config HID_CHICONY
 	tristate "Chicony devices"
 	depends on USB_HID
-	default !EXPERT
 	help
 	Support for Chicony Tactical pad and special keys on Chicony keyboards.
 
@@ -322,7 +317,6 @@ config HID_CREATIVE_SB0540
 
 config HID_CYPRESS
 	tristate "Cypress mouse and barcode readers"
-	default !EXPERT
 	help
 	Support for cypress mouse and barcode readers.
 
@@ -388,7 +382,6 @@ config HID_EVISION
 
 config HID_EZKEY
 	tristate "Ezkey BTC 8193 keyboard"
-	default !EXPERT
 	help
 	Support for Ezkey BTC 8193 keyboard.
 
@@ -564,7 +557,6 @@ config HID_ICADE
 
 config HID_ITE
 	tristate "ITE devices"
-	default !EXPERT
 	help
 	Support for ITE devices not fully compliant with HID standard.
 
@@ -585,7 +577,6 @@ config HID_TWINHAN
 
 config HID_KENSINGTON
 	tristate "Kensington Slimblade Trackball"
-	default !EXPERT
 	help
 	Support for Kensington Slimblade Trackball.
 
@@ -666,7 +657,6 @@ config HID_LOGITECH
 	depends on USB_HID
 	depends on LEDS_CLASS
 	depends on LEDS_CLASS_MULTICOLOR
-	default !EXPERT
 	help
 	Support for Logitech devices that are not fully compliant with HID standard.
 
@@ -781,20 +771,17 @@ config HID_MEGAWORLD_FF
 
 config HID_REDRAGON
 	tristate "Redragon keyboards"
-	default !EXPERT
 	help
     Support for Redragon keyboards that need fix-ups to work properly.
 
 config HID_MICROSOFT
 	tristate "Microsoft non-fully HID-compliant devices"
-	default !EXPERT
 	select INPUT_FF_MEMLESS
 	help
 	Support for Microsoft devices that are not fully compliant with HID standard.
 
 config HID_MONTEREY
 	tristate "Monterey Genius KB29E keyboard"
-	default !EXPERT
 	help
 	Support for Monterey Genius KB29E.
 

---
base-commit: 2bbe306cc6f970474cb2ad7d5332220e81119bbd
change-id: 20260321-hid-expert-87fcf609230a

Best regards,
-- 
Thomas Weißschuh <linux@weissschuh.net>


^ permalink raw reply related

* [PATCH 0/3] Input: adafruit-seesaw: use dev_err_probe and add IRQ support
From: charles.embedded @ 2026-03-21 20:24 UTC (permalink / raw)
  To: Anshul Dalal, Dmitry Torokhov
  Cc: Shuah Khan, Brigham Campbell, linux-input, linux-kernel,
	Charles Dias

From: Charles Dias <charlesdias.cd@outlook.com>

This series improves the Adafruit seesaw gamepad driver in two steps.

The first patch switches to using dev_err_probe() in seesaw_probe()
to improve error handling.

The second patch adds optional interrupt support for button events when
an IRQ is described in DTS. Joystick axes remain polled because the
default Adafruit seesaw gamepad firmware exposes button interrupts
through the GPIO module, while the joystick positions are read from ADC
channels.

When no IRQ is described in DTS, the driver continues to operate in
pure polling mode.

This series was validated on a BeaglePlay board with the Adafruit
Seesaw Gamepad, both with the interrupt enabled in DTS and without it.

Charles Dias (3):
  Input: adafruit-seesaw - switch to using dev_err_probe()
  Input: adafruit-seesaw - add interrupt support
  dt-bindings: input: adafruit-seesaw-gamepad: fix interrupt polarity

 .../input/adafruit,seesaw-gamepad.yaml        |  12 +-
 drivers/input/joystick/adafruit-seesaw.c      | 160 ++++++++++++------
 2 files changed, 119 insertions(+), 53 deletions(-)


^ permalink raw reply

* [PATCH 1/3] Input: adafruit-seesaw - switch to using dev_err_probe()
From: charles.embedded @ 2026-03-21 20:24 UTC (permalink / raw)
  To: Anshul Dalal, Dmitry Torokhov
  Cc: Shuah Khan, Brigham Campbell, linux-input, linux-kernel,
	Charles Dias
In-Reply-To: <20260321202446.724277-1-charles.embedded@gmail.com>

From: Charles Dias <charlesdias.cd@outlook.com>

Use dev_err_probe() instead of dev_err() in seesaw_probe function
to improve error handling.

Signed-off-by: Charles Dias <charlesdias.cd@outlook.com>
---
 drivers/input/joystick/adafruit-seesaw.c | 19 ++++++-------------
 1 file changed, 6 insertions(+), 13 deletions(-)

diff --git a/drivers/input/joystick/adafruit-seesaw.c b/drivers/input/joystick/adafruit-seesaw.c
index c248c15b849d..177b42446e9b 100644
--- a/drivers/input/joystick/adafruit-seesaw.c
+++ b/drivers/input/joystick/adafruit-seesaw.c
@@ -277,17 +277,12 @@ static int seesaw_probe(struct i2c_client *client)
 			     SEESAW_JOYSTICK_FUZZ, SEESAW_JOYSTICK_FLAT);
 
 	err = sparse_keymap_setup(seesaw->input_dev, seesaw_buttons_new, NULL);
-	if (err) {
-		dev_err(&client->dev,
-			"failed to set up input device keymap: %d\n", err);
-		return err;
-	}
+	if (err)
+		return dev_err_probe(&client->dev, err, "failed to set up input device keymap\n");
 
 	err = input_setup_polling(seesaw->input_dev, seesaw_poll);
-	if (err) {
-		dev_err(&client->dev, "failed to set up polling: %d\n", err);
-		return err;
-	}
+	if (err)
+		return dev_err_probe(&client->dev, err, "failed to set up polling\n");
 
 	input_set_poll_interval(seesaw->input_dev,
 				SEESAW_GAMEPAD_POLL_INTERVAL_MS);
@@ -295,10 +290,8 @@ static int seesaw_probe(struct i2c_client *client)
 	input_set_min_poll_interval(seesaw->input_dev, SEESAW_GAMEPAD_POLL_MIN);
 
 	err = input_register_device(seesaw->input_dev);
-	if (err) {
-		dev_err(&client->dev, "failed to register joystick: %d\n", err);
-		return err;
-	}
+	if (err)
+		return dev_err_probe(&client->dev, err, "failed to register joystick\n");
 
 	return 0;
 }

^ permalink raw reply related


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