aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Achatz <erazor_de@users.sourceforge.net>2011-01-30 07:38:24 -0500
committerJiri Kosina <jkosina@suse.cz>2011-02-03 10:37:27 -0500
commit0e70f97f257edcef4daa92ab9371a9aac0c851ed (patch)
tree42eb30225a199e78eb404b03dbe262a4bded00de
parent5772f63613ce0a6777e82a7e8fb553e49da27719 (diff)
HID: roccat: Add support for Kova[+] mouse
This patch adds support for Roccat Kova[+] mouse. Userland tools can soon be found at http://sourceforge.net/projects/roccat Signed-off-by: Stefan Achatz <erazor_de@users.sourceforge.net> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
-rw-r--r--Documentation/ABI/testing/sysfs-driver-hid-roccat-kovaplus91
-rw-r--r--drivers/hid/Kconfig8
-rw-r--r--drivers/hid/Makefile1
-rw-r--r--drivers/hid/hid-core.c1
-rw-r--r--drivers/hid/hid-ids.h1
-rw-r--r--drivers/hid/hid-roccat-kovaplus.c715
-rw-r--r--drivers/hid/hid-roccat-kovaplus.h157
7 files changed, 974 insertions, 0 deletions
diff --git a/Documentation/ABI/testing/sysfs-driver-hid-roccat-kovaplus b/Documentation/ABI/testing/sysfs-driver-hid-roccat-kovaplus
new file mode 100644
index 000000000000..9e54af4c8fc0
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-driver-hid-roccat-kovaplus
@@ -0,0 +1,91 @@
1What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/kovaplus/roccatkovaplus<minor>/actual_cpi
2Date: January 2011
3Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
4Description: The integer value of this attribute ranges from 1-4.
5 When read, this attribute returns the number of the active
6 cpi level.
7 This file is readonly.
8
9What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/kovaplus/roccatkovaplus<minor>/actual_profile
10Date: January 2011
11Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
12Description: The integer value of this attribute ranges from 0-4.
13 When read, this attribute returns the number of the active
14 profile.
15 When written, the mouse activates this profile immediately.
16 The profile that's active when powered down is the same that's
17 active when the mouse is powered on.
18
19What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/kovaplus/roccatkovaplus<minor>/actual_sensitivity_x
20Date: January 2011
21Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
22Description: The integer value of this attribute ranges from 1-10.
23 When read, this attribute returns the number of the actual
24 sensitivity in x direction.
25 This file is readonly.
26
27What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/kovaplus/roccatkovaplus<minor>/actual_sensitivity_y
28Date: January 2011
29Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
30Description: The integer value of this attribute ranges from 1-10.
31 When read, this attribute returns the number of the actual
32 sensitivity in y direction.
33 This file is readonly.
34
35What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/kovaplus/roccatkovaplus<minor>/firmware_version
36Date: January 2011
37Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
38Description: When read, this file returns the raw integer version number of the
39 firmware reported by the mouse. Using the integer value eases
40 further usage in other programs. To receive the real version
41 number the decimal point has to be shifted 2 positions to the
42 left. E.g. a returned value of 121 means 1.21
43 This file is readonly.
44
45What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/kovaplus/roccatkovaplus<minor>/profile_buttons
46Date: January 2011
47Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
48Description: The mouse can store 5 profiles which can be switched by the
49 press of a button. A profile is split in settings and buttons.
50 profile_buttons holds informations about button layout.
51 When written, this file lets one write the respective profile
52 buttons back to the mouse. The data has to be 23 bytes long.
53 The mouse will reject invalid data.
54 Which profile to write is determined by the profile number
55 contained in the data.
56 This file is writeonly.
57
58What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/kovaplus/roccatkovaplus<minor>/profile[1-5]_buttons
59Date: January 2011
60Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
61Description: The mouse can store 5 profiles which can be switched by the
62 press of a button. A profile is split in settings and buttons.
63 profile_buttons holds informations about button layout.
64 When read, these files return the respective profile buttons.
65 The returned data is 23 bytes in size.
66 This file is readonly.
67
68What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/kovaplus/roccatkovaplus<minor>/profile_settings
69Date: January 2011
70Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
71Description: The mouse can store 5 profiles which can be switched by the
72 press of a button. A profile is split in settings and buttons.
73 profile_settings holds informations like resolution, sensitivity
74 and light effects.
75 When written, this file lets one write the respective profile
76 settings back to the mouse. The data has to be 16 bytes long.
77 The mouse will reject invalid data.
78 Which profile to write is determined by the profile number
79 contained in the data.
80 This file is writeonly.
81
82What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/kovaplus/roccatkovaplus<minor>/profile[1-5]_settings
83Date: January 2011
84Contact: Stefan Achatz <erazor_de@users.sourceforge.net>
85Description: The mouse can store 5 profiles which can be switched by the
86 press of a button. A profile is split in settings and buttons.
87 profile_settings holds informations like resolution, sensitivity
88 and light effects.
89 When read, these files return the respective profile settings.
90 The returned data is 16 bytes in size.
91 This file is readonly.
diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 795236963066..022515fdd6fe 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -444,6 +444,14 @@ config HID_ROCCAT_KONEPLUS
444 ---help--- 444 ---help---
445 Support for Roccat Kone[+] mouse. 445 Support for Roccat Kone[+] mouse.
446 446
447config HID_ROCCAT_KOVAPLUS
448 tristate "Roccat Kova[+] mouse support"
449 depends on USB_HID
450 select HID_ROCCAT
451 select HID_ROCCAT_COMMON
452 ---help---
453 Support for Roccat Kova[+] mouse.
454
447config HID_ROCCAT_PYRA 455config HID_ROCCAT_PYRA
448 tristate "Roccat Pyra mouse support" 456 tristate "Roccat Pyra mouse support"
449 depends on USB_HID 457 depends on USB_HID
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
index 086cf62184eb..cb80181da905 100644
--- a/drivers/hid/Makefile
+++ b/drivers/hid/Makefile
@@ -60,6 +60,7 @@ obj-$(CONFIG_HID_ROCCAT_COMMON) += hid-roccat-common.o
60obj-$(CONFIG_HID_ROCCAT_ARVO) += hid-roccat-arvo.o 60obj-$(CONFIG_HID_ROCCAT_ARVO) += hid-roccat-arvo.o
61obj-$(CONFIG_HID_ROCCAT_KONE) += hid-roccat-kone.o 61obj-$(CONFIG_HID_ROCCAT_KONE) += hid-roccat-kone.o
62obj-$(CONFIG_HID_ROCCAT_KONEPLUS) += hid-roccat-koneplus.o 62obj-$(CONFIG_HID_ROCCAT_KONEPLUS) += hid-roccat-koneplus.o
63obj-$(CONFIG_HID_ROCCAT_KOVAPLUS) += hid-roccat-kovaplus.o
63obj-$(CONFIG_HID_ROCCAT_PYRA) += hid-roccat-pyra.o 64obj-$(CONFIG_HID_ROCCAT_PYRA) += hid-roccat-pyra.o
64obj-$(CONFIG_HID_SAMSUNG) += hid-samsung.o 65obj-$(CONFIG_HID_SAMSUNG) += hid-samsung.o
65obj-$(CONFIG_HID_SMARTJOYPLUS) += hid-sjoy.o 66obj-$(CONFIG_HID_SMARTJOYPLUS) += hid-sjoy.o
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
index 6178025a95f6..0180f20d4fa7 100644
--- a/drivers/hid/hid-core.c
+++ b/drivers/hid/hid-core.c
@@ -1407,6 +1407,7 @@ static const struct hid_device_id hid_have_special_driver[] = {
1407 { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONE) }, 1407 { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONE) },
1408 { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ARVO) }, 1408 { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ARVO) },
1409 { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEPLUS) }, 1409 { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEPLUS) },
1410 { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KOVAPLUS) },
1410 { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_PYRA_WIRED) }, 1411 { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_PYRA_WIRED) },
1411 { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) }, 1412 { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) },
1412 { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE) }, 1413 { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE) },
diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
index 92e0fa1856c6..75f2e1430a55 100644
--- a/drivers/hid/hid-ids.h
+++ b/drivers/hid/hid-ids.h
@@ -499,6 +499,7 @@
499#define USB_DEVICE_ID_ROCCAT_ARVO 0x30d4 499#define USB_DEVICE_ID_ROCCAT_ARVO 0x30d4
500#define USB_DEVICE_ID_ROCCAT_KONE 0x2ced 500#define USB_DEVICE_ID_ROCCAT_KONE 0x2ced
501#define USB_DEVICE_ID_ROCCAT_KONEPLUS 0x2d51 501#define USB_DEVICE_ID_ROCCAT_KONEPLUS 0x2d51
502#define USB_DEVICE_ID_ROCCAT_KOVAPLUS 0x2d50
502#define USB_DEVICE_ID_ROCCAT_PYRA_WIRED 0x2c24 503#define USB_DEVICE_ID_ROCCAT_PYRA_WIRED 0x2c24
503#define USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS 0x2cf6 504#define USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS 0x2cf6
504 505
diff --git a/drivers/hid/hid-roccat-kovaplus.c b/drivers/hid/hid-roccat-kovaplus.c
new file mode 100644
index 000000000000..1f547a2b39de
--- /dev/null
+++ b/drivers/hid/hid-roccat-kovaplus.c
@@ -0,0 +1,715 @@
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 "hid-ids.h"
24#include "hid-roccat.h"
25#include "hid-roccat-common.h"
26#include "hid-roccat-kovaplus.h"
27
28static uint profile_numbers[5] = {0, 1, 2, 3, 4};
29
30static struct class *kovaplus_class;
31
32static uint kovaplus_convert_event_cpi(uint value)
33{
34 return (value == 7 ? 4 : (value == 4 ? 3 : value));
35}
36
37static 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
46static int kovaplus_send_control(struct usb_device *usb_dev, uint value,
47 enum kovaplus_control_requests request)
48{
49 int retval;
50 struct kovaplus_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 = KOVAPLUS_COMMAND_CONTROL;
58 control.value = value;
59 control.request = request;
60
61 retval = roccat_common_send(usb_dev, KOVAPLUS_USB_COMMAND_CONTROL,
62 &control, sizeof(struct kovaplus_control));
63
64 return retval;
65}
66
67static int kovaplus_receive_control_status(struct usb_device *usb_dev)
68{
69 int retval;
70 struct kovaplus_control control;
71
72 do {
73 retval = roccat_common_receive(usb_dev, KOVAPLUS_USB_COMMAND_CONTROL,
74 &control, sizeof(struct kovaplus_control));
75
76 /* check if we get a completely wrong answer */
77 if (retval)
78 return retval;
79
80 if (control.value == KOVAPLUS_CONTROL_REQUEST_STATUS_OK)
81 return 0;
82
83 /* indicates that hardware needs some more time to complete action */
84 if (control.value == KOVAPLUS_CONTROL_REQUEST_STATUS_WAIT) {
85 msleep(500); /* windows driver uses 1000 */
86 continue;
87 }
88
89 /* seems to be critical - replug necessary */
90 if (control.value == KOVAPLUS_CONTROL_REQUEST_STATUS_OVERLOAD)
91 return -EINVAL;
92
93 hid_err(usb_dev, "kovaplus_receive_control_status: "
94 "unknown response value 0x%x\n", control.value);
95 return -EINVAL;
96 } while (1);
97}
98
99static int kovaplus_send(struct usb_device *usb_dev, uint command,
100 void const *buf, uint size)
101{
102 int retval;
103
104 retval = roccat_common_send(usb_dev, command, buf, size);
105 if (retval)
106 return retval;
107
108 msleep(100);
109
110 return kovaplus_receive_control_status(usb_dev);
111}
112
113static int kovaplus_select_profile(struct usb_device *usb_dev, uint number,
114 enum kovaplus_control_requests request)
115{
116 return kovaplus_send_control(usb_dev, number, request);
117}
118
119static int kovaplus_get_info(struct usb_device *usb_dev,
120 struct kovaplus_info *buf)
121{
122 return roccat_common_receive(usb_dev, KOVAPLUS_USB_COMMAND_INFO,
123 buf, sizeof(struct kovaplus_info));
124}
125
126static int kovaplus_get_profile_settings(struct usb_device *usb_dev,
127 struct kovaplus_profile_settings *buf, uint number)
128{
129 int retval;
130
131 retval = kovaplus_select_profile(usb_dev, number,
132 KOVAPLUS_CONTROL_REQUEST_PROFILE_SETTINGS);
133 if (retval)
134 return retval;
135
136 return roccat_common_receive(usb_dev, KOVAPLUS_USB_COMMAND_PROFILE_SETTINGS,
137 buf, sizeof(struct kovaplus_profile_settings));
138}
139
140static int kovaplus_set_profile_settings(struct usb_device *usb_dev,
141 struct kovaplus_profile_settings const *settings)
142{
143 return kovaplus_send(usb_dev, KOVAPLUS_USB_COMMAND_PROFILE_SETTINGS,
144 settings, sizeof(struct kovaplus_profile_settings));
145}
146
147static int kovaplus_get_profile_buttons(struct usb_device *usb_dev,
148 struct kovaplus_profile_buttons *buf, int number)
149{
150 int retval;
151
152 retval = kovaplus_select_profile(usb_dev, number,
153 KOVAPLUS_CONTROL_REQUEST_PROFILE_BUTTONS);
154 if (retval)
155 return retval;
156
157 return roccat_common_receive(usb_dev, KOVAPLUS_USB_COMMAND_PROFILE_BUTTONS,
158 buf, sizeof(struct kovaplus_profile_buttons));
159}
160
161static int kovaplus_set_profile_buttons(struct usb_device *usb_dev,
162 struct kovaplus_profile_buttons const *buttons)
163{
164 return kovaplus_send(usb_dev, KOVAPLUS_USB_COMMAND_PROFILE_BUTTONS,
165 buttons, sizeof(struct kovaplus_profile_buttons));
166}
167
168/* retval is 0-4 on success, < 0 on error */
169static int kovaplus_get_actual_profile(struct usb_device *usb_dev)
170{
171 struct kovaplus_actual_profile buf;
172 int retval;
173
174 retval = roccat_common_receive(usb_dev, KOVAPLUS_USB_COMMAND_ACTUAL_PROFILE,
175 &buf, sizeof(struct kovaplus_actual_profile));
176
177 return retval ? retval : buf.actual_profile;
178}
179
180static int kovaplus_set_actual_profile(struct usb_device *usb_dev,
181 int new_profile)
182{
183 struct kovaplus_actual_profile buf;
184
185 buf.command = KOVAPLUS_COMMAND_ACTUAL_PROFILE;
186 buf.size = sizeof(struct kovaplus_actual_profile);
187 buf.actual_profile = new_profile;
188
189 return kovaplus_send(usb_dev, KOVAPLUS_USB_COMMAND_ACTUAL_PROFILE,
190 &buf, sizeof(struct kovaplus_actual_profile));
191}
192
193static ssize_t kovaplus_sysfs_read_profilex_settings(struct file *fp,
194 struct kobject *kobj, struct bin_attribute *attr, char *buf,
195 loff_t off, size_t count)
196{
197 struct device *dev =
198 container_of(kobj, struct device, kobj)->parent->parent;
199 struct kovaplus_device *kovaplus = hid_get_drvdata(dev_get_drvdata(dev));
200
201 if (off >= sizeof(struct kovaplus_profile_settings))
202 return 0;
203
204 if (off + count > sizeof(struct kovaplus_profile_settings))
205 count = sizeof(struct kovaplus_profile_settings) - off;
206
207 mutex_lock(&kovaplus->kovaplus_lock);
208 memcpy(buf, ((char const *)&kovaplus->profile_settings[*(uint *)(attr->private)]) + off,
209 count);
210 mutex_unlock(&kovaplus->kovaplus_lock);
211
212 return count;
213}
214
215static ssize_t kovaplus_sysfs_write_profile_settings(struct file *fp,
216 struct kobject *kobj, struct bin_attribute *attr, char *buf,
217 loff_t off, size_t count)
218{
219 struct device *dev =
220 container_of(kobj, struct device, kobj)->parent->parent;
221 struct kovaplus_device *kovaplus = hid_get_drvdata(dev_get_drvdata(dev));
222 struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
223 int retval = 0;
224 int difference;
225 int profile_index;
226 struct kovaplus_profile_settings *profile_settings;
227
228 if (off != 0 || count != sizeof(struct kovaplus_profile_settings))
229 return -EINVAL;
230
231 profile_index = ((struct kovaplus_profile_settings const *)buf)->profile_index;
232 profile_settings = &kovaplus->profile_settings[profile_index];
233
234 mutex_lock(&kovaplus->kovaplus_lock);
235 difference = memcmp(buf, profile_settings,
236 sizeof(struct kovaplus_profile_settings));
237 if (difference) {
238 retval = kovaplus_set_profile_settings(usb_dev,
239 (struct kovaplus_profile_settings const *)buf);
240 if (!retval)
241 memcpy(profile_settings, buf,
242 sizeof(struct kovaplus_profile_settings));
243 }
244 mutex_unlock(&kovaplus->kovaplus_lock);
245
246 if (retval)
247 return retval;
248
249 return sizeof(struct kovaplus_profile_settings);
250}
251
252static ssize_t kovaplus_sysfs_read_profilex_buttons(struct file *fp,
253 struct kobject *kobj, struct bin_attribute *attr, char *buf,
254 loff_t off, size_t count)
255{
256 struct device *dev =
257 container_of(kobj, struct device, kobj)->parent->parent;
258 struct kovaplus_device *kovaplus = hid_get_drvdata(dev_get_drvdata(dev));
259
260 if (off >= sizeof(struct kovaplus_profile_buttons))
261 return 0;
262
263 if (off + count > sizeof(struct kovaplus_profile_buttons))
264 count = sizeof(struct kovaplus_profile_buttons) - off;
265
266 mutex_lock(&kovaplus->kovaplus_lock);
267 memcpy(buf, ((char const *)&kovaplus->profile_buttons[*(uint *)(attr->private)]) + off,
268 count);
269 mutex_unlock(&kovaplus->kovaplus_lock);
270
271 return count;
272}
273
274static ssize_t kovaplus_sysfs_write_profile_buttons(struct file *fp,
275 struct kobject *kobj, struct bin_attribute *attr, char *buf,
276 loff_t off, size_t count)
277{
278 struct device *dev =
279 container_of(kobj, struct device, kobj)->parent->parent;
280 struct kovaplus_device *kovaplus = hid_get_drvdata(dev_get_drvdata(dev));
281 struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
282 int retval = 0;
283 int difference;
284 uint profile_index;
285 struct kovaplus_profile_buttons *profile_buttons;
286
287 if (off != 0 || count != sizeof(struct kovaplus_profile_buttons))
288 return -EINVAL;
289
290 profile_index = ((struct kovaplus_profile_buttons const *)buf)->profile_index;
291 profile_buttons = &kovaplus->profile_buttons[profile_index];
292
293 mutex_lock(&kovaplus->kovaplus_lock);
294 difference = memcmp(buf, profile_buttons,
295 sizeof(struct kovaplus_profile_buttons));
296 if (difference) {
297 retval = kovaplus_set_profile_buttons(usb_dev,
298 (struct kovaplus_profile_buttons const *)buf);
299 if (!retval)
300 memcpy(profile_buttons, buf,
301 sizeof(struct kovaplus_profile_buttons));
302 }
303 mutex_unlock(&kovaplus->kovaplus_lock);
304
305 if (retval)
306 return retval;
307
308 return sizeof(struct kovaplus_profile_buttons);
309}
310
311static ssize_t kovaplus_sysfs_show_actual_profile(struct device *dev,
312 struct device_attribute *attr, char *buf)
313{
314 struct kovaplus_device *kovaplus =
315 hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
316 return snprintf(buf, PAGE_SIZE, "%d\n", kovaplus->actual_profile);
317}
318
319static ssize_t kovaplus_sysfs_set_actual_profile(struct device *dev,
320 struct device_attribute *attr, char const *buf, size_t size)
321{
322 struct kovaplus_device *kovaplus;
323 struct usb_device *usb_dev;
324 unsigned long profile;
325 int retval;
326
327 dev = dev->parent->parent;
328 kovaplus = hid_get_drvdata(dev_get_drvdata(dev));
329 usb_dev = interface_to_usbdev(to_usb_interface(dev));
330
331 retval = strict_strtoul(buf, 10, &profile);
332 if (retval)
333 return retval;
334
335 if (profile >= 5)
336 return -EINVAL;
337
338 mutex_lock(&kovaplus->kovaplus_lock);
339 retval = kovaplus_set_actual_profile(usb_dev, profile);
340 kovaplus->actual_profile = profile;
341 mutex_unlock(&kovaplus->kovaplus_lock);
342 if (retval)
343 return retval;
344
345 return size;
346}
347
348static ssize_t kovaplus_sysfs_show_actual_cpi(struct device *dev,
349 struct device_attribute *attr, char *buf)
350{
351 struct kovaplus_device *kovaplus =
352 hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
353 return snprintf(buf, PAGE_SIZE, "%d\n", kovaplus->actual_cpi);
354}
355
356static ssize_t kovaplus_sysfs_show_actual_sensitivity_x(struct device *dev,
357 struct device_attribute *attr, char *buf)
358{
359 struct kovaplus_device *kovaplus =
360 hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
361 return snprintf(buf, PAGE_SIZE, "%d\n", kovaplus->actual_x_sensitivity);
362}
363
364static ssize_t kovaplus_sysfs_show_actual_sensitivity_y(struct device *dev,
365 struct device_attribute *attr, char *buf)
366{
367 struct kovaplus_device *kovaplus =
368 hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
369 return snprintf(buf, PAGE_SIZE, "%d\n", kovaplus->actual_y_sensitivity);
370}
371
372static ssize_t kovaplus_sysfs_show_firmware_version(struct device *dev,
373 struct device_attribute *attr, char *buf)
374{
375 struct kovaplus_device *kovaplus =
376 hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
377 return snprintf(buf, PAGE_SIZE, "%d\n", kovaplus->info.firmware_version);
378}
379
380static struct device_attribute kovaplus_attributes[] = {
381 __ATTR(actual_cpi, 0440,
382 kovaplus_sysfs_show_actual_cpi, NULL),
383 __ATTR(firmware_version, 0440,
384 kovaplus_sysfs_show_firmware_version, NULL),
385 __ATTR(actual_profile, 0660,
386 kovaplus_sysfs_show_actual_profile,
387 kovaplus_sysfs_set_actual_profile),
388 __ATTR(actual_sensitivity_x, 0440,
389 kovaplus_sysfs_show_actual_sensitivity_x, NULL),
390 __ATTR(actual_sensitivity_y, 0440,
391 kovaplus_sysfs_show_actual_sensitivity_y, NULL),
392 __ATTR_NULL
393};
394
395static struct bin_attribute kovaplus_bin_attributes[] = {
396 {
397 .attr = { .name = "profile_settings", .mode = 0220 },
398 .size = sizeof(struct kovaplus_profile_settings),
399 .write = kovaplus_sysfs_write_profile_settings
400 },
401 {
402 .attr = { .name = "profile1_settings", .mode = 0440 },
403 .size = sizeof(struct kovaplus_profile_settings),
404 .read = kovaplus_sysfs_read_profilex_settings,
405 .private = &profile_numbers[0]
406 },
407 {
408 .attr = { .name = "profile2_settings", .mode = 0440 },
409 .size = sizeof(struct kovaplus_profile_settings),
410 .read = kovaplus_sysfs_read_profilex_settings,
411 .private = &profile_numbers[1]
412 },
413 {
414 .attr = { .name = "profile3_settings", .mode = 0440 },
415 .size = sizeof(struct kovaplus_profile_settings),
416 .read = kovaplus_sysfs_read_profilex_settings,
417 .private = &profile_numbers[2]
418 },
419 {
420 .attr = { .name = "profile4_settings", .mode = 0440 },
421 .size = sizeof(struct kovaplus_profile_settings),
422 .read = kovaplus_sysfs_read_profilex_settings,
423 .private = &profile_numbers[3]
424 },
425 {
426 .attr = { .name = "profile5_settings", .mode = 0440 },
427 .size = sizeof(struct kovaplus_profile_settings),
428 .read = kovaplus_sysfs_read_profilex_settings,
429 .private = &profile_numbers[4]
430 },
431 {
432 .attr = { .name = "profile_buttons", .mode = 0220 },
433 .size = sizeof(struct kovaplus_profile_buttons),
434 .write = kovaplus_sysfs_write_profile_buttons
435 },
436 {
437 .attr = { .name = "profile1_buttons", .mode = 0440 },
438 .size = sizeof(struct kovaplus_profile_buttons),
439 .read = kovaplus_sysfs_read_profilex_buttons,
440 .private = &profile_numbers[0]
441 },
442 {
443 .attr = { .name = "profile2_buttons", .mode = 0440 },
444 .size = sizeof(struct kovaplus_profile_buttons),
445 .read = kovaplus_sysfs_read_profilex_buttons,
446 .private = &profile_numbers[1]
447 },
448 {
449 .attr = { .name = "profile3_buttons", .mode = 0440 },
450 .size = sizeof(struct kovaplus_profile_buttons),
451 .read = kovaplus_sysfs_read_profilex_buttons,
452 .private = &profile_numbers[2]
453 },
454 {
455 .attr = { .name = "profile4_buttons", .mode = 0440 },
456 .size = sizeof(struct kovaplus_profile_buttons),
457 .read = kovaplus_sysfs_read_profilex_buttons,
458 .private = &profile_numbers[3]
459 },
460 {
461 .attr = { .name = "profile5_buttons", .mode = 0440 },
462 .size = sizeof(struct kovaplus_profile_buttons),
463 .read = kovaplus_sysfs_read_profilex_buttons,
464 .private = &profile_numbers[4]
465 },
466 __ATTR_NULL
467};
468
469static int kovaplus_init_kovaplus_device_struct(struct usb_device *usb_dev,
470 struct kovaplus_device *kovaplus)
471{
472 int retval, i;
473 static uint wait = 70; /* device will freeze with just 60 */
474
475 mutex_init(&kovaplus->kovaplus_lock);
476
477 retval = kovaplus_get_info(usb_dev, &kovaplus->info);
478 if (retval)
479 return retval;
480
481 for (i = 0; i < 5; ++i) {
482 msleep(wait);
483 retval = kovaplus_get_profile_settings(usb_dev,
484 &kovaplus->profile_settings[i], i);
485 if (retval)
486 return retval;
487
488 msleep(wait);
489 retval = kovaplus_get_profile_buttons(usb_dev,
490 &kovaplus->profile_buttons[i], i);
491 if (retval)
492 return retval;
493 }
494
495 msleep(wait);
496 retval = kovaplus_get_actual_profile(usb_dev);
497 if (retval < 0)
498 return retval;
499 kovaplus_profile_activated(kovaplus, retval);
500
501 return 0;
502}
503
504static int kovaplus_init_specials(struct hid_device *hdev)
505{
506 struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
507 struct usb_device *usb_dev = interface_to_usbdev(intf);
508 struct kovaplus_device *kovaplus;
509 int retval;
510
511 if (intf->cur_altsetting->desc.bInterfaceProtocol
512 == USB_INTERFACE_PROTOCOL_MOUSE) {
513
514 kovaplus = kzalloc(sizeof(*kovaplus), GFP_KERNEL);
515 if (!kovaplus) {
516 hid_err(hdev, "can't alloc device descriptor\n");
517 return -ENOMEM;
518 }
519 hid_set_drvdata(hdev, kovaplus);
520
521 retval = kovaplus_init_kovaplus_device_struct(usb_dev, kovaplus);
522 if (retval) {
523 hid_err(hdev, "couldn't init struct kovaplus_device\n");
524 goto exit_free;
525 }
526
527 retval = roccat_connect(kovaplus_class, hdev);
528 if (retval < 0) {
529 hid_err(hdev, "couldn't init char dev\n");
530 } else {
531 kovaplus->chrdev_minor = retval;
532 kovaplus->roccat_claimed = 1;
533 }
534
535 } else {
536 hid_set_drvdata(hdev, NULL);
537 }
538
539 return 0;
540exit_free:
541 kfree(kovaplus);
542 return retval;
543}
544
545static void kovaplus_remove_specials(struct hid_device *hdev)
546{
547 struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
548 struct kovaplus_device *kovaplus;
549
550 if (intf->cur_altsetting->desc.bInterfaceProtocol
551 == USB_INTERFACE_PROTOCOL_MOUSE) {
552 kovaplus = hid_get_drvdata(hdev);
553 if (kovaplus->roccat_claimed)
554 roccat_disconnect(kovaplus->chrdev_minor);
555 kfree(kovaplus);
556 }
557}
558
559static int kovaplus_probe(struct hid_device *hdev,
560 const struct hid_device_id *id)
561{
562 int retval;
563
564 retval = hid_parse(hdev);
565 if (retval) {
566 hid_err(hdev, "parse failed\n");
567 goto exit;
568 }
569
570 retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
571 if (retval) {
572 hid_err(hdev, "hw start failed\n");
573 goto exit;
574 }
575
576 retval = kovaplus_init_specials(hdev);
577 if (retval) {
578 hid_err(hdev, "couldn't install mouse\n");
579 goto exit_stop;
580 }
581
582 return 0;
583
584exit_stop:
585 hid_hw_stop(hdev);
586exit:
587 return retval;
588}
589
590static void kovaplus_remove(struct hid_device *hdev)
591{
592 kovaplus_remove_specials(hdev);
593 hid_hw_stop(hdev);
594}
595
596static void kovaplus_keep_values_up_to_date(struct kovaplus_device *kovaplus,
597 u8 const *data)
598{
599 struct kovaplus_mouse_report_button const *button_report;
600
601 if (data[0] != KOVAPLUS_MOUSE_REPORT_NUMBER_BUTTON)
602 return;
603
604 button_report = (struct kovaplus_mouse_report_button const *)data;
605
606 switch (button_report->type) {
607 case KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_PROFILE_1:
608 kovaplus_profile_activated(kovaplus, button_report->data1 - 1);
609 break;
610 case KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_CPI:
611 kovaplus->actual_cpi = kovaplus_convert_event_cpi(button_report->data1);
612 case KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_SENSITIVITY:
613 kovaplus->actual_x_sensitivity = button_report->data1;
614 kovaplus->actual_y_sensitivity = button_report->data2;
615 }
616}
617
618static void kovaplus_report_to_chrdev(struct kovaplus_device const *kovaplus,
619 u8 const *data)
620{
621 struct kovaplus_roccat_report roccat_report;
622 struct kovaplus_mouse_report_button const *button_report;
623
624 if (data[0] != KOVAPLUS_MOUSE_REPORT_NUMBER_BUTTON)
625 return;
626
627 button_report = (struct kovaplus_mouse_report_button const *)data;
628
629 if (button_report->type == KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_PROFILE_2)
630 return;
631
632 roccat_report.type = button_report->type;
633 roccat_report.profile = kovaplus->actual_profile + 1;
634
635 if (roccat_report.type == KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_MACRO ||
636 roccat_report.type == KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_SHORTCUT ||
637 roccat_report.type == KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_QUICKLAUNCH ||
638 roccat_report.type == KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_TIMER)
639 roccat_report.button = button_report->data1;
640 else
641 roccat_report.button = 0;
642
643 if (roccat_report.type == KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_CPI)
644 roccat_report.data1 = kovaplus_convert_event_cpi(button_report->data1);
645 else
646 roccat_report.data1 = button_report->data1;
647
648 roccat_report.data2 = button_report->data2;
649
650 roccat_report_event(kovaplus->chrdev_minor,
651 (uint8_t const *)&roccat_report,
652 sizeof(struct kovaplus_roccat_report));
653}
654
655static int kovaplus_raw_event(struct hid_device *hdev,
656 struct hid_report *report, u8 *data, int size)
657{
658 struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
659 struct kovaplus_device *kovaplus = hid_get_drvdata(hdev);
660
661 if (intf->cur_altsetting->desc.bInterfaceProtocol
662 != USB_INTERFACE_PROTOCOL_MOUSE)
663 return 0;
664
665 kovaplus_keep_values_up_to_date(kovaplus, data);
666
667 if (kovaplus->roccat_claimed)
668 kovaplus_report_to_chrdev(kovaplus, data);
669
670 return 0;
671}
672
673static const struct hid_device_id kovaplus_devices[] = {
674 { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KOVAPLUS) },
675 { }
676};
677
678MODULE_DEVICE_TABLE(hid, kovaplus_devices);
679
680static struct hid_driver kovaplus_driver = {
681 .name = "kovaplus",
682 .id_table = kovaplus_devices,
683 .probe = kovaplus_probe,
684 .remove = kovaplus_remove,
685 .raw_event = kovaplus_raw_event
686};
687
688static int __init kovaplus_init(void)
689{
690 int retval;
691
692 kovaplus_class = class_create(THIS_MODULE, "kovaplus");
693 if (IS_ERR(kovaplus_class))
694 return PTR_ERR(kovaplus_class);
695 kovaplus_class->dev_attrs = kovaplus_attributes;
696 kovaplus_class->dev_bin_attrs = kovaplus_bin_attributes;
697
698 retval = hid_register_driver(&kovaplus_driver);
699 if (retval)
700 class_destroy(kovaplus_class);
701 return retval;
702}
703
704static void __exit kovaplus_exit(void)
705{
706 class_destroy(kovaplus_class);
707 hid_unregister_driver(&kovaplus_driver);
708}
709
710module_init(kovaplus_init);
711module_exit(kovaplus_exit);
712
713MODULE_AUTHOR("Stefan Achatz");
714MODULE_DESCRIPTION("USB Roccat Kova[+] driver");
715MODULE_LICENSE("GPL v2");
diff --git a/drivers/hid/hid-roccat-kovaplus.h b/drivers/hid/hid-roccat-kovaplus.h
new file mode 100644
index 000000000000..ce40607d21c7
--- /dev/null
+++ b/drivers/hid/hid-roccat-kovaplus.h
@@ -0,0 +1,157 @@
1#ifndef __HID_ROCCAT_KOVAPLUS_H
2#define __HID_ROCCAT_KOVAPLUS_H
3
4/*
5 * Copyright (c) 2010 Stefan Achatz <erazor_de@users.sourceforge.net>
6 */
7
8/*
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by the Free
11 * Software Foundation; either version 2 of the License, or (at your option)
12 * any later version.
13 */
14
15#include <linux/types.h>
16
17struct kovaplus_control {
18 uint8_t command; /* KOVAPLUS_COMMAND_CONTROL */
19 uint8_t value;
20 uint8_t request;
21} __packed;
22
23enum kovaplus_control_requests {
24 /* read after write; value = 1 */
25 KOVAPLUS_CONTROL_REQUEST_STATUS = 0x0,
26 /* write; value = profile number range 0-4 */
27 KOVAPLUS_CONTROL_REQUEST_PROFILE_SETTINGS = 0x10,
28 /* write; value = profile number range 0-4 */
29 KOVAPLUS_CONTROL_REQUEST_PROFILE_BUTTONS = 0x20,
30};
31
32enum kovaplus_control_values {
33 KOVAPLUS_CONTROL_REQUEST_STATUS_OVERLOAD = 0, /* supposed */
34 KOVAPLUS_CONTROL_REQUEST_STATUS_OK = 1,
35 KOVAPLUS_CONTROL_REQUEST_STATUS_WAIT = 3, /* supposed */
36};
37
38struct kovaplus_actual_profile {
39 uint8_t command; /* KOVAPLUS_COMMAND_ACTUAL_PROFILE */
40 uint8_t size; /* always 3 */
41 uint8_t actual_profile; /* Range 0-4! */
42} __packed;
43
44struct kovaplus_profile_settings {
45 uint8_t command; /* KOVAPLUS_COMMAND_PROFILE_SETTINGS */
46 uint8_t size; /* 16 */
47 uint8_t profile_index; /* range 0-4 */
48 uint8_t unknown1;
49 uint8_t sensitivity_x; /* range 1-10 */
50 uint8_t sensitivity_y; /* range 1-10 */
51 uint8_t cpi_levels_enabled;
52 uint8_t cpi_startup_level; /* range 1-4 */
53 uint8_t data[8];
54} __packed;
55
56struct kovaplus_profile_buttons {
57 uint8_t command; /* KOVAPLUS_COMMAND_PROFILE_BUTTONS */
58 uint8_t size; /* 23 */
59 uint8_t profile_index; /* range 0-4 */
60 uint8_t data[20];
61} __packed;
62
63struct kovaplus_info {
64 uint8_t command; /* KOVAPLUS_COMMAND_INFO */
65 uint8_t size; /* 6 */
66 uint8_t firmware_version;
67 uint8_t unknown[3];
68} __packed;
69
70/* writes 1 on plugin */
71struct kovaplus_a {
72 uint8_t command; /* KOVAPLUS_COMMAND_A */
73 uint8_t size; /* 3 */
74 uint8_t unknown;
75} __packed;
76
77enum kovaplus_commands {
78 KOVAPLUS_COMMAND_CONTROL = 0x4,
79 KOVAPLUS_COMMAND_ACTUAL_PROFILE = 0x5,
80 KOVAPLUS_COMMAND_PROFILE_SETTINGS = 0x6,
81 KOVAPLUS_COMMAND_PROFILE_BUTTONS = 0x7,
82 KOVAPLUS_COMMAND_INFO = 0x9,
83 KOVAPLUS_COMMAND_A = 0xa,
84};
85
86enum kovaplus_usb_commands {
87 KOVAPLUS_USB_COMMAND_CONTROL = 0x304,
88 KOVAPLUS_USB_COMMAND_ACTUAL_PROFILE = 0x305,
89 KOVAPLUS_USB_COMMAND_PROFILE_SETTINGS = 0x306,
90 KOVAPLUS_USB_COMMAND_PROFILE_BUTTONS = 0x307,
91 KOVAPLUS_USB_COMMAND_INFO = 0x309,
92 KOVAPLUS_USB_COMMAND_A = 0x30a,
93};
94
95enum kovaplus_mouse_report_numbers {
96 KOVAPLUS_MOUSE_REPORT_NUMBER_MOUSE = 1,
97 KOVAPLUS_MOUSE_REPORT_NUMBER_AUDIO = 2,
98 KOVAPLUS_MOUSE_REPORT_NUMBER_BUTTON = 3,
99 KOVAPLUS_MOUSE_REPORT_NUMBER_KBD = 4,
100};
101
102struct kovaplus_mouse_report_button {
103 uint8_t report_number; /* KOVAPLUS_MOUSE_REPORT_NUMBER_BUTTON */
104 uint8_t unknown1;
105 uint8_t type;
106 uint8_t data1;
107 uint8_t data2;
108} __packed;
109
110enum kovaplus_mouse_report_button_types {
111 /* data1 = profile_number range 1-5; no release event */
112 KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_PROFILE_1 = 0x20,
113 /* data1 = profile_number range 1-5; no release event */
114 KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_PROFILE_2 = 0x30,
115 /* data1 = button_number range 1-18; data2 = action */
116 KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_MACRO = 0x40,
117 /* data1 = button_number range 1-18; data2 = action */
118 KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_SHORTCUT = 0x50,
119 /* data1 = button_number range 1-18; data2 = action */
120 KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_QUICKLAUNCH = 0x60,
121 /* data1 = button_number range 1-18; data2 = action */
122 KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_TIMER = 0x80,
123 /* data1 = 1 = 400, 2 = 800, 4 = 1600, 7 = 3200; no release event */
124 KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_CPI = 0xb0,
125 /* data1 + data2 = sense range 1-10; no release event */
126 KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_SENSITIVITY = 0xc0,
127 /* data1 = type as in profile_buttons; data2 = action */
128 KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_MULTIMEDIA = 0xf0,
129};
130
131enum kovaplus_mouse_report_button_actions {
132 KOVAPLUS_MOUSE_REPORT_BUTTON_ACTION_PRESS = 0,
133 KOVAPLUS_MOUSE_REPORT_BUTTON_ACTION_RELEASE = 1,
134};
135
136struct kovaplus_roccat_report {
137 uint8_t type;
138 uint8_t profile;
139 uint8_t button;
140 uint8_t data1;
141 uint8_t data2;
142} __packed;
143
144struct kovaplus_device {
145 int actual_profile;
146 int actual_cpi;
147 int actual_x_sensitivity;
148 int actual_y_sensitivity;
149 int roccat_claimed;
150 int chrdev_minor;
151 struct mutex kovaplus_lock;
152 struct kovaplus_info info;
153 struct kovaplus_profile_settings profile_settings[5];
154 struct kovaplus_profile_buttons profile_buttons[5];
155};
156
157#endif