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 int map_more_buttons; 41 unsigned int num_leds; 42 struct winwing_led leds[]; 43 }; 44 45 static int winwing_led_write(struct led_classdev *cdev, 46 enum led_brightness br) 47 { 48 struct winwing_led *led = (struct winwing_led *) cdev; 49 struct winwing_drv_data *data = hid_get_drvdata(led->hdev); 50 __u8 *buf = data->report_buf; 51 int ret; 52 53 mutex_lock(&data->lock); 54 55 buf[0] = 0x02; 56 buf[1] = 0x60; 57 buf[2] = 0xbe; 58 buf[3] = 0x00; 59 buf[4] = 0x00; 60 buf[5] = 0x03; 61 buf[6] = 0x49; 62 buf[7] = led->number; 63 buf[8] = br; 64 buf[9] = 0x00; 65 buf[10] = 0; 66 buf[11] = 0; 67 buf[12] = 0; 68 buf[13] = 0; 69 70 ret = hid_hw_output_report(led->hdev, buf, 14); 71 72 mutex_unlock(&data->lock); 73 74 return ret; 75 } 76 77 static int winwing_init_led(struct hid_device *hdev, 78 struct input_dev *input) 79 { 80 struct winwing_drv_data *data; 81 struct winwing_led *led; 82 int ret; 83 int i; 84 85 data = hid_get_drvdata(hdev); 86 87 if (!data) 88 return -EINVAL; 89 90 data->report_buf = devm_kmalloc(&hdev->dev, MAX_REPORT, GFP_KERNEL); 91 92 if (!data->report_buf) 93 return -ENOMEM; 94 95 for (i = 0; i < 3; i += 1) { 96 const struct winwing_led_info *info = &led_info[i]; 97 98 led = &data->leds[i]; 99 led->hdev = hdev; 100 led->number = info->number; 101 led->cdev.max_brightness = info->max_brightness; 102 led->cdev.brightness_set_blocking = winwing_led_write; 103 led->cdev.flags = LED_HW_PLUGGABLE; 104 led->cdev.name = devm_kasprintf(&hdev->dev, GFP_KERNEL, 105 "%s::%s", 106 dev_name(&input->dev), 107 info->led_name); 108 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 return ret; 118 } 119 120 static int winwing_map_button(int button, int map_more_buttons) 121 { 122 if (button < 1) 123 return KEY_RESERVED; 124 125 if (button > 112) 126 return KEY_RESERVED; 127 128 if (button <= 16) { 129 /* 130 * Grip buttons [1 .. 16] are mapped to 131 * key codes BTN_TRIGGER .. BTN_DEAD 132 */ 133 return (button - 1) + BTN_JOYSTICK; 134 } 135 136 if (button >= 65) { 137 /* 138 * Base buttons [65 .. 112] are mapped to 139 * key codes BTN_TRIGGER_HAPPY17 .. KEY_MAX 140 */ 141 return (button - 65) + BTN_TRIGGER_HAPPY17; 142 } 143 144 if (!map_more_buttons) { 145 /* 146 * Not mapping numbers [33 .. 64] which 147 * are not assigned to any real buttons 148 */ 149 if (button >= 33) 150 return KEY_RESERVED; 151 /* 152 * Grip buttons [17 .. 32] are mapped to 153 * BTN_TRIGGER_HAPPY1 .. BTN_TRIGGER_HAPPY16 154 */ 155 return (button - 17) + BTN_TRIGGER_HAPPY1; 156 } 157 158 if (button >= 49) { 159 /* 160 * Grip buttons [49 .. 64] are mapped to 161 * BTN_TRIGGER_HAPPY1 .. BTN_TRIGGER_HAPPY16 162 */ 163 return (button - 49) + BTN_TRIGGER_HAPPY1; 164 } 165 166 /* 167 * Grip buttons [17 .. 44] are mapped to 168 * key codes KEY_MACRO1 .. KEY_MACRO28; 169 * also mapping numbers [45 .. 48] which 170 * are not assigned to any real buttons. 171 */ 172 return (button - 17) + KEY_MACRO1; 173 } 174 175 static int winwing_input_mapping(struct hid_device *hdev, 176 struct hid_input *hi, struct hid_field *field, struct hid_usage *usage, 177 unsigned long **bit, int *max) 178 { 179 struct winwing_drv_data *data; 180 int code = KEY_RESERVED; 181 int button = 0; 182 183 data = hid_get_drvdata(hdev); 184 185 if (!data) 186 return -EINVAL; 187 188 if ((usage->hid & HID_USAGE_PAGE) != HID_UP_BUTTON) 189 return 0; 190 191 if (field->application != HID_GD_JOYSTICK) 192 return 0; 193 194 /* Button numbers start with 1 */ 195 button = usage->hid & HID_USAGE; 196 197 code = winwing_map_button(button, data->map_more_buttons); 198 199 hid_map_usage(hi, usage, bit, max, EV_KEY, code); 200 201 return 1; 202 } 203 204 static int winwing_probe(struct hid_device *hdev, 205 const struct hid_device_id *id) 206 { 207 struct winwing_drv_data *data; 208 size_t data_size = struct_size(data, leds, 3); 209 int ret; 210 211 ret = hid_parse(hdev); 212 if (ret) { 213 hid_err(hdev, "parse failed\n"); 214 return ret; 215 } 216 217 data = devm_kzalloc(&hdev->dev, data_size, GFP_KERNEL); 218 219 if (!data) 220 return -ENOMEM; 221 222 data->map_more_buttons = id->driver_data; 223 224 hid_set_drvdata(hdev, data); 225 226 ret = hid_hw_start(hdev, HID_CONNECT_DEFAULT); 227 if (ret) { 228 hid_err(hdev, "hw start failed\n"); 229 return ret; 230 } 231 232 return 0; 233 } 234 235 static int winwing_input_configured(struct hid_device *hdev, 236 struct hid_input *hidinput) 237 { 238 int ret; 239 240 ret = winwing_init_led(hdev, hidinput->input); 241 242 if (ret) 243 hid_err(hdev, "led init failed\n"); 244 245 return ret; 246 } 247 248 static const struct hid_device_id winwing_devices[] = { 249 { HID_USB_DEVICE(0x4098, 0xbd65), .driver_data = 1 }, /* TGRIP-15E */ 250 { HID_USB_DEVICE(0x4098, 0xbd64), .driver_data = 1 }, /* TGRIP-15EX */ 251 { HID_USB_DEVICE(0x4098, 0xbe68), .driver_data = 0 }, /* TGRIP-16EX */ 252 { HID_USB_DEVICE(0x4098, 0xbe62), .driver_data = 0 }, /* TGRIP-18 */ 253 {} 254 }; 255 256 MODULE_DEVICE_TABLE(hid, winwing_devices); 257 258 static struct hid_driver winwing_driver = { 259 .name = "winwing", 260 .id_table = winwing_devices, 261 .input_configured = winwing_input_configured, 262 .input_mapping = winwing_input_mapping, 263 .probe = winwing_probe, 264 }; 265 module_hid_driver(winwing_driver); 266 267 MODULE_DESCRIPTION("HID driver for WinWing Orion 2 throttle"); 268 MODULE_LICENSE("GPL"); 269