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 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 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 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 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 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 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