1266c990dSIvan Gorinov // SPDX-License-Identifier: GPL-2.0 2266c990dSIvan Gorinov 3266c990dSIvan Gorinov /* 4266c990dSIvan Gorinov * HID driver for WinWing Orion 2 throttle 5266c990dSIvan Gorinov * 6266c990dSIvan Gorinov * Copyright (c) 2023 Ivan Gorinov 7266c990dSIvan Gorinov */ 8266c990dSIvan Gorinov 9266c990dSIvan Gorinov #include <linux/device.h> 10266c990dSIvan Gorinov #include <linux/hid.h> 11266c990dSIvan Gorinov #include <linux/hidraw.h> 12266c990dSIvan Gorinov #include <linux/kernel.h> 13266c990dSIvan Gorinov #include <linux/module.h> 14266c990dSIvan Gorinov #include <linux/mutex.h> 15266c990dSIvan Gorinov 16266c990dSIvan Gorinov #define MAX_REPORT 16 17266c990dSIvan Gorinov 18266c990dSIvan Gorinov struct winwing_led { 19266c990dSIvan Gorinov struct led_classdev cdev; 20266c990dSIvan Gorinov struct hid_device *hdev; 21266c990dSIvan Gorinov int number; 22266c990dSIvan Gorinov }; 23266c990dSIvan Gorinov 24266c990dSIvan Gorinov struct winwing_led_info { 25266c990dSIvan Gorinov int number; 26266c990dSIvan Gorinov int max_brightness; 27266c990dSIvan Gorinov const char *led_name; 28266c990dSIvan Gorinov }; 29266c990dSIvan Gorinov 30266c990dSIvan Gorinov static struct winwing_led_info led_info[3] = { 31266c990dSIvan Gorinov { 0, 255, "backlight" }, 32266c990dSIvan Gorinov { 1, 1, "a-a" }, 33266c990dSIvan Gorinov { 2, 1, "a-g" }, 34266c990dSIvan Gorinov }; 35266c990dSIvan Gorinov 36266c990dSIvan Gorinov struct winwing_drv_data { 37266c990dSIvan Gorinov struct hid_device *hdev; 38266c990dSIvan Gorinov __u8 *report_buf; 39266c990dSIvan Gorinov struct mutex lock; 40266c990dSIvan Gorinov unsigned int num_leds; 41266c990dSIvan Gorinov struct winwing_led leds[]; 42266c990dSIvan Gorinov }; 43266c990dSIvan Gorinov 44266c990dSIvan Gorinov static int winwing_led_write(struct led_classdev *cdev, 45266c990dSIvan Gorinov enum led_brightness br) 46266c990dSIvan Gorinov { 47266c990dSIvan Gorinov struct winwing_led *led = (struct winwing_led *) cdev; 48266c990dSIvan Gorinov struct winwing_drv_data *data = hid_get_drvdata(led->hdev); 49266c990dSIvan Gorinov __u8 *buf = data->report_buf; 50266c990dSIvan Gorinov int ret; 51266c990dSIvan Gorinov 52266c990dSIvan Gorinov mutex_lock(&data->lock); 53266c990dSIvan Gorinov 54266c990dSIvan Gorinov buf[0] = 0x02; 55266c990dSIvan Gorinov buf[1] = 0x60; 56266c990dSIvan Gorinov buf[2] = 0xbe; 57266c990dSIvan Gorinov buf[3] = 0x00; 58266c990dSIvan Gorinov buf[4] = 0x00; 59266c990dSIvan Gorinov buf[5] = 0x03; 60266c990dSIvan Gorinov buf[6] = 0x49; 61266c990dSIvan Gorinov buf[7] = led->number; 62266c990dSIvan Gorinov buf[8] = br; 63266c990dSIvan Gorinov buf[9] = 0x00; 64266c990dSIvan Gorinov buf[10] = 0; 65266c990dSIvan Gorinov buf[11] = 0; 66266c990dSIvan Gorinov buf[12] = 0; 67266c990dSIvan Gorinov buf[13] = 0; 68266c990dSIvan Gorinov 69266c990dSIvan Gorinov ret = hid_hw_output_report(led->hdev, buf, 14); 70266c990dSIvan Gorinov 71266c990dSIvan Gorinov mutex_unlock(&data->lock); 72266c990dSIvan Gorinov 73266c990dSIvan Gorinov return ret; 74266c990dSIvan Gorinov } 75266c990dSIvan Gorinov 76266c990dSIvan Gorinov static int winwing_init_led(struct hid_device *hdev, 77266c990dSIvan Gorinov struct input_dev *input) 78266c990dSIvan Gorinov { 79266c990dSIvan Gorinov struct winwing_drv_data *data; 80266c990dSIvan Gorinov struct winwing_led *led; 81266c990dSIvan Gorinov int ret; 82266c990dSIvan Gorinov int i; 83266c990dSIvan Gorinov 84266c990dSIvan Gorinov size_t data_size = struct_size(data, leds, 3); 85266c990dSIvan Gorinov 86266c990dSIvan Gorinov data = devm_kzalloc(&hdev->dev, data_size, GFP_KERNEL); 87266c990dSIvan Gorinov 88266c990dSIvan Gorinov if (!data) 89266c990dSIvan Gorinov return -ENOMEM; 90266c990dSIvan Gorinov 91266c990dSIvan Gorinov data->report_buf = devm_kmalloc(&hdev->dev, MAX_REPORT, GFP_KERNEL); 92266c990dSIvan Gorinov 93266c990dSIvan Gorinov if (!data->report_buf) 94266c990dSIvan Gorinov return -ENOMEM; 95266c990dSIvan Gorinov 96266c990dSIvan Gorinov for (i = 0; i < 3; i += 1) { 97266c990dSIvan Gorinov struct winwing_led_info *info = &led_info[i]; 98266c990dSIvan Gorinov 99266c990dSIvan Gorinov led = &data->leds[i]; 100266c990dSIvan Gorinov led->hdev = hdev; 101266c990dSIvan Gorinov led->number = info->number; 102266c990dSIvan Gorinov led->cdev.max_brightness = info->max_brightness; 103266c990dSIvan Gorinov led->cdev.brightness_set_blocking = winwing_led_write; 104266c990dSIvan Gorinov led->cdev.flags = LED_HW_PLUGGABLE; 105266c990dSIvan Gorinov led->cdev.name = devm_kasprintf(&hdev->dev, GFP_KERNEL, 106266c990dSIvan Gorinov "%s::%s", 107266c990dSIvan Gorinov dev_name(&input->dev), 108266c990dSIvan Gorinov info->led_name); 109266c990dSIvan Gorinov 110266c990dSIvan Gorinov ret = devm_led_classdev_register(&hdev->dev, &led->cdev); 111266c990dSIvan Gorinov if (ret) 112266c990dSIvan Gorinov return ret; 113266c990dSIvan Gorinov } 114266c990dSIvan Gorinov 115266c990dSIvan Gorinov hid_set_drvdata(hdev, data); 116266c990dSIvan Gorinov 117266c990dSIvan Gorinov return ret; 118266c990dSIvan Gorinov } 119266c990dSIvan Gorinov 120266c990dSIvan Gorinov static int winwing_probe(struct hid_device *hdev, 121266c990dSIvan Gorinov const struct hid_device_id *id) 122266c990dSIvan Gorinov { 123266c990dSIvan Gorinov int ret; 124266c990dSIvan Gorinov 125266c990dSIvan Gorinov ret = hid_parse(hdev); 126266c990dSIvan Gorinov if (ret) { 127266c990dSIvan Gorinov hid_err(hdev, "parse failed\n"); 128266c990dSIvan Gorinov return ret; 129266c990dSIvan Gorinov } 130266c990dSIvan Gorinov 131266c990dSIvan Gorinov ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); 132266c990dSIvan Gorinov if (ret) { 133266c990dSIvan Gorinov hid_err(hdev, "hw start failed\n"); 134266c990dSIvan Gorinov return ret; 135266c990dSIvan Gorinov } 136266c990dSIvan Gorinov 137266c990dSIvan Gorinov return 0; 138266c990dSIvan Gorinov } 139266c990dSIvan Gorinov 140266c990dSIvan Gorinov static int winwing_input_configured(struct hid_device *hdev, 141266c990dSIvan Gorinov struct hid_input *hidinput) 142266c990dSIvan Gorinov { 143266c990dSIvan Gorinov int ret; 144266c990dSIvan Gorinov 145266c990dSIvan Gorinov ret = winwing_init_led(hdev, hidinput->input); 146266c990dSIvan Gorinov 147266c990dSIvan Gorinov if (ret) 148266c990dSIvan Gorinov hid_err(hdev, "led init failed\n"); 149266c990dSIvan Gorinov 150266c990dSIvan Gorinov return ret; 151266c990dSIvan Gorinov } 152266c990dSIvan Gorinov 153266c990dSIvan Gorinov static __u8 original_rdesc_buttons[] = { 154266c990dSIvan Gorinov 0x05, 0x09, 0x19, 0x01, 0x29, 0x6F, 155266c990dSIvan Gorinov 0x15, 0x00, 0x25, 0x01, 0x35, 0x00, 156266c990dSIvan Gorinov 0x45, 0x01, 0x75, 0x01, 0x95, 0x6F, 157266c990dSIvan Gorinov 0x81, 0x02, 0x75, 0x01, 0x95, 0x01, 158266c990dSIvan Gorinov 0x81, 0x01 159266c990dSIvan Gorinov }; 160266c990dSIvan Gorinov 161266c990dSIvan Gorinov /* 162266c990dSIvan Gorinov * HID report descriptor shows 111 buttons, which exceeds maximum 163266c990dSIvan Gorinov * number of buttons (80) supported by Linux kernel HID subsystem. 164266c990dSIvan Gorinov * 165266c990dSIvan Gorinov * This module skips numbers 32-63, unused on some throttle grips. 166266c990dSIvan Gorinov */ 167266c990dSIvan Gorinov 168*fe73965dSThomas Weißschuh static const __u8 *winwing_report_fixup(struct hid_device *hdev, __u8 *rdesc, 169266c990dSIvan Gorinov unsigned int *rsize) 170266c990dSIvan Gorinov { 171266c990dSIvan Gorinov int sig_length = sizeof(original_rdesc_buttons); 172266c990dSIvan Gorinov int unused_button_numbers = 32; 173266c990dSIvan Gorinov 174266c990dSIvan Gorinov if (*rsize < 34) 175266c990dSIvan Gorinov return rdesc; 176266c990dSIvan Gorinov 177266c990dSIvan Gorinov if (memcmp(rdesc + 8, original_rdesc_buttons, sig_length) == 0) { 178266c990dSIvan Gorinov 179266c990dSIvan Gorinov /* Usage Maximum */ 180266c990dSIvan Gorinov rdesc[13] -= unused_button_numbers; 181266c990dSIvan Gorinov 182266c990dSIvan Gorinov /* Report Count for buttons */ 183266c990dSIvan Gorinov rdesc[25] -= unused_button_numbers; 184266c990dSIvan Gorinov 185266c990dSIvan Gorinov /* Report Count for padding [HID1_11, 6.2.2.9] */ 186266c990dSIvan Gorinov rdesc[31] += unused_button_numbers; 187266c990dSIvan Gorinov 188266c990dSIvan Gorinov hid_info(hdev, "winwing descriptor fixed\n"); 189266c990dSIvan Gorinov } 190266c990dSIvan Gorinov 191266c990dSIvan Gorinov return rdesc; 192266c990dSIvan Gorinov } 193266c990dSIvan Gorinov 194266c990dSIvan Gorinov static int winwing_raw_event(struct hid_device *hdev, 195266c990dSIvan Gorinov struct hid_report *report, u8 *raw_data, int size) 196266c990dSIvan Gorinov { 197266c990dSIvan Gorinov if (size >= 15) { 198266c990dSIvan Gorinov /* Skip buttons 32 .. 63 */ 199266c990dSIvan Gorinov memmove(raw_data + 5, raw_data + 9, 6); 200266c990dSIvan Gorinov 201266c990dSIvan Gorinov /* Clear the padding */ 202266c990dSIvan Gorinov memset(raw_data + 11, 0, 4); 203266c990dSIvan Gorinov } 204266c990dSIvan Gorinov 205266c990dSIvan Gorinov return 0; 206266c990dSIvan Gorinov } 207266c990dSIvan Gorinov 208266c990dSIvan Gorinov static const struct hid_device_id winwing_devices[] = { 209266c990dSIvan Gorinov { HID_USB_DEVICE(0x4098, 0xbe62) }, /* TGRIP-18 */ 210266c990dSIvan Gorinov { HID_USB_DEVICE(0x4098, 0xbe68) }, /* TGRIP-16EX */ 211266c990dSIvan Gorinov {} 212266c990dSIvan Gorinov }; 213266c990dSIvan Gorinov 214266c990dSIvan Gorinov MODULE_DEVICE_TABLE(hid, winwing_devices); 215266c990dSIvan Gorinov 216266c990dSIvan Gorinov static struct hid_driver winwing_driver = { 217266c990dSIvan Gorinov .name = "winwing", 218266c990dSIvan Gorinov .id_table = winwing_devices, 219266c990dSIvan Gorinov .probe = winwing_probe, 220266c990dSIvan Gorinov .input_configured = winwing_input_configured, 221266c990dSIvan Gorinov .report_fixup = winwing_report_fixup, 222266c990dSIvan Gorinov .raw_event = winwing_raw_event, 223266c990dSIvan Gorinov }; 224266c990dSIvan Gorinov module_hid_driver(winwing_driver); 225266c990dSIvan Gorinov 2265bd8d707SJeff Johnson MODULE_DESCRIPTION("HID driver for WinWing Orion 2 throttle"); 227266c990dSIvan Gorinov MODULE_LICENSE("GPL"); 228