Commit e68cc603b063416c85f3e408184219fb71d4a9ff
Committed by
Jiri Kosina
1 parent
dacfecdbf3
Exists in
master
and in
39 other branches
HID: roccat: Add support for Roccat Arvo keyboard
This patch add support for Roccat Arvo keyboard. Arvo has 5 additional configurable buttons and the ability to deactivate certain keys. 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>
Showing 7 changed files with 672 additions and 0 deletions Side-by-side Diff
Documentation/ABI/testing/sysfs-driver-hid-roccat-arvo
1 | +What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/arvo/roccatarvo<minor>/actual_profile | |
2 | +Date: Januar 2011 | |
3 | +Contact: Stefan Achatz <erazor_de@users.sourceforge.net> | |
4 | +Description: The integer value of this attribute ranges from 1-5. | |
5 | + When read, this attribute returns the number of the actual | |
6 | + profile which is also the profile that's active on device startup. | |
7 | + When written this attribute activates the selected profile | |
8 | + immediately. | |
9 | + | |
10 | +What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/arvo/roccatarvo<minor>/button | |
11 | +Date: Januar 2011 | |
12 | +Contact: Stefan Achatz <erazor_de@users.sourceforge.net> | |
13 | +Description: The keyboard can store short macros with consist of 1 button with | |
14 | + several modifier keys internally. | |
15 | + When written, this file lets one set the sequence for a specific | |
16 | + button for a specific profile. Button and profile numbers are | |
17 | + included in written data. The data has to be 24 bytes long. | |
18 | + This file is writeonly. | |
19 | + | |
20 | +What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/arvo/roccatarvo<minor>/info | |
21 | +Date: Januar 2011 | |
22 | +Contact: Stefan Achatz <erazor_de@users.sourceforge.net> | |
23 | +Description: When read, this file returns some info about the device like the | |
24 | + installed firmware version. | |
25 | + The size of the data is 8 bytes in size. | |
26 | + This file is readonly. | |
27 | + | |
28 | +What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/arvo/roccatarvo<minor>/key_mask | |
29 | +Date: Januar 2011 | |
30 | +Contact: Stefan Achatz <erazor_de@users.sourceforge.net> | |
31 | +Description: The keyboard lets the user deactivate 5 certain keys like the | |
32 | + windows and application keys, to protect the user from the outcome | |
33 | + of accidentally pressing them. | |
34 | + The integer value of this attribute has bits 0-4 set depending | |
35 | + on the state of the corresponding key. | |
36 | + When read, this file returns the current state of the buttons. | |
37 | + When written, the given buttons are activated/deactivated | |
38 | + immediately. | |
39 | + | |
40 | +What: /sys/bus/usb/devices/<busnum>-<devnum>:<config num>.<interface num>/<hid-bus>:<vendor-id>:<product-id>.<num>/arvo/roccatarvo<minor>/mode_key | |
41 | +Date: Januar 2011 | |
42 | +Contact: Stefan Achatz <erazor_de@users.sourceforge.net> | |
43 | +Description: The keyboard has a condensed layout without num-lock key. | |
44 | + Instead it uses a mode-key which activates a gaming mode where | |
45 | + the assignment of the number block changes. | |
46 | + The integer value of this attribute ranges from 0 (OFF) to 1 (ON). | |
47 | + When read, this file returns the actual state of the key. | |
48 | + When written, the key is activated/deactivated immediately. |
drivers/hid/Kconfig
... | ... | @@ -417,6 +417,13 @@ |
417 | 417 | Say Y here if you have a Roccat mouse or keyboard and want OSD or |
418 | 418 | macro execution support. |
419 | 419 | |
420 | +config HID_ROCCAT_ARVO | |
421 | + tristate "Roccat Arvo keyboard support" | |
422 | + depends on USB_HID | |
423 | + select HID_ROCCAT | |
424 | + ---help--- | |
425 | + Support for Roccat Arvo keyboard. | |
426 | + | |
420 | 427 | config HID_ROCCAT_KONE |
421 | 428 | tristate "Roccat Kone Mouse support" |
422 | 429 | depends on USB_HID |
drivers/hid/Makefile
... | ... | @@ -56,6 +56,7 @@ |
56 | 56 | obj-$(CONFIG_HID_PETALYNX) += hid-petalynx.o |
57 | 57 | obj-$(CONFIG_HID_PICOLCD) += hid-picolcd.o |
58 | 58 | obj-$(CONFIG_HID_ROCCAT) += hid-roccat.o |
59 | +obj-$(CONFIG_HID_ROCCAT_ARVO) += hid-roccat-arvo.o | |
59 | 60 | obj-$(CONFIG_HID_ROCCAT_KONE) += hid-roccat-kone.o |
60 | 61 | obj-$(CONFIG_HID_ROCCAT_KONEPLUS) += hid-roccat-koneplus.o |
61 | 62 | obj-$(CONFIG_HID_ROCCAT_PYRA) += hid-roccat-pyra.o |
drivers/hid/hid-core.c
... | ... | @@ -1405,6 +1405,7 @@ |
1405 | 1405 | { HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_QUANTA_OPTICAL_TOUCH) }, |
1406 | 1406 | { HID_USB_DEVICE(USB_VENDOR_ID_QUANTA, USB_DEVICE_ID_PIXART_IMAGING_INC_OPTICAL_TOUCH_SCREEN) }, |
1407 | 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 | 1409 | { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KONEPLUS) }, |
1409 | 1410 | { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_PYRA_WIRED) }, |
1410 | 1411 | { HID_USB_DEVICE(USB_VENDOR_ID_SAMSUNG, USB_DEVICE_ID_SAMSUNG_IR_REMOTE) }, |
drivers/hid/hid-ids.h
... | ... | @@ -496,6 +496,7 @@ |
496 | 496 | #define USB_DEVICE_ID_PIXART_IMAGING_INC_OPTICAL_TOUCH_SCREEN 0x3001 |
497 | 497 | |
498 | 498 | #define USB_VENDOR_ID_ROCCAT 0x1e7d |
499 | +#define USB_DEVICE_ID_ROCCAT_ARVO 0x30d4 | |
499 | 500 | #define USB_DEVICE_ID_ROCCAT_KONE 0x2ced |
500 | 501 | #define USB_DEVICE_ID_ROCCAT_KONEPLUS 0x2d51 |
501 | 502 | #define USB_DEVICE_ID_ROCCAT_PYRA_WIRED 0x2c24 |
drivers/hid/hid-roccat-arvo.c
1 | +/* | |
2 | + * Roccat Arvo 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 Arvo is a gamer keyboard with 5 macro keys that can be configured in | |
16 | + * 5 profiles. | |
17 | + */ | |
18 | + | |
19 | +#include <linux/device.h> | |
20 | +#include <linux/input.h> | |
21 | +#include <linux/hid.h> | |
22 | +#include <linux/usb.h> | |
23 | +#include <linux/module.h> | |
24 | +#include <linux/slab.h> | |
25 | +#include "hid-ids.h" | |
26 | +#include "hid-roccat.h" | |
27 | +#include "hid-roccat-arvo.h" | |
28 | + | |
29 | +static struct class *arvo_class; | |
30 | + | |
31 | +static int arvo_receive(struct usb_device *usb_dev, uint usb_command, | |
32 | + void *buf, uint size) | |
33 | +{ | |
34 | + int len; | |
35 | + | |
36 | + len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0), | |
37 | + USB_REQ_CLEAR_FEATURE, | |
38 | + USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN, | |
39 | + usb_command, 0, buf, size, USB_CTRL_SET_TIMEOUT); | |
40 | + | |
41 | + return (len != size) ? -EIO : 0; | |
42 | +} | |
43 | + | |
44 | +static int arvo_send(struct usb_device *usb_dev, uint usb_command, | |
45 | + void const *buf, uint size) | |
46 | +{ | |
47 | + int len; | |
48 | + | |
49 | + len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0), | |
50 | + USB_REQ_SET_CONFIGURATION, | |
51 | + USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT, | |
52 | + usb_command, 0, (void *)buf, size, USB_CTRL_SET_TIMEOUT); | |
53 | + | |
54 | + return (len != size) ? -EIO : 0; | |
55 | +} | |
56 | + | |
57 | +static ssize_t arvo_sysfs_show_mode_key(struct device *dev, | |
58 | + struct device_attribute *attr, char *buf) | |
59 | +{ | |
60 | + struct arvo_device *arvo = | |
61 | + hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); | |
62 | + struct usb_device *usb_dev = | |
63 | + interface_to_usbdev(to_usb_interface(dev->parent->parent)); | |
64 | + struct arvo_mode_key *temp_buf; | |
65 | + int retval; | |
66 | + | |
67 | + temp_buf = kmalloc(sizeof(struct arvo_mode_key), GFP_KERNEL); | |
68 | + if (!temp_buf) | |
69 | + return -ENOMEM; | |
70 | + | |
71 | + mutex_lock(&arvo->arvo_lock); | |
72 | + retval = arvo_receive(usb_dev, ARVO_USB_COMMAND_MODE_KEY, | |
73 | + temp_buf, sizeof(struct arvo_mode_key)); | |
74 | + mutex_unlock(&arvo->arvo_lock); | |
75 | + if (retval) | |
76 | + goto out; | |
77 | + | |
78 | + retval = snprintf(buf, PAGE_SIZE, "%d\n", temp_buf->state); | |
79 | +out: | |
80 | + kfree(temp_buf); | |
81 | + return retval; | |
82 | +} | |
83 | + | |
84 | +static ssize_t arvo_sysfs_set_mode_key(struct device *dev, | |
85 | + struct device_attribute *attr, char const *buf, size_t size) | |
86 | +{ | |
87 | + struct arvo_device *arvo = | |
88 | + hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); | |
89 | + struct usb_device *usb_dev = | |
90 | + interface_to_usbdev(to_usb_interface(dev->parent->parent)); | |
91 | + struct arvo_mode_key *temp_buf; | |
92 | + unsigned long state; | |
93 | + int retval; | |
94 | + | |
95 | + temp_buf = kmalloc(sizeof(struct arvo_mode_key), GFP_KERNEL); | |
96 | + if (!temp_buf) | |
97 | + return -ENOMEM; | |
98 | + | |
99 | + retval = strict_strtoul(buf, 10, &state); | |
100 | + if (retval) | |
101 | + goto out; | |
102 | + | |
103 | + temp_buf->command = ARVO_COMMAND_MODE_KEY; | |
104 | + temp_buf->state = state; | |
105 | + | |
106 | + mutex_lock(&arvo->arvo_lock); | |
107 | + retval = arvo_send(usb_dev, ARVO_USB_COMMAND_MODE_KEY, | |
108 | + temp_buf, sizeof(struct arvo_mode_key)); | |
109 | + mutex_unlock(&arvo->arvo_lock); | |
110 | + if (retval) | |
111 | + goto out; | |
112 | + | |
113 | + retval = size; | |
114 | +out: | |
115 | + kfree(temp_buf); | |
116 | + return retval; | |
117 | +} | |
118 | + | |
119 | +static ssize_t arvo_sysfs_show_key_mask(struct device *dev, | |
120 | + struct device_attribute *attr, char *buf) | |
121 | +{ | |
122 | + struct arvo_device *arvo = | |
123 | + hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); | |
124 | + struct usb_device *usb_dev = | |
125 | + interface_to_usbdev(to_usb_interface(dev->parent->parent)); | |
126 | + struct arvo_key_mask *temp_buf; | |
127 | + int retval; | |
128 | + | |
129 | + temp_buf = kmalloc(sizeof(struct arvo_key_mask), GFP_KERNEL); | |
130 | + if (!temp_buf) | |
131 | + return -ENOMEM; | |
132 | + | |
133 | + mutex_lock(&arvo->arvo_lock); | |
134 | + retval = arvo_receive(usb_dev, ARVO_USB_COMMAND_KEY_MASK, | |
135 | + temp_buf, sizeof(struct arvo_key_mask)); | |
136 | + mutex_unlock(&arvo->arvo_lock); | |
137 | + if (retval) | |
138 | + goto out; | |
139 | + | |
140 | + retval = snprintf(buf, PAGE_SIZE, "%d\n", temp_buf->key_mask); | |
141 | +out: | |
142 | + kfree(temp_buf); | |
143 | + return retval; | |
144 | +} | |
145 | + | |
146 | +static ssize_t arvo_sysfs_set_key_mask(struct device *dev, | |
147 | + struct device_attribute *attr, char const *buf, size_t size) | |
148 | +{ | |
149 | + struct arvo_device *arvo = | |
150 | + hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); | |
151 | + struct usb_device *usb_dev = | |
152 | + interface_to_usbdev(to_usb_interface(dev->parent->parent)); | |
153 | + struct arvo_key_mask *temp_buf; | |
154 | + unsigned long key_mask; | |
155 | + int retval; | |
156 | + | |
157 | + temp_buf = kmalloc(sizeof(struct arvo_key_mask), GFP_KERNEL); | |
158 | + if (!temp_buf) | |
159 | + return -ENOMEM; | |
160 | + | |
161 | + retval = strict_strtoul(buf, 10, &key_mask); | |
162 | + if (retval) | |
163 | + goto out; | |
164 | + | |
165 | + temp_buf->command = ARVO_COMMAND_KEY_MASK; | |
166 | + temp_buf->key_mask = key_mask; | |
167 | + | |
168 | + mutex_lock(&arvo->arvo_lock); | |
169 | + retval = arvo_send(usb_dev, ARVO_USB_COMMAND_KEY_MASK, | |
170 | + temp_buf, sizeof(struct arvo_key_mask)); | |
171 | + mutex_unlock(&arvo->arvo_lock); | |
172 | + if (retval) | |
173 | + goto out; | |
174 | + | |
175 | + retval = size; | |
176 | +out: | |
177 | + kfree(temp_buf); | |
178 | + return retval; | |
179 | +} | |
180 | + | |
181 | +/* retval is 1-5 on success, < 0 on error */ | |
182 | +static int arvo_get_actual_profile(struct usb_device *usb_dev) | |
183 | +{ | |
184 | + struct arvo_actual_profile *temp_buf; | |
185 | + int retval; | |
186 | + | |
187 | + temp_buf = kmalloc(sizeof(struct arvo_actual_profile), GFP_KERNEL); | |
188 | + if (!temp_buf) | |
189 | + return -ENOMEM; | |
190 | + | |
191 | + retval = arvo_receive(usb_dev, ARVO_USB_COMMAND_ACTUAL_PROFILE, | |
192 | + temp_buf, sizeof(struct arvo_actual_profile)); | |
193 | + | |
194 | + if (!retval) | |
195 | + retval = temp_buf->actual_profile; | |
196 | + | |
197 | + kfree(temp_buf); | |
198 | + return retval; | |
199 | +} | |
200 | + | |
201 | +static ssize_t arvo_sysfs_show_actual_profile(struct device *dev, | |
202 | + struct device_attribute *attr, char *buf) | |
203 | +{ | |
204 | + struct arvo_device *arvo = | |
205 | + hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); | |
206 | + | |
207 | + return snprintf(buf, PAGE_SIZE, "%d\n", arvo->actual_profile); | |
208 | +} | |
209 | + | |
210 | +static ssize_t arvo_sysfs_set_actual_profile(struct device *dev, | |
211 | + struct device_attribute *attr, char const *buf, size_t size) | |
212 | +{ | |
213 | + struct arvo_device *arvo = | |
214 | + hid_get_drvdata(dev_get_drvdata(dev->parent->parent)); | |
215 | + struct usb_device *usb_dev = | |
216 | + interface_to_usbdev(to_usb_interface(dev->parent->parent)); | |
217 | + struct arvo_actual_profile *temp_buf; | |
218 | + unsigned long profile; | |
219 | + int retval; | |
220 | + | |
221 | + temp_buf = kmalloc(sizeof(struct arvo_actual_profile), GFP_KERNEL); | |
222 | + if (!temp_buf) | |
223 | + return -ENOMEM; | |
224 | + | |
225 | + retval = strict_strtoul(buf, 10, &profile); | |
226 | + if (retval) | |
227 | + goto out; | |
228 | + | |
229 | + temp_buf->command = ARVO_COMMAND_ACTUAL_PROFILE; | |
230 | + temp_buf->actual_profile = profile; | |
231 | + | |
232 | + mutex_lock(&arvo->arvo_lock); | |
233 | + retval = arvo_send(usb_dev, ARVO_USB_COMMAND_ACTUAL_PROFILE, | |
234 | + temp_buf, sizeof(struct arvo_actual_profile)); | |
235 | + if (!retval) { | |
236 | + arvo->actual_profile = profile; | |
237 | + retval = size; | |
238 | + } | |
239 | + mutex_unlock(&arvo->arvo_lock); | |
240 | + | |
241 | +out: | |
242 | + kfree(temp_buf); | |
243 | + return retval; | |
244 | +} | |
245 | + | |
246 | +static ssize_t arvo_sysfs_write(struct file *fp, | |
247 | + struct kobject *kobj, void const *buf, | |
248 | + loff_t off, size_t count, size_t real_size, uint command) | |
249 | +{ | |
250 | + struct device *dev = | |
251 | + container_of(kobj, struct device, kobj)->parent->parent; | |
252 | + struct arvo_device *arvo = hid_get_drvdata(dev_get_drvdata(dev)); | |
253 | + struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); | |
254 | + int retval; | |
255 | + | |
256 | + if (off != 0 || count != real_size) | |
257 | + return -EINVAL; | |
258 | + | |
259 | + mutex_lock(&arvo->arvo_lock); | |
260 | + retval = arvo_send(usb_dev, command, buf, real_size); | |
261 | + mutex_unlock(&arvo->arvo_lock); | |
262 | + | |
263 | + return (retval ? retval : real_size); | |
264 | +} | |
265 | + | |
266 | +static ssize_t arvo_sysfs_read(struct file *fp, | |
267 | + struct kobject *kobj, void *buf, loff_t off, | |
268 | + size_t count, size_t real_size, uint command) | |
269 | +{ | |
270 | + struct device *dev = | |
271 | + container_of(kobj, struct device, kobj)->parent->parent; | |
272 | + struct arvo_device *arvo = hid_get_drvdata(dev_get_drvdata(dev)); | |
273 | + struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); | |
274 | + int retval; | |
275 | + | |
276 | + if (off >= real_size) | |
277 | + return 0; | |
278 | + | |
279 | + if (off != 0 || count != real_size) | |
280 | + return -EINVAL; | |
281 | + | |
282 | + mutex_lock(&arvo->arvo_lock); | |
283 | + retval = arvo_receive(usb_dev, command, buf, real_size); | |
284 | + mutex_unlock(&arvo->arvo_lock); | |
285 | + | |
286 | + return (retval ? retval : real_size); | |
287 | +} | |
288 | + | |
289 | +static ssize_t arvo_sysfs_write_button(struct file *fp, | |
290 | + struct kobject *kobj, struct bin_attribute *attr, char *buf, | |
291 | + loff_t off, size_t count) | |
292 | +{ | |
293 | + return arvo_sysfs_write(fp, kobj, buf, off, count, | |
294 | + sizeof(struct arvo_button), ARVO_USB_COMMAND_BUTTON); | |
295 | +} | |
296 | + | |
297 | +static ssize_t arvo_sysfs_read_info(struct file *fp, | |
298 | + struct kobject *kobj, struct bin_attribute *attr, char *buf, | |
299 | + loff_t off, size_t count) | |
300 | +{ | |
301 | + return arvo_sysfs_read(fp, kobj, buf, off, count, | |
302 | + sizeof(struct arvo_info), ARVO_USB_COMMAND_INFO); | |
303 | +} | |
304 | + | |
305 | + | |
306 | +static struct device_attribute arvo_attributes[] = { | |
307 | + __ATTR(mode_key, 0660, | |
308 | + arvo_sysfs_show_mode_key, arvo_sysfs_set_mode_key), | |
309 | + __ATTR(key_mask, 0660, | |
310 | + arvo_sysfs_show_key_mask, arvo_sysfs_set_key_mask), | |
311 | + __ATTR(actual_profile, 0660, | |
312 | + arvo_sysfs_show_actual_profile, | |
313 | + arvo_sysfs_set_actual_profile), | |
314 | + __ATTR_NULL | |
315 | +}; | |
316 | + | |
317 | +static struct bin_attribute arvo_bin_attributes[] = { | |
318 | + { | |
319 | + .attr = { .name = "button", .mode = 0220 }, | |
320 | + .size = sizeof(struct arvo_button), | |
321 | + .write = arvo_sysfs_write_button | |
322 | + }, | |
323 | + { | |
324 | + .attr = { .name = "info", .mode = 0440 }, | |
325 | + .size = sizeof(struct arvo_info), | |
326 | + .read = arvo_sysfs_read_info | |
327 | + }, | |
328 | + __ATTR_NULL | |
329 | +}; | |
330 | + | |
331 | +static int arvo_init_arvo_device_struct(struct usb_device *usb_dev, | |
332 | + struct arvo_device *arvo) | |
333 | +{ | |
334 | + int retval; | |
335 | + | |
336 | + mutex_init(&arvo->arvo_lock); | |
337 | + | |
338 | + retval = arvo_get_actual_profile(usb_dev); | |
339 | + if (retval < 0) | |
340 | + return retval; | |
341 | + arvo->actual_profile = retval; | |
342 | + | |
343 | + return 0; | |
344 | +} | |
345 | + | |
346 | +static int arvo_init_specials(struct hid_device *hdev) | |
347 | +{ | |
348 | + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); | |
349 | + struct usb_device *usb_dev = interface_to_usbdev(intf); | |
350 | + struct arvo_device *arvo; | |
351 | + int retval; | |
352 | + | |
353 | + if (intf->cur_altsetting->desc.bInterfaceProtocol | |
354 | + == USB_INTERFACE_PROTOCOL_KEYBOARD) { | |
355 | + hid_set_drvdata(hdev, NULL); | |
356 | + return 0; | |
357 | + } | |
358 | + | |
359 | + arvo = kzalloc(sizeof(*arvo), GFP_KERNEL); | |
360 | + if (!arvo) { | |
361 | + dev_err(&hdev->dev, "can't alloc device descriptor\n"); | |
362 | + return -ENOMEM; | |
363 | + } | |
364 | + hid_set_drvdata(hdev, arvo); | |
365 | + | |
366 | + retval = arvo_init_arvo_device_struct(usb_dev, arvo); | |
367 | + if (retval) { | |
368 | + dev_err(&hdev->dev, | |
369 | + "couldn't init struct arvo_device\n"); | |
370 | + goto exit_free; | |
371 | + } | |
372 | + | |
373 | + retval = roccat_connect(arvo_class, hdev); | |
374 | + if (retval < 0) { | |
375 | + dev_err(&hdev->dev, "couldn't init char dev\n"); | |
376 | + } else { | |
377 | + arvo->chrdev_minor = retval; | |
378 | + arvo->roccat_claimed = 1; | |
379 | + } | |
380 | + | |
381 | + return 0; | |
382 | +exit_free: | |
383 | + kfree(arvo); | |
384 | + return retval; | |
385 | +} | |
386 | + | |
387 | +static void arvo_remove_specials(struct hid_device *hdev) | |
388 | +{ | |
389 | + struct usb_interface *intf = to_usb_interface(hdev->dev.parent); | |
390 | + struct arvo_device *arvo; | |
391 | + | |
392 | + if (intf->cur_altsetting->desc.bInterfaceProtocol | |
393 | + == USB_INTERFACE_PROTOCOL_KEYBOARD) | |
394 | + return; | |
395 | + | |
396 | + arvo = hid_get_drvdata(hdev); | |
397 | + if (arvo->roccat_claimed) | |
398 | + roccat_disconnect(arvo->chrdev_minor); | |
399 | + kfree(arvo); | |
400 | +} | |
401 | + | |
402 | +static int arvo_probe(struct hid_device *hdev, | |
403 | + const struct hid_device_id *id) | |
404 | +{ | |
405 | + int retval; | |
406 | + | |
407 | + retval = hid_parse(hdev); | |
408 | + if (retval) { | |
409 | + dev_err(&hdev->dev, "parse failed\n"); | |
410 | + goto exit; | |
411 | + } | |
412 | + | |
413 | + retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT); | |
414 | + if (retval) { | |
415 | + dev_err(&hdev->dev, "hw start failed\n"); | |
416 | + goto exit; | |
417 | + } | |
418 | + | |
419 | + retval = arvo_init_specials(hdev); | |
420 | + if (retval) { | |
421 | + dev_err(&hdev->dev, "couldn't install keyboard\n"); | |
422 | + goto exit_stop; | |
423 | + } | |
424 | + | |
425 | + return 0; | |
426 | + | |
427 | +exit_stop: | |
428 | + hid_hw_stop(hdev); | |
429 | +exit: | |
430 | + return retval; | |
431 | +} | |
432 | + | |
433 | +static void arvo_remove(struct hid_device *hdev) | |
434 | +{ | |
435 | + arvo_remove_specials(hdev); | |
436 | + hid_hw_stop(hdev); | |
437 | +} | |
438 | + | |
439 | +static void arvo_report_to_chrdev(struct arvo_device const *arvo, | |
440 | + u8 const *data) | |
441 | +{ | |
442 | + struct arvo_special_report const *special_report; | |
443 | + struct arvo_roccat_report roccat_report; | |
444 | + | |
445 | + special_report = (struct arvo_special_report const *)data; | |
446 | + | |
447 | + roccat_report.profile = arvo->actual_profile; | |
448 | + roccat_report.button = special_report->event & | |
449 | + ARVO_SPECIAL_REPORT_EVENT_MASK_BUTTON; | |
450 | + if ((special_report->event & ARVO_SPECIAL_REPORT_EVENT_MASK_ACTION) == | |
451 | + ARVO_SPECIAL_REPORT_EVENT_ACTION_PRESS) | |
452 | + roccat_report.action = ARVO_ROCCAT_REPORT_ACTION_PRESS; | |
453 | + else | |
454 | + roccat_report.action = ARVO_ROCCAT_REPORT_ACTION_RELEASE; | |
455 | + | |
456 | + roccat_report_event(arvo->chrdev_minor, (uint8_t const *)&roccat_report, | |
457 | + sizeof(struct arvo_roccat_report)); | |
458 | +} | |
459 | + | |
460 | +static int arvo_raw_event(struct hid_device *hdev, | |
461 | + struct hid_report *report, u8 *data, int size) | |
462 | +{ | |
463 | + struct arvo_device *arvo = hid_get_drvdata(hdev); | |
464 | + | |
465 | + if (size != 3) | |
466 | + return 0; | |
467 | + | |
468 | + if (arvo->roccat_claimed) | |
469 | + arvo_report_to_chrdev(arvo, data); | |
470 | + | |
471 | + return 0; | |
472 | +} | |
473 | + | |
474 | +static const struct hid_device_id arvo_devices[] = { | |
475 | + { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ARVO) }, | |
476 | + { } | |
477 | +}; | |
478 | + | |
479 | +MODULE_DEVICE_TABLE(hid, arvo_devices); | |
480 | + | |
481 | +static struct hid_driver arvo_driver = { | |
482 | + .name = "arvo", | |
483 | + .id_table = arvo_devices, | |
484 | + .probe = arvo_probe, | |
485 | + .remove = arvo_remove, | |
486 | + .raw_event = arvo_raw_event | |
487 | +}; | |
488 | + | |
489 | +static int __init arvo_init(void) | |
490 | +{ | |
491 | + int retval; | |
492 | + | |
493 | + arvo_class = class_create(THIS_MODULE, "arvo"); | |
494 | + if (IS_ERR(arvo_class)) | |
495 | + return PTR_ERR(arvo_class); | |
496 | + arvo_class->dev_attrs = arvo_attributes; | |
497 | + arvo_class->dev_bin_attrs = arvo_bin_attributes; | |
498 | + | |
499 | + retval = hid_register_driver(&arvo_driver); | |
500 | + if (retval) | |
501 | + class_destroy(arvo_class); | |
502 | + return retval; | |
503 | +} | |
504 | + | |
505 | +static void __exit arvo_exit(void) | |
506 | +{ | |
507 | + class_destroy(arvo_class); | |
508 | + hid_unregister_driver(&arvo_driver); | |
509 | +} | |
510 | + | |
511 | +module_init(arvo_init); | |
512 | +module_exit(arvo_exit); | |
513 | + | |
514 | +MODULE_AUTHOR("Stefan Achatz"); | |
515 | +MODULE_DESCRIPTION("USB Roccat Arvo driver"); | |
516 | +MODULE_LICENSE("GPL v2"); |
drivers/hid/hid-roccat-arvo.h
1 | +#ifndef __HID_ROCCAT_ARVO_H | |
2 | +#define __HID_ROCCAT_ARVO_H | |
3 | + | |
4 | +/* | |
5 | + * Copyright (c) 2011 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 | +struct arvo_mode_key { /* 2 bytes */ | |
18 | + uint8_t command; /* ARVO_COMMAND_MODE_KEY */ | |
19 | + uint8_t state; | |
20 | +} __packed; | |
21 | + | |
22 | +struct arvo_button { | |
23 | + uint8_t unknown[24]; | |
24 | +} __packed; | |
25 | + | |
26 | +struct arvo_info { | |
27 | + uint8_t unknown[8]; | |
28 | +} __packed; | |
29 | + | |
30 | +struct arvo_key_mask { /* 2 bytes */ | |
31 | + uint8_t command; /* ARVO_COMMAND_KEY_MASK */ | |
32 | + uint8_t key_mask; | |
33 | +} __packed; | |
34 | + | |
35 | +/* selected profile is persistent */ | |
36 | +struct arvo_actual_profile { /* 2 bytes */ | |
37 | + uint8_t command; /* ARVO_COMMAND_ACTUAL_PROFILE */ | |
38 | + uint8_t actual_profile; | |
39 | +} __packed; | |
40 | + | |
41 | +enum arvo_commands { | |
42 | + ARVO_COMMAND_MODE_KEY = 0x3, | |
43 | + ARVO_COMMAND_BUTTON = 0x4, | |
44 | + ARVO_COMMAND_INFO = 0x5, | |
45 | + ARVO_COMMAND_KEY_MASK = 0x6, | |
46 | + ARVO_COMMAND_ACTUAL_PROFILE = 0x7, | |
47 | +}; | |
48 | + | |
49 | +enum arvo_usb_commands { | |
50 | + ARVO_USB_COMMAND_MODE_KEY = 0x303, | |
51 | + /* | |
52 | + * read/write | |
53 | + * Read uses both index bytes as profile/key indexes | |
54 | + * Write has index 0, profile/key is determined by payload | |
55 | + */ | |
56 | + ARVO_USB_COMMAND_BUTTON = 0x304, | |
57 | + ARVO_USB_COMMAND_INFO = 0x305, | |
58 | + ARVO_USB_COMMAND_KEY_MASK = 0x306, | |
59 | + ARVO_USB_COMMAND_ACTUAL_PROFILE = 0x307, | |
60 | +}; | |
61 | + | |
62 | +struct arvo_special_report { | |
63 | + uint8_t unknown1; /* always 0x01 */ | |
64 | + uint8_t event; | |
65 | + uint8_t unknown2; /* always 0x70 */ | |
66 | +} __packed; | |
67 | + | |
68 | +enum arvo_special_report_events { | |
69 | + ARVO_SPECIAL_REPORT_EVENT_ACTION_PRESS = 0x10, | |
70 | + ARVO_SPECIAL_REPORT_EVENT_ACTION_RELEASE = 0x0, | |
71 | +}; | |
72 | + | |
73 | +enum arvo_special_report_event_masks { | |
74 | + ARVO_SPECIAL_REPORT_EVENT_MASK_ACTION = 0xf0, | |
75 | + ARVO_SPECIAL_REPORT_EVENT_MASK_BUTTON = 0x0f, | |
76 | +}; | |
77 | + | |
78 | +struct arvo_roccat_report { | |
79 | + uint8_t profile; | |
80 | + uint8_t button; | |
81 | + uint8_t action; | |
82 | +} __packed; | |
83 | + | |
84 | +enum arvo_roccat_report_action { | |
85 | + ARVO_ROCCAT_REPORT_ACTION_RELEASE = 0, | |
86 | + ARVO_ROCCAT_REPORT_ACTION_PRESS = 1, | |
87 | +}; | |
88 | + | |
89 | +struct arvo_device { | |
90 | + int roccat_claimed; | |
91 | + int chrdev_minor; | |
92 | + | |
93 | + struct mutex arvo_lock; | |
94 | + | |
95 | + int actual_profile; | |
96 | +}; | |
97 | + | |
98 | +#endif |