summaryrefslogtreecommitdiffstats
path: root/drivers/hid/hid-elecom.c
diff options
context:
space:
mode:
authorYazen Ghannam <yazen.ghannam@amd.com>2026-09-03 10:43:25 -0500
committerBorislav Petkov (AMD) <bp@alien8.de>2026-09-10 18:05:43 -0700
commit27600805e62f800bacf990354632eae4e487d34c (patch)
treee8aec0ddfaa0bc264d79af632e3de3f1b75ae4c8 /drivers/hid/hid-elecom.c
downloadlinux-stable-27600805e62f800bacf990354632eae4e487d34c.tar.gz
linux-stable-27600805e62f800bacf990354632eae4e487d34c.zip
x86/amd_node: Fix PCI device reference counting in amd_smn_init()grafted
The local "root" pointer is a temporary variable used during the device search. Therefore, refcount related to the search iterators should be cleaned up after the search is complete. Use the __free() cleanup macro to ensure the refcount is decremented when the temporary pointer goes out of scope. Additionally, increment the refcount when caching a root pointer. This ensures the in-use refcount is separate from the temporary search refcounting. Finally, drop the redundant "root = NULL" before the second search loop. The pci_get_class() iterator always decrements the refcount of its "from" argument, so the first loop can only fall through with "root" already NULL. Fixes: 0a4b61d9c2e4 ("x86/amd_node: Fix AMD root device caching") Closes: https://sashiko.dev/#/patchset/20260806160159.230453-1-jason.andryuk%40amd.com Reported-by: Sashiko <sashiko-bot@kernel.org> Assisted-by: LLM Signed-off-by: Yazen Ghannam <yazen.ghannam@amd.com> Signed-off-by: Borislav Petkov (AMD) <bp@alien8.de> Reviewed-by: Mario Limonciello (AMD) <superm1@kernel.org> Cc: <stable@kernel.org> Link: https://patch.msgid.link/20260903154325.74343-1-yazen.ghannam@amd.com
Diffstat (limited to 'drivers/hid/hid-elecom.c')
-rw-r--r--drivers/hid/hid-elecom.c173
1 files changed, 173 insertions, 0 deletions
diff --git a/drivers/hid/hid-elecom.c b/drivers/hid/hid-elecom.c
new file mode 100644
index 000000000..37d88ce57
--- /dev/null
+++ b/drivers/hid/hid-elecom.c
@@ -0,0 +1,173 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * HID driver for ELECOM devices:
+ * - BM084 Bluetooth Mouse
+ * - EX-G Trackballs (M-XT3DRBK, M-XT3URBK, M-XT4DRBK)
+ * - DEFT Trackballs (M-DT1DRBK, M-DT1URBK, M-DT2DRBK, M-DT2URBK)
+ * - HUGE Trackballs (M-HT1DRBK, M-HT1URBK)
+ * - HUGE Plus Trackball (M-HT1MRBK)
+ *
+ * Copyright (c) 2010 Richard Nauber <Richard.Nauber@gmail.com>
+ * Copyright (c) 2016 Yuxuan Shui <yshuiv7@gmail.com>
+ * Copyright (c) 2017 Diego Elio Pettenò <flameeyes@flameeyes.eu>
+ * Copyright (c) 2017 Alex Manoussakis <amanou@gnu.org>
+ * Copyright (c) 2017 Tomasz Kramkowski <tk@the-tk.com>
+ * Copyright (c) 2020 YOSHIOKA Takuma <lo48576@hard-wi.red>
+ * Copyright (c) 2022 Takahiro Fujii <fujii@xaxxi.net>
+ */
+
+/*
+ */
+
+#include <linux/device.h>
+#include <linux/hid.h>
+#include <linux/module.h>
+
+#include "hid-ids.h"
+
+/*
+ * Certain ELECOM mice misreport their button count meaning that they only work
+ * correctly with the ELECOM mouse assistant software which is unavailable for
+ * Linux. A four extra INPUT reports and a FEATURE report are described by the
+ * report descriptor but it does not appear that these enable software to
+ * control what the extra buttons map to. The only simple and straightforward
+ * solution seems to involve fixing up the report descriptor.
+ */
+#define MOUSE_BUTTONS_MAX 8
+static void mouse_button_fixup(struct hid_device *hdev,
+ __u8 *rdesc, unsigned int rsize,
+ unsigned int button_bit_count,
+ unsigned int padding_bit,
+ unsigned int button_report_size,
+ unsigned int button_usage_maximum,
+ int nbuttons)
+{
+ if (rsize < 32 || rdesc[button_bit_count] != 0x95 ||
+ rdesc[button_report_size] != 0x75 ||
+ rdesc[button_report_size + 1] != 0x01 ||
+ rdesc[button_usage_maximum] != 0x29 || rdesc[padding_bit] != 0x75)
+ return;
+ hid_info(hdev, "Fixing up Elecom mouse button count\n");
+ nbuttons = clamp(nbuttons, 0, MOUSE_BUTTONS_MAX);
+ rdesc[button_bit_count + 1] = nbuttons;
+ rdesc[button_usage_maximum + 1] = nbuttons;
+ rdesc[padding_bit + 1] = MOUSE_BUTTONS_MAX - nbuttons;
+}
+
+static const __u8 *elecom_report_fixup(struct hid_device *hdev, __u8 *rdesc,
+ unsigned int *rsize)
+{
+ switch (hdev->product) {
+ case USB_DEVICE_ID_ELECOM_BM084:
+ /* The BM084 Bluetooth mouse includes a non-existing horizontal
+ * wheel in the HID descriptor. */
+ if (*rsize >= 48 && rdesc[46] == 0x05 && rdesc[47] == 0x0c) {
+ hid_info(hdev, "Fixing up Elecom BM084 report descriptor\n");
+ rdesc[47] = 0x00;
+ }
+ break;
+ case USB_DEVICE_ID_ELECOM_M_XGL20DLBK:
+ /*
+ * Report descriptor format:
+ * 20: button bit count
+ * 28: padding bit count
+ * 22: button report size
+ * 14: button usage maximum
+ */
+ mouse_button_fixup(hdev, rdesc, *rsize, 20, 28, 22, 14, 8);
+ break;
+ case USB_DEVICE_ID_ELECOM_M_XT3URBK_00FB:
+ case USB_DEVICE_ID_ELECOM_M_XT3URBK_018F:
+ case USB_DEVICE_ID_ELECOM_M_XT3DRBK_00FC:
+ case USB_DEVICE_ID_ELECOM_M_XT4DRBK:
+ /*
+ * Report descriptor format:
+ * 12: button bit count
+ * 30: padding bit count
+ * 14: button report size
+ * 20: button usage maximum
+ */
+ mouse_button_fixup(hdev, rdesc, *rsize, 12, 30, 14, 20, 6);
+ break;
+ case USB_DEVICE_ID_ELECOM_M_DT1URBK:
+ case USB_DEVICE_ID_ELECOM_M_DT1DRBK:
+ case USB_DEVICE_ID_ELECOM_M_HT1URBK_010C:
+ case USB_DEVICE_ID_ELECOM_M_HT1URBK_019B:
+ case USB_DEVICE_ID_ELECOM_M_HT1DRBK_010D:
+ /*
+ * Report descriptor format:
+ * 12: button bit count
+ * 30: padding bit count
+ * 14: button report size
+ * 20: button usage maximum
+ */
+ mouse_button_fixup(hdev, rdesc, *rsize, 12, 30, 14, 20, 8);
+ break;
+ case USB_DEVICE_ID_ELECOM_M_XT3DRBK_018C:
+ /*
+ * Report descriptor format:
+ * 22: button bit count
+ * 30: padding bit count
+ * 24: button report size
+ * 16: button usage maximum
+ */
+ mouse_button_fixup(hdev, rdesc, *rsize, 22, 30, 24, 16, 6);
+ break;
+ case USB_DEVICE_ID_ELECOM_M_DT2DRBK:
+ case USB_DEVICE_ID_ELECOM_M_HT1DRBK_011C:
+ /*
+ * Report descriptor format:
+ * 22: button bit count
+ * 30: padding bit count
+ * 24: button report size
+ * 16: button usage maximum
+ */
+ mouse_button_fixup(hdev, rdesc, *rsize, 22, 30, 24, 16, 8);
+ break;
+ case USB_DEVICE_ID_ELECOM_M_HT1MRBK:
+ case USB_DEVICE_ID_ELECOM_M_HT1MRBK_01AB:
+ case USB_DEVICE_ID_ELECOM_M_HT1MRBK_01AC:
+ /*
+ * Report descriptor format:
+ * 24: button bit count
+ * 28: padding bit count
+ * 22: button report size
+ * 16: button usage maximum
+ */
+ mouse_button_fixup(hdev, rdesc, *rsize, 24, 28, 22, 16, 8);
+ break;
+ }
+ return rdesc;
+}
+
+static const struct hid_device_id elecom_devices[] = {
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_BM084) },
+ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_HT1MRBK_01AC) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XGL20DLBK) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3URBK_00FB) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3URBK_018F) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3DRBK_00FC) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT3DRBK_018C) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_XT4DRBK) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_DT1URBK) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_DT1DRBK) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_DT2DRBK) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_HT1URBK_010C) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_HT1URBK_019B) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_HT1DRBK_010D) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_HT1DRBK_011C) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_HT1MRBK) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ELECOM, USB_DEVICE_ID_ELECOM_M_HT1MRBK_01AB) },
+ { }
+};
+MODULE_DEVICE_TABLE(hid, elecom_devices);
+
+static struct hid_driver elecom_driver = {
+ .name = "elecom",
+ .id_table = elecom_devices,
+ .report_fixup = elecom_report_fixup
+};
+module_hid_driver(elecom_driver);
+
+MODULE_DESCRIPTION("HID driver for ELECOM devices");
+MODULE_LICENSE("GPL");