]> Git Repo - linux.git/blob - drivers/hid/hid-winwing.c
Merge patch series "riscv: Extension parsing fixes"
[linux.git] / drivers / hid / hid-winwing.c
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 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
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
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                 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
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
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 __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
168 static __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
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_LICENSE("GPL");
This page took 0.048366 seconds and 4 git commands to generate.