Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
hid-roccat-kovaplus.c
Go to the documentation of this file.
1 /*
2  * Roccat Kova[+] driver for Linux
3  *
4  * Copyright (c) 2011 Stefan Achatz <[email protected]>
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,
48 {
49  int retval;
51 
54  value > 4)
55  return -EINVAL;
56 
58  control.value = value;
59  control.request = request;
60 
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 {
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,
87  if (retval)
88  return retval;
89 
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,
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,
109  if (retval)
110  return retval;
111 
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,
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 
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 
142  buf.size = sizeof(struct kovaplus_actual_profile);
143  buf.actual_profile = new_profile;
144 
145  return roccat_common2_send_with_status(usb_dev,
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;
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 
306  roccat_report.profile = profile + 1;
307  roccat_report.button = 0;
308  roccat_report.data1 = profile + 1;
309  roccat_report.data2 = 0;
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),
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  },
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
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
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 
573  return;
574 
575  button_report = (struct kovaplus_mouse_report_button const *)data;
576 
577  switch (button_report->type) {
579  kovaplus_profile_activated(kovaplus, button_report->data1 - 1);
580  break;
582  kovaplus->actual_cpi = kovaplus_convert_event_cpi(button_report->data1);
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 {
593  struct kovaplus_mouse_report_button const *button_report;
594 
596  return;
597 
598  button_report = (struct kovaplus_mouse_report_button const *)data;
599 
601  return;
602 
603  roccat_report.type = button_report->type;
604  roccat_report.profile = kovaplus->actual_profile + 1;
605 
610  roccat_report.button = button_report->data1;
611  else
612  roccat_report.button = 0;
613 
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 
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
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[] = {
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");