xref: /linux/drivers/hid/hid-winwing.c (revision 3a39d672e7f48b8d6b91a09afa4b55352773b4b5)
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