Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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#include <linux/workqueue.h>
16
17#define MAX_REPORT 16
18
19struct winwing_led {
20 struct led_classdev cdev;
21 struct hid_device *hdev;
22 int number;
23};
24
25struct winwing_led_info {
26 int number;
27 int max_brightness;
28 const char *led_name;
29};
30
31static const struct winwing_led_info led_info[3] = {
32 { 0, 255, "backlight" },
33 { 1, 1, "a-a" },
34 { 2, 1, "a-g" },
35};
36
37struct winwing_drv_data {
38 struct hid_device *hdev;
39 struct mutex lights_lock;
40 __u8 *report_lights;
41 __u8 *report_rumble;
42 struct work_struct rumble_work;
43 struct ff_rumble_effect rumble;
44 int rumble_left;
45 int rumble_right;
46 int has_grip15;
47 struct winwing_led leds[];
48};
49
50static int winwing_led_write(struct led_classdev *cdev,
51 enum led_brightness br)
52{
53 struct winwing_led *led = (struct winwing_led *) cdev;
54 struct winwing_drv_data *data = hid_get_drvdata(led->hdev);
55 __u8 *buf = data->report_lights;
56 int ret;
57
58 mutex_lock(&data->lights_lock);
59
60 /*
61 * Mimicking requests captured by usbmon when LEDs
62 * are controlled by the vendor's app in a VM.
63 */
64 buf[0] = 0x02;
65 buf[1] = 0x60;
66 buf[2] = 0xbe;
67 buf[3] = 0x00;
68 buf[4] = 0x00;
69 buf[5] = 0x03;
70 buf[6] = 0x49;
71 buf[7] = led->number;
72 buf[8] = br;
73 buf[9] = 0x00;
74 buf[10] = 0;
75 buf[11] = 0;
76 buf[12] = 0;
77 buf[13] = 0;
78
79 ret = hid_hw_output_report(led->hdev, buf, 14);
80
81 mutex_unlock(&data->lights_lock);
82
83 return ret;
84}
85
86static int winwing_init_led(struct hid_device *hdev,
87 struct input_dev *input)
88{
89 struct winwing_drv_data *data;
90 struct winwing_led *led;
91 int ret;
92 int i;
93
94 data = hid_get_drvdata(hdev);
95
96 if (!data)
97 return -EINVAL;
98
99 data->report_lights = devm_kzalloc(&hdev->dev, MAX_REPORT, GFP_KERNEL);
100
101 if (!data->report_lights)
102 return -ENOMEM;
103
104 for (i = 0; i < 3; i += 1) {
105 const struct winwing_led_info *info = &led_info[i];
106
107 led = &data->leds[i];
108 led->hdev = hdev;
109 led->number = info->number;
110 led->cdev.max_brightness = info->max_brightness;
111 led->cdev.brightness_set_blocking = winwing_led_write;
112 led->cdev.flags = LED_HW_PLUGGABLE;
113 led->cdev.name = devm_kasprintf(&hdev->dev, GFP_KERNEL,
114 "%s::%s",
115 dev_name(&input->dev),
116 info->led_name);
117
118 if (!led->cdev.name)
119 return -ENOMEM;
120
121 ret = devm_led_classdev_register(&hdev->dev, &led->cdev);
122 if (ret)
123 return ret;
124 }
125
126 return ret;
127}
128
129static int winwing_map_button(int button, int has_grip15)
130{
131 if (button < 1)
132 return KEY_RESERVED;
133
134 if (button > 112)
135 return KEY_RESERVED;
136
137 if (button <= 16) {
138 /*
139 * Grip buttons [1 .. 16] are mapped to
140 * key codes BTN_TRIGGER .. BTN_DEAD
141 */
142 return (button - 1) + BTN_JOYSTICK;
143 }
144
145 if (button >= 65) {
146 /*
147 * Base buttons [65 .. 112] are mapped to
148 * key codes BTN_TRIGGER_HAPPY17 .. KEY_MAX
149 */
150 return (button - 65) + BTN_TRIGGER_HAPPY17;
151 }
152
153 if (!has_grip15) {
154 /*
155 * Not mapping numbers [33 .. 64] which
156 * are not assigned to any real buttons
157 */
158 if (button >= 33)
159 return KEY_RESERVED;
160 /*
161 * Grip buttons [17 .. 32] are mapped to
162 * BTN_TRIGGER_HAPPY1 .. BTN_TRIGGER_HAPPY16
163 */
164 return (button - 17) + BTN_TRIGGER_HAPPY1;
165 }
166
167 if (button >= 49) {
168 /*
169 * Grip buttons [49 .. 64] are mapped to
170 * BTN_TRIGGER_HAPPY1 .. BTN_TRIGGER_HAPPY16
171 */
172 return (button - 49) + BTN_TRIGGER_HAPPY1;
173 }
174
175 /*
176 * Grip buttons [17 .. 44] are mapped to
177 * key codes KEY_MACRO1 .. KEY_MACRO28;
178 * also mapping numbers [45 .. 48] which
179 * are not assigned to any real buttons.
180 */
181 return (button - 17) + KEY_MACRO1;
182}
183
184static int winwing_input_mapping(struct hid_device *hdev,
185 struct hid_input *hi, struct hid_field *field, struct hid_usage *usage,
186 unsigned long **bit, int *max)
187{
188 struct winwing_drv_data *data;
189 int code = KEY_RESERVED;
190 int button = 0;
191
192 data = hid_get_drvdata(hdev);
193
194 if (!data)
195 return -EINVAL;
196
197 if ((usage->hid & HID_USAGE_PAGE) != HID_UP_BUTTON)
198 return 0;
199
200 if (field->application != HID_GD_JOYSTICK)
201 return 0;
202
203 /* Button numbers start with 1 */
204 button = usage->hid & HID_USAGE;
205
206 code = winwing_map_button(button, data->has_grip15);
207
208 hid_map_usage(hi, usage, bit, max, EV_KEY, code);
209
210 return 1;
211}
212
213/*
214 * If x ≤ 0, return 0;
215 * if x is in [1 .. 65535], return a value in [1 .. 255]
216 */
217static inline int convert_magnitude(int x)
218{
219 if (x < 1)
220 return 0;
221
222 return ((x * 255) >> 16) + 1;
223}
224
225static int winwing_haptic_rumble(struct winwing_drv_data *data)
226{
227 __u8 *buf;
228 __u8 m;
229
230 if (!data)
231 return -EINVAL;
232
233 if (!data->hdev)
234 return -EINVAL;
235
236 buf = data->report_rumble;
237
238 if (!buf)
239 return -EINVAL;
240
241 m = convert_magnitude(data->rumble.strong_magnitude);
242 if (m != data->rumble_left) {
243 int ret;
244
245 /*
246 * Mimicking requests captured by usbmon when rumble
247 * is activated by the vendor's app in a VM.
248 */
249 buf[0] = 0x02;
250 buf[1] = 0x01;
251 buf[2] = 0xbf;
252 buf[3] = 0x00;
253 buf[4] = 0x00;
254 buf[5] = 0x03;
255 buf[6] = 0x49;
256 buf[7] = 0x00;
257 buf[8] = m;
258 buf[9] = 0x00;
259 buf[10] = 0;
260 buf[11] = 0;
261 buf[12] = 0;
262 buf[13] = 0;
263
264 ret = hid_hw_output_report(data->hdev, buf, 14);
265 if (ret < 0) {
266 hid_err(data->hdev, "error %d (%*ph)\n", ret, 14, buf);
267 return ret;
268 }
269 data->rumble_left = m;
270 }
271
272 m = convert_magnitude(data->rumble.weak_magnitude);
273 if (m != data->rumble_right) {
274 int ret;
275
276 /*
277 * Mimicking requests captured by usbmon when rumble
278 * is activated by the vendor's app in a VM.
279 */
280 buf[0] = 0x02;
281 buf[1] = 0x03;
282 buf[2] = 0xbf;
283 buf[3] = 0x00;
284 buf[4] = 0x00;
285 buf[5] = 0x03;
286 buf[6] = 0x49;
287 buf[7] = 0x00;
288 buf[8] = m;
289 buf[9] = 0x00;
290 buf[10] = 0;
291 buf[11] = 0;
292 buf[12] = 0;
293 buf[13] = 0;
294
295 ret = hid_hw_output_report(data->hdev, buf, 14);
296 if (ret < 0) {
297 hid_err(data->hdev, "error %d (%*ph)\n", ret, 14, buf);
298 return ret;
299 }
300 data->rumble_right = m;
301 }
302
303 return 0;
304}
305
306
307static void winwing_haptic_rumble_cb(struct work_struct *work)
308{
309 struct winwing_drv_data *data;
310
311 data = container_of(work, struct winwing_drv_data, rumble_work);
312 winwing_haptic_rumble(data);
313}
314
315static int winwing_play_effect(struct input_dev *dev, void *context,
316 struct ff_effect *effect)
317{
318 struct winwing_drv_data *data = (struct winwing_drv_data *) context;
319
320 if (effect->type != FF_RUMBLE)
321 return 0;
322
323 if (!data)
324 return -EINVAL;
325
326 data->rumble = effect->u.rumble;
327
328 return schedule_work(&data->rumble_work);
329}
330
331static int winwing_init_ff(struct hid_device *hdev, struct hid_input *hidinput)
332{
333 struct winwing_drv_data *data;
334
335 data = (struct winwing_drv_data *) hid_get_drvdata(hdev);
336 if (!data)
337 return -EINVAL;
338
339 data->report_rumble = devm_kzalloc(&hdev->dev, MAX_REPORT, GFP_KERNEL);
340 data->rumble_left = -1;
341 data->rumble_right = -1;
342
343 input_set_capability(hidinput->input, EV_FF, FF_RUMBLE);
344
345 return input_ff_create_memless(hidinput->input, data,
346 winwing_play_effect);
347}
348
349static int winwing_probe(struct hid_device *hdev,
350 const struct hid_device_id *id)
351{
352 struct winwing_drv_data *data;
353 size_t data_size = struct_size(data, leds, 3);
354 int ret;
355
356 ret = hid_parse(hdev);
357 if (ret) {
358 hid_err(hdev, "parse failed\n");
359 return ret;
360 }
361
362 data = devm_kzalloc(&hdev->dev, data_size, GFP_KERNEL);
363
364 if (!data)
365 return -ENOMEM;
366
367 data->hdev = hdev;
368 data->has_grip15 = id->driver_data;
369 hid_set_drvdata(hdev, data);
370
371 INIT_WORK(&data->rumble_work, winwing_haptic_rumble_cb);
372
373 ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
374 if (ret) {
375 hid_err(hdev, "hw start failed\n");
376 return ret;
377 }
378
379 return 0;
380}
381
382static void winwing_remove(struct hid_device *hdev)
383{
384 struct winwing_drv_data *data;
385
386 data = (struct winwing_drv_data *) hid_get_drvdata(hdev);
387
388 if (data)
389 cancel_work_sync(&data->rumble_work);
390
391 hid_hw_close(hdev);
392 hid_hw_stop(hdev);
393}
394
395static int winwing_input_configured(struct hid_device *hdev,
396 struct hid_input *hidinput)
397{
398 struct winwing_drv_data *data;
399 int ret;
400
401 data = (struct winwing_drv_data *) hid_get_drvdata(hdev);
402
403 ret = winwing_init_led(hdev, hidinput->input);
404
405 if (ret)
406 hid_err(hdev, "led init failed\n");
407
408 if (data->has_grip15)
409 winwing_init_ff(hdev, hidinput);
410
411 return ret;
412}
413
414/* Set driver_data to 1 for grips with rumble motor and more than 32 buttons */
415static const struct hid_device_id winwing_devices[] = {
416 { HID_USB_DEVICE(0x4098, 0xbd65), .driver_data = 1 }, /* TGRIP-15E */
417 { HID_USB_DEVICE(0x4098, 0xbd64), .driver_data = 1 }, /* TGRIP-15EX */
418 { HID_USB_DEVICE(0x4098, 0xbe68), .driver_data = 0 }, /* TGRIP-16EX */
419 { HID_USB_DEVICE(0x4098, 0xbe62), .driver_data = 0 }, /* TGRIP-18 */
420 {}
421};
422
423MODULE_DEVICE_TABLE(hid, winwing_devices);
424
425static struct hid_driver winwing_driver = {
426 .name = "winwing",
427 .id_table = winwing_devices,
428 .input_configured = winwing_input_configured,
429 .input_mapping = winwing_input_mapping,
430 .probe = winwing_probe,
431 .remove = winwing_remove,
432};
433module_hid_driver(winwing_driver);
434
435MODULE_DESCRIPTION("HID driver for WinWing Orion 2 throttle");
436MODULE_LICENSE("GPL");