1 // SPDX-License-Identifier: GPL-2.0
2
3 /*
4 * HID driver for WinWing Orion 2 throttle
5 *
6 * Copyright (c) 2023 Ivan Gorinov
7 */
8
9 #include <linux/device.h>
10 #include <linux/hid.h>
11 #include <linux/hidraw.h>
12 #include <linux/kernel.h>
13 #include <linux/module.h>
14 #include <linux/mutex.h>
15
16 #define MAX_REPORT 16
17
18 struct winwing_led {
19 struct led_classdev cdev;
20 struct hid_device *hdev;
21 int number;
22 };
23
24 struct winwing_led_info {
25 int number;
26 int max_brightness;
27 const char *led_name;
28 };
29
30 static const struct winwing_led_info led_info[3] = {
31 { 0, 255, "backlight" },
32 { 1, 1, "a-a" },
33 { 2, 1, "a-g" },
34 };
35
36 struct winwing_drv_data {
37 struct hid_device *hdev;
38 __u8 *report_buf;
39 struct mutex lock;
40 unsigned int num_leds;
41 struct winwing_led leds[];
42 };
43
winwing_led_write(struct led_classdev * cdev,enum led_brightness br)44 static int winwing_led_write(struct led_classdev *cdev,
45 enum led_brightness br)
46 {
47 struct winwing_led *led = (struct winwing_led *) cdev;
48 struct winwing_drv_data *data = hid_get_drvdata(led->hdev);
49 __u8 *buf = data->report_buf;
50 int ret;
51
52 mutex_lock(&data->lock);
53
54 buf[0] = 0x02;
55 buf[1] = 0x60;
56 buf[2] = 0xbe;
57 buf[3] = 0x00;
58 buf[4] = 0x00;
59 buf[5] = 0x03;
60 buf[6] = 0x49;
61 buf[7] = led->number;
62 buf[8] = br;
63 buf[9] = 0x00;
64 buf[10] = 0;
65 buf[11] = 0;
66 buf[12] = 0;
67 buf[13] = 0;
68
69 ret = hid_hw_output_report(led->hdev, buf, 14);
70
71 mutex_unlock(&data->lock);
72
73 return ret;
74 }
75
winwing_init_led(struct hid_device * hdev,struct input_dev * input)76 static int winwing_init_led(struct hid_device *hdev,
77 struct input_dev *input)
78 {
79 struct winwing_drv_data *data;
80 struct winwing_led *led;
81 int ret;
82 int i;
83
84 size_t data_size = struct_size(data, leds, 3);
85
86 data = devm_kzalloc(&hdev->dev, data_size, GFP_KERNEL);
87
88 if (!data)
89 return -ENOMEM;
90
91 data->report_buf = devm_kmalloc(&hdev->dev, MAX_REPORT, GFP_KERNEL);
92
93 if (!data->report_buf)
94 return -ENOMEM;
95
96 for (i = 0; i < 3; i += 1) {
97 const struct winwing_led_info *info = &led_info[i];
98
99 led = &data->leds[i];
100 led->hdev = hdev;
101 led->number = info->number;
102 led->cdev.max_brightness = info->max_brightness;
103 led->cdev.brightness_set_blocking = winwing_led_write;
104 led->cdev.flags = LED_HW_PLUGGABLE;
105 led->cdev.name = devm_kasprintf(&hdev->dev, GFP_KERNEL,
106 "%s::%s",
107 dev_name(&input->dev),
108 info->led_name);
109 if (!led->cdev.name)
110 return -ENOMEM;
111
112 ret = devm_led_classdev_register(&hdev->dev, &led->cdev);
113 if (ret)
114 return ret;
115 }
116
117 hid_set_drvdata(hdev, data);
118
119 return ret;
120 }
121
winwing_probe(struct hid_device * hdev,const struct hid_device_id * id)122 static int winwing_probe(struct hid_device *hdev,
123 const struct hid_device_id *id)
124 {
125 int ret;
126
127 ret = hid_parse(hdev);
128 if (ret) {
129 hid_err(hdev, "parse failed\n");
130 return ret;
131 }
132
133 ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
134 if (ret) {
135 hid_err(hdev, "hw start failed\n");
136 return ret;
137 }
138
139 return 0;
140 }
141
winwing_input_configured(struct hid_device * hdev,struct hid_input * hidinput)142 static int winwing_input_configured(struct hid_device *hdev,
143 struct hid_input *hidinput)
144 {
145 int ret;
146
147 ret = winwing_init_led(hdev, hidinput->input);
148
149 if (ret)
150 hid_err(hdev, "led init failed\n");
151
152 return ret;
153 }
154
155 static const __u8 original_rdesc_buttons[] = {
156 0x05, 0x09, 0x19, 0x01, 0x29, 0x6F,
157 0x15, 0x00, 0x25, 0x01, 0x35, 0x00,
158 0x45, 0x01, 0x75, 0x01, 0x95, 0x6F,
159 0x81, 0x02, 0x75, 0x01, 0x95, 0x01,
160 0x81, 0x01
161 };
162
163 /*
164 * HID report descriptor shows 111 buttons, which exceeds maximum
165 * number of buttons (80) supported by Linux kernel HID subsystem.
166 *
167 * This module skips numbers 32-63, unused on some throttle grips.
168 */
169
winwing_report_fixup(struct hid_device * hdev,__u8 * rdesc,unsigned int * rsize)170 static const __u8 *winwing_report_fixup(struct hid_device *hdev, __u8 *rdesc,
171 unsigned int *rsize)
172 {
173 int sig_length = sizeof(original_rdesc_buttons);
174 int unused_button_numbers = 32;
175
176 if (*rsize < 34)
177 return rdesc;
178
179 if (memcmp(rdesc + 8, original_rdesc_buttons, sig_length) == 0) {
180
181 /* Usage Maximum */
182 rdesc[13] -= unused_button_numbers;
183
184 /* Report Count for buttons */
185 rdesc[25] -= unused_button_numbers;
186
187 /* Report Count for padding [HID1_11, 6.2.2.9] */
188 rdesc[31] += unused_button_numbers;
189
190 hid_info(hdev, "winwing descriptor fixed\n");
191 }
192
193 return rdesc;
194 }
195
winwing_raw_event(struct hid_device * hdev,struct hid_report * report,u8 * raw_data,int size)196 static int winwing_raw_event(struct hid_device *hdev,
197 struct hid_report *report, u8 *raw_data, int size)
198 {
199 if (size >= 15) {
200 /* Skip buttons 32 .. 63 */
201 memmove(raw_data + 5, raw_data + 9, 6);
202
203 /* Clear the padding */
204 memset(raw_data + 11, 0, 4);
205 }
206
207 return 0;
208 }
209
210 static const struct hid_device_id winwing_devices[] = {
211 { HID_USB_DEVICE(0x4098, 0xbe62) }, /* TGRIP-18 */
212 { HID_USB_DEVICE(0x4098, 0xbe68) }, /* TGRIP-16EX */
213 {}
214 };
215
216 MODULE_DEVICE_TABLE(hid, winwing_devices);
217
218 static struct hid_driver winwing_driver = {
219 .name = "winwing",
220 .id_table = winwing_devices,
221 .probe = winwing_probe,
222 .input_configured = winwing_input_configured,
223 .report_fixup = winwing_report_fixup,
224 .raw_event = winwing_raw_event,
225 };
226 module_hid_driver(winwing_driver);
227
228 MODULE_DESCRIPTION("HID driver for WinWing Orion 2 throttle");
229 MODULE_LICENSE("GPL");
230