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