Root/
1 | /* |
2 | * Roccat Kova[+] driver for Linux |
3 | * |
4 | * Copyright (c) 2011 Stefan Achatz <erazor_de@users.sourceforge.net> |
5 | */ |
6 | |
7 | /* |
8 | * This program is free software; you can redistribute it and/or modify it |
9 | * under the terms of the GNU General Public License as published by the Free |
10 | * Software Foundation; either version 2 of the License, or (at your option) |
11 | * any later version. |
12 | */ |
13 | |
14 | /* |
15 | * Roccat Kova[+] is a bigger version of the Pyra with two more side buttons. |
16 | */ |
17 | |
18 | #include <linux/device.h> |
19 | #include <linux/input.h> |
20 | #include <linux/hid.h> |
21 | #include <linux/module.h> |
22 | #include <linux/slab.h> |
23 | #include <linux/hid-roccat.h> |
24 | #include "hid-ids.h" |
25 | #include "hid-roccat-common.h" |
26 | #include "hid-roccat-kovaplus.h" |
27 | |
28 | static uint profile_numbers[5] = {0, 1, 2, 3, 4}; |
29 | |
30 | static struct class *kovaplus_class; |
31 | |
32 | static uint kovaplus_convert_event_cpi(uint value) |
33 | { |
34 | return (value == 7 ? 4 : (value == 4 ? 3 : value)); |
35 | } |
36 | |
37 | static void kovaplus_profile_activated(struct kovaplus_device *kovaplus, |
38 | uint new_profile_index) |
39 | { |
40 | kovaplus->actual_profile = new_profile_index; |
41 | kovaplus->actual_cpi = kovaplus->profile_settings[new_profile_index].cpi_startup_level; |
42 | kovaplus->actual_x_sensitivity = kovaplus->profile_settings[new_profile_index].sensitivity_x; |
43 | kovaplus->actual_y_sensitivity = kovaplus->profile_settings[new_profile_index].sensitivity_y; |
44 | } |
45 | |
46 | static int kovaplus_send_control(struct usb_device *usb_dev, uint value, |
47 | enum kovaplus_control_requests request) |
48 | { |
49 | int retval; |
50 | struct roccat_common2_control control; |
51 | |
52 | if ((request == KOVAPLUS_CONTROL_REQUEST_PROFILE_SETTINGS || |
53 | request == KOVAPLUS_CONTROL_REQUEST_PROFILE_BUTTONS) && |
54 | value > 4) |
55 | return -EINVAL; |
56 | |
57 | control.command = ROCCAT_COMMON_COMMAND_CONTROL; |
58 | control.value = value; |
59 | control.request = request; |
60 | |
61 | retval = roccat_common2_send(usb_dev, ROCCAT_COMMON_COMMAND_CONTROL, |
62 | &control, sizeof(struct roccat_common2_control)); |
63 | |
64 | return retval; |
65 | } |
66 | |
67 | static int kovaplus_select_profile(struct usb_device *usb_dev, uint number, |
68 | enum kovaplus_control_requests request) |
69 | { |
70 | return kovaplus_send_control(usb_dev, number, request); |
71 | } |
72 | |
73 | static int kovaplus_get_info(struct usb_device *usb_dev, |
74 | struct kovaplus_info *buf) |
75 | { |
76 | return roccat_common2_receive(usb_dev, KOVAPLUS_COMMAND_INFO, |
77 | buf, sizeof(struct kovaplus_info)); |
78 | } |
79 | |
80 | static int kovaplus_get_profile_settings(struct usb_device *usb_dev, |
81 | struct kovaplus_profile_settings *buf, uint number) |
82 | { |
83 | int retval; |
84 | |
85 | retval = kovaplus_select_profile(usb_dev, number, |
86 | KOVAPLUS_CONTROL_REQUEST_PROFILE_SETTINGS); |
87 | if (retval) |
88 | return retval; |
89 | |
90 | return roccat_common2_receive(usb_dev, KOVAPLUS_COMMAND_PROFILE_SETTINGS, |
91 | buf, sizeof(struct kovaplus_profile_settings)); |
92 | } |
93 | |
94 | static int kovaplus_set_profile_settings(struct usb_device *usb_dev, |
95 | struct kovaplus_profile_settings const *settings) |
96 | { |
97 | return roccat_common2_send_with_status(usb_dev, |
98 | KOVAPLUS_COMMAND_PROFILE_SETTINGS, |
99 | settings, sizeof(struct kovaplus_profile_settings)); |
100 | } |
101 | |
102 | static int kovaplus_get_profile_buttons(struct usb_device *usb_dev, |
103 | struct kovaplus_profile_buttons *buf, int number) |
104 | { |
105 | int retval; |
106 | |
107 | retval = kovaplus_select_profile(usb_dev, number, |
108 | KOVAPLUS_CONTROL_REQUEST_PROFILE_BUTTONS); |
109 | if (retval) |
110 | return retval; |
111 | |
112 | return roccat_common2_receive(usb_dev, KOVAPLUS_COMMAND_PROFILE_BUTTONS, |
113 | buf, sizeof(struct kovaplus_profile_buttons)); |
114 | } |
115 | |
116 | static int kovaplus_set_profile_buttons(struct usb_device *usb_dev, |
117 | struct kovaplus_profile_buttons const *buttons) |
118 | { |
119 | return roccat_common2_send_with_status(usb_dev, |
120 | KOVAPLUS_COMMAND_PROFILE_BUTTONS, |
121 | buttons, sizeof(struct kovaplus_profile_buttons)); |
122 | } |
123 | |
124 | /* retval is 0-4 on success, < 0 on error */ |
125 | static int kovaplus_get_actual_profile(struct usb_device *usb_dev) |
126 | { |
127 | struct kovaplus_actual_profile buf; |
128 | int retval; |
129 | |
130 | retval = roccat_common2_receive(usb_dev, KOVAPLUS_COMMAND_ACTUAL_PROFILE, |
131 | &buf, sizeof(struct kovaplus_actual_profile)); |
132 | |
133 | return retval ? retval : buf.actual_profile; |
134 | } |
135 | |
136 | static int kovaplus_set_actual_profile(struct usb_device *usb_dev, |
137 | int new_profile) |
138 | { |
139 | struct kovaplus_actual_profile buf; |
140 | |
141 | buf.command = KOVAPLUS_COMMAND_ACTUAL_PROFILE; |
142 | buf.size = sizeof(struct kovaplus_actual_profile); |
143 | buf.actual_profile = new_profile; |
144 | |
145 | return roccat_common2_send_with_status(usb_dev, |
146 | KOVAPLUS_COMMAND_ACTUAL_PROFILE, |
147 | &buf, sizeof(struct kovaplus_actual_profile)); |
148 | } |
149 | |
150 | static ssize_t kovaplus_sysfs_read_profilex_settings(struct file *fp, |
151 | struct kobject *kobj, struct bin_attribute *attr, char *buf, |
152 | loff_t off, size_t count) |
153 | { |
154 | struct device *dev = |
155 | container_of(kobj, struct device, kobj)->parent->parent; |
156 | struct kovaplus_device *kovaplus = hid_get_drvdata(dev_get_drvdata(dev)); |
157 | |
158 | if (off >= sizeof(struct kovaplus_profile_settings)) |
159 | return 0; |
160 | |
161 | if (off + count > sizeof(struct kovaplus_profile_settings)) |
162 | count = sizeof(struct kovaplus_profile_settings) - off; |
163 | |
164 | mutex_lock(&kovaplus->kovaplus_lock); |
165 | memcpy(buf, ((char const *)&kovaplus->profile_settings[*(uint *)(attr->private)]) + off, |
166 | count); |
167 | mutex_unlock(&kovaplus->kovaplus_lock); |
168 | |
169 | return count; |
170 | } |
171 | |
172 | static ssize_t kovaplus_sysfs_write_profile_settings(struct file *fp, |
173 | struct kobject *kobj, struct bin_attribute *attr, char *buf, |
174 | loff_t off, size_t count) |
175 | { |
176 | struct device *dev = |
177 | container_of(kobj, struct device, kobj)->parent->parent; |
178 | struct kovaplus_device *kovaplus = hid_get_drvdata(dev_get_drvdata(dev)); |
179 | struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); |
180 | int retval = 0; |
181 | int difference; |
182 | int profile_index; |
183 | struct kovaplus_profile_settings *profile_settings; |
184 | |
185 | if (off != 0 || count != sizeof(struct kovaplus_profile_settings)) |
186 | return -EINVAL; |
187 | |
188 | profile_index = ((struct kovaplus_profile_settings const *)buf)->profile_index; |
189 | profile_settings = &kovaplus->profile_settings[profile_index]; |
190 | |
191 | mutex_lock(&kovaplus->kovaplus_lock); |
192 | difference = memcmp(buf, profile_settings, |
193 | sizeof(struct kovaplus_profile_settings)); |
194 | if (difference) { |
195 | retval = kovaplus_set_profile_settings(usb_dev, |
196 | (struct kovaplus_profile_settings const *)buf); |
197 | if (!retval) |
198 | memcpy(profile_settings, buf, |
199 | sizeof(struct kovaplus_profile_settings)); |
200 | } |
201 | mutex_unlock(&kovaplus->kovaplus_lock); |
202 | |
203 | if (retval) |
204 | return retval; |
205 | |
206 | return sizeof(struct kovaplus_profile_settings); |
207 | } |
208 | |
209 | static ssize_t kovaplus_sysfs_read_profilex_buttons(struct file *fp, |
210 | struct kobject *kobj, struct bin_attribute *attr, char *buf, |
211 | loff_t off, size_t count) |
212 | { |
213 | struct device *dev = |
214 | container_of(kobj, struct device, kobj)->parent->parent; |
215 | struct kovaplus_device *kovaplus = hid_get_drvdata(dev_get_drvdata(dev)); |
216 | |
217 | if (off >= sizeof(struct kovaplus_profile_buttons)) |
218 | return 0; |
219 | |
220 | if (off + count > sizeof(struct kovaplus_profile_buttons)) |
221 | count = sizeof(struct kovaplus_profile_buttons) - off; |
222 | |
223 | mutex_lock(&kovaplus->kovaplus_lock); |
224 | memcpy(buf, ((char const *)&kovaplus->profile_buttons[*(uint *)(attr->private)]) + off, |
225 | count); |
226 | mutex_unlock(&kovaplus->kovaplus_lock); |
227 | |
228 | return count; |
229 | } |
230 | |
231 | static ssize_t kovaplus_sysfs_write_profile_buttons(struct file *fp, |
232 | struct kobject *kobj, struct bin_attribute *attr, char *buf, |
233 | loff_t off, size_t count) |
234 | { |
235 | struct device *dev = |
236 | container_of(kobj, struct device, kobj)->parent->parent; |
237 | struct kovaplus_device *kovaplus = hid_get_drvdata(dev_get_drvdata(dev)); |
238 | struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); |
239 | int retval = 0; |
240 | int difference; |
241 | uint profile_index; |
242 | struct kovaplus_profile_buttons *profile_buttons; |
243 | |
244 | if (off != 0 || count != sizeof(struct kovaplus_profile_buttons)) |
245 | return -EINVAL; |
246 | |
247 | profile_index = ((struct kovaplus_profile_buttons const *)buf)->profile_index; |
248 | profile_buttons = &kovaplus->profile_buttons[profile_index]; |
249 | |
250 | mutex_lock(&kovaplus->kovaplus_lock); |
251 | difference = memcmp(buf, profile_buttons, |
252 | sizeof(struct kovaplus_profile_buttons)); |
253 | if (difference) { |
254 | retval = kovaplus_set_profile_buttons(usb_dev, |
255 | (struct kovaplus_profile_buttons const *)buf); |
256 | if (!retval) |
257 | memcpy(profile_buttons, buf, |
258 | sizeof(struct kovaplus_profile_buttons)); |
259 | } |
260 | mutex_unlock(&kovaplus->kovaplus_lock); |
261 | |
262 | if (retval) |
263 | return retval; |
264 | |
265 | return sizeof(struct kovaplus_profile_buttons); |
266 | } |
267 | |
268 | static ssize_t kovaplus_sysfs_show_actual_profile(struct device *dev, |
269 | struct device_attribute *attr, char *buf) |
270 | { |
271 | struct kovaplus_device *kovaplus = |
272 | hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); |
273 | return snprintf(buf, PAGE_SIZE, "%d\n", kovaplus->actual_profile); |
274 | } |
275 | |
276 | static ssize_t kovaplus_sysfs_set_actual_profile(struct device *dev, |
277 | struct device_attribute *attr, char const *buf, size_t size) |
278 | { |
279 | struct kovaplus_device *kovaplus; |
280 | struct usb_device *usb_dev; |
281 | unsigned long profile; |
282 | int retval; |
283 | struct kovaplus_roccat_report roccat_report; |
284 | |
285 | dev = dev->parent->parent; |
286 | kovaplus = hid_get_drvdata(dev_get_drvdata(dev)); |
287 | usb_dev = interface_to_usbdev(to_usb_interface(dev)); |
288 | |
289 | retval = strict_strtoul(buf, 10, &profile); |
290 | if (retval) |
291 | return retval; |
292 | |
293 | if (profile >= 5) |
294 | return -EINVAL; |
295 | |
296 | mutex_lock(&kovaplus->kovaplus_lock); |
297 | retval = kovaplus_set_actual_profile(usb_dev, profile); |
298 | if (retval) { |
299 | mutex_unlock(&kovaplus->kovaplus_lock); |
300 | return retval; |
301 | } |
302 | |
303 | kovaplus_profile_activated(kovaplus, profile); |
304 | |
305 | roccat_report.type = KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_PROFILE_1; |
306 | roccat_report.profile = profile + 1; |
307 | roccat_report.button = 0; |
308 | roccat_report.data1 = profile + 1; |
309 | roccat_report.data2 = 0; |
310 | roccat_report_event(kovaplus->chrdev_minor, |
311 | (uint8_t const *)&roccat_report); |
312 | |
313 | mutex_unlock(&kovaplus->kovaplus_lock); |
314 | |
315 | return size; |
316 | } |
317 | |
318 | static ssize_t kovaplus_sysfs_show_actual_cpi(struct device *dev, |
319 | struct device_attribute *attr, char *buf) |
320 | { |
321 | struct kovaplus_device *kovaplus = |
322 | hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); |
323 | return snprintf(buf, PAGE_SIZE, "%d\n", kovaplus->actual_cpi); |
324 | } |
325 | |
326 | static ssize_t kovaplus_sysfs_show_actual_sensitivity_x(struct device *dev, |
327 | struct device_attribute *attr, char *buf) |
328 | { |
329 | struct kovaplus_device *kovaplus = |
330 | hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); |
331 | return snprintf(buf, PAGE_SIZE, "%d\n", kovaplus->actual_x_sensitivity); |
332 | } |
333 | |
334 | static ssize_t kovaplus_sysfs_show_actual_sensitivity_y(struct device *dev, |
335 | struct device_attribute *attr, char *buf) |
336 | { |
337 | struct kovaplus_device *kovaplus = |
338 | hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); |
339 | return snprintf(buf, PAGE_SIZE, "%d\n", kovaplus->actual_y_sensitivity); |
340 | } |
341 | |
342 | static ssize_t kovaplus_sysfs_show_firmware_version(struct device *dev, |
343 | struct device_attribute *attr, char *buf) |
344 | { |
345 | struct kovaplus_device *kovaplus = |
346 | hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); |
347 | return snprintf(buf, PAGE_SIZE, "%d\n", kovaplus->info.firmware_version); |
348 | } |
349 | |
350 | static struct device_attribute kovaplus_attributes[] = { |
351 | __ATTR(actual_cpi, 0440, |
352 | kovaplus_sysfs_show_actual_cpi, NULL), |
353 | __ATTR(firmware_version, 0440, |
354 | kovaplus_sysfs_show_firmware_version, NULL), |
355 | __ATTR(actual_profile, 0660, |
356 | kovaplus_sysfs_show_actual_profile, |
357 | kovaplus_sysfs_set_actual_profile), |
358 | __ATTR(actual_sensitivity_x, 0440, |
359 | kovaplus_sysfs_show_actual_sensitivity_x, NULL), |
360 | __ATTR(actual_sensitivity_y, 0440, |
361 | kovaplus_sysfs_show_actual_sensitivity_y, NULL), |
362 | __ATTR_NULL |
363 | }; |
364 | |
365 | static struct bin_attribute kovaplus_bin_attributes[] = { |
366 | { |
367 | .attr = { .name = "profile_settings", .mode = 0220 }, |
368 | .size = sizeof(struct kovaplus_profile_settings), |
369 | .write = kovaplus_sysfs_write_profile_settings |
370 | }, |
371 | { |
372 | .attr = { .name = "profile1_settings", .mode = 0440 }, |
373 | .size = sizeof(struct kovaplus_profile_settings), |
374 | .read = kovaplus_sysfs_read_profilex_settings, |
375 | .private = &profile_numbers[0] |
376 | }, |
377 | { |
378 | .attr = { .name = "profile2_settings", .mode = 0440 }, |
379 | .size = sizeof(struct kovaplus_profile_settings), |
380 | .read = kovaplus_sysfs_read_profilex_settings, |
381 | .private = &profile_numbers[1] |
382 | }, |
383 | { |
384 | .attr = { .name = "profile3_settings", .mode = 0440 }, |
385 | .size = sizeof(struct kovaplus_profile_settings), |
386 | .read = kovaplus_sysfs_read_profilex_settings, |
387 | .private = &profile_numbers[2] |
388 | }, |
389 | { |
390 | .attr = { .name = "profile4_settings", .mode = 0440 }, |
391 | .size = sizeof(struct kovaplus_profile_settings), |
392 | .read = kovaplus_sysfs_read_profilex_settings, |
393 | .private = &profile_numbers[3] |
394 | }, |
395 | { |
396 | .attr = { .name = "profile5_settings", .mode = 0440 }, |
397 | .size = sizeof(struct kovaplus_profile_settings), |
398 | .read = kovaplus_sysfs_read_profilex_settings, |
399 | .private = &profile_numbers[4] |
400 | }, |
401 | { |
402 | .attr = { .name = "profile_buttons", .mode = 0220 }, |
403 | .size = sizeof(struct kovaplus_profile_buttons), |
404 | .write = kovaplus_sysfs_write_profile_buttons |
405 | }, |
406 | { |
407 | .attr = { .name = "profile1_buttons", .mode = 0440 }, |
408 | .size = sizeof(struct kovaplus_profile_buttons), |
409 | .read = kovaplus_sysfs_read_profilex_buttons, |
410 | .private = &profile_numbers[0] |
411 | }, |
412 | { |
413 | .attr = { .name = "profile2_buttons", .mode = 0440 }, |
414 | .size = sizeof(struct kovaplus_profile_buttons), |
415 | .read = kovaplus_sysfs_read_profilex_buttons, |
416 | .private = &profile_numbers[1] |
417 | }, |
418 | { |
419 | .attr = { .name = "profile3_buttons", .mode = 0440 }, |
420 | .size = sizeof(struct kovaplus_profile_buttons), |
421 | .read = kovaplus_sysfs_read_profilex_buttons, |
422 | .private = &profile_numbers[2] |
423 | }, |
424 | { |
425 | .attr = { .name = "profile4_buttons", .mode = 0440 }, |
426 | .size = sizeof(struct kovaplus_profile_buttons), |
427 | .read = kovaplus_sysfs_read_profilex_buttons, |
428 | .private = &profile_numbers[3] |
429 | }, |
430 | { |
431 | .attr = { .name = "profile5_buttons", .mode = 0440 }, |
432 | .size = sizeof(struct kovaplus_profile_buttons), |
433 | .read = kovaplus_sysfs_read_profilex_buttons, |
434 | .private = &profile_numbers[4] |
435 | }, |
436 | __ATTR_NULL |
437 | }; |
438 | |
439 | static int kovaplus_init_kovaplus_device_struct(struct usb_device *usb_dev, |
440 | struct kovaplus_device *kovaplus) |
441 | { |
442 | int retval, i; |
443 | static uint wait = 70; /* device will freeze with just 60 */ |
444 | |
445 | mutex_init(&kovaplus->kovaplus_lock); |
446 | |
447 | retval = kovaplus_get_info(usb_dev, &kovaplus->info); |
448 | if (retval) |
449 | return retval; |
450 | |
451 | for (i = 0; i < 5; ++i) { |
452 | msleep(wait); |
453 | retval = kovaplus_get_profile_settings(usb_dev, |
454 | &kovaplus->profile_settings[i], i); |
455 | if (retval) |
456 | return retval; |
457 | |
458 | msleep(wait); |
459 | retval = kovaplus_get_profile_buttons(usb_dev, |
460 | &kovaplus->profile_buttons[i], i); |
461 | if (retval) |
462 | return retval; |
463 | } |
464 | |
465 | msleep(wait); |
466 | retval = kovaplus_get_actual_profile(usb_dev); |
467 | if (retval < 0) |
468 | return retval; |
469 | kovaplus_profile_activated(kovaplus, retval); |
470 | |
471 | return 0; |
472 | } |
473 | |
474 | static int kovaplus_init_specials(struct hid_device *hdev) |
475 | { |
476 | struct usb_interface *intf = to_usb_interface(hdev->dev.parent); |
477 | struct usb_device *usb_dev = interface_to_usbdev(intf); |
478 | struct kovaplus_device *kovaplus; |
479 | int retval; |
480 | |
481 | if (intf->cur_altsetting->desc.bInterfaceProtocol |
482 | == USB_INTERFACE_PROTOCOL_MOUSE) { |
483 | |
484 | kovaplus = kzalloc(sizeof(*kovaplus), GFP_KERNEL); |
485 | if (!kovaplus) { |
486 | hid_err(hdev, "can't alloc device descriptor\n"); |
487 | return -ENOMEM; |
488 | } |
489 | hid_set_drvdata(hdev, kovaplus); |
490 | |
491 | retval = kovaplus_init_kovaplus_device_struct(usb_dev, kovaplus); |
492 | if (retval) { |
493 | hid_err(hdev, "couldn't init struct kovaplus_device\n"); |
494 | goto exit_free; |
495 | } |
496 | |
497 | retval = roccat_connect(kovaplus_class, hdev, |
498 | sizeof(struct kovaplus_roccat_report)); |
499 | if (retval < 0) { |
500 | hid_err(hdev, "couldn't init char dev\n"); |
501 | } else { |
502 | kovaplus->chrdev_minor = retval; |
503 | kovaplus->roccat_claimed = 1; |
504 | } |
505 | |
506 | } else { |
507 | hid_set_drvdata(hdev, NULL); |
508 | } |
509 | |
510 | return 0; |
511 | exit_free: |
512 | kfree(kovaplus); |
513 | return retval; |
514 | } |
515 | |
516 | static void kovaplus_remove_specials(struct hid_device *hdev) |
517 | { |
518 | struct usb_interface *intf = to_usb_interface(hdev->dev.parent); |
519 | struct kovaplus_device *kovaplus; |
520 | |
521 | if (intf->cur_altsetting->desc.bInterfaceProtocol |
522 | == USB_INTERFACE_PROTOCOL_MOUSE) { |
523 | kovaplus = hid_get_drvdata(hdev); |
524 | if (kovaplus->roccat_claimed) |
525 | roccat_disconnect(kovaplus->chrdev_minor); |
526 | kfree(kovaplus); |
527 | } |
528 | } |
529 | |
530 | static int kovaplus_probe(struct hid_device *hdev, |
531 | const struct hid_device_id *id) |
532 | { |
533 | int retval; |
534 | |
535 | retval = hid_parse(hdev); |
536 | if (retval) { |
537 | hid_err(hdev, "parse failed\n"); |
538 | goto exit; |
539 | } |
540 | |
541 | retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT); |
542 | if (retval) { |
543 | hid_err(hdev, "hw start failed\n"); |
544 | goto exit; |
545 | } |
546 | |
547 | retval = kovaplus_init_specials(hdev); |
548 | if (retval) { |
549 | hid_err(hdev, "couldn't install mouse\n"); |
550 | goto exit_stop; |
551 | } |
552 | |
553 | return 0; |
554 | |
555 | exit_stop: |
556 | hid_hw_stop(hdev); |
557 | exit: |
558 | return retval; |
559 | } |
560 | |
561 | static void kovaplus_remove(struct hid_device *hdev) |
562 | { |
563 | kovaplus_remove_specials(hdev); |
564 | hid_hw_stop(hdev); |
565 | } |
566 | |
567 | static void kovaplus_keep_values_up_to_date(struct kovaplus_device *kovaplus, |
568 | u8 const *data) |
569 | { |
570 | struct kovaplus_mouse_report_button const *button_report; |
571 | |
572 | if (data[0] != KOVAPLUS_MOUSE_REPORT_NUMBER_BUTTON) |
573 | return; |
574 | |
575 | button_report = (struct kovaplus_mouse_report_button const *)data; |
576 | |
577 | switch (button_report->type) { |
578 | case KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_PROFILE_1: |
579 | kovaplus_profile_activated(kovaplus, button_report->data1 - 1); |
580 | break; |
581 | case KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_CPI: |
582 | kovaplus->actual_cpi = kovaplus_convert_event_cpi(button_report->data1); |
583 | case KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_SENSITIVITY: |
584 | kovaplus->actual_x_sensitivity = button_report->data1; |
585 | kovaplus->actual_y_sensitivity = button_report->data2; |
586 | } |
587 | } |
588 | |
589 | static void kovaplus_report_to_chrdev(struct kovaplus_device const *kovaplus, |
590 | u8 const *data) |
591 | { |
592 | struct kovaplus_roccat_report roccat_report; |
593 | struct kovaplus_mouse_report_button const *button_report; |
594 | |
595 | if (data[0] != KOVAPLUS_MOUSE_REPORT_NUMBER_BUTTON) |
596 | return; |
597 | |
598 | button_report = (struct kovaplus_mouse_report_button const *)data; |
599 | |
600 | if (button_report->type == KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_PROFILE_2) |
601 | return; |
602 | |
603 | roccat_report.type = button_report->type; |
604 | roccat_report.profile = kovaplus->actual_profile + 1; |
605 | |
606 | if (roccat_report.type == KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_MACRO || |
607 | roccat_report.type == KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_SHORTCUT || |
608 | roccat_report.type == KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_QUICKLAUNCH || |
609 | roccat_report.type == KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_TIMER) |
610 | roccat_report.button = button_report->data1; |
611 | else |
612 | roccat_report.button = 0; |
613 | |
614 | if (roccat_report.type == KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_CPI) |
615 | roccat_report.data1 = kovaplus_convert_event_cpi(button_report->data1); |
616 | else |
617 | roccat_report.data1 = button_report->data1; |
618 | |
619 | roccat_report.data2 = button_report->data2; |
620 | |
621 | roccat_report_event(kovaplus->chrdev_minor, |
622 | (uint8_t const *)&roccat_report); |
623 | } |
624 | |
625 | static int kovaplus_raw_event(struct hid_device *hdev, |
626 | struct hid_report *report, u8 *data, int size) |
627 | { |
628 | struct usb_interface *intf = to_usb_interface(hdev->dev.parent); |
629 | struct kovaplus_device *kovaplus = hid_get_drvdata(hdev); |
630 | |
631 | if (intf->cur_altsetting->desc.bInterfaceProtocol |
632 | != USB_INTERFACE_PROTOCOL_MOUSE) |
633 | return 0; |
634 | |
635 | if (kovaplus == NULL) |
636 | return 0; |
637 | |
638 | kovaplus_keep_values_up_to_date(kovaplus, data); |
639 | |
640 | if (kovaplus->roccat_claimed) |
641 | kovaplus_report_to_chrdev(kovaplus, data); |
642 | |
643 | return 0; |
644 | } |
645 | |
646 | static const struct hid_device_id kovaplus_devices[] = { |
647 | { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KOVAPLUS) }, |
648 | { } |
649 | }; |
650 | |
651 | MODULE_DEVICE_TABLE(hid, kovaplus_devices); |
652 | |
653 | static struct hid_driver kovaplus_driver = { |
654 | .name = "kovaplus", |
655 | .id_table = kovaplus_devices, |
656 | .probe = kovaplus_probe, |
657 | .remove = kovaplus_remove, |
658 | .raw_event = kovaplus_raw_event |
659 | }; |
660 | |
661 | static int __init kovaplus_init(void) |
662 | { |
663 | int retval; |
664 | |
665 | kovaplus_class = class_create(THIS_MODULE, "kovaplus"); |
666 | if (IS_ERR(kovaplus_class)) |
667 | return PTR_ERR(kovaplus_class); |
668 | kovaplus_class->dev_attrs = kovaplus_attributes; |
669 | kovaplus_class->dev_bin_attrs = kovaplus_bin_attributes; |
670 | |
671 | retval = hid_register_driver(&kovaplus_driver); |
672 | if (retval) |
673 | class_destroy(kovaplus_class); |
674 | return retval; |
675 | } |
676 | |
677 | static void __exit kovaplus_exit(void) |
678 | { |
679 | hid_unregister_driver(&kovaplus_driver); |
680 | class_destroy(kovaplus_class); |
681 | } |
682 | |
683 | module_init(kovaplus_init); |
684 | module_exit(kovaplus_exit); |
685 | |
686 | MODULE_AUTHOR("Stefan Achatz"); |
687 | MODULE_DESCRIPTION("USB Roccat Kova[+] driver"); |
688 | MODULE_LICENSE("GPL v2"); |
689 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9