Linux Input/HID development
 help / color / mirror / Atom feed
* [PATCH 4/7] Input: cros_ec_keyb - use BIT() macro instead of open-coding shifts
From: Dmitry Torokhov @ 2026-02-22  0:37 UTC (permalink / raw)
  To: Fabio Baltieri, Benson Leung
  Cc: Guenter Roeck, Simon Glass, Tzung-Bi Shih, linux-input,
	chrome-platform, linux-kernel
In-Reply-To: <20260222003717.471977-1-dmitry.torokhov@gmail.com>

Using the macro clarifies the code.

Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
---
 drivers/input/keyboard/cros_ec_keyb.c | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c
index 2f78a19521ab..fabbdbc22785 100644
--- a/drivers/input/keyboard/cros_ec_keyb.c
+++ b/drivers/input/keyboard/cros_ec_keyb.c
@@ -277,8 +277,8 @@ static void cros_ec_keyb_process(struct cros_ec_keyb *ckdev, u8 *kb_state, int l
 
 	for (col = 0; col < ckdev->cols; col++) {
 		for (row = 0; row < ckdev->rows; row++) {
-			new_state = kb_state[col] & (1 << row);
-			old_state = ckdev->old_kb_state[col] & (1 << row);
+			new_state = kb_state[col] & BIT(row);
+			old_state = ckdev->old_kb_state[col] & BIT(row);
 
 			if (new_state == old_state)
 				continue;
@@ -411,7 +411,7 @@ static void cros_ec_keyb_compute_valid_keys(struct cros_ec_keyb *ckdev)
 		for (row = 0; row < ckdev->rows; row++) {
 			code = keymap[MATRIX_SCAN_CODE(row, col, row_shift)];
 			if (code && (code != KEY_BATTERY))
-				ckdev->valid_keys[col] |= 1 << row;
+				ckdev->valid_keys[col] |= BIT(row);
 		}
 		dev_dbg(ckdev->dev, "valid_keys[%02d] = 0x%02x\n",
 			col, ckdev->valid_keys[col]);
@@ -780,8 +780,7 @@ static int cros_ec_keyb_register_matrix(struct cros_ec_keyb *ckdev)
 	idev->dev.parent = dev;
 	idev->setkeycode = cros_ec_keyb_setkeycode;
 
-	ckdev->ghost_filter = device_property_read_bool(dev,
-					"google,needs-ghost-filter");
+	ckdev->ghost_filter = device_property_read_bool(dev, "google,needs-ghost-filter");
 
 	err = matrix_keypad_build_keymap(NULL, ckdev->rows * 2, ckdev->cols,
 					 NULL, idev);
-- 
2.53.0.345.g96ddfc5eaa-goog


^ permalink raw reply related

* [PATCH 5/7] Input: cros_ec_keyb - simplify cros_ec_keyb_work()
From: Dmitry Torokhov @ 2026-02-22  0:37 UTC (permalink / raw)
  To: Fabio Baltieri, Benson Leung
  Cc: Guenter Roeck, Simon Glass, Tzung-Bi Shih, linux-input,
	chrome-platform, linux-kernel
In-Reply-To: <20260222003717.471977-1-dmitry.torokhov@gmail.com>

Introduce temporaries for event_data pointer and event_size to simplify
the code a bit.

In cros_ec_keyb_compute_valid_keys() explicitly compare with
KEY_RESERVED to make the intent of the comparison more clear.

Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
---
 drivers/input/keyboard/cros_ec_keyb.c | 28 +++++++++++++--------------
 1 file changed, 14 insertions(+), 14 deletions(-)

diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c
index fabbdbc22785..1b4ee30e1998 100644
--- a/drivers/input/keyboard/cros_ec_keyb.c
+++ b/drivers/input/keyboard/cros_ec_keyb.c
@@ -330,8 +330,10 @@ static int cros_ec_keyb_work(struct notifier_block *nb,
 {
 	struct cros_ec_keyb *ckdev = container_of(nb, struct cros_ec_keyb,
 						  notifier);
-	u32 val;
+	struct ec_response_get_next_event_v3 *event_data;
+	unsigned int event_size;
 	unsigned int ev_type;
+	u32 val;
 
 	/*
 	 * If not wake enabled, discard key state changes during
@@ -341,32 +343,32 @@ static int cros_ec_keyb_work(struct notifier_block *nb,
 	if (queued_during_suspend && !device_may_wakeup(ckdev->dev))
 		return NOTIFY_OK;
 
-	switch (ckdev->ec->event_data.event_type) {
+	event_data = &ckdev->ec->event_data;
+	event_size = ckdev->ec->event_size;
+
+	switch (event_data->event_type) {
 	case EC_MKBP_EVENT_KEY_MATRIX:
 		pm_wakeup_event(ckdev->dev, 0);
 
 		if (!ckdev->idev) {
-			dev_warn_once(ckdev->dev,
-				      "Unexpected key matrix event\n");
+			dev_warn_once(ckdev->dev, "Unexpected key matrix event\n");
 			return NOTIFY_OK;
 		}
 
-		if (ckdev->ec->event_size != ckdev->cols) {
+		if (event_size != ckdev->cols) {
 			dev_err(ckdev->dev,
 				"Discarded key matrix event, unexpected length: %d != %d\n",
 				ckdev->ec->event_size, ckdev->cols);
 			return NOTIFY_OK;
 		}
 
-		cros_ec_keyb_process(ckdev,
-				     ckdev->ec->event_data.data.key_matrix,
-				     ckdev->ec->event_size);
+		cros_ec_keyb_process(ckdev, event_data->data.key_matrix, event_size);
 		break;
 
 	case EC_MKBP_EVENT_SYSRQ:
 		pm_wakeup_event(ckdev->dev, 0);
 
-		val = get_unaligned_le32(&ckdev->ec->event_data.data.sysrq);
+		val = get_unaligned_le32(&event_data->data.sysrq);
 		dev_dbg(ckdev->dev, "sysrq code from EC: %#x\n", val);
 		handle_sysrq(val);
 		break;
@@ -376,12 +378,10 @@ static int cros_ec_keyb_work(struct notifier_block *nb,
 		pm_wakeup_event(ckdev->dev, 0);
 
 		if (ckdev->ec->event_data.event_type == EC_MKBP_EVENT_BUTTON) {
-			val = get_unaligned_le32(
-					&ckdev->ec->event_data.data.buttons);
+			val = get_unaligned_le32(&event_data->data.buttons);
 			ev_type = EV_KEY;
 		} else {
-			val = get_unaligned_le32(
-					&ckdev->ec->event_data.data.switches);
+			val = get_unaligned_le32(&event_data->data.switches);
 			ev_type = EV_SW;
 		}
 		cros_ec_keyb_report_bs(ckdev, ev_type, val);
@@ -410,7 +410,7 @@ static void cros_ec_keyb_compute_valid_keys(struct cros_ec_keyb *ckdev)
 	for (col = 0; col < ckdev->cols; col++) {
 		for (row = 0; row < ckdev->rows; row++) {
 			code = keymap[MATRIX_SCAN_CODE(row, col, row_shift)];
-			if (code && (code != KEY_BATTERY))
+			if (code != KEY_RESERVED && code != KEY_BATTERY)
 				ckdev->valid_keys[col] |= BIT(row);
 		}
 		dev_dbg(ckdev->dev, "valid_keys[%02d] = 0x%02x\n",
-- 
2.53.0.345.g96ddfc5eaa-goog


^ permalink raw reply related

* [PATCH 7/7] Input: cros_ec_keyb - factor out column processing
From: Dmitry Torokhov @ 2026-02-22  0:37 UTC (permalink / raw)
  To: Fabio Baltieri, Benson Leung
  Cc: Guenter Roeck, Simon Glass, Tzung-Bi Shih, linux-input,
	chrome-platform, linux-kernel
In-Reply-To: <20260222003717.471977-1-dmitry.torokhov@gmail.com>

Factor out column processing and eagerly skip processing columns that do
not have any changes in them.

Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
---
 drivers/input/keyboard/cros_ec_keyb.c | 47 +++++++++++++++------------
 1 file changed, 27 insertions(+), 20 deletions(-)

diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c
index 02176aee0530..8cf9129f6198 100644
--- a/drivers/input/keyboard/cros_ec_keyb.c
+++ b/drivers/input/keyboard/cros_ec_keyb.c
@@ -254,6 +254,26 @@ static void cros_ec_keyb_process_key_fn_map(struct cros_ec_keyb *ckdev,
 	input_report_key(idev, code, state);
 }
 
+static void cros_ec_keyy_process_col(struct cros_ec_keyb *ckdev, int col,
+				     u8 col_state, u8 changed)
+{
+	for (int row = 0; row < ckdev->rows; row++) {
+		if (changed & BIT(row)) {
+			u8 key_state = col_state & BIT(row);
+
+			dev_dbg(ckdev->dev, "changed: [r%d c%d]: byte %02x\n",
+				row, col, key_state);
+
+			if (ckdev->has_fn_map)
+				cros_ec_keyb_process_key_fn_map(ckdev, row, col,
+								key_state);
+			else
+				cros_ec_keyb_process_key_plain(ckdev, row, col,
+							       key_state);
+		}
+	}
+}
+
 /*
  * Compares the new keyboard state to the old one and produces key
  * press/release events accordingly.  The keyboard state is one byte
@@ -261,10 +281,6 @@ static void cros_ec_keyb_process_key_fn_map(struct cros_ec_keyb *ckdev,
  */
 static void cros_ec_keyb_process(struct cros_ec_keyb *ckdev, u8 *kb_state, int len)
 {
-	int col, row;
-	int new_state;
-	int old_state;
-
 	if (ckdev->ghost_filter && cros_ec_keyb_has_ghosting(ckdev, kb_state)) {
 		/*
 		 * Simple-minded solution: ignore this state. The obvious
@@ -275,24 +291,15 @@ static void cros_ec_keyb_process(struct cros_ec_keyb *ckdev, u8 *kb_state, int l
 		return;
 	}
 
-	for (col = 0; col < ckdev->cols; col++) {
-		for (row = 0; row < ckdev->rows; row++) {
-			new_state = kb_state[col] & BIT(row);
-			old_state = ckdev->old_kb_state[col] & BIT(row);
-
-			if (new_state == old_state)
-				continue;
-
-			dev_dbg(ckdev->dev, "changed: [r%d c%d]: byte %02x\n",
-				row, col, new_state);
+	for (int col = 0; col < ckdev->cols; col++) {
+		u8 changed = kb_state[col] ^ ckdev->old_kb_state[col];
 
-			if (ckdev->has_fn_map)
-				cros_ec_keyb_process_key_fn_map(ckdev, row, col, new_state);
-			else
-				cros_ec_keyb_process_key_plain(ckdev, row, col, new_state);
-		}
-		ckdev->old_kb_state[col] = kb_state[col];
+		if (changed)
+			cros_ec_keyy_process_col(ckdev, col, kb_state[col],
+						 changed);
 	}
+
+	memcpy(ckdev->old_kb_state, kb_state, sizeof(ckdev->old_kb_state));
 	input_sync(ckdev->idev);
 }
 
-- 
2.53.0.345.g96ddfc5eaa-goog


^ permalink raw reply related

* [PATCH 6/7] Input: cros_ec_keyb - do not allocate keyboard state separately
From: Dmitry Torokhov @ 2026-02-22  0:37 UTC (permalink / raw)
  To: Fabio Baltieri, Benson Leung
  Cc: Guenter Roeck, Simon Glass, Tzung-Bi Shih, linux-input,
	chrome-platform, linux-kernel
In-Reply-To: <20260222003717.471977-1-dmitry.torokhov@gmail.com>

Now that we know the upper bound for the number of columnts, and know
that it is pretty small, there is no point in allocating it separately.
We are wasting more memory tracking the allocations.

Embed valid_keys and old_kb_state directly into cros_ec_keyb structure.

Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
---
 drivers/input/keyboard/cros_ec_keyb.c | 12 ++----------
 1 file changed, 2 insertions(+), 10 deletions(-)

diff --git a/drivers/input/keyboard/cros_ec_keyb.c b/drivers/input/keyboard/cros_ec_keyb.c
index 1b4ee30e1998..02176aee0530 100644
--- a/drivers/input/keyboard/cros_ec_keyb.c
+++ b/drivers/input/keyboard/cros_ec_keyb.c
@@ -59,8 +59,8 @@ struct cros_ec_keyb {
 	unsigned int cols;
 	int row_shift;
 	bool ghost_filter;
-	u8 *valid_keys;
-	u8 *old_kb_state;
+	u8 valid_keys[CROS_EC_KEYBOARD_COLS_MAX];
+	u8 old_kb_state[CROS_EC_KEYBOARD_COLS_MAX];
 
 	struct device *dev;
 	struct cros_ec_device *ec;
@@ -750,14 +750,6 @@ static int cros_ec_keyb_register_matrix(struct cros_ec_keyb *ckdev)
 		return -EINVAL;
 	}
 
-	ckdev->valid_keys = devm_kzalloc(dev, ckdev->cols, GFP_KERNEL);
-	if (!ckdev->valid_keys)
-		return -ENOMEM;
-
-	ckdev->old_kb_state = devm_kzalloc(dev, ckdev->cols, GFP_KERNEL);
-	if (!ckdev->old_kb_state)
-		return -ENOMEM;
-
 	/*
 	 * We call the keyboard matrix 'input0'. Allocate phys before input
 	 * dev, to ensure correct tear-down ordering.
-- 
2.53.0.345.g96ddfc5eaa-goog


^ permalink raw reply related

* Re: [PATCH v7 2/2] Input: cros_ec_keyb - add function key support
From: Dmitry Torokhov @ 2026-02-22  0:39 UTC (permalink / raw)
  To: Fabio Baltieri
  Cc: Benson Leung, Guenter Roeck, Tzung-Bi Shih, Simon Glass,
	linux-input, chrome-platform, linux-kernel
In-Reply-To: <20260211173421.1206478-3-fabiobaltieri@chromium.org>

Hi Fabio,

On Wed, Feb 11, 2026 at 05:34:21PM +0000, Fabio Baltieri wrote:
>  
> +/* Returns true if there is a KEY_FN code defined in the normal keymap */
> +static bool cros_ec_keyb_has_fn_key(struct cros_ec_keyb *ckdev)
> +{
> +	struct input_dev *idev = ckdev->idev;
> +	const unsigned short *keycodes = idev->keycode;
> +
> +	for (int row = 0; row < ckdev->rows; row++) {
> +		for (int col = 0; col < ckdev->cols; col++) {
> +			int pos = MATRIX_SCAN_CODE(row, col, ckdev->row_shift);
> +
> +			if (keycodes[pos] == KEY_FN)
> +				return true;
> +		}

We can simply scan the bottom half of the map linearly, I think this is
simpler.

> +	}
> +
> +	return false;
> +}
> +
> +/*
> + * Returns true if there is a KEY_FN defined and at least one key in the fn
> + * layer keymap
> + */
> +static bool cros_ec_keyb_has_fn_map(struct cros_ec_keyb *ckdev)
> +{
> +	if (!cros_ec_keyb_has_fn_key(ckdev))
> +		return false;
> +
> +	for (int row = 0; row < ckdev->rows; row++) {
> +		for (int col = 0; col < ckdev->cols; col++) {
> +			if (cros_ec_keyb_fn_code(ckdev, row, col, NULL) != 0)
> +				return true;

Same here.

I made a bunch of changes to your patch and then a bunch of changes to
the driver in general. Please take a look.

Thanks.

-- 
Dmitry

^ permalink raw reply

* Re: [PATCH 1/7] Input: export input_default_setkeycode
From: Tzung-Bi Shih @ 2026-02-22 10:46 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Fabio Baltieri, Benson Leung, Guenter Roeck, Simon Glass,
	linux-input, chrome-platform, linux-kernel
In-Reply-To: <20260222003717.471977-1-dmitry.torokhov@gmail.com>

On Sat, Feb 21, 2026 at 04:37:09PM -0800, Dmitry Torokhov wrote:
> From: Fabio Baltieri <fabiobaltieri@chromium.org>
> 
> Export input_default_setkeycode so that a driver can set a custom
> setkeycode handler to take some driver specific action but still call
> the default handler at some point.
> 
> Signed-off-by: Fabio Baltieri <fabiobaltieri@chromium.org>
> Link: https://patch.msgid.link/20260211173421.1206478-2-fabiobaltieri@chromium.org
> Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>

Reviewed-by: Tzung-Bi Shih <tzungbi@kernel.org>

^ permalink raw reply

* Re: [PATCH 2/7] Input: cros_ec_keyb - add function key support
From: Tzung-Bi Shih @ 2026-02-22 10:46 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Fabio Baltieri, Benson Leung, Guenter Roeck, Simon Glass,
	linux-input, chrome-platform, linux-kernel
In-Reply-To: <20260222003717.471977-2-dmitry.torokhov@gmail.com>

On Sat, Feb 21, 2026 at 04:37:10PM -0800, Dmitry Torokhov wrote:
> From: Fabio Baltieri <fabiobaltieri@chromium.org>
> 
> Add support for handling an Fn button and sending separate keycodes for
> a subset of keys in the matrix defined in the upper half of the keymap.
> 
> Signed-off-by: Fabio Baltieri <fabiobaltieri@chromium.org>
> Reviewed-by: Simon Glass <sjg@chromium.org>
> Link: https://patch.msgid.link/20260211173421.1206478-3-fabiobaltieri@chromium.org
> Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>

The patch looks good to me, just a few minor nits below.  I don't insist on
these being fixed for this patch to be merged,

Reviewed-by: Tzung-Bi Shih <tzungbi@kernel.org>

> +static void cros_ec_keyb_process_key_fn_map(struct cros_ec_keyb *ckdev,
> +					    int row, int col, bool state)
> +{
> +	struct input_dev *idev = ckdev->idev;
> +	const unsigned short *keycodes = idev->keycode;
> +	unsigned int pos, fn_pos;
> +	unsigned int code, fn_code;

Nit: does declaring `code` and `fn_code` as unsigned short make more sense?
Or they can be declared in the same line.

> +
> +	pos = MATRIX_SCAN_CODE(row, col, ckdev->row_shift);
> +	code = keycodes[pos];
> +
> +	if (code == KEY_FN) {
> +		ckdev->fn_active = state;
> +		if (state) {
> +			ckdev->fn_combo_active = false;
> +		} else if (!ckdev->fn_combo_active) {
> +			/*
> +			 * Send both Fn press and release events if nothing
> +			 * else has been pressed together with Fn.
> +			 */
> +			cros_ec_emit_fn_key(idev, pos);
> +		}
> +		return;
> +	}
> +
> +	fn_pos = MATRIX_SCAN_CODE(row + ckdev->rows, col, ckdev->row_shift);
> +	fn_code = keycodes[fn_pos];
> +
> +	if (state) {
> +		if (ckdev->fn_active) {
> +			ckdev->fn_combo_active = true;
> +			if (!fn_code)
> +				return; /* Discard if no Fn mapping exists */
> +
> +			code = fn_code;
> +			pos = fn_pos;

Nit: assigning `pos` before `code` makes it neater.

> +		}
> +	} else {
> +		/*
> +		 * If the Fn-remapped code is currently pressed, release it.
> +		 * Otherwise, release the standard code (if it was pressed).
> +		 */
> +		if (fn_code && test_bit(fn_code, idev->key)) {
> +			code = fn_code;
> +			pos = fn_pos;

Nit: same here.

^ permalink raw reply

* Re: [PATCH 3/7] Input: cros_ec_keyb - use u8 instead of uint8_t
From: Tzung-Bi Shih @ 2026-02-22 10:47 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Fabio Baltieri, Benson Leung, Guenter Roeck, Simon Glass,
	linux-input, chrome-platform, linux-kernel
In-Reply-To: <20260222003717.471977-3-dmitry.torokhov@gmail.com>

On Sat, Feb 21, 2026 at 04:37:11PM -0800, Dmitry Torokhov wrote:
> In the kernel u8/u16/u32 are preferred to the uint*_t variants.
> 
> Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>

Reviewed-by: Tzung-Bi Shih <tzungbi@kernel.org>

^ permalink raw reply

* Re: [PATCH 4/7] Input: cros_ec_keyb - use BIT() macro instead of open-coding shifts
From: Tzung-Bi Shih @ 2026-02-22 10:47 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Fabio Baltieri, Benson Leung, Guenter Roeck, Simon Glass,
	linux-input, chrome-platform, linux-kernel
In-Reply-To: <20260222003717.471977-4-dmitry.torokhov@gmail.com>

On Sat, Feb 21, 2026 at 04:37:12PM -0800, Dmitry Torokhov wrote:
> Using the macro clarifies the code.
> 
> Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>

Reviewed-by: Tzung-Bi Shih <tzungbi@kernel.org>

> @@ -780,8 +780,7 @@ static int cros_ec_keyb_register_matrix(struct cros_ec_keyb *ckdev)
>  	idev->dev.parent = dev;
>  	idev->setkeycode = cros_ec_keyb_setkeycode;
>  
> -	ckdev->ghost_filter = device_property_read_bool(dev,
> -					"google,needs-ghost-filter");
> +	ckdev->ghost_filter = device_property_read_bool(dev, "google,needs-ghost-filter");

This change seems unrelated to the patch, but it's fine as-is.  I don't think
it requires a separate commit.

^ permalink raw reply

* Re: [PATCH 5/7] Input: cros_ec_keyb - simplify cros_ec_keyb_work()
From: Tzung-Bi Shih @ 2026-02-22 10:47 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Fabio Baltieri, Benson Leung, Guenter Roeck, Simon Glass,
	linux-input, chrome-platform, linux-kernel
In-Reply-To: <20260222003717.471977-5-dmitry.torokhov@gmail.com>

On Sat, Feb 21, 2026 at 04:37:13PM -0800, Dmitry Torokhov wrote:
> @@ -376,12 +378,10 @@ static int cros_ec_keyb_work(struct notifier_block *nb,
>  		pm_wakeup_event(ckdev->dev, 0);
>  
>  		if (ckdev->ec->event_data.event_type == EC_MKBP_EVENT_BUTTON) {

This can also be simplified: `event_data->event_type`.

^ permalink raw reply

* Re: [PATCH 6/7] Input: cros_ec_keyb - do not allocate keyboard state separately
From: Tzung-Bi Shih @ 2026-02-22 10:48 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Fabio Baltieri, Benson Leung, Guenter Roeck, Simon Glass,
	linux-input, chrome-platform, linux-kernel
In-Reply-To: <20260222003717.471977-6-dmitry.torokhov@gmail.com>

On Sat, Feb 21, 2026 at 04:37:14PM -0800, Dmitry Torokhov wrote:
> Now that we know the upper bound for the number of columnts, and know
> that it is pretty small, there is no point in allocating it separately.
> We are wasting more memory tracking the allocations.
> 
> Embed valid_keys and old_kb_state directly into cros_ec_keyb structure.
> 
> Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>

Reviewed-by: Tzung-Bi Shih <tzungbi@kernel.org>

^ permalink raw reply

* Re: [PATCH 7/7] Input: cros_ec_keyb - factor out column processing
From: Tzung-Bi Shih @ 2026-02-22 10:48 UTC (permalink / raw)
  To: Dmitry Torokhov
  Cc: Fabio Baltieri, Benson Leung, Guenter Roeck, Simon Glass,
	linux-input, chrome-platform, linux-kernel
In-Reply-To: <20260222003717.471977-7-dmitry.torokhov@gmail.com>

On Sat, Feb 21, 2026 at 04:37:15PM -0800, Dmitry Torokhov wrote:
> +static void cros_ec_keyy_process_col(struct cros_ec_keyb *ckdev, int col,
> +				     u8 col_state, u8 changed)

What does the second 'y' of keyy stand for?

> +{
> +	for (int row = 0; row < ckdev->rows; row++) {
> +		if (changed & BIT(row)) {

Maybe

    if ((changed & BIT(row)) == 0)
        continue;
or
    if (~changed & BIT(row))
        continue;
or
    if (!test_bit(row, &changed))
        continue;

to save an indent level.

^ permalink raw reply

* [6.18.] ThinkPad T14 Gen 2 AMD (LEN2073) - Synaptics touchpad remains PS/2, intertouch=1 ineffective, lost sync events
From: Rácz Máté @ 2026-02-22 18:37 UTC (permalink / raw)
  To: linux-input

Hello,

I am reporting a possible missing DMI quirk or ACPI limitation on:
Lenovo ThinkPad T14 Gen 2 (AMD) Ryzen 5 Pro 5650U
Touchpad PNP ID: LEN2073 (PNP0f13)

Kernel:
6.18.12-200.fc43.x86_64

Distribution:
Fedora 43 (stock kernel)

BIOS version:
R1MET62W (1.32 )

During boot I receive the following message:

  psmouse serio1: synaptics: Your touchpad (PNP: LEN2073 PNP0f13)
  says it can support a different bus. If i2c-hid and hid-rmi are not
  used, you might want to try setting psmouse.synaptics_intertouch=1
  and report this to linux-input@vger.kernel.org.

I tested with:

  psmouse.synaptics_intertouch=1

However, the device remains in PS/2 mode:

  SynPS/2 Synaptics TouchPad
  TPPS/2 Elan TrackPoint

No i2c-hid or hid-rmi device is created.
The system has functional I2C controllers, but the touchpad does not
enumerate as an I2C HID device.

Additionally, I am observing repeated:

  psmouse serio1: TouchPad ... lost sync at byte 1

events, which temporarily freeze the touchpad.

I have observed the same behavior on 6.18.9 and 6.18.10 as well.
The machine is relatively new to me, so I cannot confirm behavior on
older major kernel versions.

ACPI tables do not appear to expose LEN2073 as an I2C device.

Is this a missing DMI quirk, or is the platform firmware not exposing
the device via I2C on this model?

I can provide full dmesg and acpidump output if required.

Best regards,
Mate Racz

^ permalink raw reply

* [PATCH v6 0/2] Initial work for Rust abstraction for HID device driver development
From: Rahul Rameshbabu @ 2026-02-22 21:56 UTC (permalink / raw)
  To: linux-input, linux-kernel, rust-for-linux
  Cc: a.hindborg, alex.gaynor, aliceryhl, benjamin.tissoires,
	benno.lossin, bjorn3_gh, boqun.feng, dakr, db48x, gary, jikos,
	ojeda, peter.hutterer, tmgross, Rahul Rameshbabu

Hi folks,

It has been a while since I last posted an updated. Had some big life changes
that have kept me from working on this more actively, so I have not been able to
get on top of this till now. Hoping my pace going forward should be a lot
faster. Using the conversation from LPC as well as other refactors needed, I
have rebased the series on top of rust-next. My next steps are to get RazerBlade
controls working with a Rust HID driver.

Link: https://youtu.be/c8JAZm-QinY
Link: https://lore.kernel.org/rust-for-linux/wjfjzjc626n55zvhksiyldobwubr2imbvfavqej333lvnka2wn@r4zfcjqtanvu/
Link: https://lore.kernel.org/rust-for-linux/175810473311.3076338.14309101339951114135.b4-ty@kernel.org/

Thanks,
Rahul Rameshbabu

Rahul Rameshbabu (2):
  rust: core abstractions for HID drivers
  rust: hid: Glorious PC Gaming Race Model O and O- mice reference
    driver

 MAINTAINERS                           |  14 +
 drivers/hid/Kconfig                   |   2 +
 drivers/hid/Makefile                  |   2 +
 drivers/hid/hid-glorious.c            |   2 +
 drivers/hid/hid_glorious_rust.rs      |  60 +++
 drivers/hid/rust/Kconfig              |  28 ++
 drivers/hid/rust/Makefile             |   6 +
 drivers/hid/rust/hid_glorious_rust.rs |  60 +++
 rust/bindings/bindings_helper.h       |   3 +
 rust/kernel/hid.rs                    | 530 ++++++++++++++++++++++++++
 rust/kernel/lib.rs                    |   2 +
 11 files changed, 709 insertions(+)
 create mode 100644 drivers/hid/hid_glorious_rust.rs
 create mode 100644 drivers/hid/rust/Kconfig
 create mode 100644 drivers/hid/rust/Makefile
 create mode 100644 drivers/hid/rust/hid_glorious_rust.rs
 create mode 100644 rust/kernel/hid.rs

-- 
2.52.0



^ permalink raw reply

* [PATCH v6 1/2] rust: core abstractions for HID drivers
From: Rahul Rameshbabu @ 2026-02-22 21:56 UTC (permalink / raw)
  To: linux-input, linux-kernel, rust-for-linux
  Cc: a.hindborg, alex.gaynor, aliceryhl, benjamin.tissoires,
	benno.lossin, bjorn3_gh, boqun.feng, dakr, db48x, gary, jikos,
	ojeda, peter.hutterer, tmgross, Rahul Rameshbabu
In-Reply-To: <20260222215611.79760-1-sergeantsagara@protonmail.com>

These abstractions enable the development of HID drivers in Rust by binding
with the HID core C API. They provide Rust types that map to the
equivalents in C. In this initial draft, only hid_device and hid_device_id
are provided direct Rust type equivalents. hid_driver is specially wrapped
with a custom Driver type. The module_hid_driver! macro provides analogous
functionality to its C equivalent. Only the .report_fixup callback is
binded to Rust so far.

Future work for these abstractions would include more bindings for common
HID-related types, such as hid_field, hid_report_enum, and hid_report as
well as more bus callbacks. Providing Rust equivalents to useful core HID
functions will also be necessary for HID driver development in Rust.

Signed-off-by: Rahul Rameshbabu <sergeantsagara@protonmail.com>
---

Notes:
    Changelog:
    
        v5->v6:
          * Converted From<u16> for Group to TryFrom<u16> to properly handle
            error case
          * Renamed into method for Group to into_u16 to not conflate with the
            From trait
          * Refactored new upstream changes to RegistrationOps
          * Implemented DriverLayout trait for hid::Adapter<T>
        v4->v5:
          * Add rust/ to drivers/hid/Makefile
          * Implement RawDeviceIdIndex trait
        v3->v4:
          * Removed specifying tree in MAINTAINERS file since that is up for
            debate
          * Minor rebase cleanup
          * Moved driver logic under drivers/hid/rust
        v2->v3:
          * Implemented AlwaysRefCounted trait using embedded struct device's
            reference counts instead of the separate reference counter in struct
            hid_device
          * Used &raw mut as appropriate
          * Binded include/linux/device.h for get_device and put_device
          * Cleaned up various comment related formatting
          * Minified dev_err! format string
          * Updated Group enum to be repr(u16)
          * Implemented From<u16> trait for Group
          * Added TODO comment when const_trait_impl stabilizes
          * Made group getter functions return a Group variant instead of a raw
            number
          * Made sure example code builds
        v1->v2:
          * Binded drivers/hid/hid-ids.h for use in Rust drivers
          * Remove pre-emptive referencing of a C HID driver instance before
            it is fully initialized in the driver registration path
          * Moved static getters to generic Device trait implementation, so
            they can be used by all device::DeviceContext
          * Use core macros for supporting DeviceContext transitions
          * Implemented the AlwaysRefCounted and AsRef traits
          * Make use for dev_err! as appropriate
        RFC->v1:
          * Use Danilo's core infrastructure
          * Account for HID device groups
          * Remove probe and remove callbacks
          * Implement report_fixup support
          * Properly comment code including SAFETY comments

 MAINTAINERS                     |   8 +
 drivers/hid/Kconfig             |   2 +
 drivers/hid/Makefile            |   2 +
 drivers/hid/rust/Kconfig        |  12 +
 rust/bindings/bindings_helper.h |   3 +
 rust/kernel/hid.rs              | 530 ++++++++++++++++++++++++++++++++
 rust/kernel/lib.rs              |   2 +
 7 files changed, 559 insertions(+)
 create mode 100644 drivers/hid/rust/Kconfig
 create mode 100644 rust/kernel/hid.rs

diff --git a/MAINTAINERS b/MAINTAINERS
index b8d8a5c41597..1fee14024fa2 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -11319,6 +11319,14 @@ F:	include/uapi/linux/hid*
 F:	samples/hid/
 F:	tools/testing/selftests/hid/
 
+HID CORE LAYER [RUST]
+M:	Rahul Rameshbabu <sergeantsagara@protonmail.com>
+R:	Benjamin Tissoires <bentiss@kernel.org>
+L:	linux-input@vger.kernel.org
+S:	Maintained
+F:	drivers/hid/rust/*.rs
+F:	rust/kernel/hid.rs
+
 HID LOGITECH DRIVERS
 R:	Filipe Laíns <lains@riseup.net>
 L:	linux-input@vger.kernel.org
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index c1d9f7c6a5f2..750c2d49a806 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -1439,6 +1439,8 @@ endmenu
 
 source "drivers/hid/bpf/Kconfig"
 
+source "drivers/hid/rust/Kconfig"
+
 source "drivers/hid/i2c-hid/Kconfig"
 
 source "drivers/hid/intel-ish-hid/Kconfig"
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index e01838239ae6..b78ab84c47b4 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -8,6 +8,8 @@ hid-$(CONFIG_HID_HAPTIC)	+= hid-haptic.o
 
 obj-$(CONFIG_HID_BPF)		+= bpf/
 
+obj-$(CONFIG_RUST_HID_ABSTRACTIONS)		+= rust/
+
 obj-$(CONFIG_HID)		+= hid.o
 obj-$(CONFIG_UHID)		+= uhid.o
 
diff --git a/drivers/hid/rust/Kconfig b/drivers/hid/rust/Kconfig
new file mode 100644
index 000000000000..d3247651829e
--- /dev/null
+++ b/drivers/hid/rust/Kconfig
@@ -0,0 +1,12 @@
+# SPDX-License-Identifier: GPL-2.0-only
+menu "Rust HID support"
+
+config RUST_HID_ABSTRACTIONS
+	bool "Rust HID abstractions support"
+	depends on RUST
+	depends on HID=y
+	help
+	  Adds support needed for HID drivers written in Rust. It provides a
+	  wrapper around the C hid core.
+
+endmenu # Rust HID support
diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index 083cc44aa952..200e58af27a3 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -48,6 +48,7 @@
 #include <linux/cpumask.h>
 #include <linux/cred.h>
 #include <linux/debugfs.h>
+#include <linux/device.h>
 #include <linux/device/faux.h>
 #include <linux/dma-direction.h>
 #include <linux/dma-mapping.h>
@@ -60,6 +61,8 @@
 #include <linux/i2c.h>
 #include <linux/interrupt.h>
 #include <linux/io-pgtable.h>
+#include <linux/hid.h>
+#include "../../drivers/hid/hid-ids.h"
 #include <linux/ioport.h>
 #include <linux/jiffies.h>
 #include <linux/jump_label.h>
diff --git a/rust/kernel/hid.rs b/rust/kernel/hid.rs
new file mode 100644
index 000000000000..b9db542d923a
--- /dev/null
+++ b/rust/kernel/hid.rs
@@ -0,0 +1,530 @@
+// SPDX-License-Identifier: GPL-2.0
+
+// Copyright (C) 2025 Rahul Rameshbabu <sergeantsagara@protonmail.com>
+
+//! Abstractions for the HID interface.
+//!
+//! C header: [`include/linux/hid.h`](srctree/include/linux/hid.h)
+
+use crate::{
+    device,
+    device_id::{
+        RawDeviceId,
+        RawDeviceIdIndex, //
+    },
+    driver,
+    error::*,
+    prelude::*,
+    types::Opaque, //
+};
+use core::{
+    marker::PhantomData,
+    ptr::{
+        addr_of_mut,
+        NonNull, //
+    } //
+};
+
+/// Indicates the item is static read-only.
+///
+/// Refer to [Device Class Definition for HID 1.11]
+/// Section 6.2.2.5 Input, Output, and Feature Items.
+///
+/// [Device Class Definition for HID 1.11]: https://www.usb.org/sites/default/files/hid1_11.pdf
+pub const MAIN_ITEM_CONSTANT: u8 = bindings::HID_MAIN_ITEM_CONSTANT as u8;
+
+/// Indicates the item represents data from a physical control.
+///
+/// Refer to [Device Class Definition for HID 1.11]
+/// Section 6.2.2.5 Input, Output, and Feature Items.
+///
+/// [Device Class Definition for HID 1.11]: https://www.usb.org/sites/default/files/hid1_11.pdf
+pub const MAIN_ITEM_VARIABLE: u8 = bindings::HID_MAIN_ITEM_VARIABLE as u8;
+
+/// Indicates the item should be treated as a relative change from the previous
+/// report.
+///
+/// Refer to [Device Class Definition for HID 1.11]
+/// Section 6.2.2.5 Input, Output, and Feature Items.
+///
+/// [Device Class Definition for HID 1.11]: https://www.usb.org/sites/default/files/hid1_11.pdf
+pub const MAIN_ITEM_RELATIVE: u8 = bindings::HID_MAIN_ITEM_RELATIVE as u8;
+
+/// Indicates the item should wrap around when reaching the extreme high or
+/// extreme low values.
+///
+/// Refer to [Device Class Definition for HID 1.11]
+/// Section 6.2.2.5 Input, Output, and Feature Items.
+///
+/// [Device Class Definition for HID 1.11]: https://www.usb.org/sites/default/files/hid1_11.pdf
+pub const MAIN_ITEM_WRAP: u8 = bindings::HID_MAIN_ITEM_WRAP as u8;
+
+/// Indicates the item should wrap around when reaching the extreme high or
+/// extreme low values.
+///
+/// Refer to [Device Class Definition for HID 1.11]
+/// Section 6.2.2.5 Input, Output, and Feature Items.
+///
+/// [Device Class Definition for HID 1.11]: https://www.usb.org/sites/default/files/hid1_11.pdf
+pub const MAIN_ITEM_NONLINEAR: u8 = bindings::HID_MAIN_ITEM_NONLINEAR as u8;
+
+/// Indicates whether the control has a preferred state it will physically
+/// return to without user intervention.
+///
+/// Refer to [Device Class Definition for HID 1.11]
+/// Section 6.2.2.5 Input, Output, and Feature Items.
+///
+/// [Device Class Definition for HID 1.11]: https://www.usb.org/sites/default/files/hid1_11.pdf
+pub const MAIN_ITEM_NO_PREFERRED: u8 = bindings::HID_MAIN_ITEM_NO_PREFERRED as u8;
+
+/// Indicates whether the control has a physical state where it will not send
+/// any reports.
+///
+/// Refer to [Device Class Definition for HID 1.11]
+/// Section 6.2.2.5 Input, Output, and Feature Items.
+///
+/// [Device Class Definition for HID 1.11]: https://www.usb.org/sites/default/files/hid1_11.pdf
+pub const MAIN_ITEM_NULL_STATE: u8 = bindings::HID_MAIN_ITEM_NULL_STATE as u8;
+
+/// Indicates whether the control requires host system logic to change state.
+///
+/// Refer to [Device Class Definition for HID 1.11]
+/// Section 6.2.2.5 Input, Output, and Feature Items.
+///
+/// [Device Class Definition for HID 1.11]: https://www.usb.org/sites/default/files/hid1_11.pdf
+pub const MAIN_ITEM_VOLATILE: u8 = bindings::HID_MAIN_ITEM_VOLATILE as u8;
+
+/// Indicates whether the item is fixed size or a variable buffer of bytes.
+///
+/// Refer to [Device Class Definition for HID 1.11]
+/// Section 6.2.2.5 Input, Output, and Feature Items.
+///
+/// [Device Class Definition for HID 1.11]: https://www.usb.org/sites/default/files/hid1_11.pdf
+pub const MAIN_ITEM_BUFFERED_BYTE: u8 = bindings::HID_MAIN_ITEM_BUFFERED_BYTE as u8;
+
+/// HID device groups are intended to help categories HID devices based on a set
+/// of common quirks and logic that they will require to function correctly.
+#[repr(u16)]
+pub enum Group {
+    /// Used to match a device against any group when probing.
+    Any = bindings::HID_GROUP_ANY as u16,
+
+    /// Indicates a generic device that should need no custom logic from the
+    /// core HID stack.
+    Generic = bindings::HID_GROUP_GENERIC as u16,
+
+    /// Maps multitouch devices to hid-multitouch instead of hid-generic.
+    Multitouch = bindings::HID_GROUP_MULTITOUCH as u16,
+
+    /// Used for autodetecing and mapping of HID sensor hubs to
+    /// hid-sensor-hub.
+    SensorHub = bindings::HID_GROUP_SENSOR_HUB as u16,
+
+    /// Used for autodetecing and mapping Win 8 multitouch devices to set the
+    /// needed quirks.
+    MultitouchWin8 = bindings::HID_GROUP_MULTITOUCH_WIN_8 as u16,
+
+    // Vendor-specific device groups.
+    /// Used to distinguish Synpatics touchscreens from other products. The
+    /// touchscreens will be handled by hid-multitouch instead, while everything
+    /// else will be managed by hid-rmi.
+    RMI = bindings::HID_GROUP_RMI as u16,
+
+    /// Used for hid-core handling to automatically identify Wacom devices and
+    /// have them probed by hid-wacom.
+    Wacom = bindings::HID_GROUP_WACOM as u16,
+
+    /// Used by logitech-djreceiver and logitech-djdevice to autodetect if
+    /// devices paied to the DJ receivers are DJ devices and handle them with
+    /// the device driver.
+    LogitechDJDevice = bindings::HID_GROUP_LOGITECH_DJ_DEVICE as u16,
+
+    /// Since the Valve Steam Controller only has vendor-specific usages,
+    /// prevent hid-generic from parsing its reports since there would be
+    /// nothing hid-generic could do for the device.
+    Steam = bindings::HID_GROUP_STEAM as u16,
+
+    /// Used to differentiate 27 Mhz frequency Logitech DJ devices from other
+    /// Logitech DJ devices.
+    Logitech27MHzDevice = bindings::HID_GROUP_LOGITECH_27MHZ_DEVICE as u16,
+
+    /// Used for autodetecting and mapping Vivaldi devices to hid-vivaldi.
+    Vivaldi = bindings::HID_GROUP_VIVALDI as u16,
+}
+
+// TODO: use `const_trait_impl` once stabilized:
+//
+// ```
+// impl const From<Group> for u16 {
+//     /// [`Group`] variants are represented by [`u16`] values.
+//     fn from(value: Group) -> Self {
+//         value as Self
+//     }
+// }
+// ```
+impl Group {
+    /// Internal function used to convert [`Group`] variants into [`u16`].
+    const fn into_u16(self) -> u16 {
+        self as u16
+    }
+}
+
+impl TryFrom<u16> for Group {
+    type Error = &'static str;
+
+    /// [`u16`] values can be safely converted to [`Group`] variants.
+    fn try_from(value: u16) -> Result<Group, Self::Error> {
+        match value.into() {
+            bindings::HID_GROUP_GENERIC => Ok(Group::Generic),
+            bindings::HID_GROUP_MULTITOUCH => Ok(Group::Multitouch),
+            bindings::HID_GROUP_SENSOR_HUB => Ok(Group::SensorHub),
+            bindings::HID_GROUP_MULTITOUCH_WIN_8 => Ok(Group::MultitouchWin8),
+            bindings::HID_GROUP_RMI => Ok(Group::RMI),
+            bindings::HID_GROUP_WACOM => Ok(Group::Wacom),
+            bindings::HID_GROUP_LOGITECH_DJ_DEVICE => Ok(Group::LogitechDJDevice),
+            bindings::HID_GROUP_STEAM => Ok(Group::Steam),
+            bindings::HID_GROUP_LOGITECH_27MHZ_DEVICE => Ok(Group::Logitech27MHzDevice),
+            bindings::HID_GROUP_VIVALDI => Ok(Group::Vivaldi),
+            _ => Err("Unknown HID group encountered!"),
+        }
+    }
+}
+
+/// The HID device representation.
+///
+/// This structure represents the Rust abstraction for a C `struct hid_device`.
+/// The implementation abstracts the usage of an already existing C `struct
+/// hid_device` within Rust code that we get passed from the C side.
+///
+/// # Invariants
+///
+/// A [`Device`] instance represents a valid `struct hid_device` created by the
+/// C portion of the kernel.
+#[repr(transparent)]
+pub struct Device<Ctx: device::DeviceContext = device::Normal>(
+    Opaque<bindings::hid_device>,
+    PhantomData<Ctx>,
+);
+
+impl<Ctx: device::DeviceContext> Device<Ctx> {
+    fn as_raw(&self) -> *mut bindings::hid_device {
+        self.0.get()
+    }
+
+    /// Returns the HID transport bus ID.
+    pub fn bus(&self) -> u16 {
+        // SAFETY: `self.as_raw` is a valid pointer to a `struct hid_device`
+        unsafe { *self.as_raw() }.bus
+    }
+
+    /// Returns the HID report group.
+    pub fn group(&self) -> Result<Group, &'static str> {
+        // SAFETY: `self.as_raw` is a valid pointer to a `struct hid_device`
+        unsafe { *self.as_raw() }.group.try_into()
+    }
+
+    /// Returns the HID vendor ID.
+    pub fn vendor(&self) -> u32 {
+        // SAFETY: `self.as_raw` is a valid pointer to a `struct hid_device`
+        unsafe { *self.as_raw() }.vendor
+    }
+
+    /// Returns the HID product ID.
+    pub fn product(&self) -> u32 {
+        // SAFETY: `self.as_raw` is a valid pointer to a `struct hid_device`
+        unsafe { *self.as_raw() }.product
+    }
+}
+
+// SAFETY: `Device` is a transparent wrapper of a type that doesn't depend on `Device`'s generic
+// argument.
+kernel::impl_device_context_deref!(unsafe { Device });
+kernel::impl_device_context_into_aref!(Device);
+
+// SAFETY: Instances of `Device` are always reference-counted.
+unsafe impl crate::types::AlwaysRefCounted for Device {
+    fn inc_ref(&self) {
+        // SAFETY: The existence of a shared reference guarantees that the refcount is non-zero.
+        unsafe { bindings::get_device(&raw mut (*self.as_raw()).dev) };
+    }
+
+    unsafe fn dec_ref(obj: NonNull<Self>) {
+        // SAFETY: The safety requirements guarantee that the refcount is non-zero.
+        unsafe { bindings::put_device(&raw mut (*obj.cast::<bindings::hid_device>().as_ptr()).dev) }
+    }
+}
+
+impl<Ctx: device::DeviceContext> AsRef<device::Device<Ctx>> for Device<Ctx> {
+    fn as_ref(&self) -> &device::Device<Ctx> {
+        // SAFETY: By the type invariant of `Self`, `self.as_raw()` is a pointer to a valid
+        // `struct hid_device`.
+        let dev = unsafe { addr_of_mut!((*self.as_raw()).dev) };
+
+        // SAFETY: `dev` points to a valid `struct device`.
+        unsafe { device::Device::from_raw(dev) }
+    }
+}
+
+/// Abstraction for the HID device ID structure `struct hid_device_id`.
+#[repr(transparent)]
+#[derive(Clone, Copy)]
+pub struct DeviceId(bindings::hid_device_id);
+
+impl DeviceId {
+    /// Equivalent to C's `HID_USB_DEVICE` macro.
+    ///
+    /// Create a new `hid::DeviceId` from a group, vendor ID, and device ID
+    /// number.
+    pub const fn new_usb(group: Group, vendor: u32, product: u32) -> Self {
+        Self(bindings::hid_device_id {
+            bus: 0x3, // BUS_USB
+            group: group.into_u16(),
+            vendor,
+            product,
+            driver_data: 0,
+        })
+    }
+
+    /// Returns the HID transport bus ID.
+    pub fn bus(&self) -> u16 {
+        self.0.bus
+    }
+
+    /// Returns the HID report group.
+    pub fn group(&self) -> Result<Group, &'static str> {
+        self.0.group.try_into()
+    }
+
+    /// Returns the HID vendor ID.
+    pub fn vendor(&self) -> u32 {
+        self.0.vendor
+    }
+
+    /// Returns the HID product ID.
+    pub fn product(&self) -> u32 {
+        self.0.product
+    }
+}
+
+// SAFETY:
+// * `DeviceId` is a `#[repr(transparent)` wrapper of `hid_device_id` and does not add
+//   additional invariants, so it's safe to transmute to `RawType`.
+// * `DRIVER_DATA_OFFSET` is the offset to the `driver_data` field.
+unsafe impl RawDeviceId for DeviceId {
+    type RawType = bindings::hid_device_id;
+}
+
+// SAFETY: `DRIVER_DATA_OFFSET` is the offset to the `driver_data` field.
+unsafe impl RawDeviceIdIndex for DeviceId {
+    const DRIVER_DATA_OFFSET: usize = core::mem::offset_of!(bindings::hid_device_id, driver_data);
+
+    fn index(&self) -> usize {
+        self.0.driver_data
+    }
+}
+
+/// [`IdTable`] type for HID.
+pub type IdTable<T> = &'static dyn kernel::device_id::IdTable<DeviceId, T>;
+
+/// Create a HID [`IdTable`] with its alias for modpost.
+#[macro_export]
+macro_rules! hid_device_table {
+    ($table_name:ident, $module_table_name:ident, $id_info_type: ty, $table_data: expr) => {
+        const $table_name: $crate::device_id::IdArray<
+            $crate::hid::DeviceId,
+            $id_info_type,
+            { $table_data.len() },
+        > = $crate::device_id::IdArray::new($table_data);
+
+        $crate::module_device_table!("hid", $module_table_name, $table_name);
+    };
+}
+
+/// The HID driver trait.
+///
+/// # Examples
+///
+/// ```
+/// use kernel::{bindings, device, hid};
+///
+/// struct MyDriver;
+///
+/// kernel::hid_device_table!(
+///     HID_TABLE,
+///     MODULE_HID_TABLE,
+///     <MyDriver as hid::Driver>::IdInfo,
+///     [(
+///         hid::DeviceId::new_usb(
+///             hid::Group::Steam,
+///             bindings::USB_VENDOR_ID_VALVE,
+///             bindings::USB_DEVICE_ID_STEAM_DECK,
+///         ),
+///         (),
+///     )]
+/// );
+///
+/// #[vtable]
+/// impl hid::Driver for MyDriver {
+///     type IdInfo = ();
+///     const ID_TABLE: hid::IdTable<Self::IdInfo> = &HID_TABLE;
+///
+///     /// This function is optional to implement.
+///     fn report_fixup<'a, 'b: 'a>(_hdev: &hid::Device<device::Core>, rdesc: &'b mut [u8]) -> &'a [u8] {
+///         // Perform some report descriptor fixup.
+///         rdesc
+///     }
+/// }
+/// ```
+/// Drivers must implement this trait in order to get a HID driver registered.
+/// Please refer to the `Adapter` documentation for an example.
+#[vtable]
+pub trait Driver: Send {
+    /// The type holding information about each device id supported by the driver.
+    // TODO: Use `associated_type_defaults` once stabilized:
+    //
+    // ```
+    // type IdInfo: 'static = ();
+    // ```
+    type IdInfo: 'static;
+
+    /// The table of device ids supported by the driver.
+    const ID_TABLE: IdTable<Self::IdInfo>;
+
+    /// Called before report descriptor parsing. Can be used to mutate the
+    /// report descriptor before the core HID logic processes the descriptor.
+    /// Useful for problematic report descriptors that prevent HID devices from
+    /// functioning correctly.
+    ///
+    /// Optional to implement.
+    fn report_fixup<'a, 'b: 'a>(_hdev: &Device<device::Core>, _rdesc: &'b mut [u8]) -> &'a [u8] {
+        build_error!(VTABLE_DEFAULT_ERROR)
+    }
+}
+
+/// An adapter for the registration of HID drivers.
+pub struct Adapter<T: Driver>(T);
+
+// SAFETY:
+// - `bindings::hid_driver` is a C type declared as `repr(C)`.
+// - `T` is the type of the driver's device private data.
+// - `struct hid_driver` embeds a `struct device_driver`.
+// - `DEVICE_DRIVER_OFFSET` is the correct byte offset to the embedded `struct device_driver`.
+unsafe impl<T: Driver + 'static> driver::DriverLayout for Adapter<T> {
+    type DriverType = bindings::hid_driver;
+    type DriverData = T;
+    const DEVICE_DRIVER_OFFSET: usize = core::mem::offset_of!(Self::DriverType, driver);
+}
+
+// SAFETY: A call to `unregister` for a given instance of `DriverType` is guaranteed to be valid if
+// a preceding call to `register` has been successful.
+unsafe impl<T: Driver + 'static> driver::RegistrationOps for Adapter<T> {
+    unsafe fn register(
+        hdrv: &Opaque<Self::DriverType>,
+        name: &'static CStr,
+        module: &'static ThisModule,
+    ) -> Result {
+        // SAFETY: It's safe to set the fields of `struct hid_driver` on initialization.
+        unsafe {
+            (*hdrv.get()).name = name.as_char_ptr();
+            (*hdrv.get()).id_table = T::ID_TABLE.as_ptr();
+            (*hdrv.get()).report_fixup = if T::HAS_REPORT_FIXUP {
+                Some(Self::report_fixup_callback)
+            } else {
+                None
+            };
+        }
+
+        // SAFETY: `hdrv` is guaranteed to be a valid `DriverType`
+        to_result(unsafe {
+            bindings::__hid_register_driver(hdrv.get(), module.0, name.as_char_ptr())
+        })
+    }
+
+    unsafe fn unregister(hdrv: &Opaque<Self::DriverType>) {
+        // SAFETY: `hdrv` is guaranteed to be a valid `DriverType`
+        unsafe { bindings::hid_unregister_driver(hdrv.get()) }
+    }
+}
+
+impl<T: Driver + 'static> Adapter<T> {
+    extern "C" fn report_fixup_callback(
+        hdev: *mut bindings::hid_device,
+        buf: *mut u8,
+        size: *mut kernel::ffi::c_uint,
+    ) -> *const u8 {
+        // SAFETY: The HID subsystem only ever calls the report_fixup callback
+        // with a valid pointer to a `struct hid_device`.
+        //
+        // INVARIANT: `hdev` is valid for the duration of
+        // `report_fixup_callback()`.
+        let hdev = unsafe { &*hdev.cast::<Device<device::Core>>() };
+
+        // SAFETY: The HID subsystem only ever calls the report_fixup callback
+        // with a valid pointer to a `kernel::ffi::c_uint`.
+        //
+        // INVARIANT: `size` is valid for the duration of
+        // `report_fixup_callback()`.
+        let buf_len: usize = match unsafe { *size }.try_into() {
+            Ok(len) => len,
+            Err(e) => {
+                dev_err!(
+                    hdev.as_ref(),
+                    "Cannot fix report description due to {:?}!\n",
+                    e
+                );
+
+                return buf;
+            }
+        };
+
+        // Build a mutable Rust slice from `buf` and `size`.
+        //
+        // SAFETY: The HID subsystem only ever calls the `report_fixup callback`
+        // with a valid pointer to a `u8` buffer.
+        //
+        // INVARIANT: `buf` is valid for the duration of
+        // `report_fixup_callback()`.
+        let rdesc_slice = unsafe { core::slice::from_raw_parts_mut(buf, buf_len) };
+        let rdesc_slice = T::report_fixup(hdev, rdesc_slice);
+
+        match rdesc_slice.len().try_into() {
+            // SAFETY: The HID subsystem only ever calls the report_fixup
+            // callback with a valid pointer to a `kernel::ffi::c_uint`.
+            //
+            // INVARIANT: `size` is valid for the duration of
+            // `report_fixup_callback()`.
+            Ok(len) => unsafe { *size = len },
+            Err(e) => {
+                dev_err!(
+                    hdev.as_ref(),
+                    "Fixed report description will not be used due to {:?}!\n",
+                    e
+                );
+
+                return buf;
+            }
+        }
+
+        rdesc_slice.as_ptr()
+    }
+}
+
+/// Declares a kernel module that exposes a single HID driver.
+///
+/// # Examples
+///
+/// ```ignore
+/// kernel::module_hid_driver! {
+///     type: MyDriver,
+///     name: "Module name",
+///     authors: ["Author name"],
+///     description: "Description",
+///     license: "GPL",
+/// }
+/// ```
+#[macro_export]
+macro_rules! module_hid_driver {
+    ($($f:tt)*) => {
+        $crate::module_driver!(<T>, $crate::hid::Adapter<T>, { $($f)* });
+    };
+}
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 3da92f18f4ee..e2dcacd9369e 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -102,6 +102,8 @@
 pub mod id_pool;
 #[doc(hidden)]
 pub mod impl_flags;
+#[cfg(CONFIG_RUST_HID_ABSTRACTIONS)]
+pub mod hid;
 pub mod init;
 pub mod io;
 pub mod ioctl;
-- 
2.52.0



^ permalink raw reply related

* [PATCH v6 2/2] rust: hid: Glorious PC Gaming Race Model O and O- mice reference driver
From: Rahul Rameshbabu @ 2026-02-22 21:56 UTC (permalink / raw)
  To: linux-input, linux-kernel, rust-for-linux
  Cc: a.hindborg, alex.gaynor, aliceryhl, benjamin.tissoires,
	benno.lossin, bjorn3_gh, boqun.feng, dakr, db48x, gary, jikos,
	ojeda, peter.hutterer, tmgross, Rahul Rameshbabu
In-Reply-To: <20260222215611.79760-1-sergeantsagara@protonmail.com>

Demonstrate how to perform a report fixup from a Rust HID driver. The mice
specify the const flag incorrectly in the consumer input report descriptor,
which leads to inputs being ignored. Correctly patch the report descriptor
for the Model O and O- mice.

Portions of the HID report post-fixup:
device 0:0
...
0x81, 0x06,                    //  Input (Data,Var,Rel)               84
...
0x81, 0x06,                    //  Input (Data,Var,Rel)               112
...
0x81, 0x06,                    //  Input (Data,Var,Rel)               140

Signed-off-by: Rahul Rameshbabu <sergeantsagara@protonmail.com>
---

Notes:
    Changelog:
    
        v5->v6:
          * NONE
        v4->v5:
          * NONE
        v3->v4:
          * Removed specifying tree in MAINTAINERS file since that is up for
            debate
          * Minor rebase cleanup
          * Moved driver logic under drivers/hid/rust
          * Use hex instead of decimal for the report descriptor comparisons
        v2->v3:
          * Fixed docstring formatting
          * Updated MAINTAINERS file based on v1 and v2 discussion
        v1->v2:
          * Use vendor id and device id from drivers/hid/hid-ids.h bindings
          * Make use for dev_err! as appropriate

 MAINTAINERS                           |  6 +++
 drivers/hid/hid-glorious.c            |  2 +
 drivers/hid/hid_glorious_rust.rs      | 60 +++++++++++++++++++++++++++
 drivers/hid/rust/Kconfig              | 16 +++++++
 drivers/hid/rust/Makefile             |  6 +++
 drivers/hid/rust/hid_glorious_rust.rs | 60 +++++++++++++++++++++++++++
 6 files changed, 150 insertions(+)
 create mode 100644 drivers/hid/hid_glorious_rust.rs
 create mode 100644 drivers/hid/rust/Makefile
 create mode 100644 drivers/hid/rust/hid_glorious_rust.rs

diff --git a/MAINTAINERS b/MAINTAINERS
index 1fee14024fa2..1f9167221639 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -10808,6 +10808,12 @@ L:	platform-driver-x86@vger.kernel.org
 S:	Maintained
 F:	drivers/platform/x86/gigabyte-wmi.c
 
+GLORIOUS RUST DRIVER [RUST]
+M:	Rahul Rameshbabu <sergeantsagara@protonmail.com>
+L:	linux-input@vger.kernel.org
+S:	Maintained
+F:	drivers/hid/rust/hid_glorious_rust.rs
+
 GNSS SUBSYSTEM
 M:	Johan Hovold <johan@kernel.org>
 S:	Maintained
diff --git a/drivers/hid/hid-glorious.c b/drivers/hid/hid-glorious.c
index 5bbd81248053..d7362852c20f 100644
--- a/drivers/hid/hid-glorious.c
+++ b/drivers/hid/hid-glorious.c
@@ -76,8 +76,10 @@ static int glorious_probe(struct hid_device *hdev,
 }
 
 static const struct hid_device_id glorious_devices[] = {
+#if !IS_ENABLED(CONFIG_HID_GLORIOUS_RUST)
 	{ HID_USB_DEVICE(USB_VENDOR_ID_SINOWEALTH,
 		USB_DEVICE_ID_GLORIOUS_MODEL_O) },
+#endif
 	{ HID_USB_DEVICE(USB_VENDOR_ID_SINOWEALTH,
 		USB_DEVICE_ID_GLORIOUS_MODEL_D) },
 	{ HID_USB_DEVICE(USB_VENDOR_ID_LAVIEW,
diff --git a/drivers/hid/hid_glorious_rust.rs b/drivers/hid/hid_glorious_rust.rs
new file mode 100644
index 000000000000..8cffc1c605dd
--- /dev/null
+++ b/drivers/hid/hid_glorious_rust.rs
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: GPL-2.0
+
+// Copyright (C) 2025 Rahul Rameshbabu <sergeantsagara@protonmail.com>
+
+//! Rust reference HID driver for Glorious Model O and O- mice.
+
+use kernel::{self, bindings, device, hid, prelude::*};
+
+struct GloriousRust;
+
+kernel::hid_device_table!(
+    HID_TABLE,
+    MODULE_HID_TABLE,
+    <GloriousRust as hid::Driver>::IdInfo,
+    [(
+        hid::DeviceId::new_usb(
+            hid::Group::Generic,
+            bindings::USB_VENDOR_ID_SINOWEALTH,
+            bindings::USB_DEVICE_ID_GLORIOUS_MODEL_O,
+        ),
+        (),
+    )]
+);
+
+#[vtable]
+impl hid::Driver for GloriousRust {
+    type IdInfo = ();
+    const ID_TABLE: hid::IdTable<Self::IdInfo> = &HID_TABLE;
+
+    /// Fix the Glorious Model O and O- consumer input report descriptor to use
+    /// the variable and relative flag, while clearing the const flag.
+    ///
+    /// Without this fixup, inputs from the mice will be ignored.
+    fn report_fixup<'a, 'b: 'a>(hdev: &hid::Device<device::Core>, rdesc: &'b mut [u8]) -> &'a [u8] {
+        if rdesc.len() == 213
+            && (rdesc[84] == 129 && rdesc[85] == 3)
+            && (rdesc[112] == 129 && rdesc[113] == 3)
+            && (rdesc[140] == 129 && rdesc[141] == 3)
+        {
+            dev_info!(
+                hdev.as_ref(),
+                "patching Glorious Model O consumer control report descriptor\n"
+            );
+
+            rdesc[85] = hid::MAIN_ITEM_VARIABLE | hid::MAIN_ITEM_RELATIVE;
+            rdesc[113] = hid::MAIN_ITEM_VARIABLE | hid::MAIN_ITEM_RELATIVE;
+            rdesc[141] = hid::MAIN_ITEM_VARIABLE | hid::MAIN_ITEM_RELATIVE;
+        }
+
+        rdesc
+    }
+}
+
+kernel::module_hid_driver! {
+    type: GloriousRust,
+    name: "GloriousRust",
+    authors: ["Rahul Rameshbabu <sergeantsagara@protonmail.com>"],
+    description: "Rust reference HID driver for Glorious Model O and O- mice",
+    license: "GPL",
+}
diff --git a/drivers/hid/rust/Kconfig b/drivers/hid/rust/Kconfig
index d3247651829e..d7a1bf26bed0 100644
--- a/drivers/hid/rust/Kconfig
+++ b/drivers/hid/rust/Kconfig
@@ -9,4 +9,20 @@ config RUST_HID_ABSTRACTIONS
 	  Adds support needed for HID drivers written in Rust. It provides a
 	  wrapper around the C hid core.
 
+if RUST_HID_ABSTRACTIONS
+
+menu "Special HID drivers"
+
+config HID_GLORIOUS_RUST
+	tristate "Glorious O and O- mice Rust reference driver"
+	depends on USB_HID
+	depends on RUST_HID_ABSTRACTIONS
+	help
+	  Support for Glorious PC Gaming Race O and O- mice
+	  in Rust.
+
+endmenu # Special HID drivers
+
+endif # RUST_HID_ABSTRACTIONS
+
 endmenu # Rust HID support
diff --git a/drivers/hid/rust/Makefile b/drivers/hid/rust/Makefile
new file mode 100644
index 000000000000..6676030a2f87
--- /dev/null
+++ b/drivers/hid/rust/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for Rust HID support
+#
+
+obj-$(CONFIG_HID_GLORIOUS_RUST)	+= hid_glorious_rust.o
diff --git a/drivers/hid/rust/hid_glorious_rust.rs b/drivers/hid/rust/hid_glorious_rust.rs
new file mode 100644
index 000000000000..dfc3f2323b60
--- /dev/null
+++ b/drivers/hid/rust/hid_glorious_rust.rs
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: GPL-2.0
+
+// Copyright (C) 2025 Rahul Rameshbabu <sergeantsagara@protonmail.com>
+
+//! Rust reference HID driver for Glorious Model O and O- mice.
+
+use kernel::{self, bindings, device, hid, prelude::*};
+
+struct GloriousRust;
+
+kernel::hid_device_table!(
+    HID_TABLE,
+    MODULE_HID_TABLE,
+    <GloriousRust as hid::Driver>::IdInfo,
+    [(
+        hid::DeviceId::new_usb(
+            hid::Group::Generic,
+            bindings::USB_VENDOR_ID_SINOWEALTH,
+            bindings::USB_DEVICE_ID_GLORIOUS_MODEL_O,
+        ),
+        (),
+    )]
+);
+
+#[vtable]
+impl hid::Driver for GloriousRust {
+    type IdInfo = ();
+    const ID_TABLE: hid::IdTable<Self::IdInfo> = &HID_TABLE;
+
+    /// Fix the Glorious Model O and O- consumer input report descriptor to use
+    /// the variable and relative flag, while clearing the const flag.
+    ///
+    /// Without this fixup, inputs from the mice will be ignored.
+    fn report_fixup<'a, 'b: 'a>(hdev: &hid::Device<device::Core>, rdesc: &'b mut [u8]) -> &'a [u8] {
+        if rdesc.len() == 213
+            && (rdesc[84] == 0x81 && rdesc[85] == 0x3)
+            && (rdesc[112] == 0x81 && rdesc[113] == 0x3)
+            && (rdesc[140] == 0x81 && rdesc[141] == 0x3)
+        {
+            dev_info!(
+                hdev.as_ref(),
+                "patching Glorious Model O consumer control report descriptor\n"
+            );
+
+            rdesc[85] = hid::MAIN_ITEM_VARIABLE | hid::MAIN_ITEM_RELATIVE;
+            rdesc[113] = hid::MAIN_ITEM_VARIABLE | hid::MAIN_ITEM_RELATIVE;
+            rdesc[141] = hid::MAIN_ITEM_VARIABLE | hid::MAIN_ITEM_RELATIVE;
+        }
+
+        rdesc
+    }
+}
+
+kernel::module_hid_driver! {
+    type: GloriousRust,
+    name: "GloriousRust",
+    authors: ["Rahul Rameshbabu <sergeantsagara@protonmail.com>"],
+    description: "Rust reference HID driver for Glorious Model O and O- mice",
+    license: "GPL",
+}
-- 
2.52.0



^ permalink raw reply related

* Re: [PATCH v6 1/2] rust: core abstractions for HID drivers
From: Terry Junge @ 2026-02-22 23:39 UTC (permalink / raw)
  To: Rahul Rameshbabu, linux-input, linux-kernel, rust-for-linux
  Cc: a.hindborg, alex.gaynor, aliceryhl, benjamin.tissoires,
	benno.lossin, bjorn3_gh, boqun.feng, dakr, db48x, gary, jikos,
	ojeda, peter.hutterer, tmgross
In-Reply-To: <20260222215611.79760-2-sergeantsagara@protonmail.com>



On 2/22/26 1:56 PM, Rahul Rameshbabu wrote:
> These abstractions enable the development of HID drivers in Rust by binding
> with the HID core C API. They provide Rust types that map to the
> equivalents in C. In this initial draft, only hid_device and hid_device_id
> are provided direct Rust type equivalents. hid_driver is specially wrapped
> with a custom Driver type. The module_hid_driver! macro provides analogous
> functionality to its C equivalent. Only the .report_fixup callback is
> binded to Rust so far.
> 
> Future work for these abstractions would include more bindings for common
> HID-related types, such as hid_field, hid_report_enum, and hid_report as
> well as more bus callbacks. Providing Rust equivalents to useful core HID
> functions will also be necessary for HID driver development in Rust.
> 
> Signed-off-by: Rahul Rameshbabu <sergeantsagara@protonmail.com>
> ---
> 
> Notes:
>     Changelog:
>     
>         v5->v6:
>           * Converted From<u16> for Group to TryFrom<u16> to properly handle
>             error case
>           * Renamed into method for Group to into_u16 to not conflate with the
>             From trait
>           * Refactored new upstream changes to RegistrationOps
>           * Implemented DriverLayout trait for hid::Adapter<T>
>         v4->v5:
>           * Add rust/ to drivers/hid/Makefile
>           * Implement RawDeviceIdIndex trait
>         v3->v4:
>           * Removed specifying tree in MAINTAINERS file since that is up for
>             debate
>           * Minor rebase cleanup
>           * Moved driver logic under drivers/hid/rust
>         v2->v3:
>           * Implemented AlwaysRefCounted trait using embedded struct device's
>             reference counts instead of the separate reference counter in struct
>             hid_device
>           * Used &raw mut as appropriate
>           * Binded include/linux/device.h for get_device and put_device
>           * Cleaned up various comment related formatting
>           * Minified dev_err! format string
>           * Updated Group enum to be repr(u16)
>           * Implemented From<u16> trait for Group
>           * Added TODO comment when const_trait_impl stabilizes
>           * Made group getter functions return a Group variant instead of a raw
>             number
>           * Made sure example code builds
>         v1->v2:
>           * Binded drivers/hid/hid-ids.h for use in Rust drivers
>           * Remove pre-emptive referencing of a C HID driver instance before
>             it is fully initialized in the driver registration path
>           * Moved static getters to generic Device trait implementation, so
>             they can be used by all device::DeviceContext
>           * Use core macros for supporting DeviceContext transitions
>           * Implemented the AlwaysRefCounted and AsRef traits
>           * Make use for dev_err! as appropriate
>         RFC->v1:
>           * Use Danilo's core infrastructure
>           * Account for HID device groups
>           * Remove probe and remove callbacks
>           * Implement report_fixup support
>           * Properly comment code including SAFETY comments
> 
>  MAINTAINERS                     |   8 +
>  drivers/hid/Kconfig             |   2 +
>  drivers/hid/Makefile            |   2 +
>  drivers/hid/rust/Kconfig        |  12 +
>  rust/bindings/bindings_helper.h |   3 +
>  rust/kernel/hid.rs              | 530 ++++++++++++++++++++++++++++++++
>  rust/kernel/lib.rs              |   2 +
>  7 files changed, 559 insertions(+)
>  create mode 100644 drivers/hid/rust/Kconfig
>  create mode 100644 rust/kernel/hid.rs
> 
> diff --git a/MAINTAINERS b/MAINTAINERS
> index b8d8a5c41597..1fee14024fa2 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -11319,6 +11319,14 @@ F:	include/uapi/linux/hid*
>  F:	samples/hid/
>  F:	tools/testing/selftests/hid/
>  
> +HID CORE LAYER [RUST]
> +M:	Rahul Rameshbabu <sergeantsagara@protonmail.com>
> +R:	Benjamin Tissoires <bentiss@kernel.org>
> +L:	linux-input@vger.kernel.org
> +S:	Maintained
> +F:	drivers/hid/rust/*.rs
> +F:	rust/kernel/hid.rs
> +
>  HID LOGITECH DRIVERS
>  R:	Filipe Laíns <lains@riseup.net>
>  L:	linux-input@vger.kernel.org
> diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
> index c1d9f7c6a5f2..750c2d49a806 100644
> --- a/drivers/hid/Kconfig
> +++ b/drivers/hid/Kconfig
> @@ -1439,6 +1439,8 @@ endmenu
>  
>  source "drivers/hid/bpf/Kconfig"
>  
> +source "drivers/hid/rust/Kconfig"
> +
>  source "drivers/hid/i2c-hid/Kconfig"
>  
>  source "drivers/hid/intel-ish-hid/Kconfig"
> diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
> index e01838239ae6..b78ab84c47b4 100644
> --- a/drivers/hid/Makefile
> +++ b/drivers/hid/Makefile
> @@ -8,6 +8,8 @@ hid-$(CONFIG_HID_HAPTIC)	+= hid-haptic.o
>  
>  obj-$(CONFIG_HID_BPF)		+= bpf/
>  
> +obj-$(CONFIG_RUST_HID_ABSTRACTIONS)		+= rust/
> +
>  obj-$(CONFIG_HID)		+= hid.o
>  obj-$(CONFIG_UHID)		+= uhid.o
>  
> diff --git a/drivers/hid/rust/Kconfig b/drivers/hid/rust/Kconfig
> new file mode 100644
> index 000000000000..d3247651829e
> --- /dev/null
> +++ b/drivers/hid/rust/Kconfig
> @@ -0,0 +1,12 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +menu "Rust HID support"
> +
> +config RUST_HID_ABSTRACTIONS
> +	bool "Rust HID abstractions support"
> +	depends on RUST
> +	depends on HID=y
> +	help
> +	  Adds support needed for HID drivers written in Rust. It provides a
> +	  wrapper around the C hid core.
> +
> +endmenu # Rust HID support
> diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
> index 083cc44aa952..200e58af27a3 100644
> --- a/rust/bindings/bindings_helper.h
> +++ b/rust/bindings/bindings_helper.h
> @@ -48,6 +48,7 @@
>  #include <linux/cpumask.h>
>  #include <linux/cred.h>
>  #include <linux/debugfs.h>
> +#include <linux/device.h>
>  #include <linux/device/faux.h>
>  #include <linux/dma-direction.h>
>  #include <linux/dma-mapping.h>
> @@ -60,6 +61,8 @@
>  #include <linux/i2c.h>
>  #include <linux/interrupt.h>
>  #include <linux/io-pgtable.h>
> +#include <linux/hid.h>
> +#include "../../drivers/hid/hid-ids.h"
>  #include <linux/ioport.h>
>  #include <linux/jiffies.h>
>  #include <linux/jump_label.h>
> diff --git a/rust/kernel/hid.rs b/rust/kernel/hid.rs
> new file mode 100644
> index 000000000000..b9db542d923a
> --- /dev/null
> +++ b/rust/kernel/hid.rs
> @@ -0,0 +1,530 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +// Copyright (C) 2025 Rahul Rameshbabu <sergeantsagara@protonmail.com>
> +
> +//! Abstractions for the HID interface.
> +//!
> +//! C header: [`include/linux/hid.h`](srctree/include/linux/hid.h)
> +
> +use crate::{
> +    device,
> +    device_id::{
> +        RawDeviceId,
> +        RawDeviceIdIndex, //
> +    },
> +    driver,
> +    error::*,
> +    prelude::*,
> +    types::Opaque, //
> +};
> +use core::{
> +    marker::PhantomData,
> +    ptr::{
> +        addr_of_mut,
> +        NonNull, //
> +    } //
> +};
> +
> +/// Indicates the item is static read-only.
> +///
> +/// Refer to [Device Class Definition for HID 1.11]
> +/// Section 6.2.2.5 Input, Output, and Feature Items.
> +///
> +/// [Device Class Definition for HID 1.11]: https://www.usb.org/sites/default/files/hid1_11.pdf
> +pub const MAIN_ITEM_CONSTANT: u8 = bindings::HID_MAIN_ITEM_CONSTANT as u8;
> +
> +/// Indicates the item represents data from a physical control.
> +///
> +/// Refer to [Device Class Definition for HID 1.11]
> +/// Section 6.2.2.5 Input, Output, and Feature Items.
> +///
> +/// [Device Class Definition for HID 1.11]: https://www.usb.org/sites/default/files/hid1_11.pdf
> +pub const MAIN_ITEM_VARIABLE: u8 = bindings::HID_MAIN_ITEM_VARIABLE as u8;
> +
> +/// Indicates the item should be treated as a relative change from the previous
> +/// report.
> +///
> +/// Refer to [Device Class Definition for HID 1.11]
> +/// Section 6.2.2.5 Input, Output, and Feature Items.
> +///
> +/// [Device Class Definition for HID 1.11]: https://www.usb.org/sites/default/files/hid1_11.pdf
> +pub const MAIN_ITEM_RELATIVE: u8 = bindings::HID_MAIN_ITEM_RELATIVE as u8;
> +
> +/// Indicates the item should wrap around when reaching the extreme high or
> +/// extreme low values.
> +///
> +/// Refer to [Device Class Definition for HID 1.11]
> +/// Section 6.2.2.5 Input, Output, and Feature Items.
> +///
> +/// [Device Class Definition for HID 1.11]: https://www.usb.org/sites/default/files/hid1_11.pdf
> +pub const MAIN_ITEM_WRAP: u8 = bindings::HID_MAIN_ITEM_WRAP as u8;
> +
> +/// Indicates the item should wrap around when reaching the extreme high or
> +/// extreme low values.
> +///
> +/// Refer to [Device Class Definition for HID 1.11]
> +/// Section 6.2.2.5 Input, Output, and Feature Items.
> +///
> +/// [Device Class Definition for HID 1.11]: https://www.usb.org/sites/default/files/hid1_11.pdf
> +pub const MAIN_ITEM_NONLINEAR: u8 = bindings::HID_MAIN_ITEM_NONLINEAR as u8;
> +
> +/// Indicates whether the control has a preferred state it will physically
> +/// return to without user intervention.
> +///
> +/// Refer to [Device Class Definition for HID 1.11]
> +/// Section 6.2.2.5 Input, Output, and Feature Items.
> +///
> +/// [Device Class Definition for HID 1.11]: https://www.usb.org/sites/default/files/hid1_11.pdf
> +pub const MAIN_ITEM_NO_PREFERRED: u8 = bindings::HID_MAIN_ITEM_NO_PREFERRED as u8;
> +
> +/// Indicates whether the control has a physical state where it will not send
> +/// any reports.
> +///
> +/// Refer to [Device Class Definition for HID 1.11]
> +/// Section 6.2.2.5 Input, Output, and Feature Items.
> +///
> +/// [Device Class Definition for HID 1.11]: https://www.usb.org/sites/default/files/hid1_11.pdf
> +pub const MAIN_ITEM_NULL_STATE: u8 = bindings::HID_MAIN_ITEM_NULL_STATE as u8;
> +
> +/// Indicates whether the control requires host system logic to change state.
> +///
> +/// Refer to [Device Class Definition for HID 1.11]
> +/// Section 6.2.2.5 Input, Output, and Feature Items.
> +///
> +/// [Device Class Definition for HID 1.11]: https://www.usb.org/sites/default/files/hid1_11.pdf
> +pub const MAIN_ITEM_VOLATILE: u8 = bindings::HID_MAIN_ITEM_VOLATILE as u8;
> +
> +/// Indicates whether the item is fixed size or a variable buffer of bytes.
> +///
> +/// Refer to [Device Class Definition for HID 1.11]
> +/// Section 6.2.2.5 Input, Output, and Feature Items.
> +///
> +/// [Device Class Definition for HID 1.11]: https://www.usb.org/sites/default/files/hid1_11.pdf
> +pub const MAIN_ITEM_BUFFERED_BYTE: u8 = bindings::HID_MAIN_ITEM_BUFFERED_BYTE as u8;
> 

HID_MAIN_ITEM_BUFFERED_BYTE has a value of 0x100 which will not fit in a u8.
Maybe all the MAIN_ITEM_* bits should bound as u16?

HID 1.11 actually defines them as a u32 with bits 9-31 reserved for future use.

Thanks
Terry

+
> +/// HID device groups are intended to help categories HID devices based on a set
> +/// of common quirks and logic that they will require to function correctly.
> +#[repr(u16)]
> +pub enum Group {
> +    /// Used to match a device against any group when probing.
> +    Any = bindings::HID_GROUP_ANY as u16,
> +
> +    /// Indicates a generic device that should need no custom logic from the
> +    /// core HID stack.
> +    Generic = bindings::HID_GROUP_GENERIC as u16,
> +
> +    /// Maps multitouch devices to hid-multitouch instead of hid-generic.
> +    Multitouch = bindings::HID_GROUP_MULTITOUCH as u16,
> +
> +    /// Used for autodetecing and mapping of HID sensor hubs to
> +    /// hid-sensor-hub.
> +    SensorHub = bindings::HID_GROUP_SENSOR_HUB as u16,
> +
> +    /// Used for autodetecing and mapping Win 8 multitouch devices to set the
> +    /// needed quirks.
> +    MultitouchWin8 = bindings::HID_GROUP_MULTITOUCH_WIN_8 as u16,
> +
> +    // Vendor-specific device groups.
> +    /// Used to distinguish Synpatics touchscreens from other products. The
> +    /// touchscreens will be handled by hid-multitouch instead, while everything
> +    /// else will be managed by hid-rmi.
> +    RMI = bindings::HID_GROUP_RMI as u16,
> +
> +    /// Used for hid-core handling to automatically identify Wacom devices and
> +    /// have them probed by hid-wacom.
> +    Wacom = bindings::HID_GROUP_WACOM as u16,
> +
> +    /// Used by logitech-djreceiver and logitech-djdevice to autodetect if
> +    /// devices paied to the DJ receivers are DJ devices and handle them with
> +    /// the device driver.
> +    LogitechDJDevice = bindings::HID_GROUP_LOGITECH_DJ_DEVICE as u16,
> +
> +    /// Since the Valve Steam Controller only has vendor-specific usages,
> +    /// prevent hid-generic from parsing its reports since there would be
> +    /// nothing hid-generic could do for the device.
> +    Steam = bindings::HID_GROUP_STEAM as u16,
> +
> +    /// Used to differentiate 27 Mhz frequency Logitech DJ devices from other
> +    /// Logitech DJ devices.
> +    Logitech27MHzDevice = bindings::HID_GROUP_LOGITECH_27MHZ_DEVICE as u16,
> +
> +    /// Used for autodetecting and mapping Vivaldi devices to hid-vivaldi.
> +    Vivaldi = bindings::HID_GROUP_VIVALDI as u16,
> +}
> +
> +// TODO: use `const_trait_impl` once stabilized:
> +//
> +// ```
> +// impl const From<Group> for u16 {
> +//     /// [`Group`] variants are represented by [`u16`] values.
> +//     fn from(value: Group) -> Self {
> +//         value as Self
> +//     }
> +// }
> +// ```
> +impl Group {
> +    /// Internal function used to convert [`Group`] variants into [`u16`].
> +    const fn into_u16(self) -> u16 {
> +        self as u16
> +    }
> +}
> +
> +impl TryFrom<u16> for Group {
> +    type Error = &'static str;
> +
> +    /// [`u16`] values can be safely converted to [`Group`] variants.
> +    fn try_from(value: u16) -> Result<Group, Self::Error> {
> +        match value.into() {
> +            bindings::HID_GROUP_GENERIC => Ok(Group::Generic),
> +            bindings::HID_GROUP_MULTITOUCH => Ok(Group::Multitouch),
> +            bindings::HID_GROUP_SENSOR_HUB => Ok(Group::SensorHub),
> +            bindings::HID_GROUP_MULTITOUCH_WIN_8 => Ok(Group::MultitouchWin8),
> +            bindings::HID_GROUP_RMI => Ok(Group::RMI),
> +            bindings::HID_GROUP_WACOM => Ok(Group::Wacom),
> +            bindings::HID_GROUP_LOGITECH_DJ_DEVICE => Ok(Group::LogitechDJDevice),
> +            bindings::HID_GROUP_STEAM => Ok(Group::Steam),
> +            bindings::HID_GROUP_LOGITECH_27MHZ_DEVICE => Ok(Group::Logitech27MHzDevice),
> +            bindings::HID_GROUP_VIVALDI => Ok(Group::Vivaldi),
> +            _ => Err("Unknown HID group encountered!"),
> +        }
> +    }
> +}
> +
> +/// The HID device representation.
> +///
> +/// This structure represents the Rust abstraction for a C `struct hid_device`.
> +/// The implementation abstracts the usage of an already existing C `struct
> +/// hid_device` within Rust code that we get passed from the C side.
> +///
> +/// # Invariants
> +///
> +/// A [`Device`] instance represents a valid `struct hid_device` created by the
> +/// C portion of the kernel.
> +#[repr(transparent)]
> +pub struct Device<Ctx: device::DeviceContext = device::Normal>(
> +    Opaque<bindings::hid_device>,
> +    PhantomData<Ctx>,
> +);
> +
> +impl<Ctx: device::DeviceContext> Device<Ctx> {
> +    fn as_raw(&self) -> *mut bindings::hid_device {
> +        self.0.get()
> +    }
> +
> +    /// Returns the HID transport bus ID.
> +    pub fn bus(&self) -> u16 {
> +        // SAFETY: `self.as_raw` is a valid pointer to a `struct hid_device`
> +        unsafe { *self.as_raw() }.bus
> +    }
> +
> +    /// Returns the HID report group.
> +    pub fn group(&self) -> Result<Group, &'static str> {
> +        // SAFETY: `self.as_raw` is a valid pointer to a `struct hid_device`
> +        unsafe { *self.as_raw() }.group.try_into()
> +    }
> +
> +    /// Returns the HID vendor ID.
> +    pub fn vendor(&self) -> u32 {
> +        // SAFETY: `self.as_raw` is a valid pointer to a `struct hid_device`
> +        unsafe { *self.as_raw() }.vendor
> +    }
> +
> +    /// Returns the HID product ID.
> +    pub fn product(&self) -> u32 {
> +        // SAFETY: `self.as_raw` is a valid pointer to a `struct hid_device`
> +        unsafe { *self.as_raw() }.product
> +    }
> +}
> +
> +// SAFETY: `Device` is a transparent wrapper of a type that doesn't depend on `Device`'s generic
> +// argument.
> +kernel::impl_device_context_deref!(unsafe { Device });
> +kernel::impl_device_context_into_aref!(Device);
> +
> +// SAFETY: Instances of `Device` are always reference-counted.
> +unsafe impl crate::types::AlwaysRefCounted for Device {
> +    fn inc_ref(&self) {
> +        // SAFETY: The existence of a shared reference guarantees that the refcount is non-zero.
> +        unsafe { bindings::get_device(&raw mut (*self.as_raw()).dev) };
> +    }
> +
> +    unsafe fn dec_ref(obj: NonNull<Self>) {
> +        // SAFETY: The safety requirements guarantee that the refcount is non-zero.
> +        unsafe { bindings::put_device(&raw mut (*obj.cast::<bindings::hid_device>().as_ptr()).dev) }
> +    }
> +}
> +
> +impl<Ctx: device::DeviceContext> AsRef<device::Device<Ctx>> for Device<Ctx> {
> +    fn as_ref(&self) -> &device::Device<Ctx> {
> +        // SAFETY: By the type invariant of `Self`, `self.as_raw()` is a pointer to a valid
> +        // `struct hid_device`.
> +        let dev = unsafe { addr_of_mut!((*self.as_raw()).dev) };
> +
> +        // SAFETY: `dev` points to a valid `struct device`.
> +        unsafe { device::Device::from_raw(dev) }
> +    }
> +}
> +
> +/// Abstraction for the HID device ID structure `struct hid_device_id`.
> +#[repr(transparent)]
> +#[derive(Clone, Copy)]
> +pub struct DeviceId(bindings::hid_device_id);
> +
> +impl DeviceId {
> +    /// Equivalent to C's `HID_USB_DEVICE` macro.
> +    ///
> +    /// Create a new `hid::DeviceId` from a group, vendor ID, and device ID
> +    /// number.
> +    pub const fn new_usb(group: Group, vendor: u32, product: u32) -> Self {
> +        Self(bindings::hid_device_id {
> +            bus: 0x3, // BUS_USB
> +            group: group.into_u16(),
> +            vendor,
> +            product,
> +            driver_data: 0,
> +        })
> +    }
> +
> +    /// Returns the HID transport bus ID.
> +    pub fn bus(&self) -> u16 {
> +        self.0.bus
> +    }
> +
> +    /// Returns the HID report group.
> +    pub fn group(&self) -> Result<Group, &'static str> {
> +        self.0.group.try_into()
> +    }
> +
> +    /// Returns the HID vendor ID.
> +    pub fn vendor(&self) -> u32 {
> +        self.0.vendor
> +    }
> +
> +    /// Returns the HID product ID.
> +    pub fn product(&self) -> u32 {
> +        self.0.product
> +    }
> +}
> +
> +// SAFETY:
> +// * `DeviceId` is a `#[repr(transparent)` wrapper of `hid_device_id` and does not add
> +//   additional invariants, so it's safe to transmute to `RawType`.
> +// * `DRIVER_DATA_OFFSET` is the offset to the `driver_data` field.
> +unsafe impl RawDeviceId for DeviceId {
> +    type RawType = bindings::hid_device_id;
> +}
> +
> +// SAFETY: `DRIVER_DATA_OFFSET` is the offset to the `driver_data` field.
> +unsafe impl RawDeviceIdIndex for DeviceId {
> +    const DRIVER_DATA_OFFSET: usize = core::mem::offset_of!(bindings::hid_device_id, driver_data);
> +
> +    fn index(&self) -> usize {
> +        self.0.driver_data
> +    }
> +}
> +
> +/// [`IdTable`] type for HID.
> +pub type IdTable<T> = &'static dyn kernel::device_id::IdTable<DeviceId, T>;
> +
> +/// Create a HID [`IdTable`] with its alias for modpost.
> +#[macro_export]
> +macro_rules! hid_device_table {
> +    ($table_name:ident, $module_table_name:ident, $id_info_type: ty, $table_data: expr) => {
> +        const $table_name: $crate::device_id::IdArray<
> +            $crate::hid::DeviceId,
> +            $id_info_type,
> +            { $table_data.len() },
> +        > = $crate::device_id::IdArray::new($table_data);
> +
> +        $crate::module_device_table!("hid", $module_table_name, $table_name);
> +    };
> +}
> +
> +/// The HID driver trait.
> +///
> +/// # Examples
> +///
> +/// ```
> +/// use kernel::{bindings, device, hid};
> +///
> +/// struct MyDriver;
> +///
> +/// kernel::hid_device_table!(
> +///     HID_TABLE,
> +///     MODULE_HID_TABLE,
> +///     <MyDriver as hid::Driver>::IdInfo,
> +///     [(
> +///         hid::DeviceId::new_usb(
> +///             hid::Group::Steam,
> +///             bindings::USB_VENDOR_ID_VALVE,
> +///             bindings::USB_DEVICE_ID_STEAM_DECK,
> +///         ),
> +///         (),
> +///     )]
> +/// );
> +///
> +/// #[vtable]
> +/// impl hid::Driver for MyDriver {
> +///     type IdInfo = ();
> +///     const ID_TABLE: hid::IdTable<Self::IdInfo> = &HID_TABLE;
> +///
> +///     /// This function is optional to implement.
> +///     fn report_fixup<'a, 'b: 'a>(_hdev: &hid::Device<device::Core>, rdesc: &'b mut [u8]) -> &'a [u8] {
> +///         // Perform some report descriptor fixup.
> +///         rdesc
> +///     }
> +/// }
> +/// ```
> +/// Drivers must implement this trait in order to get a HID driver registered.
> +/// Please refer to the `Adapter` documentation for an example.
> +#[vtable]
> +pub trait Driver: Send {
> +    /// The type holding information about each device id supported by the driver.
> +    // TODO: Use `associated_type_defaults` once stabilized:
> +    //
> +    // ```
> +    // type IdInfo: 'static = ();
> +    // ```
> +    type IdInfo: 'static;
> +
> +    /// The table of device ids supported by the driver.
> +    const ID_TABLE: IdTable<Self::IdInfo>;
> +
> +    /// Called before report descriptor parsing. Can be used to mutate the
> +    /// report descriptor before the core HID logic processes the descriptor.
> +    /// Useful for problematic report descriptors that prevent HID devices from
> +    /// functioning correctly.
> +    ///
> +    /// Optional to implement.
> +    fn report_fixup<'a, 'b: 'a>(_hdev: &Device<device::Core>, _rdesc: &'b mut [u8]) -> &'a [u8] {
> +        build_error!(VTABLE_DEFAULT_ERROR)
> +    }
> +}
> +
> +/// An adapter for the registration of HID drivers.
> +pub struct Adapter<T: Driver>(T);
> +
> +// SAFETY:
> +// - `bindings::hid_driver` is a C type declared as `repr(C)`.
> +// - `T` is the type of the driver's device private data.
> +// - `struct hid_driver` embeds a `struct device_driver`.
> +// - `DEVICE_DRIVER_OFFSET` is the correct byte offset to the embedded `struct device_driver`.
> +unsafe impl<T: Driver + 'static> driver::DriverLayout for Adapter<T> {
> +    type DriverType = bindings::hid_driver;
> +    type DriverData = T;
> +    const DEVICE_DRIVER_OFFSET: usize = core::mem::offset_of!(Self::DriverType, driver);
> +}
> +
> +// SAFETY: A call to `unregister` for a given instance of `DriverType` is guaranteed to be valid if
> +// a preceding call to `register` has been successful.
> +unsafe impl<T: Driver + 'static> driver::RegistrationOps for Adapter<T> {
> +    unsafe fn register(
> +        hdrv: &Opaque<Self::DriverType>,
> +        name: &'static CStr,
> +        module: &'static ThisModule,
> +    ) -> Result {
> +        // SAFETY: It's safe to set the fields of `struct hid_driver` on initialization.
> +        unsafe {
> +            (*hdrv.get()).name = name.as_char_ptr();
> +            (*hdrv.get()).id_table = T::ID_TABLE.as_ptr();
> +            (*hdrv.get()).report_fixup = if T::HAS_REPORT_FIXUP {
> +                Some(Self::report_fixup_callback)
> +            } else {
> +                None
> +            };
> +        }
> +
> +        // SAFETY: `hdrv` is guaranteed to be a valid `DriverType`
> +        to_result(unsafe {
> +            bindings::__hid_register_driver(hdrv.get(), module.0, name.as_char_ptr())
> +        })
> +    }
> +
> +    unsafe fn unregister(hdrv: &Opaque<Self::DriverType>) {
> +        // SAFETY: `hdrv` is guaranteed to be a valid `DriverType`
> +        unsafe { bindings::hid_unregister_driver(hdrv.get()) }
> +    }
> +}
> +
> +impl<T: Driver + 'static> Adapter<T> {
> +    extern "C" fn report_fixup_callback(
> +        hdev: *mut bindings::hid_device,
> +        buf: *mut u8,
> +        size: *mut kernel::ffi::c_uint,
> +    ) -> *const u8 {
> +        // SAFETY: The HID subsystem only ever calls the report_fixup callback
> +        // with a valid pointer to a `struct hid_device`.
> +        //
> +        // INVARIANT: `hdev` is valid for the duration of
> +        // `report_fixup_callback()`.
> +        let hdev = unsafe { &*hdev.cast::<Device<device::Core>>() };
> +
> +        // SAFETY: The HID subsystem only ever calls the report_fixup callback
> +        // with a valid pointer to a `kernel::ffi::c_uint`.
> +        //
> +        // INVARIANT: `size` is valid for the duration of
> +        // `report_fixup_callback()`.
> +        let buf_len: usize = match unsafe { *size }.try_into() {
> +            Ok(len) => len,
> +            Err(e) => {
> +                dev_err!(
> +                    hdev.as_ref(),
> +                    "Cannot fix report description due to {:?}!\n",
> +                    e
> +                );
> +
> +                return buf;
> +            }
> +        };
> +
> +        // Build a mutable Rust slice from `buf` and `size`.
> +        //
> +        // SAFETY: The HID subsystem only ever calls the `report_fixup callback`
> +        // with a valid pointer to a `u8` buffer.
> +        //
> +        // INVARIANT: `buf` is valid for the duration of
> +        // `report_fixup_callback()`.
> +        let rdesc_slice = unsafe { core::slice::from_raw_parts_mut(buf, buf_len) };
> +        let rdesc_slice = T::report_fixup(hdev, rdesc_slice);
> +
> +        match rdesc_slice.len().try_into() {
> +            // SAFETY: The HID subsystem only ever calls the report_fixup
> +            // callback with a valid pointer to a `kernel::ffi::c_uint`.
> +            //
> +            // INVARIANT: `size` is valid for the duration of
> +            // `report_fixup_callback()`.
> +            Ok(len) => unsafe { *size = len },
> +            Err(e) => {
> +                dev_err!(
> +                    hdev.as_ref(),
> +                    "Fixed report description will not be used due to {:?}!\n",
> +                    e
> +                );
> +
> +                return buf;
> +            }
> +        }
> +
> +        rdesc_slice.as_ptr()
> +    }
> +}
> +
> +/// Declares a kernel module that exposes a single HID driver.
> +///
> +/// # Examples
> +///
> +/// ```ignore
> +/// kernel::module_hid_driver! {
> +///     type: MyDriver,
> +///     name: "Module name",
> +///     authors: ["Author name"],
> +///     description: "Description",
> +///     license: "GPL",
> +/// }
> +/// ```
> +#[macro_export]
> +macro_rules! module_hid_driver {
> +    ($($f:tt)*) => {
> +        $crate::module_driver!(<T>, $crate::hid::Adapter<T>, { $($f)* });
> +    };
> +}
> diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
> index 3da92f18f4ee..e2dcacd9369e 100644
> --- a/rust/kernel/lib.rs
> +++ b/rust/kernel/lib.rs
> @@ -102,6 +102,8 @@
>  pub mod id_pool;
>  #[doc(hidden)]
>  pub mod impl_flags;
> +#[cfg(CONFIG_RUST_HID_ABSTRACTIONS)]
> +pub mod hid;
>  pub mod init;
>  pub mod io;
>  pub mod ioctl;


^ permalink raw reply

* [PATCH] Input: adxl34x - Drop redundant error variable in adxl34x_i2c_probe
From: Thorsten Blum @ 2026-02-23  0:03 UTC (permalink / raw)
  To: Dmitry Torokhov; +Cc: Thorsten Blum, linux-input, linux-kernel

Inline i2c_check_functionality(), which really returns a bool and not an
error code, and remove the local variable.  No functional changes.

Signed-off-by: Thorsten Blum <thorsten.blum@linux.dev>
---
 drivers/input/misc/adxl34x-i2c.c | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/drivers/input/misc/adxl34x-i2c.c b/drivers/input/misc/adxl34x-i2c.c
index c05d898898e8..5ea0ce42a507 100644
--- a/drivers/input/misc/adxl34x-i2c.c
+++ b/drivers/input/misc/adxl34x-i2c.c
@@ -77,11 +77,8 @@ static const struct adxl34x_bus_ops adxl34x_i2c_bops = {
 static int adxl34x_i2c_probe(struct i2c_client *client)
 {
 	struct adxl34x *ac;
-	int error;
 
-	error = i2c_check_functionality(client->adapter,
-			I2C_FUNC_SMBUS_BYTE_DATA);
-	if (!error) {
+	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) {
 		dev_err(&client->dev, "SMBUS Byte Data not Supported\n");
 		return -EIO;
 	}
-- 
Thorsten Blum <thorsten.blum@linux.dev>
GPG: 1D60 735E 8AEF 3BE4 73B6  9D84 7336 78FD 8DFE EAD4


^ permalink raw reply related

* Re: [PATCH v6 2/2] rust: hid: Glorious PC Gaming Race Model O and O- mice reference driver
From: Greg KH @ 2026-02-23  2:38 UTC (permalink / raw)
  To: Rahul Rameshbabu
  Cc: linux-input, linux-kernel, rust-for-linux, a.hindborg,
	alex.gaynor, aliceryhl, benjamin.tissoires, benno.lossin,
	bjorn3_gh, boqun.feng, dakr, db48x, gary, jikos, ojeda,
	peter.hutterer, tmgross
In-Reply-To: <20260222215611.79760-3-sergeantsagara@protonmail.com>

On Sun, Feb 22, 2026 at 09:56:51PM +0000, Rahul Rameshbabu wrote:
>  MAINTAINERS                           |  6 +++
>  drivers/hid/hid-glorious.c            |  2 +
>  drivers/hid/hid_glorious_rust.rs      | 60 +++++++++++++++++++++++++++
>  drivers/hid/rust/Kconfig              | 16 +++++++
>  drivers/hid/rust/Makefile             |  6 +++
>  drivers/hid/rust/hid_glorious_rust.rs | 60 +++++++++++++++++++++++++++
>  6 files changed, 150 insertions(+)
>  create mode 100644 drivers/hid/hid_glorious_rust.rs
>  create mode 100644 drivers/hid/rust/Makefile
>  create mode 100644 drivers/hid/rust/hid_glorious_rust.rs

You seem to have the same file in here in two different locations :(


^ permalink raw reply

* Re: [PATCH v6 1/2] rust: core abstractions for HID drivers
From: Rahul Rameshbabu @ 2026-02-23  2:47 UTC (permalink / raw)
  To: Terry Junge
  Cc: linux-input, linux-kernel, rust-for-linux, a.hindborg,
	alex.gaynor, aliceryhl, benjamin.tissoires, benno.lossin,
	bjorn3_gh, boqun.feng, dakr, db48x, gary, jikos, ojeda,
	peter.hutterer, tmgross
In-Reply-To: <b1ea6e0a-35cf-4bc7-804b-9b0e08a40783@cosmicgizmosystems.com>

On Sun, 22 Feb, 2026 15:39:45 -0800 "Terry Junge" <linuxhid@cosmicgizmosystems.com> wrote:
> On 2/22/26 1:56 PM, Rahul Rameshbabu wrote:
>> These abstractions enable the development of HID drivers in Rust by binding
>> with the HID core C API. They provide Rust types that map to the
>> equivalents in C. In this initial draft, only hid_device and hid_device_id
>> are provided direct Rust type equivalents. hid_driver is specially wrapped
>> with a custom Driver type. The module_hid_driver! macro provides analogous
>> functionality to its C equivalent. Only the .report_fixup callback is
>> binded to Rust so far.
>>
>> Future work for these abstractions would include more bindings for common
>> HID-related types, such as hid_field, hid_report_enum, and hid_report as
>> well as more bus callbacks. Providing Rust equivalents to useful core HID
>> functions will also be necessary for HID driver development in Rust.
>>
>> Signed-off-by: Rahul Rameshbabu <sergeantsagara@protonmail.com>
>> ---
>>
>> Notes:
>>     Changelog:
>>
>>         v5->v6:
>>           * Converted From<u16> for Group to TryFrom<u16> to properly handle
>>             error case
>>           * Renamed into method for Group to into_u16 to not conflate with the
>>             From trait
>>           * Refactored new upstream changes to RegistrationOps
>>           * Implemented DriverLayout trait for hid::Adapter<T>
>>         v4->v5:
>>           * Add rust/ to drivers/hid/Makefile
>>           * Implement RawDeviceIdIndex trait
>>         v3->v4:
>>           * Removed specifying tree in MAINTAINERS file since that is up for
>>             debate
>>           * Minor rebase cleanup
>>           * Moved driver logic under drivers/hid/rust
>>         v2->v3:
>>           * Implemented AlwaysRefCounted trait using embedded struct device's
>>             reference counts instead of the separate reference counter in struct
>>             hid_device
>>           * Used &raw mut as appropriate
>>           * Binded include/linux/device.h for get_device and put_device
>>           * Cleaned up various comment related formatting
>>           * Minified dev_err! format string
>>           * Updated Group enum to be repr(u16)
>>           * Implemented From<u16> trait for Group
>>           * Added TODO comment when const_trait_impl stabilizes
>>           * Made group getter functions return a Group variant instead of a raw
>>             number
>>           * Made sure example code builds
>>         v1->v2:
>>           * Binded drivers/hid/hid-ids.h for use in Rust drivers
>>           * Remove pre-emptive referencing of a C HID driver instance before
>>             it is fully initialized in the driver registration path
>>           * Moved static getters to generic Device trait implementation, so
>>             they can be used by all device::DeviceContext
>>           * Use core macros for supporting DeviceContext transitions
>>           * Implemented the AlwaysRefCounted and AsRef traits
>>           * Make use for dev_err! as appropriate
>>         RFC->v1:
>>           * Use Danilo's core infrastructure
>>           * Account for HID device groups
>>           * Remove probe and remove callbacks
>>           * Implement report_fixup support
>>           * Properly comment code including SAFETY comments
>>
>>  MAINTAINERS                     |   8 +
>>  drivers/hid/Kconfig             |   2 +
>>  drivers/hid/Makefile            |   2 +
>>  drivers/hid/rust/Kconfig        |  12 +
>>  rust/bindings/bindings_helper.h |   3 +
>>  rust/kernel/hid.rs              | 530 ++++++++++++++++++++++++++++++++
>>  rust/kernel/lib.rs              |   2 +
>>  7 files changed, 559 insertions(+)
>>  create mode 100644 drivers/hid/rust/Kconfig
>>  create mode 100644 rust/kernel/hid.rs
>>
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index b8d8a5c41597..1fee14024fa2 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -11319,6 +11319,14 @@ F:	include/uapi/linux/hid*
>>  F:	samples/hid/
>>  F:	tools/testing/selftests/hid/
>>
>> +HID CORE LAYER [RUST]
>> +M:	Rahul Rameshbabu <sergeantsagara@protonmail.com>
>> +R:	Benjamin Tissoires <bentiss@kernel.org>
>> +L:	linux-input@vger.kernel.org
>> +S:	Maintained
>> +F:	drivers/hid/rust/*.rs
>> +F:	rust/kernel/hid.rs
>> +
>>  HID LOGITECH DRIVERS
>>  R:	Filipe Laíns <lains@riseup.net>
>>  L:	linux-input@vger.kernel.org
>> diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
>> index c1d9f7c6a5f2..750c2d49a806 100644
>> --- a/drivers/hid/Kconfig
>> +++ b/drivers/hid/Kconfig
>> @@ -1439,6 +1439,8 @@ endmenu
>>
>>  source "drivers/hid/bpf/Kconfig"
>>
>> +source "drivers/hid/rust/Kconfig"
>> +
>>  source "drivers/hid/i2c-hid/Kconfig"
>>
>>  source "drivers/hid/intel-ish-hid/Kconfig"
>> diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
>> index e01838239ae6..b78ab84c47b4 100644
>> --- a/drivers/hid/Makefile
>> +++ b/drivers/hid/Makefile
>> @@ -8,6 +8,8 @@ hid-$(CONFIG_HID_HAPTIC)	+= hid-haptic.o
>>
>>  obj-$(CONFIG_HID_BPF)		+= bpf/
>>
>> +obj-$(CONFIG_RUST_HID_ABSTRACTIONS)		+= rust/
>> +
>>  obj-$(CONFIG_HID)		+= hid.o
>>  obj-$(CONFIG_UHID)		+= uhid.o
>>
>> diff --git a/drivers/hid/rust/Kconfig b/drivers/hid/rust/Kconfig
>> new file mode 100644
>> index 000000000000..d3247651829e
>> --- /dev/null
>> +++ b/drivers/hid/rust/Kconfig
>> @@ -0,0 +1,12 @@
>> +# SPDX-License-Identifier: GPL-2.0-only
>> +menu "Rust HID support"
>> +
>> +config RUST_HID_ABSTRACTIONS
>> +	bool "Rust HID abstractions support"
>> +	depends on RUST
>> +	depends on HID=y
>> +	help
>> +	  Adds support needed for HID drivers written in Rust. It provides a
>> +	  wrapper around the C hid core.
>> +
>> +endmenu # Rust HID support
>> diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
>> index 083cc44aa952..200e58af27a3 100644
>> --- a/rust/bindings/bindings_helper.h
>> +++ b/rust/bindings/bindings_helper.h
>> @@ -48,6 +48,7 @@
>>  #include <linux/cpumask.h>
>>  #include <linux/cred.h>
>>  #include <linux/debugfs.h>
>> +#include <linux/device.h>
>>  #include <linux/device/faux.h>
>>  #include <linux/dma-direction.h>
>>  #include <linux/dma-mapping.h>
>> @@ -60,6 +61,8 @@
>>  #include <linux/i2c.h>
>>  #include <linux/interrupt.h>
>>  #include <linux/io-pgtable.h>
>> +#include <linux/hid.h>
>> +#include "../../drivers/hid/hid-ids.h"
>>  #include <linux/ioport.h>
>>  #include <linux/jiffies.h>
>>  #include <linux/jump_label.h>
>> diff --git a/rust/kernel/hid.rs b/rust/kernel/hid.rs
>> new file mode 100644
>> index 000000000000..b9db542d923a
>> --- /dev/null
>> +++ b/rust/kernel/hid.rs
>> @@ -0,0 +1,530 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +
>> +// Copyright (C) 2025 Rahul Rameshbabu <sergeantsagara@protonmail.com>
>> +
>> +//! Abstractions for the HID interface.
>> +//!
>> +//! C header: [`include/linux/hid.h`](srctree/include/linux/hid.h)
>> +
>> +use crate::{
>> +    device,
>> +    device_id::{
>> +        RawDeviceId,
>> +        RawDeviceIdIndex, //
>> +    },
>> +    driver,
>> +    error::*,
>> +    prelude::*,
>> +    types::Opaque, //
>> +};
>> +use core::{
>> +    marker::PhantomData,
>> +    ptr::{
>> +        addr_of_mut,
>> +        NonNull, //
>> +    } //
>> +};
>> +
>> +/// Indicates the item is static read-only.
>> +///
>> +/// Refer to [Device Class Definition for HID 1.11]
>> +/// Section 6.2.2.5 Input, Output, and Feature Items.
>> +///
>> +/// [Device Class Definition for HID 1.11]: https://www.usb.org/sites/default/files/hid1_11.pdf
>> +pub const MAIN_ITEM_CONSTANT: u8 = bindings::HID_MAIN_ITEM_CONSTANT as u8;
>> +
>> +/// Indicates the item represents data from a physical control.
>> +///
>> +/// Refer to [Device Class Definition for HID 1.11]
>> +/// Section 6.2.2.5 Input, Output, and Feature Items.
>> +///
>> +/// [Device Class Definition for HID 1.11]: https://www.usb.org/sites/default/files/hid1_11.pdf
>> +pub const MAIN_ITEM_VARIABLE: u8 = bindings::HID_MAIN_ITEM_VARIABLE as u8;
>> +
>> +/// Indicates the item should be treated as a relative change from the previous
>> +/// report.
>> +///
>> +/// Refer to [Device Class Definition for HID 1.11]
>> +/// Section 6.2.2.5 Input, Output, and Feature Items.
>> +///
>> +/// [Device Class Definition for HID 1.11]: https://www.usb.org/sites/default/files/hid1_11.pdf
>> +pub const MAIN_ITEM_RELATIVE: u8 = bindings::HID_MAIN_ITEM_RELATIVE as u8;
>> +
>> +/// Indicates the item should wrap around when reaching the extreme high or
>> +/// extreme low values.
>> +///
>> +/// Refer to [Device Class Definition for HID 1.11]
>> +/// Section 6.2.2.5 Input, Output, and Feature Items.
>> +///
>> +/// [Device Class Definition for HID 1.11]: https://www.usb.org/sites/default/files/hid1_11.pdf
>> +pub const MAIN_ITEM_WRAP: u8 = bindings::HID_MAIN_ITEM_WRAP as u8;
>> +
>> +/// Indicates the item should wrap around when reaching the extreme high or
>> +/// extreme low values.
>> +///
>> +/// Refer to [Device Class Definition for HID 1.11]
>> +/// Section 6.2.2.5 Input, Output, and Feature Items.
>> +///
>> +/// [Device Class Definition for HID 1.11]: https://www.usb.org/sites/default/files/hid1_11.pdf
>> +pub const MAIN_ITEM_NONLINEAR: u8 = bindings::HID_MAIN_ITEM_NONLINEAR as u8;
>> +
>> +/// Indicates whether the control has a preferred state it will physically
>> +/// return to without user intervention.
>> +///
>> +/// Refer to [Device Class Definition for HID 1.11]
>> +/// Section 6.2.2.5 Input, Output, and Feature Items.
>> +///
>> +/// [Device Class Definition for HID 1.11]: https://www.usb.org/sites/default/files/hid1_11.pdf
>> +pub const MAIN_ITEM_NO_PREFERRED: u8 = bindings::HID_MAIN_ITEM_NO_PREFERRED as u8;
>> +
>> +/// Indicates whether the control has a physical state where it will not send
>> +/// any reports.
>> +///
>> +/// Refer to [Device Class Definition for HID 1.11]
>> +/// Section 6.2.2.5 Input, Output, and Feature Items.
>> +///
>> +/// [Device Class Definition for HID 1.11]: https://www.usb.org/sites/default/files/hid1_11.pdf
>> +pub const MAIN_ITEM_NULL_STATE: u8 = bindings::HID_MAIN_ITEM_NULL_STATE as u8;
>> +
>> +/// Indicates whether the control requires host system logic to change state.
>> +///
>> +/// Refer to [Device Class Definition for HID 1.11]
>> +/// Section 6.2.2.5 Input, Output, and Feature Items.
>> +///
>> +/// [Device Class Definition for HID 1.11]: https://www.usb.org/sites/default/files/hid1_11.pdf
>> +pub const MAIN_ITEM_VOLATILE: u8 = bindings::HID_MAIN_ITEM_VOLATILE as u8;
>> +
>> +/// Indicates whether the item is fixed size or a variable buffer of bytes.
>> +///
>> +/// Refer to [Device Class Definition for HID 1.11]
>> +/// Section 6.2.2.5 Input, Output, and Feature Items.
>> +///
>> +/// [Device Class Definition for HID 1.11]: https://www.usb.org/sites/default/files/hid1_11.pdf
>> +pub const MAIN_ITEM_BUFFERED_BYTE: u8 = bindings::HID_MAIN_ITEM_BUFFERED_BYTE as u8;
>>
>
> HID_MAIN_ITEM_BUFFERED_BYTE has a value of 0x100 which will not fit in a u8.
> Maybe all the MAIN_ITEM_* bits should bound as u16?
>
> HID 1.11 actually defines them as a u32 with bits 9-31 reserved for future use.

Thanks Terry for pointing this out. I will give some time for other
reviewers to comment but will make the needed change for a v7.

Thanks,
Rahul Rameshbabu

>
> Thanks
> Terry
>
> +
>> +/// HID device groups are intended to help categories HID devices based on a set
>> +/// of common quirks and logic that they will require to function correctly.
>> +#[repr(u16)]
>> +pub enum Group {
>> +    /// Used to match a device against any group when probing.
>> +    Any = bindings::HID_GROUP_ANY as u16,
>> +
>> +    /// Indicates a generic device that should need no custom logic from the
>> +    /// core HID stack.
>> +    Generic = bindings::HID_GROUP_GENERIC as u16,
>> +
>> +    /// Maps multitouch devices to hid-multitouch instead of hid-generic.
>> +    Multitouch = bindings::HID_GROUP_MULTITOUCH as u16,
>> +
>> +    /// Used for autodetecing and mapping of HID sensor hubs to
>> +    /// hid-sensor-hub.
>> +    SensorHub = bindings::HID_GROUP_SENSOR_HUB as u16,
>> +
>> +    /// Used for autodetecing and mapping Win 8 multitouch devices to set the
>> +    /// needed quirks.
>> +    MultitouchWin8 = bindings::HID_GROUP_MULTITOUCH_WIN_8 as u16,
>> +
>> +    // Vendor-specific device groups.
>> +    /// Used to distinguish Synpatics touchscreens from other products. The
>> +    /// touchscreens will be handled by hid-multitouch instead, while everything
>> +    /// else will be managed by hid-rmi.
>> +    RMI = bindings::HID_GROUP_RMI as u16,
>> +
>> +    /// Used for hid-core handling to automatically identify Wacom devices and
>> +    /// have them probed by hid-wacom.
>> +    Wacom = bindings::HID_GROUP_WACOM as u16,
>> +
>> +    /// Used by logitech-djreceiver and logitech-djdevice to autodetect if
>> +    /// devices paied to the DJ receivers are DJ devices and handle them with
>> +    /// the device driver.
>> +    LogitechDJDevice = bindings::HID_GROUP_LOGITECH_DJ_DEVICE as u16,
>> +
>> +    /// Since the Valve Steam Controller only has vendor-specific usages,
>> +    /// prevent hid-generic from parsing its reports since there would be
>> +    /// nothing hid-generic could do for the device.
>> +    Steam = bindings::HID_GROUP_STEAM as u16,
>> +
>> +    /// Used to differentiate 27 Mhz frequency Logitech DJ devices from other
>> +    /// Logitech DJ devices.
>> +    Logitech27MHzDevice = bindings::HID_GROUP_LOGITECH_27MHZ_DEVICE as u16,
>> +
>> +    /// Used for autodetecting and mapping Vivaldi devices to hid-vivaldi.
>> +    Vivaldi = bindings::HID_GROUP_VIVALDI as u16,
>> +}
>> +
>> +// TODO: use `const_trait_impl` once stabilized:
>> +//
>> +// ```
>> +// impl const From<Group> for u16 {
>> +//     /// [`Group`] variants are represented by [`u16`] values.
>> +//     fn from(value: Group) -> Self {
>> +//         value as Self
>> +//     }
>> +// }
>> +// ```
>> +impl Group {
>> +    /// Internal function used to convert [`Group`] variants into [`u16`].
>> +    const fn into_u16(self) -> u16 {
>> +        self as u16
>> +    }
>> +}
>> +
>> +impl TryFrom<u16> for Group {
>> +    type Error = &'static str;
>> +
>> +    /// [`u16`] values can be safely converted to [`Group`] variants.
>> +    fn try_from(value: u16) -> Result<Group, Self::Error> {
>> +        match value.into() {
>> +            bindings::HID_GROUP_GENERIC => Ok(Group::Generic),
>> +            bindings::HID_GROUP_MULTITOUCH => Ok(Group::Multitouch),
>> +            bindings::HID_GROUP_SENSOR_HUB => Ok(Group::SensorHub),
>> +            bindings::HID_GROUP_MULTITOUCH_WIN_8 => Ok(Group::MultitouchWin8),
>> +            bindings::HID_GROUP_RMI => Ok(Group::RMI),
>> +            bindings::HID_GROUP_WACOM => Ok(Group::Wacom),
>> +            bindings::HID_GROUP_LOGITECH_DJ_DEVICE => Ok(Group::LogitechDJDevice),
>> +            bindings::HID_GROUP_STEAM => Ok(Group::Steam),
>> +            bindings::HID_GROUP_LOGITECH_27MHZ_DEVICE => Ok(Group::Logitech27MHzDevice),
>> +            bindings::HID_GROUP_VIVALDI => Ok(Group::Vivaldi),
>> +            _ => Err("Unknown HID group encountered!"),
>> +        }
>> +    }
>> +}
>> +
>> +/// The HID device representation.
>> +///
>> +/// This structure represents the Rust abstraction for a C `struct hid_device`.
>> +/// The implementation abstracts the usage of an already existing C `struct
>> +/// hid_device` within Rust code that we get passed from the C side.
>> +///
>> +/// # Invariants
>> +///
>> +/// A [`Device`] instance represents a valid `struct hid_device` created by the
>> +/// C portion of the kernel.
>> +#[repr(transparent)]
>> +pub struct Device<Ctx: device::DeviceContext = device::Normal>(
>> +    Opaque<bindings::hid_device>,
>> +    PhantomData<Ctx>,
>> +);
>> +
>> +impl<Ctx: device::DeviceContext> Device<Ctx> {
>> +    fn as_raw(&self) -> *mut bindings::hid_device {
>> +        self.0.get()
>> +    }
>> +
>> +    /// Returns the HID transport bus ID.
>> +    pub fn bus(&self) -> u16 {
>> +        // SAFETY: `self.as_raw` is a valid pointer to a `struct hid_device`
>> +        unsafe { *self.as_raw() }.bus
>> +    }
>> +
>> +    /// Returns the HID report group.
>> +    pub fn group(&self) -> Result<Group, &'static str> {
>> +        // SAFETY: `self.as_raw` is a valid pointer to a `struct hid_device`
>> +        unsafe { *self.as_raw() }.group.try_into()
>> +    }
>> +
>> +    /// Returns the HID vendor ID.
>> +    pub fn vendor(&self) -> u32 {
>> +        // SAFETY: `self.as_raw` is a valid pointer to a `struct hid_device`
>> +        unsafe { *self.as_raw() }.vendor
>> +    }
>> +
>> +    /// Returns the HID product ID.
>> +    pub fn product(&self) -> u32 {
>> +        // SAFETY: `self.as_raw` is a valid pointer to a `struct hid_device`
>> +        unsafe { *self.as_raw() }.product
>> +    }
>> +}
>> +
>> +// SAFETY: `Device` is a transparent wrapper of a type that doesn't depend on `Device`'s generic
>> +// argument.
>> +kernel::impl_device_context_deref!(unsafe { Device });
>> +kernel::impl_device_context_into_aref!(Device);
>> +
>> +// SAFETY: Instances of `Device` are always reference-counted.
>> +unsafe impl crate::types::AlwaysRefCounted for Device {
>> +    fn inc_ref(&self) {
>> +        // SAFETY: The existence of a shared reference guarantees that the refcount is non-zero.
>> +        unsafe { bindings::get_device(&raw mut (*self.as_raw()).dev) };
>> +    }
>> +
>> +    unsafe fn dec_ref(obj: NonNull<Self>) {
>> +        // SAFETY: The safety requirements guarantee that the refcount is non-zero.
>> +        unsafe { bindings::put_device(&raw mut (*obj.cast::<bindings::hid_device>().as_ptr()).dev) }
>> +    }
>> +}
>> +
>> +impl<Ctx: device::DeviceContext> AsRef<device::Device<Ctx>> for Device<Ctx> {
>> +    fn as_ref(&self) -> &device::Device<Ctx> {
>> +        // SAFETY: By the type invariant of `Self`, `self.as_raw()` is a pointer to a valid
>> +        // `struct hid_device`.
>> +        let dev = unsafe { addr_of_mut!((*self.as_raw()).dev) };
>> +
>> +        // SAFETY: `dev` points to a valid `struct device`.
>> +        unsafe { device::Device::from_raw(dev) }
>> +    }
>> +}
>> +
>> +/// Abstraction for the HID device ID structure `struct hid_device_id`.
>> +#[repr(transparent)]
>> +#[derive(Clone, Copy)]
>> +pub struct DeviceId(bindings::hid_device_id);
>> +
>> +impl DeviceId {
>> +    /// Equivalent to C's `HID_USB_DEVICE` macro.
>> +    ///
>> +    /// Create a new `hid::DeviceId` from a group, vendor ID, and device ID
>> +    /// number.
>> +    pub const fn new_usb(group: Group, vendor: u32, product: u32) -> Self {
>> +        Self(bindings::hid_device_id {
>> +            bus: 0x3, // BUS_USB
>> +            group: group.into_u16(),
>> +            vendor,
>> +            product,
>> +            driver_data: 0,
>> +        })
>> +    }
>> +
>> +    /// Returns the HID transport bus ID.
>> +    pub fn bus(&self) -> u16 {
>> +        self.0.bus
>> +    }
>> +
>> +    /// Returns the HID report group.
>> +    pub fn group(&self) -> Result<Group, &'static str> {
>> +        self.0.group.try_into()
>> +    }
>> +
>> +    /// Returns the HID vendor ID.
>> +    pub fn vendor(&self) -> u32 {
>> +        self.0.vendor
>> +    }
>> +
>> +    /// Returns the HID product ID.
>> +    pub fn product(&self) -> u32 {
>> +        self.0.product
>> +    }
>> +}
>> +
>> +// SAFETY:
>> +// * `DeviceId` is a `#[repr(transparent)` wrapper of `hid_device_id` and does not add
>> +//   additional invariants, so it's safe to transmute to `RawType`.
>> +// * `DRIVER_DATA_OFFSET` is the offset to the `driver_data` field.
>> +unsafe impl RawDeviceId for DeviceId {
>> +    type RawType = bindings::hid_device_id;
>> +}
>> +
>> +// SAFETY: `DRIVER_DATA_OFFSET` is the offset to the `driver_data` field.
>> +unsafe impl RawDeviceIdIndex for DeviceId {
>> +    const DRIVER_DATA_OFFSET: usize = core::mem::offset_of!(bindings::hid_device_id, driver_data);
>> +
>> +    fn index(&self) -> usize {
>> +        self.0.driver_data
>> +    }
>> +}
>> +
>> +/// [`IdTable`] type for HID.
>> +pub type IdTable<T> = &'static dyn kernel::device_id::IdTable<DeviceId, T>;
>> +
>> +/// Create a HID [`IdTable`] with its alias for modpost.
>> +#[macro_export]
>> +macro_rules! hid_device_table {
>> +    ($table_name:ident, $module_table_name:ident, $id_info_type: ty, $table_data: expr) => {
>> +        const $table_name: $crate::device_id::IdArray<
>> +            $crate::hid::DeviceId,
>> +            $id_info_type,
>> +            { $table_data.len() },
>> +        > = $crate::device_id::IdArray::new($table_data);
>> +
>> +        $crate::module_device_table!("hid", $module_table_name, $table_name);
>> +    };
>> +}
>> +
>> +/// The HID driver trait.
>> +///
>> +/// # Examples
>> +///
>> +/// ```
>> +/// use kernel::{bindings, device, hid};
>> +///
>> +/// struct MyDriver;
>> +///
>> +/// kernel::hid_device_table!(
>> +///     HID_TABLE,
>> +///     MODULE_HID_TABLE,
>> +///     <MyDriver as hid::Driver>::IdInfo,
>> +///     [(
>> +///         hid::DeviceId::new_usb(
>> +///             hid::Group::Steam,
>> +///             bindings::USB_VENDOR_ID_VALVE,
>> +///             bindings::USB_DEVICE_ID_STEAM_DECK,
>> +///         ),
>> +///         (),
>> +///     )]
>> +/// );
>> +///
>> +/// #[vtable]
>> +/// impl hid::Driver for MyDriver {
>> +///     type IdInfo = ();
>> +///     const ID_TABLE: hid::IdTable<Self::IdInfo> = &HID_TABLE;
>> +///
>> +///     /// This function is optional to implement.
>> +///     fn report_fixup<'a, 'b: 'a>(_hdev: &hid::Device<device::Core>, rdesc: &'b mut [u8]) -> &'a [u8] {
>> +///         // Perform some report descriptor fixup.
>> +///         rdesc
>> +///     }
>> +/// }
>> +/// ```
>> +/// Drivers must implement this trait in order to get a HID driver registered.
>> +/// Please refer to the `Adapter` documentation for an example.
>> +#[vtable]
>> +pub trait Driver: Send {
>> +    /// The type holding information about each device id supported by the driver.
>> +    // TODO: Use `associated_type_defaults` once stabilized:
>> +    //
>> +    // ```
>> +    // type IdInfo: 'static = ();
>> +    // ```
>> +    type IdInfo: 'static;
>> +
>> +    /// The table of device ids supported by the driver.
>> +    const ID_TABLE: IdTable<Self::IdInfo>;
>> +
>> +    /// Called before report descriptor parsing. Can be used to mutate the
>> +    /// report descriptor before the core HID logic processes the descriptor.
>> +    /// Useful for problematic report descriptors that prevent HID devices from
>> +    /// functioning correctly.
>> +    ///
>> +    /// Optional to implement.
>> +    fn report_fixup<'a, 'b: 'a>(_hdev: &Device<device::Core>, _rdesc: &'b mut [u8]) -> &'a [u8] {
>> +        build_error!(VTABLE_DEFAULT_ERROR)
>> +    }
>> +}
>> +
>> +/// An adapter for the registration of HID drivers.
>> +pub struct Adapter<T: Driver>(T);
>> +
>> +// SAFETY:
>> +// - `bindings::hid_driver` is a C type declared as `repr(C)`.
>> +// - `T` is the type of the driver's device private data.
>> +// - `struct hid_driver` embeds a `struct device_driver`.
>> +// - `DEVICE_DRIVER_OFFSET` is the correct byte offset to the embedded `struct device_driver`.
>> +unsafe impl<T: Driver + 'static> driver::DriverLayout for Adapter<T> {
>> +    type DriverType = bindings::hid_driver;
>> +    type DriverData = T;
>> +    const DEVICE_DRIVER_OFFSET: usize = core::mem::offset_of!(Self::DriverType, driver);
>> +}
>> +
>> +// SAFETY: A call to `unregister` for a given instance of `DriverType` is guaranteed to be valid if
>> +// a preceding call to `register` has been successful.
>> +unsafe impl<T: Driver + 'static> driver::RegistrationOps for Adapter<T> {
>> +    unsafe fn register(
>> +        hdrv: &Opaque<Self::DriverType>,
>> +        name: &'static CStr,
>> +        module: &'static ThisModule,
>> +    ) -> Result {
>> +        // SAFETY: It's safe to set the fields of `struct hid_driver` on initialization.
>> +        unsafe {
>> +            (*hdrv.get()).name = name.as_char_ptr();
>> +            (*hdrv.get()).id_table = T::ID_TABLE.as_ptr();
>> +            (*hdrv.get()).report_fixup = if T::HAS_REPORT_FIXUP {
>> +                Some(Self::report_fixup_callback)
>> +            } else {
>> +                None
>> +            };
>> +        }
>> +
>> +        // SAFETY: `hdrv` is guaranteed to be a valid `DriverType`
>> +        to_result(unsafe {
>> +            bindings::__hid_register_driver(hdrv.get(), module.0, name.as_char_ptr())
>> +        })
>> +    }
>> +
>> +    unsafe fn unregister(hdrv: &Opaque<Self::DriverType>) {
>> +        // SAFETY: `hdrv` is guaranteed to be a valid `DriverType`
>> +        unsafe { bindings::hid_unregister_driver(hdrv.get()) }
>> +    }
>> +}
>> +
>> +impl<T: Driver + 'static> Adapter<T> {
>> +    extern "C" fn report_fixup_callback(
>> +        hdev: *mut bindings::hid_device,
>> +        buf: *mut u8,
>> +        size: *mut kernel::ffi::c_uint,
>> +    ) -> *const u8 {
>> +        // SAFETY: The HID subsystem only ever calls the report_fixup callback
>> +        // with a valid pointer to a `struct hid_device`.
>> +        //
>> +        // INVARIANT: `hdev` is valid for the duration of
>> +        // `report_fixup_callback()`.
>> +        let hdev = unsafe { &*hdev.cast::<Device<device::Core>>() };
>> +
>> +        // SAFETY: The HID subsystem only ever calls the report_fixup callback
>> +        // with a valid pointer to a `kernel::ffi::c_uint`.
>> +        //
>> +        // INVARIANT: `size` is valid for the duration of
>> +        // `report_fixup_callback()`.
>> +        let buf_len: usize = match unsafe { *size }.try_into() {
>> +            Ok(len) => len,
>> +            Err(e) => {
>> +                dev_err!(
>> +                    hdev.as_ref(),
>> +                    "Cannot fix report description due to {:?}!\n",
>> +                    e
>> +                );
>> +
>> +                return buf;
>> +            }
>> +        };
>> +
>> +        // Build a mutable Rust slice from `buf` and `size`.
>> +        //
>> +        // SAFETY: The HID subsystem only ever calls the `report_fixup callback`
>> +        // with a valid pointer to a `u8` buffer.
>> +        //
>> +        // INVARIANT: `buf` is valid for the duration of
>> +        // `report_fixup_callback()`.
>> +        let rdesc_slice = unsafe { core::slice::from_raw_parts_mut(buf, buf_len) };
>> +        let rdesc_slice = T::report_fixup(hdev, rdesc_slice);
>> +
>> +        match rdesc_slice.len().try_into() {
>> +            // SAFETY: The HID subsystem only ever calls the report_fixup
>> +            // callback with a valid pointer to a `kernel::ffi::c_uint`.
>> +            //
>> +            // INVARIANT: `size` is valid for the duration of
>> +            // `report_fixup_callback()`.
>> +            Ok(len) => unsafe { *size = len },
>> +            Err(e) => {
>> +                dev_err!(
>> +                    hdev.as_ref(),
>> +                    "Fixed report description will not be used due to {:?}!\n",
>> +                    e
>> +                );
>> +
>> +                return buf;
>> +            }
>> +        }
>> +
>> +        rdesc_slice.as_ptr()
>> +    }
>> +}
>> +
>> +/// Declares a kernel module that exposes a single HID driver.
>> +///
>> +/// # Examples
>> +///
>> +/// ```ignore
>> +/// kernel::module_hid_driver! {
>> +///     type: MyDriver,
>> +///     name: "Module name",
>> +///     authors: ["Author name"],
>> +///     description: "Description",
>> +///     license: "GPL",
>> +/// }
>> +/// ```
>> +#[macro_export]
>> +macro_rules! module_hid_driver {
>> +    ($($f:tt)*) => {
>> +        $crate::module_driver!(<T>, $crate::hid::Adapter<T>, { $($f)* });
>> +    };
>> +}
>> diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
>> index 3da92f18f4ee..e2dcacd9369e 100644
>> --- a/rust/kernel/lib.rs
>> +++ b/rust/kernel/lib.rs
>> @@ -102,6 +102,8 @@
>>  pub mod id_pool;
>>  #[doc(hidden)]
>>  pub mod impl_flags;
>> +#[cfg(CONFIG_RUST_HID_ABSTRACTIONS)]
>> +pub mod hid;
>>  pub mod init;
>>  pub mod io;
>>  pub mod ioctl;


^ permalink raw reply

* Re: [PATCH v6 2/2] rust: hid: Glorious PC Gaming Race Model O and O- mice reference driver
From: Rahul Rameshbabu @ 2026-02-23  2:52 UTC (permalink / raw)
  To: Greg KH
  Cc: linux-input, linux-kernel, rust-for-linux, a.hindborg,
	alex.gaynor, aliceryhl, benjamin.tissoires, benno.lossin,
	bjorn3_gh, boqun.feng, dakr, db48x, gary, jikos, ojeda,
	peter.hutterer, tmgross
In-Reply-To: <2026022359-dental-jelly-cb6e@gregkh>

On Mon, 23 Feb, 2026 03:38:23 +0100 "Greg KH" <gregkh@linuxfoundation.org> wrote:
> On Sun, Feb 22, 2026 at 09:56:51PM +0000, Rahul Rameshbabu wrote:
>>  MAINTAINERS                           |  6 +++
>>  drivers/hid/hid-glorious.c            |  2 +
>>  drivers/hid/hid_glorious_rust.rs      | 60 +++++++++++++++++++++++++++
>>  drivers/hid/rust/Kconfig              | 16 +++++++
>>  drivers/hid/rust/Makefile             |  6 +++
>>  drivers/hid/rust/hid_glorious_rust.rs | 60 +++++++++++++++++++++++++++
>>  6 files changed, 150 insertions(+)
>>  create mode 100644 drivers/hid/hid_glorious_rust.rs
>>  create mode 100644 drivers/hid/rust/Makefile
>>  create mode 100644 drivers/hid/rust/hid_glorious_rust.rs
>
> You seem to have the same file in here in two different locations :(

Yeah, I noticed this right after sending out the patches....
drivers/hid/hid_glorious_rust.rs was meant to be removed.

  https://lore.kernel.org/rust-for-linux/wjfjzjc626n55zvhksiyldobwubr2imbvfavqej333lvnka2wn@r4zfcjqtanvu/

I accidentally had a local copy in my work tree and managed to re-add
it. I will need a v7 of this series either way, but that was
unfortunate....

Thanks,
Rahul Rameshbabu


^ permalink raw reply

* Re: [PATCH 2/7] Input: cros_ec_keyb - add function key support
From: Dmitry Torokhov @ 2026-02-23  3:47 UTC (permalink / raw)
  To: Tzung-Bi Shih
  Cc: Fabio Baltieri, Benson Leung, Guenter Roeck, Simon Glass,
	linux-input, chrome-platform, linux-kernel
In-Reply-To: <aZrencp4ZQnFC9OD@tzungbi-laptop>

On Sun, Feb 22, 2026 at 06:46:53PM +0800, Tzung-Bi Shih wrote:
> On Sat, Feb 21, 2026 at 04:37:10PM -0800, Dmitry Torokhov wrote:
> > From: Fabio Baltieri <fabiobaltieri@chromium.org>
> > 
> > Add support for handling an Fn button and sending separate keycodes for
> > a subset of keys in the matrix defined in the upper half of the keymap.
> > 
> > Signed-off-by: Fabio Baltieri <fabiobaltieri@chromium.org>
> > Reviewed-by: Simon Glass <sjg@chromium.org>
> > Link: https://patch.msgid.link/20260211173421.1206478-3-fabiobaltieri@chromium.org
> > Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
> 
> The patch looks good to me, just a few minor nits below.  I don't insist on
> these being fixed for this patch to be merged,
> 
> Reviewed-by: Tzung-Bi Shih <tzungbi@kernel.org>

Thanks Tzung-Bi.

> 
> > +static void cros_ec_keyb_process_key_fn_map(struct cros_ec_keyb *ckdev,
> > +					    int row, int col, bool state)
> > +{
> > +	struct input_dev *idev = ckdev->idev;
> > +	const unsigned short *keycodes = idev->keycode;
> > +	unsigned int pos, fn_pos;
> > +	unsigned int code, fn_code;
> 
> Nit: does declaring `code` and `fn_code` as unsigned short make more sense?
> Or they can be declared in the same line.

I do not think making temporary variables unsigned short changes
anything, so I will leave it as is.

> 
> > +
> > +	pos = MATRIX_SCAN_CODE(row, col, ckdev->row_shift);
> > +	code = keycodes[pos];
> > +
> > +	if (code == KEY_FN) {
> > +		ckdev->fn_active = state;
> > +		if (state) {
> > +			ckdev->fn_combo_active = false;
> > +		} else if (!ckdev->fn_combo_active) {
> > +			/*
> > +			 * Send both Fn press and release events if nothing
> > +			 * else has been pressed together with Fn.
> > +			 */
> > +			cros_ec_emit_fn_key(idev, pos);
> > +		}
> > +		return;
> > +	}
> > +
> > +	fn_pos = MATRIX_SCAN_CODE(row + ckdev->rows, col, ckdev->row_shift);
> > +	fn_code = keycodes[fn_pos];
> > +
> > +	if (state) {
> > +		if (ckdev->fn_active) {
> > +			ckdev->fn_combo_active = true;
> > +			if (!fn_code)
> > +				return; /* Discard if no Fn mapping exists */
> > +
> > +			code = fn_code;
> > +			pos = fn_pos;
> 
> Nit: assigning `pos` before `code` makes it neater.
> 
> > +		}
> > +	} else {
> > +		/*
> > +		 * If the Fn-remapped code is currently pressed, release it.
> > +		 * Otherwise, release the standard code (if it was pressed).
> > +		 */
> > +		if (fn_code && test_bit(fn_code, idev->key)) {
> > +			code = fn_code;
> > +			pos = fn_pos;
> 
> Nit: same here.

Yep, I'll update before applying.

Thanks.

-- 
Dmitry

^ permalink raw reply

* Re: [PATCH 5/7] Input: cros_ec_keyb - simplify cros_ec_keyb_work()
From: Dmitry Torokhov @ 2026-02-23  3:49 UTC (permalink / raw)
  To: Tzung-Bi Shih
  Cc: Fabio Baltieri, Benson Leung, Guenter Roeck, Simon Glass,
	linux-input, chrome-platform, linux-kernel
In-Reply-To: <aZre0qH8rMcSJsa6@tzungbi-laptop>

On Sun, Feb 22, 2026 at 06:47:46PM +0800, Tzung-Bi Shih wrote:
> On Sat, Feb 21, 2026 at 04:37:13PM -0800, Dmitry Torokhov wrote:
> > @@ -376,12 +378,10 @@ static int cros_ec_keyb_work(struct notifier_block *nb,
> >  		pm_wakeup_event(ckdev->dev, 0);
> >  
> >  		if (ckdev->ec->event_data.event_type == EC_MKBP_EVENT_BUTTON) {
> 
> This can also be simplified: `event_data->event_type`.

Indeed, will update.

Thanks.

-- 
Dmitry

^ permalink raw reply

* Re: [PATCH 7/7] Input: cros_ec_keyb - factor out column processing
From: Dmitry Torokhov @ 2026-02-23  3:51 UTC (permalink / raw)
  To: Tzung-Bi Shih
  Cc: Fabio Baltieri, Benson Leung, Guenter Roeck, Simon Glass,
	linux-input, chrome-platform, linux-kernel
In-Reply-To: <aZre8rWDvVDZ_T6p@tzungbi-laptop>

On Sun, Feb 22, 2026 at 06:48:18PM +0800, Tzung-Bi Shih wrote:
> On Sat, Feb 21, 2026 at 04:37:15PM -0800, Dmitry Torokhov wrote:
> > +static void cros_ec_keyy_process_col(struct cros_ec_keyb *ckdev, int col,
> > +				     u8 col_state, u8 changed)
> 
> What does the second 'y' of keyy stand for?

It was supposed to be "keyb", I'll fix it before applying.

> 
> > +{
> > +	for (int row = 0; row < ckdev->rows; row++) {
> > +		if (changed & BIT(row)) {
> 
> Maybe
> 
>     if ((changed & BIT(row)) == 0)
>         continue;
> or
>     if (~changed & BIT(row))
>         continue;
> or
>     if (!test_bit(row, &changed))
>         continue;
> 
> to save an indent level.

I do not think indent is too deep here so we can't spare one level...

Thanks.

-- 
Dmitry

^ permalink raw reply

* Re: [PATCH] Input: adxl34x - Drop redundant error variable in adxl34x_i2c_probe
From: Dmitry Torokhov @ 2026-02-23  3:53 UTC (permalink / raw)
  To: Thorsten Blum; +Cc: linux-input, linux-kernel
In-Reply-To: <20260223000308.319335-1-thorsten.blum@linux.dev>

On Mon, Feb 23, 2026 at 01:03:04AM +0100, Thorsten Blum wrote:
> Inline i2c_check_functionality(), which really returns a bool and not an
> error code, and remove the local variable.  No functional changes.
> 
> Signed-off-by: Thorsten Blum <thorsten.blum@linux.dev>

Applied, thank you.

-- 
Dmitry

^ permalink raw reply


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