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_DESCRIPTION("HID driver for WinWing Orion 2 throttle"); 227 MODULE_LICENSE("GPL"); 228