1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Roccat Arvo driver for Linux |
4 | * |
5 | * Copyright (c) 2011 Stefan Achatz <erazor_de@users.sourceforge.net> |
6 | */ |
7 | |
8 | /* |
9 | */ |
10 | |
11 | /* |
12 | * Roccat Arvo is a gamer keyboard with 5 macro keys that can be configured in |
13 | * 5 profiles. |
14 | */ |
15 | |
16 | #include <linux/device.h> |
17 | #include <linux/input.h> |
18 | #include <linux/hid.h> |
19 | #include <linux/module.h> |
20 | #include <linux/slab.h> |
21 | #include <linux/hid-roccat.h> |
22 | #include "hid-ids.h" |
23 | #include "hid-roccat-common.h" |
24 | #include "hid-roccat-arvo.h" |
25 | |
26 | static ssize_t arvo_sysfs_show_mode_key(struct device *dev, |
27 | struct device_attribute *attr, char *buf) |
28 | { |
29 | struct arvo_device *arvo = |
30 | hid_get_drvdata(hdev: dev_get_drvdata(dev: dev->parent->parent)); |
31 | struct usb_device *usb_dev = |
32 | interface_to_usbdev(to_usb_interface(dev->parent->parent)); |
33 | struct arvo_mode_key temp_buf; |
34 | int retval; |
35 | |
36 | mutex_lock(&arvo->arvo_lock); |
37 | retval = roccat_common2_receive(usb_dev, report_id: ARVO_COMMAND_MODE_KEY, |
38 | data: &temp_buf, size: sizeof(struct arvo_mode_key)); |
39 | mutex_unlock(lock: &arvo->arvo_lock); |
40 | if (retval) |
41 | return retval; |
42 | |
43 | return sysfs_emit(buf, fmt: "%d\n" , temp_buf.state); |
44 | } |
45 | |
46 | static ssize_t arvo_sysfs_set_mode_key(struct device *dev, |
47 | struct device_attribute *attr, char const *buf, size_t size) |
48 | { |
49 | struct arvo_device *arvo = |
50 | hid_get_drvdata(hdev: dev_get_drvdata(dev: dev->parent->parent)); |
51 | struct usb_device *usb_dev = |
52 | interface_to_usbdev(to_usb_interface(dev->parent->parent)); |
53 | struct arvo_mode_key temp_buf; |
54 | unsigned long state; |
55 | int retval; |
56 | |
57 | retval = kstrtoul(s: buf, base: 10, res: &state); |
58 | if (retval) |
59 | return retval; |
60 | |
61 | temp_buf.command = ARVO_COMMAND_MODE_KEY; |
62 | temp_buf.state = state; |
63 | |
64 | mutex_lock(&arvo->arvo_lock); |
65 | retval = roccat_common2_send(usb_dev, report_id: ARVO_COMMAND_MODE_KEY, |
66 | data: &temp_buf, size: sizeof(struct arvo_mode_key)); |
67 | mutex_unlock(lock: &arvo->arvo_lock); |
68 | if (retval) |
69 | return retval; |
70 | |
71 | return size; |
72 | } |
73 | static DEVICE_ATTR(mode_key, 0660, |
74 | arvo_sysfs_show_mode_key, arvo_sysfs_set_mode_key); |
75 | |
76 | static ssize_t arvo_sysfs_show_key_mask(struct device *dev, |
77 | struct device_attribute *attr, char *buf) |
78 | { |
79 | struct arvo_device *arvo = |
80 | hid_get_drvdata(hdev: dev_get_drvdata(dev: dev->parent->parent)); |
81 | struct usb_device *usb_dev = |
82 | interface_to_usbdev(to_usb_interface(dev->parent->parent)); |
83 | struct arvo_key_mask temp_buf; |
84 | int retval; |
85 | |
86 | mutex_lock(&arvo->arvo_lock); |
87 | retval = roccat_common2_receive(usb_dev, report_id: ARVO_COMMAND_KEY_MASK, |
88 | data: &temp_buf, size: sizeof(struct arvo_key_mask)); |
89 | mutex_unlock(lock: &arvo->arvo_lock); |
90 | if (retval) |
91 | return retval; |
92 | |
93 | return sysfs_emit(buf, fmt: "%d\n" , temp_buf.key_mask); |
94 | } |
95 | |
96 | static ssize_t arvo_sysfs_set_key_mask(struct device *dev, |
97 | struct device_attribute *attr, char const *buf, size_t size) |
98 | { |
99 | struct arvo_device *arvo = |
100 | hid_get_drvdata(hdev: dev_get_drvdata(dev: dev->parent->parent)); |
101 | struct usb_device *usb_dev = |
102 | interface_to_usbdev(to_usb_interface(dev->parent->parent)); |
103 | struct arvo_key_mask temp_buf; |
104 | unsigned long key_mask; |
105 | int retval; |
106 | |
107 | retval = kstrtoul(s: buf, base: 10, res: &key_mask); |
108 | if (retval) |
109 | return retval; |
110 | |
111 | temp_buf.command = ARVO_COMMAND_KEY_MASK; |
112 | temp_buf.key_mask = key_mask; |
113 | |
114 | mutex_lock(&arvo->arvo_lock); |
115 | retval = roccat_common2_send(usb_dev, report_id: ARVO_COMMAND_KEY_MASK, |
116 | data: &temp_buf, size: sizeof(struct arvo_key_mask)); |
117 | mutex_unlock(lock: &arvo->arvo_lock); |
118 | if (retval) |
119 | return retval; |
120 | |
121 | return size; |
122 | } |
123 | static DEVICE_ATTR(key_mask, 0660, |
124 | arvo_sysfs_show_key_mask, arvo_sysfs_set_key_mask); |
125 | |
126 | /* retval is 1-5 on success, < 0 on error */ |
127 | static int arvo_get_actual_profile(struct usb_device *usb_dev) |
128 | { |
129 | struct arvo_actual_profile temp_buf; |
130 | int retval; |
131 | |
132 | retval = roccat_common2_receive(usb_dev, report_id: ARVO_COMMAND_ACTUAL_PROFILE, |
133 | data: &temp_buf, size: sizeof(struct arvo_actual_profile)); |
134 | |
135 | if (retval) |
136 | return retval; |
137 | |
138 | return temp_buf.actual_profile; |
139 | } |
140 | |
141 | static ssize_t arvo_sysfs_show_actual_profile(struct device *dev, |
142 | struct device_attribute *attr, char *buf) |
143 | { |
144 | struct arvo_device *arvo = |
145 | hid_get_drvdata(hdev: dev_get_drvdata(dev: dev->parent->parent)); |
146 | |
147 | return sysfs_emit(buf, fmt: "%d\n" , arvo->actual_profile); |
148 | } |
149 | |
150 | static ssize_t arvo_sysfs_set_actual_profile(struct device *dev, |
151 | struct device_attribute *attr, char const *buf, size_t size) |
152 | { |
153 | struct arvo_device *arvo = |
154 | hid_get_drvdata(hdev: dev_get_drvdata(dev: dev->parent->parent)); |
155 | struct usb_device *usb_dev = |
156 | interface_to_usbdev(to_usb_interface(dev->parent->parent)); |
157 | struct arvo_actual_profile temp_buf; |
158 | unsigned long profile; |
159 | int retval; |
160 | |
161 | retval = kstrtoul(s: buf, base: 10, res: &profile); |
162 | if (retval) |
163 | return retval; |
164 | |
165 | if (profile < 1 || profile > 5) |
166 | return -EINVAL; |
167 | |
168 | temp_buf.command = ARVO_COMMAND_ACTUAL_PROFILE; |
169 | temp_buf.actual_profile = profile; |
170 | |
171 | mutex_lock(&arvo->arvo_lock); |
172 | retval = roccat_common2_send(usb_dev, report_id: ARVO_COMMAND_ACTUAL_PROFILE, |
173 | data: &temp_buf, size: sizeof(struct arvo_actual_profile)); |
174 | if (!retval) { |
175 | arvo->actual_profile = profile; |
176 | retval = size; |
177 | } |
178 | mutex_unlock(lock: &arvo->arvo_lock); |
179 | return retval; |
180 | } |
181 | static DEVICE_ATTR(actual_profile, 0660, |
182 | arvo_sysfs_show_actual_profile, |
183 | arvo_sysfs_set_actual_profile); |
184 | |
185 | static ssize_t arvo_sysfs_write(struct file *fp, |
186 | struct kobject *kobj, void const *buf, |
187 | loff_t off, size_t count, size_t real_size, uint command) |
188 | { |
189 | struct device *dev = kobj_to_dev(kobj)->parent->parent; |
190 | struct arvo_device *arvo = hid_get_drvdata(hdev: dev_get_drvdata(dev)); |
191 | struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); |
192 | int retval; |
193 | |
194 | if (off != 0 || count != real_size) |
195 | return -EINVAL; |
196 | |
197 | mutex_lock(&arvo->arvo_lock); |
198 | retval = roccat_common2_send(usb_dev, report_id: command, data: buf, size: real_size); |
199 | mutex_unlock(lock: &arvo->arvo_lock); |
200 | |
201 | return (retval ? retval : real_size); |
202 | } |
203 | |
204 | static ssize_t arvo_sysfs_read(struct file *fp, |
205 | struct kobject *kobj, void *buf, loff_t off, |
206 | size_t count, size_t real_size, uint command) |
207 | { |
208 | struct device *dev = kobj_to_dev(kobj)->parent->parent; |
209 | struct arvo_device *arvo = hid_get_drvdata(hdev: dev_get_drvdata(dev)); |
210 | struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev)); |
211 | int retval; |
212 | |
213 | if (off >= real_size) |
214 | return 0; |
215 | |
216 | if (off != 0 || count != real_size) |
217 | return -EINVAL; |
218 | |
219 | mutex_lock(&arvo->arvo_lock); |
220 | retval = roccat_common2_receive(usb_dev, report_id: command, data: buf, size: real_size); |
221 | mutex_unlock(lock: &arvo->arvo_lock); |
222 | |
223 | return (retval ? retval : real_size); |
224 | } |
225 | |
226 | static ssize_t arvo_sysfs_write_button(struct file *fp, |
227 | struct kobject *kobj, struct bin_attribute *attr, char *buf, |
228 | loff_t off, size_t count) |
229 | { |
230 | return arvo_sysfs_write(fp, kobj, buf, off, count, |
231 | real_size: sizeof(struct arvo_button), command: ARVO_COMMAND_BUTTON); |
232 | } |
233 | static BIN_ATTR(button, 0220, NULL, arvo_sysfs_write_button, |
234 | sizeof(struct arvo_button)); |
235 | |
236 | static ssize_t arvo_sysfs_read_info(struct file *fp, |
237 | struct kobject *kobj, struct bin_attribute *attr, char *buf, |
238 | loff_t off, size_t count) |
239 | { |
240 | return arvo_sysfs_read(fp, kobj, buf, off, count, |
241 | real_size: sizeof(struct arvo_info), command: ARVO_COMMAND_INFO); |
242 | } |
243 | static BIN_ATTR(info, 0440, arvo_sysfs_read_info, NULL, |
244 | sizeof(struct arvo_info)); |
245 | |
246 | static struct attribute *arvo_attrs[] = { |
247 | &dev_attr_mode_key.attr, |
248 | &dev_attr_key_mask.attr, |
249 | &dev_attr_actual_profile.attr, |
250 | NULL, |
251 | }; |
252 | |
253 | static struct bin_attribute *arvo_bin_attributes[] = { |
254 | &bin_attr_button, |
255 | &bin_attr_info, |
256 | NULL, |
257 | }; |
258 | |
259 | static const struct attribute_group arvo_group = { |
260 | .attrs = arvo_attrs, |
261 | .bin_attrs = arvo_bin_attributes, |
262 | }; |
263 | |
264 | static const struct attribute_group *arvo_groups[] = { |
265 | &arvo_group, |
266 | NULL, |
267 | }; |
268 | |
269 | static const struct class arvo_class = { |
270 | .name = "arvo" , |
271 | .dev_groups = arvo_groups, |
272 | }; |
273 | |
274 | static int arvo_init_arvo_device_struct(struct usb_device *usb_dev, |
275 | struct arvo_device *arvo) |
276 | { |
277 | int retval; |
278 | |
279 | mutex_init(&arvo->arvo_lock); |
280 | |
281 | retval = arvo_get_actual_profile(usb_dev); |
282 | if (retval < 0) |
283 | return retval; |
284 | arvo->actual_profile = retval; |
285 | |
286 | return 0; |
287 | } |
288 | |
289 | static int arvo_init_specials(struct hid_device *hdev) |
290 | { |
291 | struct usb_interface *intf = to_usb_interface(hdev->dev.parent); |
292 | struct usb_device *usb_dev = interface_to_usbdev(intf); |
293 | struct arvo_device *arvo; |
294 | int retval; |
295 | |
296 | if (intf->cur_altsetting->desc.bInterfaceProtocol |
297 | == USB_INTERFACE_PROTOCOL_KEYBOARD) { |
298 | hid_set_drvdata(hdev, NULL); |
299 | return 0; |
300 | } |
301 | |
302 | arvo = kzalloc(size: sizeof(*arvo), GFP_KERNEL); |
303 | if (!arvo) { |
304 | hid_err(hdev, "can't alloc device descriptor\n" ); |
305 | return -ENOMEM; |
306 | } |
307 | hid_set_drvdata(hdev, data: arvo); |
308 | |
309 | retval = arvo_init_arvo_device_struct(usb_dev, arvo); |
310 | if (retval) { |
311 | hid_err(hdev, "couldn't init struct arvo_device\n" ); |
312 | goto exit_free; |
313 | } |
314 | |
315 | retval = roccat_connect(klass: &arvo_class, hid: hdev, |
316 | report_size: sizeof(struct arvo_roccat_report)); |
317 | if (retval < 0) { |
318 | hid_err(hdev, "couldn't init char dev\n" ); |
319 | } else { |
320 | arvo->chrdev_minor = retval; |
321 | arvo->roccat_claimed = 1; |
322 | } |
323 | |
324 | return 0; |
325 | exit_free: |
326 | kfree(objp: arvo); |
327 | return retval; |
328 | } |
329 | |
330 | static void arvo_remove_specials(struct hid_device *hdev) |
331 | { |
332 | struct usb_interface *intf = to_usb_interface(hdev->dev.parent); |
333 | struct arvo_device *arvo; |
334 | |
335 | if (intf->cur_altsetting->desc.bInterfaceProtocol |
336 | == USB_INTERFACE_PROTOCOL_KEYBOARD) |
337 | return; |
338 | |
339 | arvo = hid_get_drvdata(hdev); |
340 | if (arvo->roccat_claimed) |
341 | roccat_disconnect(minor: arvo->chrdev_minor); |
342 | kfree(objp: arvo); |
343 | } |
344 | |
345 | static int arvo_probe(struct hid_device *hdev, |
346 | const struct hid_device_id *id) |
347 | { |
348 | int retval; |
349 | |
350 | if (!hid_is_usb(hdev)) |
351 | return -EINVAL; |
352 | |
353 | retval = hid_parse(hdev); |
354 | if (retval) { |
355 | hid_err(hdev, "parse failed\n" ); |
356 | goto exit; |
357 | } |
358 | |
359 | retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT); |
360 | if (retval) { |
361 | hid_err(hdev, "hw start failed\n" ); |
362 | goto exit; |
363 | } |
364 | |
365 | retval = arvo_init_specials(hdev); |
366 | if (retval) { |
367 | hid_err(hdev, "couldn't install keyboard\n" ); |
368 | goto exit_stop; |
369 | } |
370 | |
371 | return 0; |
372 | |
373 | exit_stop: |
374 | hid_hw_stop(hdev); |
375 | exit: |
376 | return retval; |
377 | } |
378 | |
379 | static void arvo_remove(struct hid_device *hdev) |
380 | { |
381 | arvo_remove_specials(hdev); |
382 | hid_hw_stop(hdev); |
383 | } |
384 | |
385 | static void arvo_report_to_chrdev(struct arvo_device const *arvo, |
386 | u8 const *data) |
387 | { |
388 | struct arvo_special_report const *special_report; |
389 | struct arvo_roccat_report roccat_report; |
390 | |
391 | special_report = (struct arvo_special_report const *)data; |
392 | |
393 | roccat_report.profile = arvo->actual_profile; |
394 | roccat_report.button = special_report->event & |
395 | ARVO_SPECIAL_REPORT_EVENT_MASK_BUTTON; |
396 | if ((special_report->event & ARVO_SPECIAL_REPORT_EVENT_MASK_ACTION) == |
397 | ARVO_SPECIAL_REPORT_EVENT_ACTION_PRESS) |
398 | roccat_report.action = ARVO_ROCCAT_REPORT_ACTION_PRESS; |
399 | else |
400 | roccat_report.action = ARVO_ROCCAT_REPORT_ACTION_RELEASE; |
401 | |
402 | roccat_report_event(minor: arvo->chrdev_minor, |
403 | data: (uint8_t const *)&roccat_report); |
404 | } |
405 | |
406 | static int arvo_raw_event(struct hid_device *hdev, |
407 | struct hid_report *report, u8 *data, int size) |
408 | { |
409 | struct arvo_device *arvo = hid_get_drvdata(hdev); |
410 | |
411 | if (size != 3) |
412 | return 0; |
413 | |
414 | if (arvo && arvo->roccat_claimed) |
415 | arvo_report_to_chrdev(arvo, data); |
416 | |
417 | return 0; |
418 | } |
419 | |
420 | static const struct hid_device_id arvo_devices[] = { |
421 | { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ARVO) }, |
422 | { } |
423 | }; |
424 | |
425 | MODULE_DEVICE_TABLE(hid, arvo_devices); |
426 | |
427 | static struct hid_driver arvo_driver = { |
428 | .name = "arvo" , |
429 | .id_table = arvo_devices, |
430 | .probe = arvo_probe, |
431 | .remove = arvo_remove, |
432 | .raw_event = arvo_raw_event |
433 | }; |
434 | |
435 | static int __init arvo_init(void) |
436 | { |
437 | int retval; |
438 | |
439 | retval = class_register(class: &arvo_class); |
440 | if (retval) |
441 | return retval; |
442 | |
443 | retval = hid_register_driver(&arvo_driver); |
444 | if (retval) |
445 | class_unregister(class: &arvo_class); |
446 | return retval; |
447 | } |
448 | |
449 | static void __exit arvo_exit(void) |
450 | { |
451 | hid_unregister_driver(&arvo_driver); |
452 | class_unregister(class: &arvo_class); |
453 | } |
454 | |
455 | module_init(arvo_init); |
456 | module_exit(arvo_exit); |
457 | |
458 | MODULE_AUTHOR("Stefan Achatz" ); |
459 | MODULE_DESCRIPTION("USB Roccat Arvo driver" ); |
460 | MODULE_LICENSE("GPL v2" ); |
461 | |