diff options
author | Stefan Achatz <erazor_de@users.sourceforge.net> | 2012-05-20 16:44:54 -0400 |
---|---|---|
committer | Jiri Kosina <jkosina@suse.cz> | 2012-06-28 04:34:01 -0400 |
commit | 6a2a6390cf098b899a30146ef5c1fb85c9fefb3c (patch) | |
tree | 1ffce47d4cd2967ab441c4b48d99e93e4817d278 /drivers/hid | |
parent | 60d2c25251cac20d8c142a03e230237528b68430 (diff) |
HID: roccat: add support for Roccat Savu
This patch adds rupport for Roccat Savu gaming mouse.
In comparison to the other Roccat modules I tried to move even more
functionality to userland.
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>
Diffstat (limited to 'drivers/hid')
-rw-r--r-- | drivers/hid/Makefile | 3 | ||||
-rw-r--r-- | drivers/hid/hid-core.c | 1 | ||||
-rw-r--r-- | drivers/hid/hid-ids.h | 1 | ||||
-rw-r--r-- | drivers/hid/hid-roccat-savu.c | 357 | ||||
-rw-r--r-- | drivers/hid/hid-roccat-savu.h | 103 |
5 files changed, 464 insertions, 1 deletions
diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index ca6cc9f0485..348b9040459 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile | |||
@@ -69,7 +69,8 @@ obj-$(CONFIG_HID_PICOLCD) += hid-picolcd.o | |||
69 | obj-$(CONFIG_HID_PRIMAX) += hid-primax.o | 69 | obj-$(CONFIG_HID_PRIMAX) += hid-primax.o |
70 | obj-$(CONFIG_HID_ROCCAT) += hid-roccat.o hid-roccat-common.o \ | 70 | obj-$(CONFIG_HID_ROCCAT) += hid-roccat.o hid-roccat-common.o \ |
71 | hid-roccat-arvo.o hid-roccat-isku.o hid-roccat-kone.o \ | 71 | hid-roccat-arvo.o hid-roccat-isku.o hid-roccat-kone.o \ |
72 | hid-roccat-koneplus.o hid-roccat-kovaplus.o hid-roccat-pyra.o | 72 | hid-roccat-koneplus.o hid-roccat-kovaplus.o hid-roccat-pyra.o \ |
73 | hid-roccat-savu.o | ||
73 | obj-$(CONFIG_HID_SAITEK) += hid-saitek.o | 74 | obj-$(CONFIG_HID_SAITEK) += hid-saitek.o |
74 | obj-$(CONFIG_HID_SAMSUNG) += hid-samsung.o | 75 | obj-$(CONFIG_HID_SAMSUNG) += hid-samsung.o |
75 | obj-$(CONFIG_HID_SMARTJOYPLUS) += hid-sjoy.o | 76 | obj-$(CONFIG_HID_SMARTJOYPLUS) += hid-sjoy.o |
diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 6ac0286b537..fd95df8d1e0 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c | |||
@@ -1617,6 +1617,7 @@ static const struct hid_device_id hid_have_special_driver[] = { | |||
1617 | { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KOVAPLUS) }, | 1617 | { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KOVAPLUS) }, |
1618 | { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_PYRA_WIRED) }, | 1618 | { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_PYRA_WIRED) }, |
1619 | { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS) }, | 1619 | { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS) }, |
1620 | { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_SAVU) }, | ||
1620 | { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_PS1000) }, | 1621 | { HID_USB_DEVICE(USB_VENDOR_ID_SAITEK, USB_DEVICE_ID_SAITEK_PS1000) }, |
1621 | { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) }, | 1622 | { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) }, |
1622 | { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_WIRELESS_KBD_MOUSE) }, | 1623 | { 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 d1cdd2d2840..ddc293d827f 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h | |||
@@ -644,6 +644,7 @@ | |||
644 | #define USB_DEVICE_ID_ROCCAT_KOVAPLUS 0x2d50 | 644 | #define USB_DEVICE_ID_ROCCAT_KOVAPLUS 0x2d50 |
645 | #define USB_DEVICE_ID_ROCCAT_PYRA_WIRED 0x2c24 | 645 | #define USB_DEVICE_ID_ROCCAT_PYRA_WIRED 0x2c24 |
646 | #define USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS 0x2cf6 | 646 | #define USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS 0x2cf6 |
647 | #define USB_DEVICE_ID_ROCCAT_SAVU 0x2d5a | ||
647 | 648 | ||
648 | #define USB_VENDOR_ID_SAITEK 0x06a3 | 649 | #define USB_VENDOR_ID_SAITEK 0x06a3 |
649 | #define USB_DEVICE_ID_SAITEK_RUMBLEPAD 0xff17 | 650 | #define USB_DEVICE_ID_SAITEK_RUMBLEPAD 0xff17 |
diff --git a/drivers/hid/hid-roccat-savu.c b/drivers/hid/hid-roccat-savu.c new file mode 100644 index 00000000000..d6c82d57408 --- /dev/null +++ b/drivers/hid/hid-roccat-savu.c | |||
@@ -0,0 +1,357 @@ | |||
1 | /* | ||
2 | * Roccat Savu driver for Linux | ||
3 | * | ||
4 | * Copyright (c) 2012 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 | /* Roccat Savu is a gamer mouse with macro keys that can be configured in | ||
15 | * 5 profiles. | ||
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-savu.h" | ||
27 | |||
28 | static struct class *savu_class; | ||
29 | |||
30 | static int savu_receive_control_status(struct usb_device *usb_dev) | ||
31 | { | ||
32 | int retval; | ||
33 | struct savu_control control; | ||
34 | |||
35 | do { | ||
36 | msleep(50); | ||
37 | retval = roccat_common_receive(usb_dev, SAVU_COMMAND_CONTROL, | ||
38 | &control, sizeof(struct savu_control)); | ||
39 | |||
40 | if (retval) | ||
41 | return retval; | ||
42 | |||
43 | switch (control.value) { | ||
44 | case SAVU_CONTROL_REQUEST_WRITE_CHECK_OK: | ||
45 | return 0; | ||
46 | case SAVU_CONTROL_REQUEST_WRITE_CHECK_WAIT: | ||
47 | continue; | ||
48 | case SAVU_CONTROL_REQUEST_WRITE_CHECK_INVALID: | ||
49 | /* seems to be critical - replug necessary */ | ||
50 | case SAVU_CONTROL_REQUEST_WRITE_CHECK_OVERLOAD: | ||
51 | return -EINVAL; | ||
52 | default: | ||
53 | hid_err(usb_dev, "savu_receive_control_status: " | ||
54 | "unknown response value 0x%x\n", | ||
55 | control.value); | ||
56 | return -EINVAL; | ||
57 | } | ||
58 | |||
59 | } while (1); | ||
60 | } | ||
61 | |||
62 | static int savu_send(struct usb_device *usb_dev, uint command, | ||
63 | void const *buf, uint size) | ||
64 | { | ||
65 | int retval; | ||
66 | |||
67 | retval = roccat_common_send(usb_dev, command, buf, size); | ||
68 | if (retval) | ||
69 | return retval; | ||
70 | |||
71 | return savu_receive_control_status(usb_dev); | ||
72 | } | ||
73 | |||
74 | static ssize_t savu_sysfs_read(struct file *fp, struct kobject *kobj, | ||
75 | char *buf, loff_t off, size_t count, | ||
76 | size_t real_size, uint command) | ||
77 | { | ||
78 | struct device *dev = | ||
79 | container_of(kobj, struct device, kobj)->parent->parent; | ||
80 | struct savu_device *savu = hid_get_drvdata(dev_get_drvdata(dev)); | ||
81 | struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); | ||
82 | int retval; | ||
83 | |||
84 | if (off >= real_size) | ||
85 | return 0; | ||
86 | |||
87 | if (off != 0 || count != real_size) | ||
88 | return -EINVAL; | ||
89 | |||
90 | mutex_lock(&savu->savu_lock); | ||
91 | retval = roccat_common_receive(usb_dev, command, buf, real_size); | ||
92 | mutex_unlock(&savu->savu_lock); | ||
93 | |||
94 | return retval ? retval : real_size; | ||
95 | } | ||
96 | |||
97 | static ssize_t savu_sysfs_write(struct file *fp, struct kobject *kobj, | ||
98 | void const *buf, loff_t off, size_t count, | ||
99 | size_t real_size, uint command) | ||
100 | { | ||
101 | struct device *dev = | ||
102 | container_of(kobj, struct device, kobj)->parent->parent; | ||
103 | struct savu_device *savu = hid_get_drvdata(dev_get_drvdata(dev)); | ||
104 | struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); | ||
105 | int retval; | ||
106 | |||
107 | if (off != 0 || count != real_size) | ||
108 | return -EINVAL; | ||
109 | |||
110 | mutex_lock(&savu->savu_lock); | ||
111 | retval = savu_send(usb_dev, command, (void *)buf, real_size); | ||
112 | mutex_unlock(&savu->savu_lock); | ||
113 | |||
114 | return retval ? retval : real_size; | ||
115 | } | ||
116 | |||
117 | #define SAVU_SYSFS_W(thingy, THINGY) \ | ||
118 | static ssize_t savu_sysfs_write_ ## thingy(struct file *fp, \ | ||
119 | struct kobject *kobj, struct bin_attribute *attr, char *buf, \ | ||
120 | loff_t off, size_t count) \ | ||
121 | { \ | ||
122 | return savu_sysfs_write(fp, kobj, buf, off, count, \ | ||
123 | SAVU_SIZE_ ## THINGY, SAVU_COMMAND_ ## THINGY); \ | ||
124 | } | ||
125 | |||
126 | #define SAVU_SYSFS_R(thingy, THINGY) \ | ||
127 | static ssize_t savu_sysfs_read_ ## thingy(struct file *fp, \ | ||
128 | struct kobject *kobj, struct bin_attribute *attr, char *buf, \ | ||
129 | loff_t off, size_t count) \ | ||
130 | { \ | ||
131 | return savu_sysfs_read(fp, kobj, buf, off, count, \ | ||
132 | SAVU_SIZE_ ## THINGY, SAVU_COMMAND_ ## THINGY); \ | ||
133 | } | ||
134 | |||
135 | #define SAVU_SYSFS_RW(thingy, THINGY) \ | ||
136 | SAVU_SYSFS_W(thingy, THINGY) \ | ||
137 | SAVU_SYSFS_R(thingy, THINGY) | ||
138 | |||
139 | #define SAVU_BIN_ATTRIBUTE_RW(thingy, THINGY) \ | ||
140 | { \ | ||
141 | .attr = { .name = #thingy, .mode = 0660 }, \ | ||
142 | .size = SAVU_SIZE_ ## THINGY, \ | ||
143 | .read = savu_sysfs_read_ ## thingy, \ | ||
144 | .write = savu_sysfs_write_ ## thingy \ | ||
145 | } | ||
146 | |||
147 | #define SAVU_BIN_ATTRIBUTE_R(thingy, THINGY) \ | ||
148 | { \ | ||
149 | .attr = { .name = #thingy, .mode = 0440 }, \ | ||
150 | .size = SAVU_SIZE_ ## THINGY, \ | ||
151 | .read = savu_sysfs_read_ ## thingy, \ | ||
152 | } | ||
153 | |||
154 | #define SAVU_BIN_ATTRIBUTE_W(thingy, THINGY) \ | ||
155 | { \ | ||
156 | .attr = { .name = #thingy, .mode = 0220 }, \ | ||
157 | .size = SAVU_SIZE_ ## THINGY, \ | ||
158 | .write = savu_sysfs_write_ ## thingy \ | ||
159 | } | ||
160 | |||
161 | SAVU_SYSFS_W(control, CONTROL) | ||
162 | SAVU_SYSFS_RW(profile, PROFILE) | ||
163 | SAVU_SYSFS_RW(general, GENERAL) | ||
164 | SAVU_SYSFS_RW(buttons, BUTTONS) | ||
165 | SAVU_SYSFS_RW(macro, MACRO) | ||
166 | SAVU_SYSFS_R(info, INFO) | ||
167 | |||
168 | static struct bin_attribute savu_bin_attributes[] = { | ||
169 | SAVU_BIN_ATTRIBUTE_W(control, CONTROL), | ||
170 | SAVU_BIN_ATTRIBUTE_RW(profile, PROFILE), | ||
171 | SAVU_BIN_ATTRIBUTE_RW(general, GENERAL), | ||
172 | SAVU_BIN_ATTRIBUTE_RW(buttons, BUTTONS), | ||
173 | SAVU_BIN_ATTRIBUTE_RW(macro, MACRO), | ||
174 | SAVU_BIN_ATTRIBUTE_R(info, INFO), | ||
175 | __ATTR_NULL | ||
176 | }; | ||
177 | |||
178 | static int savu_init_savu_device_struct(struct usb_device *usb_dev, | ||
179 | struct savu_device *savu) | ||
180 | { | ||
181 | mutex_init(&savu->savu_lock); | ||
182 | |||
183 | return 0; | ||
184 | } | ||
185 | |||
186 | static int savu_init_specials(struct hid_device *hdev) | ||
187 | { | ||
188 | struct usb_interface *intf = to_usb_interface(hdev->dev.parent); | ||
189 | struct usb_device *usb_dev = interface_to_usbdev(intf); | ||
190 | struct savu_device *savu; | ||
191 | int retval; | ||
192 | |||
193 | if (intf->cur_altsetting->desc.bInterfaceProtocol | ||
194 | != USB_INTERFACE_PROTOCOL_MOUSE) { | ||
195 | hid_set_drvdata(hdev, NULL); | ||
196 | return 0; | ||
197 | } | ||
198 | |||
199 | savu = kzalloc(sizeof(*savu), GFP_KERNEL); | ||
200 | if (!savu) { | ||
201 | hid_err(hdev, "can't alloc device descriptor\n"); | ||
202 | return -ENOMEM; | ||
203 | } | ||
204 | hid_set_drvdata(hdev, savu); | ||
205 | |||
206 | retval = savu_init_savu_device_struct(usb_dev, savu); | ||
207 | if (retval) { | ||
208 | hid_err(hdev, "couldn't init struct savu_device\n"); | ||
209 | goto exit_free; | ||
210 | } | ||
211 | |||
212 | retval = roccat_connect(savu_class, hdev, | ||
213 | sizeof(struct savu_roccat_report)); | ||
214 | if (retval < 0) { | ||
215 | hid_err(hdev, "couldn't init char dev\n"); | ||
216 | } else { | ||
217 | savu->chrdev_minor = retval; | ||
218 | savu->roccat_claimed = 1; | ||
219 | } | ||
220 | |||
221 | return 0; | ||
222 | exit_free: | ||
223 | kfree(savu); | ||
224 | return retval; | ||
225 | } | ||
226 | |||
227 | static void savu_remove_specials(struct hid_device *hdev) | ||
228 | { | ||
229 | struct usb_interface *intf = to_usb_interface(hdev->dev.parent); | ||
230 | struct savu_device *savu; | ||
231 | |||
232 | if (intf->cur_altsetting->desc.bInterfaceProtocol | ||
233 | != USB_INTERFACE_PROTOCOL_MOUSE) | ||
234 | return; | ||
235 | |||
236 | savu = hid_get_drvdata(hdev); | ||
237 | if (savu->roccat_claimed) | ||
238 | roccat_disconnect(savu->chrdev_minor); | ||
239 | kfree(savu); | ||
240 | } | ||
241 | |||
242 | static int savu_probe(struct hid_device *hdev, | ||
243 | const struct hid_device_id *id) | ||
244 | { | ||
245 | int retval; | ||
246 | |||
247 | retval = hid_parse(hdev); | ||
248 | if (retval) { | ||
249 | hid_err(hdev, "parse failed\n"); | ||
250 | goto exit; | ||
251 | } | ||
252 | |||
253 | retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT); | ||
254 | if (retval) { | ||
255 | hid_err(hdev, "hw start failed\n"); | ||
256 | goto exit; | ||
257 | } | ||
258 | |||
259 | retval = savu_init_specials(hdev); | ||
260 | if (retval) { | ||
261 | hid_err(hdev, "couldn't install mouse\n"); | ||
262 | goto exit_stop; | ||
263 | } | ||
264 | |||
265 | return 0; | ||
266 | |||
267 | exit_stop: | ||
268 | hid_hw_stop(hdev); | ||
269 | exit: | ||
270 | return retval; | ||
271 | } | ||
272 | |||
273 | static void savu_remove(struct hid_device *hdev) | ||
274 | { | ||
275 | savu_remove_specials(hdev); | ||
276 | hid_hw_stop(hdev); | ||
277 | } | ||
278 | |||
279 | static void savu_report_to_chrdev(struct savu_device const *savu, | ||
280 | u8 const *data) | ||
281 | { | ||
282 | struct savu_roccat_report roccat_report; | ||
283 | struct savu_mouse_report_special const *special_report; | ||
284 | |||
285 | if (data[0] != SAVU_MOUSE_REPORT_NUMBER_SPECIAL) | ||
286 | return; | ||
287 | |||
288 | special_report = (struct savu_mouse_report_special const *)data; | ||
289 | |||
290 | roccat_report.type = special_report->type; | ||
291 | roccat_report.data[0] = special_report->data[0]; | ||
292 | roccat_report.data[1] = special_report->data[1]; | ||
293 | roccat_report_event(savu->chrdev_minor, | ||
294 | (uint8_t const *)&roccat_report); | ||
295 | } | ||
296 | |||
297 | static int savu_raw_event(struct hid_device *hdev, | ||
298 | struct hid_report *report, u8 *data, int size) | ||
299 | { | ||
300 | struct usb_interface *intf = to_usb_interface(hdev->dev.parent); | ||
301 | struct savu_device *savu = hid_get_drvdata(hdev); | ||
302 | |||
303 | if (intf->cur_altsetting->desc.bInterfaceProtocol | ||
304 | != USB_INTERFACE_PROTOCOL_MOUSE) | ||
305 | return 0; | ||
306 | |||
307 | if (savu == NULL) | ||
308 | return 0; | ||
309 | |||
310 | if (savu->roccat_claimed) | ||
311 | savu_report_to_chrdev(savu, data); | ||
312 | |||
313 | return 0; | ||
314 | } | ||
315 | |||
316 | static const struct hid_device_id savu_devices[] = { | ||
317 | { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_SAVU) }, | ||
318 | { } | ||
319 | }; | ||
320 | |||
321 | MODULE_DEVICE_TABLE(hid, savu_devices); | ||
322 | |||
323 | static struct hid_driver savu_driver = { | ||
324 | .name = "savu", | ||
325 | .id_table = savu_devices, | ||
326 | .probe = savu_probe, | ||
327 | .remove = savu_remove, | ||
328 | .raw_event = savu_raw_event | ||
329 | }; | ||
330 | |||
331 | static int __init savu_init(void) | ||
332 | { | ||
333 | int retval; | ||
334 | |||
335 | savu_class = class_create(THIS_MODULE, "savu"); | ||
336 | if (IS_ERR(savu_class)) | ||
337 | return PTR_ERR(savu_class); | ||
338 | savu_class->dev_bin_attrs = savu_bin_attributes; | ||
339 | |||
340 | retval = hid_register_driver(&savu_driver); | ||
341 | if (retval) | ||
342 | class_destroy(savu_class); | ||
343 | return retval; | ||
344 | } | ||
345 | |||
346 | static void __exit savu_exit(void) | ||
347 | { | ||
348 | hid_unregister_driver(&savu_driver); | ||
349 | class_destroy(savu_class); | ||
350 | } | ||
351 | |||
352 | module_init(savu_init); | ||
353 | module_exit(savu_exit); | ||
354 | |||
355 | MODULE_AUTHOR("Stefan Achatz"); | ||
356 | MODULE_DESCRIPTION("USB Roccat Savu driver"); | ||
357 | MODULE_LICENSE("GPL v2"); | ||
diff --git a/drivers/hid/hid-roccat-savu.h b/drivers/hid/hid-roccat-savu.h new file mode 100644 index 00000000000..97b43d5b047 --- /dev/null +++ b/drivers/hid/hid-roccat-savu.h | |||
@@ -0,0 +1,103 @@ | |||
1 | #ifndef __HID_ROCCAT_SAVU_H | ||
2 | #define __HID_ROCCAT_SAVU_H | ||
3 | |||
4 | /* | ||
5 | * Copyright (c) 2012 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 | |||
17 | enum { | ||
18 | SAVU_SIZE_CONTROL = 0x03, | ||
19 | SAVU_SIZE_PROFILE = 0x03, | ||
20 | SAVU_SIZE_GENERAL = 0x10, | ||
21 | SAVU_SIZE_BUTTONS = 0x2f, | ||
22 | SAVU_SIZE_MACRO = 0x0823, | ||
23 | SAVU_SIZE_INFO = 0x08, | ||
24 | }; | ||
25 | |||
26 | struct savu_control { | ||
27 | uint8_t command; /* SAVU_COMMAND_CONTROL */ | ||
28 | /* | ||
29 | * value is profile number in range 0-4 for requesting settings and buttons | ||
30 | * 1 if status ok for requesting status | ||
31 | */ | ||
32 | uint8_t value; | ||
33 | uint8_t request; | ||
34 | } __packed; | ||
35 | |||
36 | enum savu_control_requests { | ||
37 | SAVU_CONTROL_REQUEST_WRITE_CHECK = 0x00, | ||
38 | SAVU_CONTROL_REQUEST_GENERAL = 0x80, | ||
39 | SAVU_CONTROL_REQUEST_BUTTONS = 0x90, | ||
40 | }; | ||
41 | |||
42 | enum savu_control_values { | ||
43 | SAVU_CONTROL_REQUEST_WRITE_CHECK_OVERLOAD = 0, | ||
44 | SAVU_CONTROL_REQUEST_WRITE_CHECK_OK = 1, | ||
45 | SAVU_CONTROL_REQUEST_WRITE_CHECK_INVALID = 2, | ||
46 | SAVU_CONTROL_REQUEST_WRITE_CHECK_WAIT = 3, | ||
47 | }; | ||
48 | |||
49 | enum savu_commands { | ||
50 | SAVU_COMMAND_CONTROL = 0x4, | ||
51 | SAVU_COMMAND_PROFILE = 0x5, | ||
52 | SAVU_COMMAND_GENERAL = 0x6, | ||
53 | SAVU_COMMAND_BUTTONS = 0x7, | ||
54 | SAVU_COMMAND_MACRO = 0x8, | ||
55 | SAVU_COMMAND_INFO = 0x9, | ||
56 | }; | ||
57 | |||
58 | struct savu_mouse_report_special { | ||
59 | uint8_t report_number; /* always 3 */ | ||
60 | uint8_t zero; | ||
61 | uint8_t type; | ||
62 | uint8_t data[2]; | ||
63 | } __packed; | ||
64 | |||
65 | enum { | ||
66 | SAVU_MOUSE_REPORT_NUMBER_SPECIAL = 3, | ||
67 | }; | ||
68 | |||
69 | enum savu_mouse_report_button_types { | ||
70 | /* data1 = new profile range 1-5 */ | ||
71 | SAVU_MOUSE_REPORT_BUTTON_TYPE_PROFILE = 0x20, | ||
72 | |||
73 | /* data1 = button number range 1-24; data2 = action */ | ||
74 | SAVU_MOUSE_REPORT_BUTTON_TYPE_QUICKLAUNCH = 0x60, | ||
75 | |||
76 | /* data1 = button number range 1-24; data2 = action */ | ||
77 | SAVU_MOUSE_REPORT_BUTTON_TYPE_TIMER = 0x80, | ||
78 | |||
79 | /* data1 = setting number range 1-5 */ | ||
80 | SAVU_MOUSE_REPORT_BUTTON_TYPE_CPI = 0xb0, | ||
81 | |||
82 | /* data1 and data2 = range 0x1-0xb */ | ||
83 | SAVU_MOUSE_REPORT_BUTTON_TYPE_SENSITIVITY = 0xc0, | ||
84 | |||
85 | /* data1 = 22 = next track... | ||
86 | * data2 = action | ||
87 | */ | ||
88 | SAVU_MOUSE_REPORT_BUTTON_TYPE_MULTIMEDIA = 0xf0, | ||
89 | }; | ||
90 | |||
91 | struct savu_roccat_report { | ||
92 | uint8_t type; | ||
93 | uint8_t data[2]; | ||
94 | } __packed; | ||
95 | |||
96 | struct savu_device { | ||
97 | int roccat_claimed; | ||
98 | int chrdev_minor; | ||
99 | |||
100 | struct mutex savu_lock; | ||
101 | }; | ||
102 | |||
103 | #endif | ||