12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2d41c2a70SStefan Achatz /*
3d41c2a70SStefan Achatz * Roccat Isku driver for Linux
4d41c2a70SStefan Achatz *
5d41c2a70SStefan Achatz * Copyright (c) 2011 Stefan Achatz <erazor_de@users.sourceforge.net>
6d41c2a70SStefan Achatz */
7d41c2a70SStefan Achatz
8d41c2a70SStefan Achatz /*
9d41c2a70SStefan Achatz */
10d41c2a70SStefan Achatz
11d41c2a70SStefan Achatz /*
12d41c2a70SStefan Achatz * Roccat Isku is a gamer keyboard with macro keys that can be configured in
13d41c2a70SStefan Achatz * 5 profiles.
14d41c2a70SStefan Achatz */
15d41c2a70SStefan Achatz
16d41c2a70SStefan Achatz #include <linux/device.h>
17d41c2a70SStefan Achatz #include <linux/input.h>
18d41c2a70SStefan Achatz #include <linux/hid.h>
19d41c2a70SStefan Achatz #include <linux/module.h>
20d41c2a70SStefan Achatz #include <linux/slab.h>
21d41c2a70SStefan Achatz #include <linux/hid-roccat.h>
22d41c2a70SStefan Achatz #include "hid-ids.h"
23d41c2a70SStefan Achatz #include "hid-roccat-common.h"
24d41c2a70SStefan Achatz #include "hid-roccat-isku.h"
25d41c2a70SStefan Achatz
isku_profile_activated(struct isku_device * isku,uint new_profile)26d41c2a70SStefan Achatz static void isku_profile_activated(struct isku_device *isku, uint new_profile)
27d41c2a70SStefan Achatz {
28d41c2a70SStefan Achatz isku->actual_profile = new_profile;
29d41c2a70SStefan Achatz }
30d41c2a70SStefan Achatz
isku_receive(struct usb_device * usb_dev,uint command,void * buf,uint size)31d41c2a70SStefan Achatz static int isku_receive(struct usb_device *usb_dev, uint command,
32d41c2a70SStefan Achatz void *buf, uint size)
33d41c2a70SStefan Achatz {
347392d73bSStefan Achatz return roccat_common2_receive(usb_dev, command, buf, size);
35d41c2a70SStefan Achatz }
36d41c2a70SStefan Achatz
isku_get_actual_profile(struct usb_device * usb_dev)37d41c2a70SStefan Achatz static int isku_get_actual_profile(struct usb_device *usb_dev)
38d41c2a70SStefan Achatz {
39d41c2a70SStefan Achatz struct isku_actual_profile buf;
40d41c2a70SStefan Achatz int retval;
41d41c2a70SStefan Achatz
42d41c2a70SStefan Achatz retval = isku_receive(usb_dev, ISKU_COMMAND_ACTUAL_PROFILE,
43d41c2a70SStefan Achatz &buf, sizeof(struct isku_actual_profile));
44d41c2a70SStefan Achatz return retval ? retval : buf.actual_profile;
45d41c2a70SStefan Achatz }
46d41c2a70SStefan Achatz
isku_set_actual_profile(struct usb_device * usb_dev,int new_profile)47d41c2a70SStefan Achatz static int isku_set_actual_profile(struct usb_device *usb_dev, int new_profile)
48d41c2a70SStefan Achatz {
49d41c2a70SStefan Achatz struct isku_actual_profile buf;
50d41c2a70SStefan Achatz
51d41c2a70SStefan Achatz buf.command = ISKU_COMMAND_ACTUAL_PROFILE;
52d41c2a70SStefan Achatz buf.size = sizeof(struct isku_actual_profile);
53d41c2a70SStefan Achatz buf.actual_profile = new_profile;
547392d73bSStefan Achatz return roccat_common2_send_with_status(usb_dev,
554728f2dcSStefan Achatz ISKU_COMMAND_ACTUAL_PROFILE, &buf,
56d41c2a70SStefan Achatz sizeof(struct isku_actual_profile));
57d41c2a70SStefan Achatz }
58d41c2a70SStefan Achatz
isku_sysfs_show_actual_profile(struct device * dev,struct device_attribute * attr,char * buf)59d41c2a70SStefan Achatz static ssize_t isku_sysfs_show_actual_profile(struct device *dev,
60d41c2a70SStefan Achatz struct device_attribute *attr, char *buf)
61d41c2a70SStefan Achatz {
62d41c2a70SStefan Achatz struct isku_device *isku =
63d41c2a70SStefan Achatz hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
64d41c2a70SStefan Achatz return snprintf(buf, PAGE_SIZE, "%d\n", isku->actual_profile);
65d41c2a70SStefan Achatz }
66d41c2a70SStefan Achatz
isku_sysfs_set_actual_profile(struct device * dev,struct device_attribute * attr,char const * buf,size_t size)67d41c2a70SStefan Achatz static ssize_t isku_sysfs_set_actual_profile(struct device *dev,
68d41c2a70SStefan Achatz struct device_attribute *attr, char const *buf, size_t size)
69d41c2a70SStefan Achatz {
70d41c2a70SStefan Achatz struct isku_device *isku;
71d41c2a70SStefan Achatz struct usb_device *usb_dev;
72d41c2a70SStefan Achatz unsigned long profile;
73d41c2a70SStefan Achatz int retval;
74d41c2a70SStefan Achatz struct isku_roccat_report roccat_report;
75d41c2a70SStefan Achatz
76d41c2a70SStefan Achatz dev = dev->parent->parent;
77d41c2a70SStefan Achatz isku = hid_get_drvdata(dev_get_drvdata(dev));
78d41c2a70SStefan Achatz usb_dev = interface_to_usbdev(to_usb_interface(dev));
79d41c2a70SStefan Achatz
80dfc450b5SJingoo Han retval = kstrtoul(buf, 10, &profile);
81d41c2a70SStefan Achatz if (retval)
82d41c2a70SStefan Achatz return retval;
83d41c2a70SStefan Achatz
84d41c2a70SStefan Achatz if (profile > 4)
85d41c2a70SStefan Achatz return -EINVAL;
86d41c2a70SStefan Achatz
87d41c2a70SStefan Achatz mutex_lock(&isku->isku_lock);
88d41c2a70SStefan Achatz
89d41c2a70SStefan Achatz retval = isku_set_actual_profile(usb_dev, profile);
90d41c2a70SStefan Achatz if (retval) {
91d41c2a70SStefan Achatz mutex_unlock(&isku->isku_lock);
92d41c2a70SStefan Achatz return retval;
93d41c2a70SStefan Achatz }
94d41c2a70SStefan Achatz
95d41c2a70SStefan Achatz isku_profile_activated(isku, profile);
96d41c2a70SStefan Achatz
97d41c2a70SStefan Achatz roccat_report.event = ISKU_REPORT_BUTTON_EVENT_PROFILE;
98d41c2a70SStefan Achatz roccat_report.data1 = profile + 1;
99d41c2a70SStefan Achatz roccat_report.data2 = 0;
100d41c2a70SStefan Achatz roccat_report.profile = profile + 1;
101d41c2a70SStefan Achatz roccat_report_event(isku->chrdev_minor, (uint8_t const *)&roccat_report);
102d41c2a70SStefan Achatz
103d41c2a70SStefan Achatz mutex_unlock(&isku->isku_lock);
104d41c2a70SStefan Achatz
105d41c2a70SStefan Achatz return size;
106d41c2a70SStefan Achatz }
10746a58c44SGreg Kroah-Hartman static DEVICE_ATTR(actual_profile, 0660, isku_sysfs_show_actual_profile,
10846a58c44SGreg Kroah-Hartman isku_sysfs_set_actual_profile);
109d41c2a70SStefan Achatz
11046a58c44SGreg Kroah-Hartman static struct attribute *isku_attrs[] = {
11146a58c44SGreg Kroah-Hartman &dev_attr_actual_profile.attr,
11246a58c44SGreg Kroah-Hartman NULL,
113d41c2a70SStefan Achatz };
114d41c2a70SStefan Achatz
isku_sysfs_read(struct file * fp,struct kobject * kobj,char * buf,loff_t off,size_t count,size_t real_size,uint command)115d41c2a70SStefan Achatz static ssize_t isku_sysfs_read(struct file *fp, struct kobject *kobj,
116d41c2a70SStefan Achatz char *buf, loff_t off, size_t count,
117d41c2a70SStefan Achatz size_t real_size, uint command)
118d41c2a70SStefan Achatz {
1192cf83833SGeliang Tang struct device *dev = kobj_to_dev(kobj)->parent->parent;
120d41c2a70SStefan Achatz struct isku_device *isku = hid_get_drvdata(dev_get_drvdata(dev));
121d41c2a70SStefan Achatz struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
122d41c2a70SStefan Achatz int retval;
123d41c2a70SStefan Achatz
124d41c2a70SStefan Achatz if (off >= real_size)
125d41c2a70SStefan Achatz return 0;
126d41c2a70SStefan Achatz
127ce716965SStefan Achatz if (off != 0 || count > real_size)
128d41c2a70SStefan Achatz return -EINVAL;
129d41c2a70SStefan Achatz
130d41c2a70SStefan Achatz mutex_lock(&isku->isku_lock);
131ce716965SStefan Achatz retval = isku_receive(usb_dev, command, buf, count);
132d41c2a70SStefan Achatz mutex_unlock(&isku->isku_lock);
133d41c2a70SStefan Achatz
134ce716965SStefan Achatz return retval ? retval : count;
135d41c2a70SStefan Achatz }
136d41c2a70SStefan Achatz
isku_sysfs_write(struct file * fp,struct kobject * kobj,void const * buf,loff_t off,size_t count,size_t real_size,uint command)137d41c2a70SStefan Achatz static ssize_t isku_sysfs_write(struct file *fp, struct kobject *kobj,
138d41c2a70SStefan Achatz void const *buf, loff_t off, size_t count,
139d41c2a70SStefan Achatz size_t real_size, uint command)
140d41c2a70SStefan Achatz {
1412cf83833SGeliang Tang struct device *dev = kobj_to_dev(kobj)->parent->parent;
142d41c2a70SStefan Achatz struct isku_device *isku = hid_get_drvdata(dev_get_drvdata(dev));
143d41c2a70SStefan Achatz struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
144d41c2a70SStefan Achatz int retval;
145d41c2a70SStefan Achatz
146ce716965SStefan Achatz if (off != 0 || count > real_size)
147d41c2a70SStefan Achatz return -EINVAL;
148d41c2a70SStefan Achatz
149d41c2a70SStefan Achatz mutex_lock(&isku->isku_lock);
1507392d73bSStefan Achatz retval = roccat_common2_send_with_status(usb_dev, command,
151ce716965SStefan Achatz (void *)buf, count);
152d41c2a70SStefan Achatz mutex_unlock(&isku->isku_lock);
153d41c2a70SStefan Achatz
154ce716965SStefan Achatz return retval ? retval : count;
155d41c2a70SStefan Achatz }
156d41c2a70SStefan Achatz
157d41c2a70SStefan Achatz #define ISKU_SYSFS_W(thingy, THINGY) \
158d41c2a70SStefan Achatz static ssize_t isku_sysfs_write_ ## thingy(struct file *fp, struct kobject *kobj, \
159d41c2a70SStefan Achatz struct bin_attribute *attr, char *buf, \
160d41c2a70SStefan Achatz loff_t off, size_t count) \
161d41c2a70SStefan Achatz { \
162d41c2a70SStefan Achatz return isku_sysfs_write(fp, kobj, buf, off, count, \
1636e5920ddSStefan Achatz ISKU_SIZE_ ## THINGY, ISKU_COMMAND_ ## THINGY); \
164d41c2a70SStefan Achatz }
165d41c2a70SStefan Achatz
166d41c2a70SStefan Achatz #define ISKU_SYSFS_R(thingy, THINGY) \
167d41c2a70SStefan Achatz static ssize_t isku_sysfs_read_ ## thingy(struct file *fp, struct kobject *kobj, \
168d41c2a70SStefan Achatz struct bin_attribute *attr, char *buf, \
169d41c2a70SStefan Achatz loff_t off, size_t count) \
170d41c2a70SStefan Achatz { \
171d41c2a70SStefan Achatz return isku_sysfs_read(fp, kobj, buf, off, count, \
1726e5920ddSStefan Achatz ISKU_SIZE_ ## THINGY, ISKU_COMMAND_ ## THINGY); \
173d41c2a70SStefan Achatz }
174d41c2a70SStefan Achatz
175d41c2a70SStefan Achatz #define ISKU_SYSFS_RW(thingy, THINGY) \
176d41c2a70SStefan Achatz ISKU_SYSFS_R(thingy, THINGY) \
177d41c2a70SStefan Achatz ISKU_SYSFS_W(thingy, THINGY)
178d41c2a70SStefan Achatz
1796e5920ddSStefan Achatz #define ISKU_BIN_ATTR_RW(thingy, THINGY) \
1808daf8c3aSGreg Kroah-Hartman ISKU_SYSFS_RW(thingy, THINGY); \
1818daf8c3aSGreg Kroah-Hartman static struct bin_attribute bin_attr_##thingy = { \
182d41c2a70SStefan Achatz .attr = { .name = #thingy, .mode = 0660 }, \
1836e5920ddSStefan Achatz .size = ISKU_SIZE_ ## THINGY, \
184d41c2a70SStefan Achatz .read = isku_sysfs_read_ ## thingy, \
185d41c2a70SStefan Achatz .write = isku_sysfs_write_ ## thingy \
186d41c2a70SStefan Achatz }
187d41c2a70SStefan Achatz
1886e5920ddSStefan Achatz #define ISKU_BIN_ATTR_R(thingy, THINGY) \
1898daf8c3aSGreg Kroah-Hartman ISKU_SYSFS_R(thingy, THINGY); \
1908daf8c3aSGreg Kroah-Hartman static struct bin_attribute bin_attr_##thingy = { \
191d41c2a70SStefan Achatz .attr = { .name = #thingy, .mode = 0440 }, \
1926e5920ddSStefan Achatz .size = ISKU_SIZE_ ## THINGY, \
193d41c2a70SStefan Achatz .read = isku_sysfs_read_ ## thingy, \
194d41c2a70SStefan Achatz }
195d41c2a70SStefan Achatz
1966e5920ddSStefan Achatz #define ISKU_BIN_ATTR_W(thingy, THINGY) \
1978daf8c3aSGreg Kroah-Hartman ISKU_SYSFS_W(thingy, THINGY); \
1988daf8c3aSGreg Kroah-Hartman static struct bin_attribute bin_attr_##thingy = { \
199d41c2a70SStefan Achatz .attr = { .name = #thingy, .mode = 0220 }, \
2006e5920ddSStefan Achatz .size = ISKU_SIZE_ ## THINGY, \
201d41c2a70SStefan Achatz .write = isku_sysfs_write_ ## thingy \
202d41c2a70SStefan Achatz }
203d41c2a70SStefan Achatz
2048daf8c3aSGreg Kroah-Hartman ISKU_BIN_ATTR_RW(macro, MACRO);
2058daf8c3aSGreg Kroah-Hartman ISKU_BIN_ATTR_RW(keys_function, KEYS_FUNCTION);
2068daf8c3aSGreg Kroah-Hartman ISKU_BIN_ATTR_RW(keys_easyzone, KEYS_EASYZONE);
2078daf8c3aSGreg Kroah-Hartman ISKU_BIN_ATTR_RW(keys_media, KEYS_MEDIA);
2088daf8c3aSGreg Kroah-Hartman ISKU_BIN_ATTR_RW(keys_thumbster, KEYS_THUMBSTER);
2098daf8c3aSGreg Kroah-Hartman ISKU_BIN_ATTR_RW(keys_macro, KEYS_MACRO);
2108daf8c3aSGreg Kroah-Hartman ISKU_BIN_ATTR_RW(keys_capslock, KEYS_CAPSLOCK);
2118daf8c3aSGreg Kroah-Hartman ISKU_BIN_ATTR_RW(light, LIGHT);
2128daf8c3aSGreg Kroah-Hartman ISKU_BIN_ATTR_RW(key_mask, KEY_MASK);
2138daf8c3aSGreg Kroah-Hartman ISKU_BIN_ATTR_RW(last_set, LAST_SET);
2148daf8c3aSGreg Kroah-Hartman ISKU_BIN_ATTR_W(talk, TALK);
2158daf8c3aSGreg Kroah-Hartman ISKU_BIN_ATTR_W(talkfx, TALKFX);
2168daf8c3aSGreg Kroah-Hartman ISKU_BIN_ATTR_W(control, CONTROL);
2178daf8c3aSGreg Kroah-Hartman ISKU_BIN_ATTR_W(reset, RESET);
2188daf8c3aSGreg Kroah-Hartman ISKU_BIN_ATTR_R(info, INFO);
219d41c2a70SStefan Achatz
2208daf8c3aSGreg Kroah-Hartman static struct bin_attribute *isku_bin_attributes[] = {
2218daf8c3aSGreg Kroah-Hartman &bin_attr_macro,
2228daf8c3aSGreg Kroah-Hartman &bin_attr_keys_function,
2238daf8c3aSGreg Kroah-Hartman &bin_attr_keys_easyzone,
2248daf8c3aSGreg Kroah-Hartman &bin_attr_keys_media,
2258daf8c3aSGreg Kroah-Hartman &bin_attr_keys_thumbster,
2268daf8c3aSGreg Kroah-Hartman &bin_attr_keys_macro,
2278daf8c3aSGreg Kroah-Hartman &bin_attr_keys_capslock,
2288daf8c3aSGreg Kroah-Hartman &bin_attr_light,
2298daf8c3aSGreg Kroah-Hartman &bin_attr_key_mask,
2308daf8c3aSGreg Kroah-Hartman &bin_attr_last_set,
2318daf8c3aSGreg Kroah-Hartman &bin_attr_talk,
2328daf8c3aSGreg Kroah-Hartman &bin_attr_talkfx,
2338daf8c3aSGreg Kroah-Hartman &bin_attr_control,
2348daf8c3aSGreg Kroah-Hartman &bin_attr_reset,
2358daf8c3aSGreg Kroah-Hartman &bin_attr_info,
2368daf8c3aSGreg Kroah-Hartman NULL,
2378daf8c3aSGreg Kroah-Hartman };
2388daf8c3aSGreg Kroah-Hartman
2398daf8c3aSGreg Kroah-Hartman static const struct attribute_group isku_group = {
2408daf8c3aSGreg Kroah-Hartman .attrs = isku_attrs,
2418daf8c3aSGreg Kroah-Hartman .bin_attrs = isku_bin_attributes,
2428daf8c3aSGreg Kroah-Hartman };
2438daf8c3aSGreg Kroah-Hartman
2448daf8c3aSGreg Kroah-Hartman static const struct attribute_group *isku_groups[] = {
2458daf8c3aSGreg Kroah-Hartman &isku_group,
2468daf8c3aSGreg Kroah-Hartman NULL,
247d41c2a70SStefan Achatz };
248d41c2a70SStefan Achatz
249*afdf5dd3SIvan Orlov static const struct class isku_class = {
250*afdf5dd3SIvan Orlov .name = "isku",
251*afdf5dd3SIvan Orlov .dev_groups = isku_groups,
252*afdf5dd3SIvan Orlov };
253*afdf5dd3SIvan Orlov
isku_init_isku_device_struct(struct usb_device * usb_dev,struct isku_device * isku)254d41c2a70SStefan Achatz static int isku_init_isku_device_struct(struct usb_device *usb_dev,
255d41c2a70SStefan Achatz struct isku_device *isku)
256d41c2a70SStefan Achatz {
257d41c2a70SStefan Achatz int retval;
258d41c2a70SStefan Achatz
259d41c2a70SStefan Achatz mutex_init(&isku->isku_lock);
260d41c2a70SStefan Achatz
261d41c2a70SStefan Achatz retval = isku_get_actual_profile(usb_dev);
262d41c2a70SStefan Achatz if (retval < 0)
263d41c2a70SStefan Achatz return retval;
264d41c2a70SStefan Achatz isku_profile_activated(isku, retval);
265d41c2a70SStefan Achatz
266d41c2a70SStefan Achatz return 0;
267d41c2a70SStefan Achatz }
268d41c2a70SStefan Achatz
isku_init_specials(struct hid_device * hdev)269d41c2a70SStefan Achatz static int isku_init_specials(struct hid_device *hdev)
270d41c2a70SStefan Achatz {
271d41c2a70SStefan Achatz struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
272d41c2a70SStefan Achatz struct usb_device *usb_dev = interface_to_usbdev(intf);
273d41c2a70SStefan Achatz struct isku_device *isku;
274d41c2a70SStefan Achatz int retval;
275d41c2a70SStefan Achatz
276d41c2a70SStefan Achatz if (intf->cur_altsetting->desc.bInterfaceProtocol
277d41c2a70SStefan Achatz != ISKU_USB_INTERFACE_PROTOCOL) {
278d41c2a70SStefan Achatz hid_set_drvdata(hdev, NULL);
279d41c2a70SStefan Achatz return 0;
280d41c2a70SStefan Achatz }
281d41c2a70SStefan Achatz
282d41c2a70SStefan Achatz isku = kzalloc(sizeof(*isku), GFP_KERNEL);
283d41c2a70SStefan Achatz if (!isku) {
284d41c2a70SStefan Achatz hid_err(hdev, "can't alloc device descriptor\n");
285d41c2a70SStefan Achatz return -ENOMEM;
286d41c2a70SStefan Achatz }
287d41c2a70SStefan Achatz hid_set_drvdata(hdev, isku);
288d41c2a70SStefan Achatz
289d41c2a70SStefan Achatz retval = isku_init_isku_device_struct(usb_dev, isku);
290d41c2a70SStefan Achatz if (retval) {
291d41c2a70SStefan Achatz hid_err(hdev, "couldn't init struct isku_device\n");
292d41c2a70SStefan Achatz goto exit_free;
293d41c2a70SStefan Achatz }
294d41c2a70SStefan Achatz
295*afdf5dd3SIvan Orlov retval = roccat_connect(&isku_class, hdev,
296d41c2a70SStefan Achatz sizeof(struct isku_roccat_report));
297d41c2a70SStefan Achatz if (retval < 0) {
298d41c2a70SStefan Achatz hid_err(hdev, "couldn't init char dev\n");
299d41c2a70SStefan Achatz } else {
300d41c2a70SStefan Achatz isku->chrdev_minor = retval;
301d41c2a70SStefan Achatz isku->roccat_claimed = 1;
302d41c2a70SStefan Achatz }
303d41c2a70SStefan Achatz
304d41c2a70SStefan Achatz return 0;
305d41c2a70SStefan Achatz exit_free:
306d41c2a70SStefan Achatz kfree(isku);
307d41c2a70SStefan Achatz return retval;
308d41c2a70SStefan Achatz }
309d41c2a70SStefan Achatz
isku_remove_specials(struct hid_device * hdev)310d41c2a70SStefan Achatz static void isku_remove_specials(struct hid_device *hdev)
311d41c2a70SStefan Achatz {
312d41c2a70SStefan Achatz struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
313d41c2a70SStefan Achatz struct isku_device *isku;
314d41c2a70SStefan Achatz
315d41c2a70SStefan Achatz if (intf->cur_altsetting->desc.bInterfaceProtocol
316d41c2a70SStefan Achatz != ISKU_USB_INTERFACE_PROTOCOL)
317d41c2a70SStefan Achatz return;
318d41c2a70SStefan Achatz
319d41c2a70SStefan Achatz isku = hid_get_drvdata(hdev);
320d41c2a70SStefan Achatz if (isku->roccat_claimed)
321d41c2a70SStefan Achatz roccat_disconnect(isku->chrdev_minor);
322d41c2a70SStefan Achatz kfree(isku);
323d41c2a70SStefan Achatz }
324d41c2a70SStefan Achatz
isku_probe(struct hid_device * hdev,const struct hid_device_id * id)325d41c2a70SStefan Achatz static int isku_probe(struct hid_device *hdev,
326d41c2a70SStefan Achatz const struct hid_device_id *id)
327d41c2a70SStefan Achatz {
328d41c2a70SStefan Achatz int retval;
329d41c2a70SStefan Achatz
33093020953SGreg Kroah-Hartman if (!hid_is_usb(hdev))
33193020953SGreg Kroah-Hartman return -EINVAL;
33293020953SGreg Kroah-Hartman
333d41c2a70SStefan Achatz retval = hid_parse(hdev);
334d41c2a70SStefan Achatz if (retval) {
335d41c2a70SStefan Achatz hid_err(hdev, "parse failed\n");
336d41c2a70SStefan Achatz goto exit;
337d41c2a70SStefan Achatz }
338d41c2a70SStefan Achatz
339d41c2a70SStefan Achatz retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
340d41c2a70SStefan Achatz if (retval) {
341d41c2a70SStefan Achatz hid_err(hdev, "hw start failed\n");
342d41c2a70SStefan Achatz goto exit;
343d41c2a70SStefan Achatz }
344d41c2a70SStefan Achatz
345d41c2a70SStefan Achatz retval = isku_init_specials(hdev);
346d41c2a70SStefan Achatz if (retval) {
347d41c2a70SStefan Achatz hid_err(hdev, "couldn't install keyboard\n");
348d41c2a70SStefan Achatz goto exit_stop;
349d41c2a70SStefan Achatz }
350d41c2a70SStefan Achatz
351d41c2a70SStefan Achatz return 0;
352d41c2a70SStefan Achatz
353d41c2a70SStefan Achatz exit_stop:
354d41c2a70SStefan Achatz hid_hw_stop(hdev);
355d41c2a70SStefan Achatz exit:
356d41c2a70SStefan Achatz return retval;
357d41c2a70SStefan Achatz }
358d41c2a70SStefan Achatz
isku_remove(struct hid_device * hdev)359d41c2a70SStefan Achatz static void isku_remove(struct hid_device *hdev)
360d41c2a70SStefan Achatz {
361d41c2a70SStefan Achatz isku_remove_specials(hdev);
362d41c2a70SStefan Achatz hid_hw_stop(hdev);
363d41c2a70SStefan Achatz }
364d41c2a70SStefan Achatz
isku_keep_values_up_to_date(struct isku_device * isku,u8 const * data)365d41c2a70SStefan Achatz static void isku_keep_values_up_to_date(struct isku_device *isku,
366d41c2a70SStefan Achatz u8 const *data)
367d41c2a70SStefan Achatz {
368d41c2a70SStefan Achatz struct isku_report_button const *button_report;
369d41c2a70SStefan Achatz
370d41c2a70SStefan Achatz switch (data[0]) {
371d41c2a70SStefan Achatz case ISKU_REPORT_NUMBER_BUTTON:
372d41c2a70SStefan Achatz button_report = (struct isku_report_button const *)data;
373d41c2a70SStefan Achatz switch (button_report->event) {
374d41c2a70SStefan Achatz case ISKU_REPORT_BUTTON_EVENT_PROFILE:
375d41c2a70SStefan Achatz isku_profile_activated(isku, button_report->data1 - 1);
376d41c2a70SStefan Achatz break;
377d41c2a70SStefan Achatz }
378d41c2a70SStefan Achatz break;
379d41c2a70SStefan Achatz }
380d41c2a70SStefan Achatz }
381d41c2a70SStefan Achatz
isku_report_to_chrdev(struct isku_device const * isku,u8 const * data)382d41c2a70SStefan Achatz static void isku_report_to_chrdev(struct isku_device const *isku,
383d41c2a70SStefan Achatz u8 const *data)
384d41c2a70SStefan Achatz {
385d41c2a70SStefan Achatz struct isku_roccat_report roccat_report;
386d41c2a70SStefan Achatz struct isku_report_button const *button_report;
387d41c2a70SStefan Achatz
388d41c2a70SStefan Achatz if (data[0] != ISKU_REPORT_NUMBER_BUTTON)
389d41c2a70SStefan Achatz return;
390d41c2a70SStefan Achatz
391d41c2a70SStefan Achatz button_report = (struct isku_report_button const *)data;
392d41c2a70SStefan Achatz
393d41c2a70SStefan Achatz roccat_report.event = button_report->event;
394d41c2a70SStefan Achatz roccat_report.data1 = button_report->data1;
395d41c2a70SStefan Achatz roccat_report.data2 = button_report->data2;
396d41c2a70SStefan Achatz roccat_report.profile = isku->actual_profile + 1;
397d41c2a70SStefan Achatz roccat_report_event(isku->chrdev_minor,
398d41c2a70SStefan Achatz (uint8_t const *)&roccat_report);
399d41c2a70SStefan Achatz }
400d41c2a70SStefan Achatz
isku_raw_event(struct hid_device * hdev,struct hid_report * report,u8 * data,int size)401d41c2a70SStefan Achatz static int isku_raw_event(struct hid_device *hdev,
402d41c2a70SStefan Achatz struct hid_report *report, u8 *data, int size)
403d41c2a70SStefan Achatz {
404d41c2a70SStefan Achatz struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
405d41c2a70SStefan Achatz struct isku_device *isku = hid_get_drvdata(hdev);
406d41c2a70SStefan Achatz
407d41c2a70SStefan Achatz if (intf->cur_altsetting->desc.bInterfaceProtocol
408d41c2a70SStefan Achatz != ISKU_USB_INTERFACE_PROTOCOL)
409d41c2a70SStefan Achatz return 0;
410d41c2a70SStefan Achatz
411d41c2a70SStefan Achatz if (isku == NULL)
412d41c2a70SStefan Achatz return 0;
413d41c2a70SStefan Achatz
414d41c2a70SStefan Achatz isku_keep_values_up_to_date(isku, data);
415d41c2a70SStefan Achatz
416d41c2a70SStefan Achatz if (isku->roccat_claimed)
417d41c2a70SStefan Achatz isku_report_to_chrdev(isku, data);
418d41c2a70SStefan Achatz
419d41c2a70SStefan Achatz return 0;
420d41c2a70SStefan Achatz }
421d41c2a70SStefan Achatz
422d41c2a70SStefan Achatz static const struct hid_device_id isku_devices[] = {
423d41c2a70SStefan Achatz { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ISKU) },
424ce716965SStefan Achatz { HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT, USB_DEVICE_ID_ROCCAT_ISKUFX) },
425d41c2a70SStefan Achatz { }
426d41c2a70SStefan Achatz };
427d41c2a70SStefan Achatz
428d41c2a70SStefan Achatz MODULE_DEVICE_TABLE(hid, isku_devices);
429d41c2a70SStefan Achatz
430d41c2a70SStefan Achatz static struct hid_driver isku_driver = {
431d41c2a70SStefan Achatz .name = "isku",
432d41c2a70SStefan Achatz .id_table = isku_devices,
433d41c2a70SStefan Achatz .probe = isku_probe,
434d41c2a70SStefan Achatz .remove = isku_remove,
435d41c2a70SStefan Achatz .raw_event = isku_raw_event
436d41c2a70SStefan Achatz };
437d41c2a70SStefan Achatz
isku_init(void)438d41c2a70SStefan Achatz static int __init isku_init(void)
439d41c2a70SStefan Achatz {
440d41c2a70SStefan Achatz int retval;
441*afdf5dd3SIvan Orlov
442*afdf5dd3SIvan Orlov retval = class_register(&isku_class);
443*afdf5dd3SIvan Orlov if (retval)
444*afdf5dd3SIvan Orlov return retval;
445d41c2a70SStefan Achatz
446d41c2a70SStefan Achatz retval = hid_register_driver(&isku_driver);
447d41c2a70SStefan Achatz if (retval)
448*afdf5dd3SIvan Orlov class_unregister(&isku_class);
449d41c2a70SStefan Achatz return retval;
450d41c2a70SStefan Achatz }
451d41c2a70SStefan Achatz
isku_exit(void)452d41c2a70SStefan Achatz static void __exit isku_exit(void)
453d41c2a70SStefan Achatz {
454d41c2a70SStefan Achatz hid_unregister_driver(&isku_driver);
455*afdf5dd3SIvan Orlov class_unregister(&isku_class);
456d41c2a70SStefan Achatz }
457d41c2a70SStefan Achatz
458d41c2a70SStefan Achatz module_init(isku_init);
459d41c2a70SStefan Achatz module_exit(isku_exit);
460d41c2a70SStefan Achatz
461d41c2a70SStefan Achatz MODULE_AUTHOR("Stefan Achatz");
462ce716965SStefan Achatz MODULE_DESCRIPTION("USB Roccat Isku/FX driver");
463d41c2a70SStefan Achatz MODULE_LICENSE("GPL v2");
464