1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Roccat Kova[+] driver for Linux
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright (c) 2011 Stefan Achatz <erazor_de@users.sourceforge.net>
6*4882a593Smuzhiyun */
7*4882a593Smuzhiyun
8*4882a593Smuzhiyun /*
9*4882a593Smuzhiyun */
10*4882a593Smuzhiyun
11*4882a593Smuzhiyun /*
12*4882a593Smuzhiyun * Roccat Kova[+] is a bigger version of the Pyra with two more side buttons.
13*4882a593Smuzhiyun */
14*4882a593Smuzhiyun
15*4882a593Smuzhiyun #include <linux/device.h>
16*4882a593Smuzhiyun #include <linux/input.h>
17*4882a593Smuzhiyun #include <linux/hid.h>
18*4882a593Smuzhiyun #include <linux/module.h>
19*4882a593Smuzhiyun #include <linux/slab.h>
20*4882a593Smuzhiyun #include <linux/hid-roccat.h>
21*4882a593Smuzhiyun #include "hid-ids.h"
22*4882a593Smuzhiyun #include "hid-roccat-common.h"
23*4882a593Smuzhiyun #include "hid-roccat-kovaplus.h"
24*4882a593Smuzhiyun
25*4882a593Smuzhiyun static uint profile_numbers[5] = {0, 1, 2, 3, 4};
26*4882a593Smuzhiyun
27*4882a593Smuzhiyun static struct class *kovaplus_class;
28*4882a593Smuzhiyun
kovaplus_convert_event_cpi(uint value)29*4882a593Smuzhiyun static uint kovaplus_convert_event_cpi(uint value)
30*4882a593Smuzhiyun {
31*4882a593Smuzhiyun return (value == 7 ? 4 : (value == 4 ? 3 : value));
32*4882a593Smuzhiyun }
33*4882a593Smuzhiyun
kovaplus_profile_activated(struct kovaplus_device * kovaplus,uint new_profile_index)34*4882a593Smuzhiyun static void kovaplus_profile_activated(struct kovaplus_device *kovaplus,
35*4882a593Smuzhiyun uint new_profile_index)
36*4882a593Smuzhiyun {
37*4882a593Smuzhiyun if (new_profile_index >= ARRAY_SIZE(kovaplus->profile_settings))
38*4882a593Smuzhiyun return;
39*4882a593Smuzhiyun kovaplus->actual_profile = new_profile_index;
40*4882a593Smuzhiyun kovaplus->actual_cpi = kovaplus->profile_settings[new_profile_index].cpi_startup_level;
41*4882a593Smuzhiyun kovaplus->actual_x_sensitivity = kovaplus->profile_settings[new_profile_index].sensitivity_x;
42*4882a593Smuzhiyun kovaplus->actual_y_sensitivity = kovaplus->profile_settings[new_profile_index].sensitivity_y;
43*4882a593Smuzhiyun }
44*4882a593Smuzhiyun
kovaplus_send_control(struct usb_device * usb_dev,uint value,enum kovaplus_control_requests request)45*4882a593Smuzhiyun static int kovaplus_send_control(struct usb_device *usb_dev, uint value,
46*4882a593Smuzhiyun enum kovaplus_control_requests request)
47*4882a593Smuzhiyun {
48*4882a593Smuzhiyun int retval;
49*4882a593Smuzhiyun struct roccat_common2_control control;
50*4882a593Smuzhiyun
51*4882a593Smuzhiyun if ((request == KOVAPLUS_CONTROL_REQUEST_PROFILE_SETTINGS ||
52*4882a593Smuzhiyun request == KOVAPLUS_CONTROL_REQUEST_PROFILE_BUTTONS) &&
53*4882a593Smuzhiyun value > 4)
54*4882a593Smuzhiyun return -EINVAL;
55*4882a593Smuzhiyun
56*4882a593Smuzhiyun control.command = ROCCAT_COMMON_COMMAND_CONTROL;
57*4882a593Smuzhiyun control.value = value;
58*4882a593Smuzhiyun control.request = request;
59*4882a593Smuzhiyun
60*4882a593Smuzhiyun retval = roccat_common2_send(usb_dev, ROCCAT_COMMON_COMMAND_CONTROL,
61*4882a593Smuzhiyun &control, sizeof(struct roccat_common2_control));
62*4882a593Smuzhiyun
63*4882a593Smuzhiyun return retval;
64*4882a593Smuzhiyun }
65*4882a593Smuzhiyun
kovaplus_select_profile(struct usb_device * usb_dev,uint number,enum kovaplus_control_requests request)66*4882a593Smuzhiyun static int kovaplus_select_profile(struct usb_device *usb_dev, uint number,
67*4882a593Smuzhiyun enum kovaplus_control_requests request)
68*4882a593Smuzhiyun {
69*4882a593Smuzhiyun return kovaplus_send_control(usb_dev, number, request);
70*4882a593Smuzhiyun }
71*4882a593Smuzhiyun
kovaplus_get_profile_settings(struct usb_device * usb_dev,struct kovaplus_profile_settings * buf,uint number)72*4882a593Smuzhiyun static int kovaplus_get_profile_settings(struct usb_device *usb_dev,
73*4882a593Smuzhiyun struct kovaplus_profile_settings *buf, uint number)
74*4882a593Smuzhiyun {
75*4882a593Smuzhiyun int retval;
76*4882a593Smuzhiyun
77*4882a593Smuzhiyun retval = kovaplus_select_profile(usb_dev, number,
78*4882a593Smuzhiyun KOVAPLUS_CONTROL_REQUEST_PROFILE_SETTINGS);
79*4882a593Smuzhiyun if (retval)
80*4882a593Smuzhiyun return retval;
81*4882a593Smuzhiyun
82*4882a593Smuzhiyun return roccat_common2_receive(usb_dev, KOVAPLUS_COMMAND_PROFILE_SETTINGS,
83*4882a593Smuzhiyun buf, KOVAPLUS_SIZE_PROFILE_SETTINGS);
84*4882a593Smuzhiyun }
85*4882a593Smuzhiyun
kovaplus_get_profile_buttons(struct usb_device * usb_dev,struct kovaplus_profile_buttons * buf,int number)86*4882a593Smuzhiyun static int kovaplus_get_profile_buttons(struct usb_device *usb_dev,
87*4882a593Smuzhiyun struct kovaplus_profile_buttons *buf, int number)
88*4882a593Smuzhiyun {
89*4882a593Smuzhiyun int retval;
90*4882a593Smuzhiyun
91*4882a593Smuzhiyun retval = kovaplus_select_profile(usb_dev, number,
92*4882a593Smuzhiyun KOVAPLUS_CONTROL_REQUEST_PROFILE_BUTTONS);
93*4882a593Smuzhiyun if (retval)
94*4882a593Smuzhiyun return retval;
95*4882a593Smuzhiyun
96*4882a593Smuzhiyun return roccat_common2_receive(usb_dev, KOVAPLUS_COMMAND_PROFILE_BUTTONS,
97*4882a593Smuzhiyun buf, KOVAPLUS_SIZE_PROFILE_BUTTONS);
98*4882a593Smuzhiyun }
99*4882a593Smuzhiyun
100*4882a593Smuzhiyun /* retval is 0-4 on success, < 0 on error */
kovaplus_get_actual_profile(struct usb_device * usb_dev)101*4882a593Smuzhiyun static int kovaplus_get_actual_profile(struct usb_device *usb_dev)
102*4882a593Smuzhiyun {
103*4882a593Smuzhiyun struct kovaplus_actual_profile buf;
104*4882a593Smuzhiyun int retval;
105*4882a593Smuzhiyun
106*4882a593Smuzhiyun retval = roccat_common2_receive(usb_dev, KOVAPLUS_COMMAND_ACTUAL_PROFILE,
107*4882a593Smuzhiyun &buf, sizeof(struct kovaplus_actual_profile));
108*4882a593Smuzhiyun
109*4882a593Smuzhiyun return retval ? retval : buf.actual_profile;
110*4882a593Smuzhiyun }
111*4882a593Smuzhiyun
kovaplus_set_actual_profile(struct usb_device * usb_dev,int new_profile)112*4882a593Smuzhiyun static int kovaplus_set_actual_profile(struct usb_device *usb_dev,
113*4882a593Smuzhiyun int new_profile)
114*4882a593Smuzhiyun {
115*4882a593Smuzhiyun struct kovaplus_actual_profile buf;
116*4882a593Smuzhiyun
117*4882a593Smuzhiyun buf.command = KOVAPLUS_COMMAND_ACTUAL_PROFILE;
118*4882a593Smuzhiyun buf.size = sizeof(struct kovaplus_actual_profile);
119*4882a593Smuzhiyun buf.actual_profile = new_profile;
120*4882a593Smuzhiyun
121*4882a593Smuzhiyun return roccat_common2_send_with_status(usb_dev,
122*4882a593Smuzhiyun KOVAPLUS_COMMAND_ACTUAL_PROFILE,
123*4882a593Smuzhiyun &buf, sizeof(struct kovaplus_actual_profile));
124*4882a593Smuzhiyun }
125*4882a593Smuzhiyun
kovaplus_sysfs_read(struct file * fp,struct kobject * kobj,char * buf,loff_t off,size_t count,size_t real_size,uint command)126*4882a593Smuzhiyun static ssize_t kovaplus_sysfs_read(struct file *fp, struct kobject *kobj,
127*4882a593Smuzhiyun char *buf, loff_t off, size_t count,
128*4882a593Smuzhiyun size_t real_size, uint command)
129*4882a593Smuzhiyun {
130*4882a593Smuzhiyun struct device *dev = kobj_to_dev(kobj)->parent->parent;
131*4882a593Smuzhiyun struct kovaplus_device *kovaplus = hid_get_drvdata(dev_get_drvdata(dev));
132*4882a593Smuzhiyun struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
133*4882a593Smuzhiyun int retval;
134*4882a593Smuzhiyun
135*4882a593Smuzhiyun if (off >= real_size)
136*4882a593Smuzhiyun return 0;
137*4882a593Smuzhiyun
138*4882a593Smuzhiyun if (off != 0 || count != real_size)
139*4882a593Smuzhiyun return -EINVAL;
140*4882a593Smuzhiyun
141*4882a593Smuzhiyun mutex_lock(&kovaplus->kovaplus_lock);
142*4882a593Smuzhiyun retval = roccat_common2_receive(usb_dev, command, buf, real_size);
143*4882a593Smuzhiyun mutex_unlock(&kovaplus->kovaplus_lock);
144*4882a593Smuzhiyun
145*4882a593Smuzhiyun if (retval)
146*4882a593Smuzhiyun return retval;
147*4882a593Smuzhiyun
148*4882a593Smuzhiyun return real_size;
149*4882a593Smuzhiyun }
150*4882a593Smuzhiyun
kovaplus_sysfs_write(struct file * fp,struct kobject * kobj,void const * buf,loff_t off,size_t count,size_t real_size,uint command)151*4882a593Smuzhiyun static ssize_t kovaplus_sysfs_write(struct file *fp, struct kobject *kobj,
152*4882a593Smuzhiyun void const *buf, loff_t off, size_t count,
153*4882a593Smuzhiyun size_t real_size, uint command)
154*4882a593Smuzhiyun {
155*4882a593Smuzhiyun struct device *dev = kobj_to_dev(kobj)->parent->parent;
156*4882a593Smuzhiyun struct kovaplus_device *kovaplus = hid_get_drvdata(dev_get_drvdata(dev));
157*4882a593Smuzhiyun struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
158*4882a593Smuzhiyun int retval;
159*4882a593Smuzhiyun
160*4882a593Smuzhiyun if (off != 0 || count != real_size)
161*4882a593Smuzhiyun return -EINVAL;
162*4882a593Smuzhiyun
163*4882a593Smuzhiyun mutex_lock(&kovaplus->kovaplus_lock);
164*4882a593Smuzhiyun retval = roccat_common2_send_with_status(usb_dev, command,
165*4882a593Smuzhiyun buf, real_size);
166*4882a593Smuzhiyun mutex_unlock(&kovaplus->kovaplus_lock);
167*4882a593Smuzhiyun
168*4882a593Smuzhiyun if (retval)
169*4882a593Smuzhiyun return retval;
170*4882a593Smuzhiyun
171*4882a593Smuzhiyun return real_size;
172*4882a593Smuzhiyun }
173*4882a593Smuzhiyun
174*4882a593Smuzhiyun #define KOVAPLUS_SYSFS_W(thingy, THINGY) \
175*4882a593Smuzhiyun static ssize_t kovaplus_sysfs_write_ ## thingy(struct file *fp, \
176*4882a593Smuzhiyun struct kobject *kobj, struct bin_attribute *attr, char *buf, \
177*4882a593Smuzhiyun loff_t off, size_t count) \
178*4882a593Smuzhiyun { \
179*4882a593Smuzhiyun return kovaplus_sysfs_write(fp, kobj, buf, off, count, \
180*4882a593Smuzhiyun KOVAPLUS_SIZE_ ## THINGY, KOVAPLUS_COMMAND_ ## THINGY); \
181*4882a593Smuzhiyun }
182*4882a593Smuzhiyun
183*4882a593Smuzhiyun #define KOVAPLUS_SYSFS_R(thingy, THINGY) \
184*4882a593Smuzhiyun static ssize_t kovaplus_sysfs_read_ ## thingy(struct file *fp, \
185*4882a593Smuzhiyun struct kobject *kobj, struct bin_attribute *attr, char *buf, \
186*4882a593Smuzhiyun loff_t off, size_t count) \
187*4882a593Smuzhiyun { \
188*4882a593Smuzhiyun return kovaplus_sysfs_read(fp, kobj, buf, off, count, \
189*4882a593Smuzhiyun KOVAPLUS_SIZE_ ## THINGY, KOVAPLUS_COMMAND_ ## THINGY); \
190*4882a593Smuzhiyun }
191*4882a593Smuzhiyun
192*4882a593Smuzhiyun #define KOVAPLUS_SYSFS_RW(thingy, THINGY) \
193*4882a593Smuzhiyun KOVAPLUS_SYSFS_W(thingy, THINGY) \
194*4882a593Smuzhiyun KOVAPLUS_SYSFS_R(thingy, THINGY)
195*4882a593Smuzhiyun
196*4882a593Smuzhiyun #define KOVAPLUS_BIN_ATTRIBUTE_RW(thingy, THINGY) \
197*4882a593Smuzhiyun KOVAPLUS_SYSFS_RW(thingy, THINGY); \
198*4882a593Smuzhiyun static struct bin_attribute bin_attr_##thingy = { \
199*4882a593Smuzhiyun .attr = { .name = #thingy, .mode = 0660 }, \
200*4882a593Smuzhiyun .size = KOVAPLUS_SIZE_ ## THINGY, \
201*4882a593Smuzhiyun .read = kovaplus_sysfs_read_ ## thingy, \
202*4882a593Smuzhiyun .write = kovaplus_sysfs_write_ ## thingy \
203*4882a593Smuzhiyun }
204*4882a593Smuzhiyun
205*4882a593Smuzhiyun #define KOVAPLUS_BIN_ATTRIBUTE_W(thingy, THINGY) \
206*4882a593Smuzhiyun KOVAPLUS_SYSFS_W(thingy, THINGY); \
207*4882a593Smuzhiyun static struct bin_attribute bin_attr_##thingy = { \
208*4882a593Smuzhiyun .attr = { .name = #thingy, .mode = 0220 }, \
209*4882a593Smuzhiyun .size = KOVAPLUS_SIZE_ ## THINGY, \
210*4882a593Smuzhiyun .write = kovaplus_sysfs_write_ ## thingy \
211*4882a593Smuzhiyun }
212*4882a593Smuzhiyun KOVAPLUS_BIN_ATTRIBUTE_W(control, CONTROL);
213*4882a593Smuzhiyun KOVAPLUS_BIN_ATTRIBUTE_RW(info, INFO);
214*4882a593Smuzhiyun KOVAPLUS_BIN_ATTRIBUTE_RW(profile_settings, PROFILE_SETTINGS);
215*4882a593Smuzhiyun KOVAPLUS_BIN_ATTRIBUTE_RW(profile_buttons, PROFILE_BUTTONS);
216*4882a593Smuzhiyun
kovaplus_sysfs_read_profilex_settings(struct file * fp,struct kobject * kobj,struct bin_attribute * attr,char * buf,loff_t off,size_t count)217*4882a593Smuzhiyun static ssize_t kovaplus_sysfs_read_profilex_settings(struct file *fp,
218*4882a593Smuzhiyun struct kobject *kobj, struct bin_attribute *attr, char *buf,
219*4882a593Smuzhiyun loff_t off, size_t count)
220*4882a593Smuzhiyun {
221*4882a593Smuzhiyun struct device *dev = kobj_to_dev(kobj)->parent->parent;
222*4882a593Smuzhiyun struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
223*4882a593Smuzhiyun ssize_t retval;
224*4882a593Smuzhiyun
225*4882a593Smuzhiyun retval = kovaplus_select_profile(usb_dev, *(uint *)(attr->private),
226*4882a593Smuzhiyun KOVAPLUS_CONTROL_REQUEST_PROFILE_SETTINGS);
227*4882a593Smuzhiyun if (retval)
228*4882a593Smuzhiyun return retval;
229*4882a593Smuzhiyun
230*4882a593Smuzhiyun return kovaplus_sysfs_read(fp, kobj, buf, off, count,
231*4882a593Smuzhiyun KOVAPLUS_SIZE_PROFILE_SETTINGS,
232*4882a593Smuzhiyun KOVAPLUS_COMMAND_PROFILE_SETTINGS);
233*4882a593Smuzhiyun }
234*4882a593Smuzhiyun
kovaplus_sysfs_read_profilex_buttons(struct file * fp,struct kobject * kobj,struct bin_attribute * attr,char * buf,loff_t off,size_t count)235*4882a593Smuzhiyun static ssize_t kovaplus_sysfs_read_profilex_buttons(struct file *fp,
236*4882a593Smuzhiyun struct kobject *kobj, struct bin_attribute *attr, char *buf,
237*4882a593Smuzhiyun loff_t off, size_t count)
238*4882a593Smuzhiyun {
239*4882a593Smuzhiyun struct device *dev = kobj_to_dev(kobj)->parent->parent;
240*4882a593Smuzhiyun struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
241*4882a593Smuzhiyun ssize_t retval;
242*4882a593Smuzhiyun
243*4882a593Smuzhiyun retval = kovaplus_select_profile(usb_dev, *(uint *)(attr->private),
244*4882a593Smuzhiyun KOVAPLUS_CONTROL_REQUEST_PROFILE_BUTTONS);
245*4882a593Smuzhiyun if (retval)
246*4882a593Smuzhiyun return retval;
247*4882a593Smuzhiyun
248*4882a593Smuzhiyun return kovaplus_sysfs_read(fp, kobj, buf, off, count,
249*4882a593Smuzhiyun KOVAPLUS_SIZE_PROFILE_BUTTONS,
250*4882a593Smuzhiyun KOVAPLUS_COMMAND_PROFILE_BUTTONS);
251*4882a593Smuzhiyun }
252*4882a593Smuzhiyun
253*4882a593Smuzhiyun #define PROFILE_ATTR(number) \
254*4882a593Smuzhiyun static struct bin_attribute bin_attr_profile##number##_settings = { \
255*4882a593Smuzhiyun .attr = { .name = "profile" #number "_settings", .mode = 0440 }, \
256*4882a593Smuzhiyun .size = KOVAPLUS_SIZE_PROFILE_SETTINGS, \
257*4882a593Smuzhiyun .read = kovaplus_sysfs_read_profilex_settings, \
258*4882a593Smuzhiyun .private = &profile_numbers[number-1], \
259*4882a593Smuzhiyun }; \
260*4882a593Smuzhiyun static struct bin_attribute bin_attr_profile##number##_buttons = { \
261*4882a593Smuzhiyun .attr = { .name = "profile" #number "_buttons", .mode = 0440 }, \
262*4882a593Smuzhiyun .size = KOVAPLUS_SIZE_PROFILE_BUTTONS, \
263*4882a593Smuzhiyun .read = kovaplus_sysfs_read_profilex_buttons, \
264*4882a593Smuzhiyun .private = &profile_numbers[number-1], \
265*4882a593Smuzhiyun };
266*4882a593Smuzhiyun PROFILE_ATTR(1);
267*4882a593Smuzhiyun PROFILE_ATTR(2);
268*4882a593Smuzhiyun PROFILE_ATTR(3);
269*4882a593Smuzhiyun PROFILE_ATTR(4);
270*4882a593Smuzhiyun PROFILE_ATTR(5);
271*4882a593Smuzhiyun
kovaplus_sysfs_show_actual_profile(struct device * dev,struct device_attribute * attr,char * buf)272*4882a593Smuzhiyun static ssize_t kovaplus_sysfs_show_actual_profile(struct device *dev,
273*4882a593Smuzhiyun struct device_attribute *attr, char *buf)
274*4882a593Smuzhiyun {
275*4882a593Smuzhiyun struct kovaplus_device *kovaplus =
276*4882a593Smuzhiyun hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
277*4882a593Smuzhiyun return snprintf(buf, PAGE_SIZE, "%d\n", kovaplus->actual_profile);
278*4882a593Smuzhiyun }
279*4882a593Smuzhiyun
kovaplus_sysfs_set_actual_profile(struct device * dev,struct device_attribute * attr,char const * buf,size_t size)280*4882a593Smuzhiyun static ssize_t kovaplus_sysfs_set_actual_profile(struct device *dev,
281*4882a593Smuzhiyun struct device_attribute *attr, char const *buf, size_t size)
282*4882a593Smuzhiyun {
283*4882a593Smuzhiyun struct kovaplus_device *kovaplus;
284*4882a593Smuzhiyun struct usb_device *usb_dev;
285*4882a593Smuzhiyun unsigned long profile;
286*4882a593Smuzhiyun int retval;
287*4882a593Smuzhiyun struct kovaplus_roccat_report roccat_report;
288*4882a593Smuzhiyun
289*4882a593Smuzhiyun dev = dev->parent->parent;
290*4882a593Smuzhiyun kovaplus = hid_get_drvdata(dev_get_drvdata(dev));
291*4882a593Smuzhiyun usb_dev = interface_to_usbdev(to_usb_interface(dev));
292*4882a593Smuzhiyun
293*4882a593Smuzhiyun retval = kstrtoul(buf, 10, &profile);
294*4882a593Smuzhiyun if (retval)
295*4882a593Smuzhiyun return retval;
296*4882a593Smuzhiyun
297*4882a593Smuzhiyun if (profile >= 5)
298*4882a593Smuzhiyun return -EINVAL;
299*4882a593Smuzhiyun
300*4882a593Smuzhiyun mutex_lock(&kovaplus->kovaplus_lock);
301*4882a593Smuzhiyun retval = kovaplus_set_actual_profile(usb_dev, profile);
302*4882a593Smuzhiyun if (retval) {
303*4882a593Smuzhiyun mutex_unlock(&kovaplus->kovaplus_lock);
304*4882a593Smuzhiyun return retval;
305*4882a593Smuzhiyun }
306*4882a593Smuzhiyun
307*4882a593Smuzhiyun kovaplus_profile_activated(kovaplus, profile);
308*4882a593Smuzhiyun
309*4882a593Smuzhiyun roccat_report.type = KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_PROFILE_1;
310*4882a593Smuzhiyun roccat_report.profile = profile + 1;
311*4882a593Smuzhiyun roccat_report.button = 0;
312*4882a593Smuzhiyun roccat_report.data1 = profile + 1;
313*4882a593Smuzhiyun roccat_report.data2 = 0;
314*4882a593Smuzhiyun roccat_report_event(kovaplus->chrdev_minor,
315*4882a593Smuzhiyun (uint8_t const *)&roccat_report);
316*4882a593Smuzhiyun
317*4882a593Smuzhiyun mutex_unlock(&kovaplus->kovaplus_lock);
318*4882a593Smuzhiyun
319*4882a593Smuzhiyun return size;
320*4882a593Smuzhiyun }
321*4882a593Smuzhiyun static DEVICE_ATTR(actual_profile, 0660,
322*4882a593Smuzhiyun kovaplus_sysfs_show_actual_profile,
323*4882a593Smuzhiyun kovaplus_sysfs_set_actual_profile);
324*4882a593Smuzhiyun
kovaplus_sysfs_show_actual_cpi(struct device * dev,struct device_attribute * attr,char * buf)325*4882a593Smuzhiyun static ssize_t kovaplus_sysfs_show_actual_cpi(struct device *dev,
326*4882a593Smuzhiyun struct device_attribute *attr, char *buf)
327*4882a593Smuzhiyun {
328*4882a593Smuzhiyun struct kovaplus_device *kovaplus =
329*4882a593Smuzhiyun hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
330*4882a593Smuzhiyun return snprintf(buf, PAGE_SIZE, "%d\n", kovaplus->actual_cpi);
331*4882a593Smuzhiyun }
332*4882a593Smuzhiyun static DEVICE_ATTR(actual_cpi, 0440, kovaplus_sysfs_show_actual_cpi, NULL);
333*4882a593Smuzhiyun
kovaplus_sysfs_show_actual_sensitivity_x(struct device * dev,struct device_attribute * attr,char * buf)334*4882a593Smuzhiyun static ssize_t kovaplus_sysfs_show_actual_sensitivity_x(struct device *dev,
335*4882a593Smuzhiyun struct device_attribute *attr, char *buf)
336*4882a593Smuzhiyun {
337*4882a593Smuzhiyun struct kovaplus_device *kovaplus =
338*4882a593Smuzhiyun hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
339*4882a593Smuzhiyun return snprintf(buf, PAGE_SIZE, "%d\n", kovaplus->actual_x_sensitivity);
340*4882a593Smuzhiyun }
341*4882a593Smuzhiyun static DEVICE_ATTR(actual_sensitivity_x, 0440,
342*4882a593Smuzhiyun kovaplus_sysfs_show_actual_sensitivity_x, NULL);
343*4882a593Smuzhiyun
kovaplus_sysfs_show_actual_sensitivity_y(struct device * dev,struct device_attribute * attr,char * buf)344*4882a593Smuzhiyun static ssize_t kovaplus_sysfs_show_actual_sensitivity_y(struct device *dev,
345*4882a593Smuzhiyun struct device_attribute *attr, char *buf)
346*4882a593Smuzhiyun {
347*4882a593Smuzhiyun struct kovaplus_device *kovaplus =
348*4882a593Smuzhiyun hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
349*4882a593Smuzhiyun return snprintf(buf, PAGE_SIZE, "%d\n", kovaplus->actual_y_sensitivity);
350*4882a593Smuzhiyun }
351*4882a593Smuzhiyun static DEVICE_ATTR(actual_sensitivity_y, 0440,
352*4882a593Smuzhiyun kovaplus_sysfs_show_actual_sensitivity_y, NULL);
353*4882a593Smuzhiyun
kovaplus_sysfs_show_firmware_version(struct device * dev,struct device_attribute * attr,char * buf)354*4882a593Smuzhiyun static ssize_t kovaplus_sysfs_show_firmware_version(struct device *dev,
355*4882a593Smuzhiyun struct device_attribute *attr, char *buf)
356*4882a593Smuzhiyun {
357*4882a593Smuzhiyun struct kovaplus_device *kovaplus;
358*4882a593Smuzhiyun struct usb_device *usb_dev;
359*4882a593Smuzhiyun struct kovaplus_info info;
360*4882a593Smuzhiyun
361*4882a593Smuzhiyun dev = dev->parent->parent;
362*4882a593Smuzhiyun kovaplus = hid_get_drvdata(dev_get_drvdata(dev));
363*4882a593Smuzhiyun usb_dev = interface_to_usbdev(to_usb_interface(dev));
364*4882a593Smuzhiyun
365*4882a593Smuzhiyun mutex_lock(&kovaplus->kovaplus_lock);
366*4882a593Smuzhiyun roccat_common2_receive(usb_dev, KOVAPLUS_COMMAND_INFO,
367*4882a593Smuzhiyun &info, KOVAPLUS_SIZE_INFO);
368*4882a593Smuzhiyun mutex_unlock(&kovaplus->kovaplus_lock);
369*4882a593Smuzhiyun
370*4882a593Smuzhiyun return snprintf(buf, PAGE_SIZE, "%d\n", info.firmware_version);
371*4882a593Smuzhiyun }
372*4882a593Smuzhiyun static DEVICE_ATTR(firmware_version, 0440,
373*4882a593Smuzhiyun kovaplus_sysfs_show_firmware_version, NULL);
374*4882a593Smuzhiyun
375*4882a593Smuzhiyun static struct attribute *kovaplus_attrs[] = {
376*4882a593Smuzhiyun &dev_attr_actual_cpi.attr,
377*4882a593Smuzhiyun &dev_attr_firmware_version.attr,
378*4882a593Smuzhiyun &dev_attr_actual_profile.attr,
379*4882a593Smuzhiyun &dev_attr_actual_sensitivity_x.attr,
380*4882a593Smuzhiyun &dev_attr_actual_sensitivity_y.attr,
381*4882a593Smuzhiyun NULL,
382*4882a593Smuzhiyun };
383*4882a593Smuzhiyun
384*4882a593Smuzhiyun static struct bin_attribute *kovaplus_bin_attributes[] = {
385*4882a593Smuzhiyun &bin_attr_control,
386*4882a593Smuzhiyun &bin_attr_info,
387*4882a593Smuzhiyun &bin_attr_profile_settings,
388*4882a593Smuzhiyun &bin_attr_profile_buttons,
389*4882a593Smuzhiyun &bin_attr_profile1_settings,
390*4882a593Smuzhiyun &bin_attr_profile2_settings,
391*4882a593Smuzhiyun &bin_attr_profile3_settings,
392*4882a593Smuzhiyun &bin_attr_profile4_settings,
393*4882a593Smuzhiyun &bin_attr_profile5_settings,
394*4882a593Smuzhiyun &bin_attr_profile1_buttons,
395*4882a593Smuzhiyun &bin_attr_profile2_buttons,
396*4882a593Smuzhiyun &bin_attr_profile3_buttons,
397*4882a593Smuzhiyun &bin_attr_profile4_buttons,
398*4882a593Smuzhiyun &bin_attr_profile5_buttons,
399*4882a593Smuzhiyun NULL,
400*4882a593Smuzhiyun };
401*4882a593Smuzhiyun
402*4882a593Smuzhiyun static const struct attribute_group kovaplus_group = {
403*4882a593Smuzhiyun .attrs = kovaplus_attrs,
404*4882a593Smuzhiyun .bin_attrs = kovaplus_bin_attributes,
405*4882a593Smuzhiyun };
406*4882a593Smuzhiyun
407*4882a593Smuzhiyun static const struct attribute_group *kovaplus_groups[] = {
408*4882a593Smuzhiyun &kovaplus_group,
409*4882a593Smuzhiyun NULL,
410*4882a593Smuzhiyun };
411*4882a593Smuzhiyun
kovaplus_init_kovaplus_device_struct(struct usb_device * usb_dev,struct kovaplus_device * kovaplus)412*4882a593Smuzhiyun static int kovaplus_init_kovaplus_device_struct(struct usb_device *usb_dev,
413*4882a593Smuzhiyun struct kovaplus_device *kovaplus)
414*4882a593Smuzhiyun {
415*4882a593Smuzhiyun int retval, i;
416*4882a593Smuzhiyun static uint wait = 70; /* device will freeze with just 60 */
417*4882a593Smuzhiyun
418*4882a593Smuzhiyun mutex_init(&kovaplus->kovaplus_lock);
419*4882a593Smuzhiyun
420*4882a593Smuzhiyun for (i = 0; i < 5; ++i) {
421*4882a593Smuzhiyun msleep(wait);
422*4882a593Smuzhiyun retval = kovaplus_get_profile_settings(usb_dev,
423*4882a593Smuzhiyun &kovaplus->profile_settings[i], i);
424*4882a593Smuzhiyun if (retval)
425*4882a593Smuzhiyun return retval;
426*4882a593Smuzhiyun
427*4882a593Smuzhiyun msleep(wait);
428*4882a593Smuzhiyun retval = kovaplus_get_profile_buttons(usb_dev,
429*4882a593Smuzhiyun &kovaplus->profile_buttons[i], i);
430*4882a593Smuzhiyun if (retval)
431*4882a593Smuzhiyun return retval;
432*4882a593Smuzhiyun }
433*4882a593Smuzhiyun
434*4882a593Smuzhiyun msleep(wait);
435*4882a593Smuzhiyun retval = kovaplus_get_actual_profile(usb_dev);
436*4882a593Smuzhiyun if (retval < 0)
437*4882a593Smuzhiyun return retval;
438*4882a593Smuzhiyun kovaplus_profile_activated(kovaplus, retval);
439*4882a593Smuzhiyun
440*4882a593Smuzhiyun return 0;
441*4882a593Smuzhiyun }
442*4882a593Smuzhiyun
kovaplus_init_specials(struct hid_device * hdev)443*4882a593Smuzhiyun static int kovaplus_init_specials(struct hid_device *hdev)
444*4882a593Smuzhiyun {
445*4882a593Smuzhiyun struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
446*4882a593Smuzhiyun struct usb_device *usb_dev = interface_to_usbdev(intf);
447*4882a593Smuzhiyun struct kovaplus_device *kovaplus;
448*4882a593Smuzhiyun int retval;
449*4882a593Smuzhiyun
450*4882a593Smuzhiyun if (intf->cur_altsetting->desc.bInterfaceProtocol
451*4882a593Smuzhiyun == USB_INTERFACE_PROTOCOL_MOUSE) {
452*4882a593Smuzhiyun
453*4882a593Smuzhiyun kovaplus = kzalloc(sizeof(*kovaplus), GFP_KERNEL);
454*4882a593Smuzhiyun if (!kovaplus) {
455*4882a593Smuzhiyun hid_err(hdev, "can't alloc device descriptor\n");
456*4882a593Smuzhiyun return -ENOMEM;
457*4882a593Smuzhiyun }
458*4882a593Smuzhiyun hid_set_drvdata(hdev, kovaplus);
459*4882a593Smuzhiyun
460*4882a593Smuzhiyun retval = kovaplus_init_kovaplus_device_struct(usb_dev, kovaplus);
461*4882a593Smuzhiyun if (retval) {
462*4882a593Smuzhiyun hid_err(hdev, "couldn't init struct kovaplus_device\n");
463*4882a593Smuzhiyun goto exit_free;
464*4882a593Smuzhiyun }
465*4882a593Smuzhiyun
466*4882a593Smuzhiyun retval = roccat_connect(kovaplus_class, hdev,
467*4882a593Smuzhiyun sizeof(struct kovaplus_roccat_report));
468*4882a593Smuzhiyun if (retval < 0) {
469*4882a593Smuzhiyun hid_err(hdev, "couldn't init char dev\n");
470*4882a593Smuzhiyun } else {
471*4882a593Smuzhiyun kovaplus->chrdev_minor = retval;
472*4882a593Smuzhiyun kovaplus->roccat_claimed = 1;
473*4882a593Smuzhiyun }
474*4882a593Smuzhiyun
475*4882a593Smuzhiyun } else {
476*4882a593Smuzhiyun hid_set_drvdata(hdev, NULL);
477*4882a593Smuzhiyun }
478*4882a593Smuzhiyun
479*4882a593Smuzhiyun return 0;
480*4882a593Smuzhiyun exit_free:
481*4882a593Smuzhiyun kfree(kovaplus);
482*4882a593Smuzhiyun return retval;
483*4882a593Smuzhiyun }
484*4882a593Smuzhiyun
kovaplus_remove_specials(struct hid_device * hdev)485*4882a593Smuzhiyun static void kovaplus_remove_specials(struct hid_device *hdev)
486*4882a593Smuzhiyun {
487*4882a593Smuzhiyun struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
488*4882a593Smuzhiyun struct kovaplus_device *kovaplus;
489*4882a593Smuzhiyun
490*4882a593Smuzhiyun if (intf->cur_altsetting->desc.bInterfaceProtocol
491*4882a593Smuzhiyun == USB_INTERFACE_PROTOCOL_MOUSE) {
492*4882a593Smuzhiyun kovaplus = hid_get_drvdata(hdev);
493*4882a593Smuzhiyun if (kovaplus->roccat_claimed)
494*4882a593Smuzhiyun roccat_disconnect(kovaplus->chrdev_minor);
495*4882a593Smuzhiyun kfree(kovaplus);
496*4882a593Smuzhiyun }
497*4882a593Smuzhiyun }
498*4882a593Smuzhiyun
kovaplus_probe(struct hid_device * hdev,const struct hid_device_id * id)499*4882a593Smuzhiyun static int kovaplus_probe(struct hid_device *hdev,
500*4882a593Smuzhiyun const struct hid_device_id *id)
501*4882a593Smuzhiyun {
502*4882a593Smuzhiyun int retval;
503*4882a593Smuzhiyun
504*4882a593Smuzhiyun if (!hid_is_usb(hdev))
505*4882a593Smuzhiyun return -EINVAL;
506*4882a593Smuzhiyun
507*4882a593Smuzhiyun retval = hid_parse(hdev);
508*4882a593Smuzhiyun if (retval) {
509*4882a593Smuzhiyun hid_err(hdev, "parse failed\n");
510*4882a593Smuzhiyun goto exit;
511*4882a593Smuzhiyun }
512*4882a593Smuzhiyun
513*4882a593Smuzhiyun retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
514*4882a593Smuzhiyun if (retval) {
515*4882a593Smuzhiyun hid_err(hdev, "hw start failed\n");
516*4882a593Smuzhiyun goto exit;
517*4882a593Smuzhiyun }
518*4882a593Smuzhiyun
519*4882a593Smuzhiyun retval = kovaplus_init_specials(hdev);
520*4882a593Smuzhiyun if (retval) {
521*4882a593Smuzhiyun hid_err(hdev, "couldn't install mouse\n");
522*4882a593Smuzhiyun goto exit_stop;
523*4882a593Smuzhiyun }
524*4882a593Smuzhiyun
525*4882a593Smuzhiyun return 0;
526*4882a593Smuzhiyun
527*4882a593Smuzhiyun exit_stop:
528*4882a593Smuzhiyun hid_hw_stop(hdev);
529*4882a593Smuzhiyun exit:
530*4882a593Smuzhiyun return retval;
531*4882a593Smuzhiyun }
532*4882a593Smuzhiyun
kovaplus_remove(struct hid_device * hdev)533*4882a593Smuzhiyun static void kovaplus_remove(struct hid_device *hdev)
534*4882a593Smuzhiyun {
535*4882a593Smuzhiyun kovaplus_remove_specials(hdev);
536*4882a593Smuzhiyun hid_hw_stop(hdev);
537*4882a593Smuzhiyun }
538*4882a593Smuzhiyun
kovaplus_keep_values_up_to_date(struct kovaplus_device * kovaplus,u8 const * data)539*4882a593Smuzhiyun static void kovaplus_keep_values_up_to_date(struct kovaplus_device *kovaplus,
540*4882a593Smuzhiyun u8 const *data)
541*4882a593Smuzhiyun {
542*4882a593Smuzhiyun struct kovaplus_mouse_report_button const *button_report;
543*4882a593Smuzhiyun
544*4882a593Smuzhiyun if (data[0] != KOVAPLUS_MOUSE_REPORT_NUMBER_BUTTON)
545*4882a593Smuzhiyun return;
546*4882a593Smuzhiyun
547*4882a593Smuzhiyun button_report = (struct kovaplus_mouse_report_button const *)data;
548*4882a593Smuzhiyun
549*4882a593Smuzhiyun switch (button_report->type) {
550*4882a593Smuzhiyun case KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_PROFILE_1:
551*4882a593Smuzhiyun kovaplus_profile_activated(kovaplus, button_report->data1 - 1);
552*4882a593Smuzhiyun break;
553*4882a593Smuzhiyun case KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_CPI:
554*4882a593Smuzhiyun kovaplus->actual_cpi = kovaplus_convert_event_cpi(button_report->data1);
555*4882a593Smuzhiyun break;
556*4882a593Smuzhiyun case KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_SENSITIVITY:
557*4882a593Smuzhiyun kovaplus->actual_x_sensitivity = button_report->data1;
558*4882a593Smuzhiyun kovaplus->actual_y_sensitivity = button_report->data2;
559*4882a593Smuzhiyun break;
560*4882a593Smuzhiyun default:
561*4882a593Smuzhiyun break;
562*4882a593Smuzhiyun }
563*4882a593Smuzhiyun }
564*4882a593Smuzhiyun
kovaplus_report_to_chrdev(struct kovaplus_device const * kovaplus,u8 const * data)565*4882a593Smuzhiyun static void kovaplus_report_to_chrdev(struct kovaplus_device const *kovaplus,
566*4882a593Smuzhiyun u8 const *data)
567*4882a593Smuzhiyun {
568*4882a593Smuzhiyun struct kovaplus_roccat_report roccat_report;
569*4882a593Smuzhiyun struct kovaplus_mouse_report_button const *button_report;
570*4882a593Smuzhiyun
571*4882a593Smuzhiyun if (data[0] != KOVAPLUS_MOUSE_REPORT_NUMBER_BUTTON)
572*4882a593Smuzhiyun return;
573*4882a593Smuzhiyun
574*4882a593Smuzhiyun button_report = (struct kovaplus_mouse_report_button const *)data;
575*4882a593Smuzhiyun
576*4882a593Smuzhiyun if (button_report->type == KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_PROFILE_2)
577*4882a593Smuzhiyun return;
578*4882a593Smuzhiyun
579*4882a593Smuzhiyun roccat_report.type = button_report->type;
580*4882a593Smuzhiyun roccat_report.profile = kovaplus->actual_profile + 1;
581*4882a593Smuzhiyun
582*4882a593Smuzhiyun if (roccat_report.type == KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_MACRO ||
583*4882a593Smuzhiyun roccat_report.type == KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_SHORTCUT ||
584*4882a593Smuzhiyun roccat_report.type == KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_QUICKLAUNCH ||
585*4882a593Smuzhiyun roccat_report.type == KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_TIMER)
586*4882a593Smuzhiyun roccat_report.button = button_report->data1;
587*4882a593Smuzhiyun else
588*4882a593Smuzhiyun roccat_report.button = 0;
589*4882a593Smuzhiyun
590*4882a593Smuzhiyun if (roccat_report.type == KOVAPLUS_MOUSE_REPORT_BUTTON_TYPE_CPI)
591*4882a593Smuzhiyun roccat_report.data1 = kovaplus_convert_event_cpi(button_report->data1);
592*4882a593Smuzhiyun else
593*4882a593Smuzhiyun roccat_report.data1 = button_report->data1;
594*4882a593Smuzhiyun
595*4882a593Smuzhiyun roccat_report.data2 = button_report->data2;
596*4882a593Smuzhiyun
597*4882a593Smuzhiyun roccat_report_event(kovaplus->chrdev_minor,
598*4882a593Smuzhiyun (uint8_t const *)&roccat_report);
599*4882a593Smuzhiyun }
600*4882a593Smuzhiyun
kovaplus_raw_event(struct hid_device * hdev,struct hid_report * report,u8 * data,int size)601*4882a593Smuzhiyun static int kovaplus_raw_event(struct hid_device *hdev,
602*4882a593Smuzhiyun struct hid_report *report, u8 *data, int size)
603*4882a593Smuzhiyun {
604*4882a593Smuzhiyun struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
605*4882a593Smuzhiyun struct kovaplus_device *kovaplus = hid_get_drvdata(hdev);
606*4882a593Smuzhiyun
607*4882a593Smuzhiyun if (intf->cur_altsetting->desc.bInterfaceProtocol
608*4882a593Smuzhiyun != USB_INTERFACE_PROTOCOL_MOUSE)
609*4882a593Smuzhiyun return 0;
610*4882a593Smuzhiyun
611*4882a593Smuzhiyun if (kovaplus == NULL)
612*4882a593Smuzhiyun return 0;
613*4882a593Smuzhiyun
614*4882a593Smuzhiyun kovaplus_keep_values_up_to_date(kovaplus, data);
615*4882a593Smuzhiyun
616*4882a593Smuzhiyun if (kovaplus->roccat_claimed)
617*4882a593Smuzhiyun kovaplus_report_to_chrdev(kovaplus, data);
618*4882a593Smuzhiyun
619*4882a593Smuzhiyun return 0;
620*4882a593Smuzhiyun }
621*4882a593Smuzhiyun
622*4882a593Smuzhiyun static const struct hid_device_id kovaplus_devices[] = {
623*4882a593Smuzhiyun { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_KOVAPLUS) },
624*4882a593Smuzhiyun { }
625*4882a593Smuzhiyun };
626*4882a593Smuzhiyun
627*4882a593Smuzhiyun MODULE_DEVICE_TABLE(hid, kovaplus_devices);
628*4882a593Smuzhiyun
629*4882a593Smuzhiyun static struct hid_driver kovaplus_driver = {
630*4882a593Smuzhiyun .name = "kovaplus",
631*4882a593Smuzhiyun .id_table = kovaplus_devices,
632*4882a593Smuzhiyun .probe = kovaplus_probe,
633*4882a593Smuzhiyun .remove = kovaplus_remove,
634*4882a593Smuzhiyun .raw_event = kovaplus_raw_event
635*4882a593Smuzhiyun };
636*4882a593Smuzhiyun
kovaplus_init(void)637*4882a593Smuzhiyun static int __init kovaplus_init(void)
638*4882a593Smuzhiyun {
639*4882a593Smuzhiyun int retval;
640*4882a593Smuzhiyun
641*4882a593Smuzhiyun kovaplus_class = class_create(THIS_MODULE, "kovaplus");
642*4882a593Smuzhiyun if (IS_ERR(kovaplus_class))
643*4882a593Smuzhiyun return PTR_ERR(kovaplus_class);
644*4882a593Smuzhiyun kovaplus_class->dev_groups = kovaplus_groups;
645*4882a593Smuzhiyun
646*4882a593Smuzhiyun retval = hid_register_driver(&kovaplus_driver);
647*4882a593Smuzhiyun if (retval)
648*4882a593Smuzhiyun class_destroy(kovaplus_class);
649*4882a593Smuzhiyun return retval;
650*4882a593Smuzhiyun }
651*4882a593Smuzhiyun
kovaplus_exit(void)652*4882a593Smuzhiyun static void __exit kovaplus_exit(void)
653*4882a593Smuzhiyun {
654*4882a593Smuzhiyun hid_unregister_driver(&kovaplus_driver);
655*4882a593Smuzhiyun class_destroy(kovaplus_class);
656*4882a593Smuzhiyun }
657*4882a593Smuzhiyun
658*4882a593Smuzhiyun module_init(kovaplus_init);
659*4882a593Smuzhiyun module_exit(kovaplus_exit);
660*4882a593Smuzhiyun
661*4882a593Smuzhiyun MODULE_AUTHOR("Stefan Achatz");
662*4882a593Smuzhiyun MODULE_DESCRIPTION("USB Roccat Kova[+] driver");
663*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
664