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