1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * HID driver for Gembird Joypad, "PC Game Controller"
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright (c) 2015 Red Hat, Inc
6*4882a593Smuzhiyun * Copyright (c) 2015 Benjamin Tissoires
7*4882a593Smuzhiyun */
8*4882a593Smuzhiyun
9*4882a593Smuzhiyun /*
10*4882a593Smuzhiyun */
11*4882a593Smuzhiyun
12*4882a593Smuzhiyun #include <linux/device.h>
13*4882a593Smuzhiyun #include <linux/hid.h>
14*4882a593Smuzhiyun #include <linux/module.h>
15*4882a593Smuzhiyun
16*4882a593Smuzhiyun #include "hid-ids.h"
17*4882a593Smuzhiyun
18*4882a593Smuzhiyun #define GEMBIRD_START_FAULTY_RDESC 8
19*4882a593Smuzhiyun
20*4882a593Smuzhiyun static const __u8 gembird_jpd_faulty_rdesc[] = {
21*4882a593Smuzhiyun 0x75, 0x08, /* Report Size (8) */
22*4882a593Smuzhiyun 0x95, 0x05, /* Report Count (5) */
23*4882a593Smuzhiyun 0x15, 0x00, /* Logical Minimum (0) */
24*4882a593Smuzhiyun 0x26, 0xff, 0x00, /* Logical Maximum (255) */
25*4882a593Smuzhiyun 0x35, 0x00, /* Physical Minimum (0) */
26*4882a593Smuzhiyun 0x46, 0xff, 0x00, /* Physical Maximum (255) */
27*4882a593Smuzhiyun 0x09, 0x30, /* Usage (X) */
28*4882a593Smuzhiyun 0x09, 0x31, /* Usage (Y) */
29*4882a593Smuzhiyun 0x09, 0x32, /* Usage (Z) */
30*4882a593Smuzhiyun 0x09, 0x32, /* Usage (Z) */
31*4882a593Smuzhiyun 0x09, 0x35, /* Usage (Rz) */
32*4882a593Smuzhiyun 0x81, 0x02, /* Input (Data,Var,Abs) */
33*4882a593Smuzhiyun };
34*4882a593Smuzhiyun
35*4882a593Smuzhiyun /*
36*4882a593Smuzhiyun * we fix the report descriptor by:
37*4882a593Smuzhiyun * - marking the first Z axis as constant (so it is ignored by HID)
38*4882a593Smuzhiyun * - assign the original second Z to Rx
39*4882a593Smuzhiyun * - assign the original Rz to Ry
40*4882a593Smuzhiyun */
41*4882a593Smuzhiyun static const __u8 gembird_jpd_fixed_rdesc[] = {
42*4882a593Smuzhiyun 0x75, 0x08, /* Report Size (8) */
43*4882a593Smuzhiyun 0x95, 0x02, /* Report Count (2) */
44*4882a593Smuzhiyun 0x15, 0x00, /* Logical Minimum (0) */
45*4882a593Smuzhiyun 0x26, 0xff, 0x00, /* Logical Maximum (255) */
46*4882a593Smuzhiyun 0x35, 0x00, /* Physical Minimum (0) */
47*4882a593Smuzhiyun 0x46, 0xff, 0x00, /* Physical Maximum (255) */
48*4882a593Smuzhiyun 0x09, 0x30, /* Usage (X) */
49*4882a593Smuzhiyun 0x09, 0x31, /* Usage (Y) */
50*4882a593Smuzhiyun 0x81, 0x02, /* Input (Data,Var,Abs) */
51*4882a593Smuzhiyun 0x95, 0x01, /* Report Count (1) */
52*4882a593Smuzhiyun 0x09, 0x32, /* Usage (Z) */
53*4882a593Smuzhiyun 0x81, 0x01, /* Input (Cnst,Arr,Abs) */
54*4882a593Smuzhiyun 0x95, 0x02, /* Report Count (2) */
55*4882a593Smuzhiyun 0x09, 0x33, /* Usage (Rx) */
56*4882a593Smuzhiyun 0x09, 0x34, /* Usage (Ry) */
57*4882a593Smuzhiyun 0x81, 0x02, /* Input (Data,Var,Abs) */
58*4882a593Smuzhiyun };
59*4882a593Smuzhiyun
gembird_report_fixup(struct hid_device * hdev,__u8 * rdesc,unsigned int * rsize)60*4882a593Smuzhiyun static __u8 *gembird_report_fixup(struct hid_device *hdev, __u8 *rdesc,
61*4882a593Smuzhiyun unsigned int *rsize)
62*4882a593Smuzhiyun {
63*4882a593Smuzhiyun __u8 *new_rdesc;
64*4882a593Smuzhiyun /* delta_size is > 0 */
65*4882a593Smuzhiyun size_t delta_size = sizeof(gembird_jpd_fixed_rdesc) -
66*4882a593Smuzhiyun sizeof(gembird_jpd_faulty_rdesc);
67*4882a593Smuzhiyun size_t new_size = *rsize + delta_size;
68*4882a593Smuzhiyun
69*4882a593Smuzhiyun if (*rsize >= 31 && !memcmp(&rdesc[GEMBIRD_START_FAULTY_RDESC],
70*4882a593Smuzhiyun gembird_jpd_faulty_rdesc,
71*4882a593Smuzhiyun sizeof(gembird_jpd_faulty_rdesc))) {
72*4882a593Smuzhiyun new_rdesc = devm_kzalloc(&hdev->dev, new_size, GFP_KERNEL);
73*4882a593Smuzhiyun if (new_rdesc == NULL)
74*4882a593Smuzhiyun return rdesc;
75*4882a593Smuzhiyun
76*4882a593Smuzhiyun dev_info(&hdev->dev,
77*4882a593Smuzhiyun "fixing Gembird JPD-DualForce 2 report descriptor.\n");
78*4882a593Smuzhiyun
79*4882a593Smuzhiyun /* start by copying the end of the rdesc */
80*4882a593Smuzhiyun memcpy(new_rdesc + delta_size, rdesc, *rsize);
81*4882a593Smuzhiyun
82*4882a593Smuzhiyun /* add the correct beginning */
83*4882a593Smuzhiyun memcpy(new_rdesc, rdesc, GEMBIRD_START_FAULTY_RDESC);
84*4882a593Smuzhiyun
85*4882a593Smuzhiyun /* replace the faulty part with the fixed one */
86*4882a593Smuzhiyun memcpy(new_rdesc + GEMBIRD_START_FAULTY_RDESC,
87*4882a593Smuzhiyun gembird_jpd_fixed_rdesc,
88*4882a593Smuzhiyun sizeof(gembird_jpd_fixed_rdesc));
89*4882a593Smuzhiyun
90*4882a593Smuzhiyun *rsize = new_size;
91*4882a593Smuzhiyun rdesc = new_rdesc;
92*4882a593Smuzhiyun }
93*4882a593Smuzhiyun
94*4882a593Smuzhiyun return rdesc;
95*4882a593Smuzhiyun }
96*4882a593Smuzhiyun
97*4882a593Smuzhiyun static const struct hid_device_id gembird_devices[] = {
98*4882a593Smuzhiyun { HID_USB_DEVICE(USB_VENDOR_ID_GEMBIRD,
99*4882a593Smuzhiyun USB_DEVICE_ID_GEMBIRD_JPD_DUALFORCE2) },
100*4882a593Smuzhiyun { }
101*4882a593Smuzhiyun };
102*4882a593Smuzhiyun MODULE_DEVICE_TABLE(hid, gembird_devices);
103*4882a593Smuzhiyun
104*4882a593Smuzhiyun static struct hid_driver gembird_driver = {
105*4882a593Smuzhiyun .name = "gembird",
106*4882a593Smuzhiyun .id_table = gembird_devices,
107*4882a593Smuzhiyun .report_fixup = gembird_report_fixup,
108*4882a593Smuzhiyun };
109*4882a593Smuzhiyun module_hid_driver(gembird_driver);
110*4882a593Smuzhiyun
111*4882a593Smuzhiyun MODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@gmail.com>");
112*4882a593Smuzhiyun MODULE_DESCRIPTION("HID Gembird joypad driver");
113*4882a593Smuzhiyun MODULE_LICENSE("GPL");
114