1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Roccat Ryos driver for Linux |
4 | * |
5 | * Copyright (c) 2013 Stefan Achatz <erazor_de@users.sourceforge.net> |
6 | */ |
7 | |
8 | /* |
9 | */ |
10 | |
11 | #include <linux/types.h> |
12 | #include <linux/device.h> |
13 | #include <linux/input.h> |
14 | #include <linux/hid.h> |
15 | #include <linux/module.h> |
16 | #include <linux/slab.h> |
17 | #include <linux/hid-roccat.h> |
18 | #include "hid-ids.h" |
19 | #include "hid-roccat-common.h" |
20 | |
21 | enum { |
22 | RYOS_REPORT_NUMBER_SPECIAL = 3, |
23 | RYOS_USB_INTERFACE_PROTOCOL = 0, |
24 | }; |
25 | |
26 | struct ryos_report_special { |
27 | uint8_t number; /* RYOS_REPORT_NUMBER_SPECIAL */ |
28 | uint8_t data[4]; |
29 | } __packed; |
30 | |
31 | ROCCAT_COMMON2_BIN_ATTRIBUTE_W(control, 0x04, 0x03); |
32 | ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(profile, 0x05, 0x03); |
33 | ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(keys_primary, 0x06, 0x7d); |
34 | ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(keys_function, 0x07, 0x5f); |
35 | ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(keys_macro, 0x08, 0x23); |
36 | ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(keys_thumbster, 0x09, 0x17); |
37 | ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(keys_extra, 0x0a, 0x08); |
38 | ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(keys_easyzone, 0x0b, 0x126); |
39 | ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(key_mask, 0x0c, 0x06); |
40 | ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(light, 0x0d, 0x10); |
41 | ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(macro, 0x0e, 0x7d2); |
42 | ROCCAT_COMMON2_BIN_ATTRIBUTE_R(info, 0x0f, 0x08); |
43 | ROCCAT_COMMON2_BIN_ATTRIBUTE_W(reset, 0x11, 0x03); |
44 | ROCCAT_COMMON2_BIN_ATTRIBUTE_W(light_control, 0x13, 0x08); |
45 | ROCCAT_COMMON2_BIN_ATTRIBUTE_W(talk, 0x16, 0x10); |
46 | ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(stored_lights, 0x17, 0x0566); |
47 | ROCCAT_COMMON2_BIN_ATTRIBUTE_W(custom_lights, 0x18, 0x14); |
48 | ROCCAT_COMMON2_BIN_ATTRIBUTE_RW(light_macro, 0x19, 0x07d2); |
49 | |
50 | static struct bin_attribute *ryos_bin_attrs[] = { |
51 | &bin_attr_control, |
52 | &bin_attr_profile, |
53 | &bin_attr_keys_primary, |
54 | &bin_attr_keys_function, |
55 | &bin_attr_keys_macro, |
56 | &bin_attr_keys_thumbster, |
57 | &bin_attr_keys_extra, |
58 | &bin_attr_keys_easyzone, |
59 | &bin_attr_key_mask, |
60 | &bin_attr_light, |
61 | &bin_attr_macro, |
62 | &bin_attr_info, |
63 | &bin_attr_reset, |
64 | &bin_attr_light_control, |
65 | &bin_attr_talk, |
66 | &bin_attr_stored_lights, |
67 | &bin_attr_custom_lights, |
68 | &bin_attr_light_macro, |
69 | NULL, |
70 | }; |
71 | |
72 | static const struct attribute_group ryos_group = { |
73 | .bin_attrs = ryos_bin_attrs, |
74 | }; |
75 | |
76 | static const struct attribute_group *ryos_groups[] = { |
77 | &ryos_group, |
78 | NULL, |
79 | }; |
80 | |
81 | static const struct class ryos_class = { |
82 | .name = "ryos" , |
83 | .dev_groups = ryos_groups, |
84 | }; |
85 | |
86 | static int ryos_init_specials(struct hid_device *hdev) |
87 | { |
88 | struct usb_interface *intf = to_usb_interface(hdev->dev.parent); |
89 | struct usb_device *usb_dev = interface_to_usbdev(intf); |
90 | struct roccat_common2_device *ryos; |
91 | int retval; |
92 | |
93 | if (intf->cur_altsetting->desc.bInterfaceProtocol |
94 | != RYOS_USB_INTERFACE_PROTOCOL) { |
95 | hid_set_drvdata(hdev, NULL); |
96 | return 0; |
97 | } |
98 | |
99 | ryos = kzalloc(size: sizeof(*ryos), GFP_KERNEL); |
100 | if (!ryos) { |
101 | hid_err(hdev, "can't alloc device descriptor\n" ); |
102 | return -ENOMEM; |
103 | } |
104 | hid_set_drvdata(hdev, data: ryos); |
105 | |
106 | retval = roccat_common2_device_init_struct(usb_dev, dev: ryos); |
107 | if (retval) { |
108 | hid_err(hdev, "couldn't init Ryos device\n" ); |
109 | goto exit_free; |
110 | } |
111 | |
112 | retval = roccat_connect(klass: &ryos_class, hid: hdev, |
113 | report_size: sizeof(struct ryos_report_special)); |
114 | if (retval < 0) { |
115 | hid_err(hdev, "couldn't init char dev\n" ); |
116 | } else { |
117 | ryos->chrdev_minor = retval; |
118 | ryos->roccat_claimed = 1; |
119 | } |
120 | |
121 | return 0; |
122 | exit_free: |
123 | kfree(objp: ryos); |
124 | return retval; |
125 | } |
126 | |
127 | static void ryos_remove_specials(struct hid_device *hdev) |
128 | { |
129 | struct usb_interface *intf = to_usb_interface(hdev->dev.parent); |
130 | struct roccat_common2_device *ryos; |
131 | |
132 | if (intf->cur_altsetting->desc.bInterfaceProtocol |
133 | != RYOS_USB_INTERFACE_PROTOCOL) |
134 | return; |
135 | |
136 | ryos = hid_get_drvdata(hdev); |
137 | if (ryos->roccat_claimed) |
138 | roccat_disconnect(minor: ryos->chrdev_minor); |
139 | kfree(objp: ryos); |
140 | } |
141 | |
142 | static int ryos_probe(struct hid_device *hdev, |
143 | const struct hid_device_id *id) |
144 | { |
145 | int retval; |
146 | |
147 | if (!hid_is_usb(hdev)) |
148 | return -EINVAL; |
149 | |
150 | retval = hid_parse(hdev); |
151 | if (retval) { |
152 | hid_err(hdev, "parse failed\n" ); |
153 | goto exit; |
154 | } |
155 | |
156 | retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT); |
157 | if (retval) { |
158 | hid_err(hdev, "hw start failed\n" ); |
159 | goto exit; |
160 | } |
161 | |
162 | retval = ryos_init_specials(hdev); |
163 | if (retval) { |
164 | hid_err(hdev, "couldn't install mouse\n" ); |
165 | goto exit_stop; |
166 | } |
167 | |
168 | return 0; |
169 | |
170 | exit_stop: |
171 | hid_hw_stop(hdev); |
172 | exit: |
173 | return retval; |
174 | } |
175 | |
176 | static void ryos_remove(struct hid_device *hdev) |
177 | { |
178 | ryos_remove_specials(hdev); |
179 | hid_hw_stop(hdev); |
180 | } |
181 | |
182 | static int ryos_raw_event(struct hid_device *hdev, |
183 | struct hid_report *report, u8 *data, int size) |
184 | { |
185 | struct usb_interface *intf = to_usb_interface(hdev->dev.parent); |
186 | struct roccat_common2_device *ryos = hid_get_drvdata(hdev); |
187 | |
188 | if (intf->cur_altsetting->desc.bInterfaceProtocol |
189 | != RYOS_USB_INTERFACE_PROTOCOL) |
190 | return 0; |
191 | |
192 | if (data[0] != RYOS_REPORT_NUMBER_SPECIAL) |
193 | return 0; |
194 | |
195 | if (ryos != NULL && ryos->roccat_claimed) |
196 | roccat_report_event(minor: ryos->chrdev_minor, data); |
197 | |
198 | return 0; |
199 | } |
200 | |
201 | static const struct hid_device_id ryos_devices[] = { |
202 | { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_RYOS_MK) }, |
203 | { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_RYOS_MK_GLOW) }, |
204 | { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_RYOS_MK_PRO) }, |
205 | { } |
206 | }; |
207 | |
208 | MODULE_DEVICE_TABLE(hid, ryos_devices); |
209 | |
210 | static struct hid_driver ryos_driver = { |
211 | .name = "ryos" , |
212 | .id_table = ryos_devices, |
213 | .probe = ryos_probe, |
214 | .remove = ryos_remove, |
215 | .raw_event = ryos_raw_event |
216 | }; |
217 | |
218 | static int __init ryos_init(void) |
219 | { |
220 | int retval; |
221 | |
222 | retval = class_register(class: &ryos_class); |
223 | if (retval) |
224 | return retval; |
225 | |
226 | retval = hid_register_driver(&ryos_driver); |
227 | if (retval) |
228 | class_unregister(class: &ryos_class); |
229 | return retval; |
230 | } |
231 | |
232 | static void __exit ryos_exit(void) |
233 | { |
234 | hid_unregister_driver(&ryos_driver); |
235 | class_unregister(class: &ryos_class); |
236 | } |
237 | |
238 | module_init(ryos_init); |
239 | module_exit(ryos_exit); |
240 | |
241 | MODULE_AUTHOR("Stefan Achatz" ); |
242 | MODULE_DESCRIPTION("USB Roccat Ryos MK/Glow/Pro driver" ); |
243 | MODULE_LICENSE("GPL v2" ); |
244 | |