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
110 ret = devm_led_classdev_register(&hdev->dev, &led->cdev);
111 if (ret)
112 return ret;
113 }
114
115 hid_set_drvdata(hdev, data);
116
117 return ret;
118 }
119
winwing_probe(struct hid_device * hdev,const struct hid_device_id * id)120 static int winwing_probe(struct hid_device *hdev,
121 const struct hid_device_id *id)
122 {
123 int ret;
124
125 ret = hid_parse(hdev);
126 if (ret) {
127 hid_err(hdev, "parse failed\n");
128 return ret;
129 }
130
131 ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
132 if (ret) {
133 hid_err(hdev, "hw start failed\n");
134 return ret;
135 }
136
137 return 0;
138 }
139
winwing_input_configured(struct hid_device * hdev,struct hid_input * hidinput)140 static int winwing_input_configured(struct hid_device *hdev,
141 struct hid_input *hidinput)
142 {
143 int ret;
144
145 ret = winwing_init_led(hdev, hidinput->input);
146
147 if (ret)
148 hid_err(hdev, "led init failed\n");
149
150 return ret;
151 }
152
153 static const __u8 original_rdesc_buttons[] = {
154 0x05, 0x09, 0x19, 0x01, 0x29, 0x6F,
155 0x15, 0x00, 0x25, 0x01, 0x35, 0x00,
156 0x45, 0x01, 0x75, 0x01, 0x95, 0x6F,
157 0x81, 0x02, 0x75, 0x01, 0x95, 0x01,
158 0x81, 0x01
159 };
160
161 /*
162 * HID report descriptor shows 111 buttons, which exceeds maximum
163 * number of buttons (80) supported by Linux kernel HID subsystem.
164 *
165 * This module skips numbers 32-63, unused on some throttle grips.
166 */
167
winwing_report_fixup(struct hid_device * hdev,__u8 * rdesc,unsigned int * rsize)168 static const __u8 *winwing_report_fixup(struct hid_device *hdev, __u8 *rdesc,
169 unsigned int *rsize)
170 {
171 int sig_length = sizeof(original_rdesc_buttons);
172 int unused_button_numbers = 32;
173
174 if (*rsize < 34)
175 return rdesc;
176
177 if (memcmp(rdesc + 8, original_rdesc_buttons, sig_length) == 0) {
178
179 /* Usage Maximum */
180 rdesc[13] -= unused_button_numbers;
181
182 /* Report Count for buttons */
183 rdesc[25] -= unused_button_numbers;
184
185 /* Report Count for padding [HID1_11, 6.2.2.9] */
186 rdesc[31] += unused_button_numbers;
187
188 hid_info(hdev, "winwing descriptor fixed\n");
189 }
190
191 return rdesc;
192 }
193
winwing_raw_event(struct hid_device * hdev,struct hid_report * report,u8 * raw_data,int size)194 static int winwing_raw_event(struct hid_device *hdev,
195 struct hid_report *report, u8 *raw_data, int size)
196 {
197 if (size >= 15) {
198 /* Skip buttons 32 .. 63 */
199 memmove(raw_data + 5, raw_data + 9, 6);
200
201 /* Clear the padding */
202 memset(raw_data + 11, 0, 4);
203 }
204
205 return 0;
206 }
207
208 static const struct hid_device_id winwing_devices[] = {
209 { HID_USB_DEVICE(0x4098, 0xbe62) }, /* TGRIP-18 */
210 { HID_USB_DEVICE(0x4098, 0xbe68) }, /* TGRIP-16EX */
211 {}
212 };
213
214 MODULE_DEVICE_TABLE(hid, winwing_devices);
215
216 static struct hid_driver winwing_driver = {
217 .name = "winwing",
218 .id_table = winwing_devices,
219 .probe = winwing_probe,
220 .input_configured = winwing_input_configured,
221 .report_fixup = winwing_report_fixup,
222 .raw_event = winwing_raw_event,
223 };
224 module_hid_driver(winwing_driver);
225
226 MODULE_DESCRIPTION("HID driver for WinWing Orion 2 throttle");
227 MODULE_LICENSE("GPL");
228