1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * HID driver for Corsair devices |
4 | * |
5 | * Supported devices: |
6 | * - Vengeance K70 Keyboard |
7 | * - K70 RAPIDFIRE Keyboard |
8 | * - Vengeance K90 Keyboard |
9 | * - Scimitar PRO RGB Gaming Mouse |
10 | * |
11 | * Copyright (c) 2015 Clement Vuchener |
12 | * Copyright (c) 2017 Oscar Campos |
13 | * Copyright (c) 2017 Aaron Bottegal |
14 | */ |
15 | |
16 | /* |
17 | */ |
18 | |
19 | #include <linux/hid.h> |
20 | #include <linux/module.h> |
21 | #include <linux/usb.h> |
22 | #include <linux/leds.h> |
23 | |
24 | #include "hid-ids.h" |
25 | |
26 | #define CORSAIR_USE_K90_MACRO (1<<0) |
27 | #define CORSAIR_USE_K90_BACKLIGHT (1<<1) |
28 | |
29 | struct k90_led { |
30 | struct led_classdev cdev; |
31 | int brightness; |
32 | struct work_struct work; |
33 | bool removed; |
34 | }; |
35 | |
36 | struct k90_drvdata { |
37 | struct k90_led record_led; |
38 | }; |
39 | |
40 | struct corsair_drvdata { |
41 | unsigned long quirks; |
42 | struct k90_drvdata *k90; |
43 | struct k90_led *backlight; |
44 | }; |
45 | |
46 | #define K90_GKEY_COUNT 18 |
47 | |
48 | static int corsair_usage_to_gkey(unsigned int usage) |
49 | { |
50 | /* G1 (0xd0) to G16 (0xdf) */ |
51 | if (usage >= 0xd0 && usage <= 0xdf) |
52 | return usage - 0xd0 + 1; |
53 | /* G17 (0xe8) to G18 (0xe9) */ |
54 | if (usage >= 0xe8 && usage <= 0xe9) |
55 | return usage - 0xe8 + 17; |
56 | return 0; |
57 | } |
58 | |
59 | static unsigned short corsair_gkey_map[K90_GKEY_COUNT] = { |
60 | BTN_TRIGGER_HAPPY1, |
61 | BTN_TRIGGER_HAPPY2, |
62 | BTN_TRIGGER_HAPPY3, |
63 | BTN_TRIGGER_HAPPY4, |
64 | BTN_TRIGGER_HAPPY5, |
65 | BTN_TRIGGER_HAPPY6, |
66 | BTN_TRIGGER_HAPPY7, |
67 | BTN_TRIGGER_HAPPY8, |
68 | BTN_TRIGGER_HAPPY9, |
69 | BTN_TRIGGER_HAPPY10, |
70 | BTN_TRIGGER_HAPPY11, |
71 | BTN_TRIGGER_HAPPY12, |
72 | BTN_TRIGGER_HAPPY13, |
73 | BTN_TRIGGER_HAPPY14, |
74 | BTN_TRIGGER_HAPPY15, |
75 | BTN_TRIGGER_HAPPY16, |
76 | BTN_TRIGGER_HAPPY17, |
77 | BTN_TRIGGER_HAPPY18, |
78 | }; |
79 | |
80 | module_param_array_named(gkey_codes, corsair_gkey_map, ushort, NULL, S_IRUGO); |
81 | MODULE_PARM_DESC(gkey_codes, "Key codes for the G-keys" ); |
82 | |
83 | static unsigned short corsair_record_keycodes[2] = { |
84 | BTN_TRIGGER_HAPPY19, |
85 | BTN_TRIGGER_HAPPY20 |
86 | }; |
87 | |
88 | module_param_array_named(recordkey_codes, corsair_record_keycodes, ushort, |
89 | NULL, S_IRUGO); |
90 | MODULE_PARM_DESC(recordkey_codes, "Key codes for the MR (start and stop record) button" ); |
91 | |
92 | static unsigned short corsair_profile_keycodes[3] = { |
93 | BTN_TRIGGER_HAPPY21, |
94 | BTN_TRIGGER_HAPPY22, |
95 | BTN_TRIGGER_HAPPY23 |
96 | }; |
97 | |
98 | module_param_array_named(profilekey_codes, corsair_profile_keycodes, ushort, |
99 | NULL, S_IRUGO); |
100 | MODULE_PARM_DESC(profilekey_codes, "Key codes for the profile buttons" ); |
101 | |
102 | #define CORSAIR_USAGE_SPECIAL_MIN 0xf0 |
103 | #define CORSAIR_USAGE_SPECIAL_MAX 0xff |
104 | |
105 | #define CORSAIR_USAGE_MACRO_RECORD_START 0xf6 |
106 | #define CORSAIR_USAGE_MACRO_RECORD_STOP 0xf7 |
107 | |
108 | #define CORSAIR_USAGE_PROFILE 0xf1 |
109 | #define CORSAIR_USAGE_M1 0xf1 |
110 | #define CORSAIR_USAGE_M2 0xf2 |
111 | #define CORSAIR_USAGE_M3 0xf3 |
112 | #define CORSAIR_USAGE_PROFILE_MAX 0xf3 |
113 | |
114 | #define CORSAIR_USAGE_META_OFF 0xf4 |
115 | #define CORSAIR_USAGE_META_ON 0xf5 |
116 | |
117 | #define CORSAIR_USAGE_LIGHT 0xfa |
118 | #define CORSAIR_USAGE_LIGHT_OFF 0xfa |
119 | #define CORSAIR_USAGE_LIGHT_DIM 0xfb |
120 | #define CORSAIR_USAGE_LIGHT_MEDIUM 0xfc |
121 | #define CORSAIR_USAGE_LIGHT_BRIGHT 0xfd |
122 | #define CORSAIR_USAGE_LIGHT_MAX 0xfd |
123 | |
124 | /* USB control protocol */ |
125 | |
126 | #define K90_REQUEST_BRIGHTNESS 49 |
127 | #define K90_REQUEST_MACRO_MODE 2 |
128 | #define K90_REQUEST_STATUS 4 |
129 | #define K90_REQUEST_GET_MODE 5 |
130 | #define K90_REQUEST_PROFILE 20 |
131 | |
132 | #define K90_MACRO_MODE_SW 0x0030 |
133 | #define K90_MACRO_MODE_HW 0x0001 |
134 | |
135 | #define K90_MACRO_LED_ON 0x0020 |
136 | #define K90_MACRO_LED_OFF 0x0040 |
137 | |
138 | /* |
139 | * LED class devices |
140 | */ |
141 | |
142 | #define K90_BACKLIGHT_LED_SUFFIX "::backlight" |
143 | #define K90_RECORD_LED_SUFFIX "::record" |
144 | |
145 | static enum led_brightness k90_backlight_get(struct led_classdev *led_cdev) |
146 | { |
147 | int ret; |
148 | struct k90_led *led = container_of(led_cdev, struct k90_led, cdev); |
149 | struct device *dev = led->cdev.dev->parent; |
150 | struct usb_interface *usbif = to_usb_interface(dev->parent); |
151 | struct usb_device *usbdev = interface_to_usbdev(usbif); |
152 | int brightness; |
153 | char *data; |
154 | |
155 | data = kmalloc(size: 8, GFP_KERNEL); |
156 | if (!data) |
157 | return -ENOMEM; |
158 | |
159 | ret = usb_control_msg(dev: usbdev, usb_rcvctrlpipe(usbdev, 0), |
160 | K90_REQUEST_STATUS, |
161 | USB_DIR_IN | USB_TYPE_VENDOR | |
162 | USB_RECIP_DEVICE, value: 0, index: 0, data, size: 8, |
163 | USB_CTRL_SET_TIMEOUT); |
164 | if (ret < 5) { |
165 | dev_warn(dev, "Failed to get K90 initial state (error %d).\n" , |
166 | ret); |
167 | ret = -EIO; |
168 | goto out; |
169 | } |
170 | brightness = data[4]; |
171 | if (brightness < 0 || brightness > 3) { |
172 | dev_warn(dev, |
173 | "Read invalid backlight brightness: %02hhx.\n" , |
174 | data[4]); |
175 | ret = -EIO; |
176 | goto out; |
177 | } |
178 | ret = brightness; |
179 | out: |
180 | kfree(objp: data); |
181 | |
182 | return ret; |
183 | } |
184 | |
185 | static enum led_brightness k90_record_led_get(struct led_classdev *led_cdev) |
186 | { |
187 | struct k90_led *led = container_of(led_cdev, struct k90_led, cdev); |
188 | |
189 | return led->brightness; |
190 | } |
191 | |
192 | static void k90_brightness_set(struct led_classdev *led_cdev, |
193 | enum led_brightness brightness) |
194 | { |
195 | struct k90_led *led = container_of(led_cdev, struct k90_led, cdev); |
196 | |
197 | led->brightness = brightness; |
198 | schedule_work(work: &led->work); |
199 | } |
200 | |
201 | static void k90_backlight_work(struct work_struct *work) |
202 | { |
203 | int ret; |
204 | struct k90_led *led = container_of(work, struct k90_led, work); |
205 | struct device *dev; |
206 | struct usb_interface *usbif; |
207 | struct usb_device *usbdev; |
208 | |
209 | if (led->removed) |
210 | return; |
211 | |
212 | dev = led->cdev.dev->parent; |
213 | usbif = to_usb_interface(dev->parent); |
214 | usbdev = interface_to_usbdev(usbif); |
215 | |
216 | ret = usb_control_msg(dev: usbdev, usb_sndctrlpipe(usbdev, 0), |
217 | K90_REQUEST_BRIGHTNESS, |
218 | USB_DIR_OUT | USB_TYPE_VENDOR | |
219 | USB_RECIP_DEVICE, value: led->brightness, index: 0, |
220 | NULL, size: 0, USB_CTRL_SET_TIMEOUT); |
221 | if (ret != 0) |
222 | dev_warn(dev, "Failed to set backlight brightness (error: %d).\n" , |
223 | ret); |
224 | } |
225 | |
226 | static void k90_record_led_work(struct work_struct *work) |
227 | { |
228 | int ret; |
229 | struct k90_led *led = container_of(work, struct k90_led, work); |
230 | struct device *dev; |
231 | struct usb_interface *usbif; |
232 | struct usb_device *usbdev; |
233 | int value; |
234 | |
235 | if (led->removed) |
236 | return; |
237 | |
238 | dev = led->cdev.dev->parent; |
239 | usbif = to_usb_interface(dev->parent); |
240 | usbdev = interface_to_usbdev(usbif); |
241 | |
242 | if (led->brightness > 0) |
243 | value = K90_MACRO_LED_ON; |
244 | else |
245 | value = K90_MACRO_LED_OFF; |
246 | |
247 | ret = usb_control_msg(dev: usbdev, usb_sndctrlpipe(usbdev, 0), |
248 | K90_REQUEST_MACRO_MODE, |
249 | USB_DIR_OUT | USB_TYPE_VENDOR | |
250 | USB_RECIP_DEVICE, value, index: 0, NULL, size: 0, |
251 | USB_CTRL_SET_TIMEOUT); |
252 | if (ret != 0) |
253 | dev_warn(dev, "Failed to set record LED state (error: %d).\n" , |
254 | ret); |
255 | } |
256 | |
257 | /* |
258 | * Keyboard attributes |
259 | */ |
260 | |
261 | static ssize_t k90_show_macro_mode(struct device *dev, |
262 | struct device_attribute *attr, char *buf) |
263 | { |
264 | int ret; |
265 | struct usb_interface *usbif = to_usb_interface(dev->parent); |
266 | struct usb_device *usbdev = interface_to_usbdev(usbif); |
267 | const char *macro_mode; |
268 | char *data; |
269 | |
270 | data = kmalloc(size: 2, GFP_KERNEL); |
271 | if (!data) |
272 | return -ENOMEM; |
273 | |
274 | ret = usb_control_msg(dev: usbdev, usb_rcvctrlpipe(usbdev, 0), |
275 | K90_REQUEST_GET_MODE, |
276 | USB_DIR_IN | USB_TYPE_VENDOR | |
277 | USB_RECIP_DEVICE, value: 0, index: 0, data, size: 2, |
278 | USB_CTRL_SET_TIMEOUT); |
279 | if (ret < 1) { |
280 | dev_warn(dev, "Failed to get K90 initial mode (error %d).\n" , |
281 | ret); |
282 | ret = -EIO; |
283 | goto out; |
284 | } |
285 | |
286 | switch (data[0]) { |
287 | case K90_MACRO_MODE_HW: |
288 | macro_mode = "HW" ; |
289 | break; |
290 | |
291 | case K90_MACRO_MODE_SW: |
292 | macro_mode = "SW" ; |
293 | break; |
294 | default: |
295 | dev_warn(dev, "K90 in unknown mode: %02hhx.\n" , |
296 | data[0]); |
297 | ret = -EIO; |
298 | goto out; |
299 | } |
300 | |
301 | ret = snprintf(buf, PAGE_SIZE, fmt: "%s\n" , macro_mode); |
302 | out: |
303 | kfree(objp: data); |
304 | |
305 | return ret; |
306 | } |
307 | |
308 | static ssize_t k90_store_macro_mode(struct device *dev, |
309 | struct device_attribute *attr, |
310 | const char *buf, size_t count) |
311 | { |
312 | int ret; |
313 | struct usb_interface *usbif = to_usb_interface(dev->parent); |
314 | struct usb_device *usbdev = interface_to_usbdev(usbif); |
315 | __u16 value; |
316 | |
317 | if (strncmp(buf, "SW" , 2) == 0) |
318 | value = K90_MACRO_MODE_SW; |
319 | else if (strncmp(buf, "HW" , 2) == 0) |
320 | value = K90_MACRO_MODE_HW; |
321 | else |
322 | return -EINVAL; |
323 | |
324 | ret = usb_control_msg(dev: usbdev, usb_sndctrlpipe(usbdev, 0), |
325 | K90_REQUEST_MACRO_MODE, |
326 | USB_DIR_OUT | USB_TYPE_VENDOR | |
327 | USB_RECIP_DEVICE, value, index: 0, NULL, size: 0, |
328 | USB_CTRL_SET_TIMEOUT); |
329 | if (ret != 0) { |
330 | dev_warn(dev, "Failed to set macro mode.\n" ); |
331 | return ret; |
332 | } |
333 | |
334 | return count; |
335 | } |
336 | |
337 | static ssize_t k90_show_current_profile(struct device *dev, |
338 | struct device_attribute *attr, |
339 | char *buf) |
340 | { |
341 | int ret; |
342 | struct usb_interface *usbif = to_usb_interface(dev->parent); |
343 | struct usb_device *usbdev = interface_to_usbdev(usbif); |
344 | int current_profile; |
345 | char *data; |
346 | |
347 | data = kmalloc(size: 8, GFP_KERNEL); |
348 | if (!data) |
349 | return -ENOMEM; |
350 | |
351 | ret = usb_control_msg(dev: usbdev, usb_rcvctrlpipe(usbdev, 0), |
352 | K90_REQUEST_STATUS, |
353 | USB_DIR_IN | USB_TYPE_VENDOR | |
354 | USB_RECIP_DEVICE, value: 0, index: 0, data, size: 8, |
355 | USB_CTRL_SET_TIMEOUT); |
356 | if (ret < 8) { |
357 | dev_warn(dev, "Failed to get K90 initial state (error %d).\n" , |
358 | ret); |
359 | ret = -EIO; |
360 | goto out; |
361 | } |
362 | current_profile = data[7]; |
363 | if (current_profile < 1 || current_profile > 3) { |
364 | dev_warn(dev, "Read invalid current profile: %02hhx.\n" , |
365 | data[7]); |
366 | ret = -EIO; |
367 | goto out; |
368 | } |
369 | |
370 | ret = snprintf(buf, PAGE_SIZE, fmt: "%d\n" , current_profile); |
371 | out: |
372 | kfree(objp: data); |
373 | |
374 | return ret; |
375 | } |
376 | |
377 | static ssize_t k90_store_current_profile(struct device *dev, |
378 | struct device_attribute *attr, |
379 | const char *buf, size_t count) |
380 | { |
381 | int ret; |
382 | struct usb_interface *usbif = to_usb_interface(dev->parent); |
383 | struct usb_device *usbdev = interface_to_usbdev(usbif); |
384 | int profile; |
385 | |
386 | if (kstrtoint(s: buf, base: 10, res: &profile)) |
387 | return -EINVAL; |
388 | if (profile < 1 || profile > 3) |
389 | return -EINVAL; |
390 | |
391 | ret = usb_control_msg(dev: usbdev, usb_sndctrlpipe(usbdev, 0), |
392 | K90_REQUEST_PROFILE, |
393 | USB_DIR_OUT | USB_TYPE_VENDOR | |
394 | USB_RECIP_DEVICE, value: profile, index: 0, NULL, size: 0, |
395 | USB_CTRL_SET_TIMEOUT); |
396 | if (ret != 0) { |
397 | dev_warn(dev, "Failed to change current profile (error %d).\n" , |
398 | ret); |
399 | return ret; |
400 | } |
401 | |
402 | return count; |
403 | } |
404 | |
405 | static DEVICE_ATTR(macro_mode, 0644, k90_show_macro_mode, k90_store_macro_mode); |
406 | static DEVICE_ATTR(current_profile, 0644, k90_show_current_profile, |
407 | k90_store_current_profile); |
408 | |
409 | static struct attribute *k90_attrs[] = { |
410 | &dev_attr_macro_mode.attr, |
411 | &dev_attr_current_profile.attr, |
412 | NULL |
413 | }; |
414 | |
415 | static const struct attribute_group k90_attr_group = { |
416 | .attrs = k90_attrs, |
417 | }; |
418 | |
419 | /* |
420 | * Driver functions |
421 | */ |
422 | |
423 | static int k90_init_backlight(struct hid_device *dev) |
424 | { |
425 | int ret; |
426 | struct corsair_drvdata *drvdata = hid_get_drvdata(hdev: dev); |
427 | size_t name_sz; |
428 | char *name; |
429 | |
430 | drvdata->backlight = kzalloc(size: sizeof(struct k90_led), GFP_KERNEL); |
431 | if (!drvdata->backlight) { |
432 | ret = -ENOMEM; |
433 | goto fail_backlight_alloc; |
434 | } |
435 | |
436 | name_sz = |
437 | strlen(dev_name(&dev->dev)) + sizeof(K90_BACKLIGHT_LED_SUFFIX); |
438 | name = kzalloc(size: name_sz, GFP_KERNEL); |
439 | if (!name) { |
440 | ret = -ENOMEM; |
441 | goto fail_name_alloc; |
442 | } |
443 | snprintf(buf: name, size: name_sz, fmt: "%s" K90_BACKLIGHT_LED_SUFFIX, |
444 | dev_name(dev: &dev->dev)); |
445 | drvdata->backlight->removed = false; |
446 | drvdata->backlight->cdev.name = name; |
447 | drvdata->backlight->cdev.max_brightness = 3; |
448 | drvdata->backlight->cdev.brightness_set = k90_brightness_set; |
449 | drvdata->backlight->cdev.brightness_get = k90_backlight_get; |
450 | INIT_WORK(&drvdata->backlight->work, k90_backlight_work); |
451 | ret = led_classdev_register(parent: &dev->dev, led_cdev: &drvdata->backlight->cdev); |
452 | if (ret != 0) |
453 | goto fail_register_cdev; |
454 | |
455 | return 0; |
456 | |
457 | fail_register_cdev: |
458 | kfree(objp: drvdata->backlight->cdev.name); |
459 | fail_name_alloc: |
460 | kfree(objp: drvdata->backlight); |
461 | drvdata->backlight = NULL; |
462 | fail_backlight_alloc: |
463 | return ret; |
464 | } |
465 | |
466 | static int k90_init_macro_functions(struct hid_device *dev) |
467 | { |
468 | int ret; |
469 | struct corsair_drvdata *drvdata = hid_get_drvdata(hdev: dev); |
470 | struct k90_drvdata *k90; |
471 | size_t name_sz; |
472 | char *name; |
473 | |
474 | k90 = kzalloc(size: sizeof(struct k90_drvdata), GFP_KERNEL); |
475 | if (!k90) { |
476 | ret = -ENOMEM; |
477 | goto fail_drvdata; |
478 | } |
479 | drvdata->k90 = k90; |
480 | |
481 | /* Init LED device for record LED */ |
482 | name_sz = strlen(dev_name(&dev->dev)) + sizeof(K90_RECORD_LED_SUFFIX); |
483 | name = kzalloc(size: name_sz, GFP_KERNEL); |
484 | if (!name) { |
485 | ret = -ENOMEM; |
486 | goto fail_record_led_alloc; |
487 | } |
488 | snprintf(buf: name, size: name_sz, fmt: "%s" K90_RECORD_LED_SUFFIX, |
489 | dev_name(dev: &dev->dev)); |
490 | k90->record_led.removed = false; |
491 | k90->record_led.cdev.name = name; |
492 | k90->record_led.cdev.max_brightness = 1; |
493 | k90->record_led.cdev.brightness_set = k90_brightness_set; |
494 | k90->record_led.cdev.brightness_get = k90_record_led_get; |
495 | INIT_WORK(&k90->record_led.work, k90_record_led_work); |
496 | k90->record_led.brightness = 0; |
497 | ret = led_classdev_register(parent: &dev->dev, led_cdev: &k90->record_led.cdev); |
498 | if (ret != 0) |
499 | goto fail_record_led; |
500 | |
501 | /* Init attributes */ |
502 | ret = sysfs_create_group(kobj: &dev->dev.kobj, grp: &k90_attr_group); |
503 | if (ret != 0) |
504 | goto fail_sysfs; |
505 | |
506 | return 0; |
507 | |
508 | fail_sysfs: |
509 | k90->record_led.removed = true; |
510 | led_classdev_unregister(led_cdev: &k90->record_led.cdev); |
511 | cancel_work_sync(work: &k90->record_led.work); |
512 | fail_record_led: |
513 | kfree(objp: k90->record_led.cdev.name); |
514 | fail_record_led_alloc: |
515 | kfree(objp: k90); |
516 | fail_drvdata: |
517 | drvdata->k90 = NULL; |
518 | return ret; |
519 | } |
520 | |
521 | static void k90_cleanup_backlight(struct hid_device *dev) |
522 | { |
523 | struct corsair_drvdata *drvdata = hid_get_drvdata(hdev: dev); |
524 | |
525 | if (drvdata->backlight) { |
526 | drvdata->backlight->removed = true; |
527 | led_classdev_unregister(led_cdev: &drvdata->backlight->cdev); |
528 | cancel_work_sync(work: &drvdata->backlight->work); |
529 | kfree(objp: drvdata->backlight->cdev.name); |
530 | kfree(objp: drvdata->backlight); |
531 | } |
532 | } |
533 | |
534 | static void k90_cleanup_macro_functions(struct hid_device *dev) |
535 | { |
536 | struct corsair_drvdata *drvdata = hid_get_drvdata(hdev: dev); |
537 | struct k90_drvdata *k90 = drvdata->k90; |
538 | |
539 | if (k90) { |
540 | sysfs_remove_group(kobj: &dev->dev.kobj, grp: &k90_attr_group); |
541 | |
542 | k90->record_led.removed = true; |
543 | led_classdev_unregister(led_cdev: &k90->record_led.cdev); |
544 | cancel_work_sync(work: &k90->record_led.work); |
545 | kfree(objp: k90->record_led.cdev.name); |
546 | |
547 | kfree(objp: k90); |
548 | } |
549 | } |
550 | |
551 | static int corsair_probe(struct hid_device *dev, const struct hid_device_id *id) |
552 | { |
553 | int ret; |
554 | unsigned long quirks = id->driver_data; |
555 | struct corsair_drvdata *drvdata; |
556 | struct usb_interface *usbif; |
557 | |
558 | if (!hid_is_usb(hdev: dev)) |
559 | return -EINVAL; |
560 | |
561 | usbif = to_usb_interface(dev->dev.parent); |
562 | |
563 | drvdata = devm_kzalloc(dev: &dev->dev, size: sizeof(struct corsair_drvdata), |
564 | GFP_KERNEL); |
565 | if (drvdata == NULL) |
566 | return -ENOMEM; |
567 | drvdata->quirks = quirks; |
568 | hid_set_drvdata(hdev: dev, data: drvdata); |
569 | |
570 | ret = hid_parse(hdev: dev); |
571 | if (ret != 0) { |
572 | hid_err(dev, "parse failed\n" ); |
573 | return ret; |
574 | } |
575 | ret = hid_hw_start(hdev: dev, HID_CONNECT_DEFAULT); |
576 | if (ret != 0) { |
577 | hid_err(dev, "hw start failed\n" ); |
578 | return ret; |
579 | } |
580 | |
581 | if (usbif->cur_altsetting->desc.bInterfaceNumber == 0) { |
582 | if (quirks & CORSAIR_USE_K90_MACRO) { |
583 | ret = k90_init_macro_functions(dev); |
584 | if (ret != 0) |
585 | hid_warn(dev, "Failed to initialize K90 macro functions.\n" ); |
586 | } |
587 | if (quirks & CORSAIR_USE_K90_BACKLIGHT) { |
588 | ret = k90_init_backlight(dev); |
589 | if (ret != 0) |
590 | hid_warn(dev, "Failed to initialize K90 backlight.\n" ); |
591 | } |
592 | } |
593 | |
594 | return 0; |
595 | } |
596 | |
597 | static void corsair_remove(struct hid_device *dev) |
598 | { |
599 | k90_cleanup_macro_functions(dev); |
600 | k90_cleanup_backlight(dev); |
601 | |
602 | hid_hw_stop(hdev: dev); |
603 | } |
604 | |
605 | static int corsair_event(struct hid_device *dev, struct hid_field *field, |
606 | struct hid_usage *usage, __s32 value) |
607 | { |
608 | struct corsair_drvdata *drvdata = hid_get_drvdata(hdev: dev); |
609 | |
610 | if (!drvdata->k90) |
611 | return 0; |
612 | |
613 | switch (usage->hid & HID_USAGE) { |
614 | case CORSAIR_USAGE_MACRO_RECORD_START: |
615 | drvdata->k90->record_led.brightness = 1; |
616 | break; |
617 | case CORSAIR_USAGE_MACRO_RECORD_STOP: |
618 | drvdata->k90->record_led.brightness = 0; |
619 | break; |
620 | default: |
621 | break; |
622 | } |
623 | |
624 | return 0; |
625 | } |
626 | |
627 | static int corsair_input_mapping(struct hid_device *dev, |
628 | struct hid_input *input, |
629 | struct hid_field *field, |
630 | struct hid_usage *usage, unsigned long **bit, |
631 | int *max) |
632 | { |
633 | int gkey; |
634 | |
635 | if ((usage->hid & HID_USAGE_PAGE) != HID_UP_KEYBOARD) |
636 | return 0; |
637 | |
638 | gkey = corsair_usage_to_gkey(usage: usage->hid & HID_USAGE); |
639 | if (gkey != 0) { |
640 | hid_map_usage_clear(hidinput: input, usage, bit, max, EV_KEY, |
641 | c: corsair_gkey_map[gkey - 1]); |
642 | return 1; |
643 | } |
644 | if ((usage->hid & HID_USAGE) >= CORSAIR_USAGE_SPECIAL_MIN && |
645 | (usage->hid & HID_USAGE) <= CORSAIR_USAGE_SPECIAL_MAX) { |
646 | switch (usage->hid & HID_USAGE) { |
647 | case CORSAIR_USAGE_MACRO_RECORD_START: |
648 | hid_map_usage_clear(hidinput: input, usage, bit, max, EV_KEY, |
649 | c: corsair_record_keycodes[0]); |
650 | return 1; |
651 | |
652 | case CORSAIR_USAGE_MACRO_RECORD_STOP: |
653 | hid_map_usage_clear(hidinput: input, usage, bit, max, EV_KEY, |
654 | c: corsair_record_keycodes[1]); |
655 | return 1; |
656 | |
657 | case CORSAIR_USAGE_M1: |
658 | hid_map_usage_clear(hidinput: input, usage, bit, max, EV_KEY, |
659 | c: corsair_profile_keycodes[0]); |
660 | return 1; |
661 | |
662 | case CORSAIR_USAGE_M2: |
663 | hid_map_usage_clear(hidinput: input, usage, bit, max, EV_KEY, |
664 | c: corsair_profile_keycodes[1]); |
665 | return 1; |
666 | |
667 | case CORSAIR_USAGE_M3: |
668 | hid_map_usage_clear(hidinput: input, usage, bit, max, EV_KEY, |
669 | c: corsair_profile_keycodes[2]); |
670 | return 1; |
671 | |
672 | default: |
673 | return -1; |
674 | } |
675 | } |
676 | |
677 | return 0; |
678 | } |
679 | |
680 | /* |
681 | * The report descriptor of some of the Corsair gaming mice is |
682 | * non parseable as they define two consecutive Logical Minimum for |
683 | * the Usage Page (Consumer) in rdescs bytes 75 and 77 being 77 0x16 |
684 | * that should be obviousy 0x26 for Logical Magimum of 16 bits. This |
685 | * prevents poper parsing of the report descriptor due Logical |
686 | * Minimum being larger than Logical Maximum. |
687 | * |
688 | * This driver fixes the report descriptor for: |
689 | * - USB ID 1b1c:1b34, sold as GLAIVE RGB Gaming mouse |
690 | * - USB ID 1b1c:1b3e, sold as Scimitar RGB Pro Gaming mouse |
691 | */ |
692 | |
693 | static __u8 *corsair_mouse_report_fixup(struct hid_device *hdev, __u8 *rdesc, |
694 | unsigned int *rsize) |
695 | { |
696 | struct usb_interface *intf = to_usb_interface(hdev->dev.parent); |
697 | |
698 | if (intf->cur_altsetting->desc.bInterfaceNumber == 1) { |
699 | /* |
700 | * Corsair GLAIVE RGB and Scimitar RGB Pro report descriptor is |
701 | * broken and defines two different Logical Minimum for the |
702 | * Consumer Application. The byte 77 should be a 0x26 defining |
703 | * a 16 bits integer for the Logical Maximum but it is a 0x16 |
704 | * instead (Logical Minimum) |
705 | */ |
706 | switch (hdev->product) { |
707 | case USB_DEVICE_ID_CORSAIR_GLAIVE_RGB: |
708 | case USB_DEVICE_ID_CORSAIR_SCIMITAR_PRO_RGB: |
709 | if (*rsize >= 172 && rdesc[75] == 0x15 && rdesc[77] == 0x16 |
710 | && rdesc[78] == 0xff && rdesc[79] == 0x0f) { |
711 | hid_info(hdev, "Fixing up report descriptor\n" ); |
712 | rdesc[77] = 0x26; |
713 | } |
714 | break; |
715 | } |
716 | |
717 | } |
718 | return rdesc; |
719 | } |
720 | |
721 | static const struct hid_device_id corsair_devices[] = { |
722 | { HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR, USB_DEVICE_ID_CORSAIR_K90), |
723 | .driver_data = CORSAIR_USE_K90_MACRO | |
724 | CORSAIR_USE_K90_BACKLIGHT }, |
725 | { HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR, |
726 | USB_DEVICE_ID_CORSAIR_GLAIVE_RGB) }, |
727 | { HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR, |
728 | USB_DEVICE_ID_CORSAIR_SCIMITAR_PRO_RGB) }, |
729 | /* |
730 | * Vengeance K70 and K70 RAPIDFIRE share product IDs. |
731 | */ |
732 | { HID_USB_DEVICE(USB_VENDOR_ID_CORSAIR, |
733 | USB_DEVICE_ID_CORSAIR_K70R) }, |
734 | {} |
735 | }; |
736 | |
737 | MODULE_DEVICE_TABLE(hid, corsair_devices); |
738 | |
739 | static struct hid_driver corsair_driver = { |
740 | .name = "corsair" , |
741 | .id_table = corsair_devices, |
742 | .probe = corsair_probe, |
743 | .event = corsair_event, |
744 | .remove = corsair_remove, |
745 | .input_mapping = corsair_input_mapping, |
746 | .report_fixup = corsair_mouse_report_fixup, |
747 | }; |
748 | |
749 | module_hid_driver(corsair_driver); |
750 | |
751 | MODULE_LICENSE("GPL" ); |
752 | /* Original K90 driver author */ |
753 | MODULE_AUTHOR("Clement Vuchener" ); |
754 | /* Scimitar PRO RGB driver author */ |
755 | MODULE_AUTHOR("Oscar Campos" ); |
756 | MODULE_DESCRIPTION("HID driver for Corsair devices" ); |
757 | |