1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * USB HID driver for Glorious PC Gaming Race |
4 | * Glorious Model O, O- and D mice. |
5 | * |
6 | * Copyright (c) 2020 Samuel Čavoj <sammko@sammserver.com> |
7 | */ |
8 | |
9 | /* |
10 | */ |
11 | |
12 | #include <linux/hid.h> |
13 | #include <linux/module.h> |
14 | |
15 | #include "hid-ids.h" |
16 | |
17 | MODULE_AUTHOR("Samuel Čavoj <sammko@sammserver.com>" ); |
18 | MODULE_DESCRIPTION("HID driver for Glorious PC Gaming Race mice" ); |
19 | |
20 | /* |
21 | * Glorious Model O and O- specify the const flag in the consumer input |
22 | * report descriptor, which leads to inputs being ignored. Fix this |
23 | * by patching the descriptor. |
24 | * |
25 | * Glorious Model I incorrectly specifes the Usage Minimum for its |
26 | * keyboard HID report, causing keycodes to be misinterpreted. |
27 | * Fix this by setting Usage Minimum to 0 in that report. |
28 | */ |
29 | static __u8 *glorious_report_fixup(struct hid_device *hdev, __u8 *rdesc, |
30 | unsigned int *rsize) |
31 | { |
32 | if (*rsize == 213 && |
33 | rdesc[84] == 129 && rdesc[112] == 129 && rdesc[140] == 129 && |
34 | rdesc[85] == 3 && rdesc[113] == 3 && rdesc[141] == 3) { |
35 | hid_info(hdev, "patching Glorious Model O consumer control report descriptor\n" ); |
36 | rdesc[85] = rdesc[113] = rdesc[141] = \ |
37 | HID_MAIN_ITEM_VARIABLE | HID_MAIN_ITEM_RELATIVE; |
38 | } |
39 | if (*rsize == 156 && rdesc[41] == 1) { |
40 | hid_info(hdev, "patching Glorious Model I keyboard report descriptor\n" ); |
41 | rdesc[41] = 0; |
42 | } |
43 | return rdesc; |
44 | } |
45 | |
46 | static void glorious_update_name(struct hid_device *hdev) |
47 | { |
48 | const char *model = "Device" ; |
49 | |
50 | switch (hdev->product) { |
51 | case USB_DEVICE_ID_GLORIOUS_MODEL_O: |
52 | model = "Model O" ; break; |
53 | case USB_DEVICE_ID_GLORIOUS_MODEL_D: |
54 | model = "Model D" ; break; |
55 | case USB_DEVICE_ID_GLORIOUS_MODEL_I: |
56 | model = "Model I" ; break; |
57 | } |
58 | |
59 | snprintf(buf: hdev->name, size: sizeof(hdev->name), fmt: "%s %s" , "Glorious" , model); |
60 | } |
61 | |
62 | static int glorious_probe(struct hid_device *hdev, |
63 | const struct hid_device_id *id) |
64 | { |
65 | int ret; |
66 | |
67 | hdev->quirks |= HID_QUIRK_INPUT_PER_APP; |
68 | |
69 | ret = hid_parse(hdev); |
70 | if (ret) |
71 | return ret; |
72 | |
73 | glorious_update_name(hdev); |
74 | |
75 | return hid_hw_start(hdev, HID_CONNECT_DEFAULT); |
76 | } |
77 | |
78 | static const struct hid_device_id glorious_devices[] = { |
79 | { HID_USB_DEVICE(USB_VENDOR_ID_SINOWEALTH, |
80 | USB_DEVICE_ID_GLORIOUS_MODEL_O) }, |
81 | { HID_USB_DEVICE(USB_VENDOR_ID_SINOWEALTH, |
82 | USB_DEVICE_ID_GLORIOUS_MODEL_D) }, |
83 | { HID_USB_DEVICE(USB_VENDOR_ID_LAVIEW, |
84 | USB_DEVICE_ID_GLORIOUS_MODEL_I) }, |
85 | { } |
86 | }; |
87 | MODULE_DEVICE_TABLE(hid, glorious_devices); |
88 | |
89 | static struct hid_driver glorious_driver = { |
90 | .name = "glorious" , |
91 | .id_table = glorious_devices, |
92 | .probe = glorious_probe, |
93 | .report_fixup = glorious_report_fixup |
94 | }; |
95 | |
96 | module_hid_driver(glorious_driver); |
97 | |
98 | MODULE_LICENSE("GPL" ); |
99 | |