xref: /openbmc/linux/drivers/hid/hid-roccat-pyra.c (revision dc186b66)
1cb7cf3daSStefan Achatz /*
2cb7cf3daSStefan Achatz  * Roccat Pyra driver for Linux
3cb7cf3daSStefan Achatz  *
4cb7cf3daSStefan Achatz  * Copyright (c) 2010 Stefan Achatz <erazor_de@users.sourceforge.net>
5cb7cf3daSStefan Achatz  */
6cb7cf3daSStefan Achatz 
7cb7cf3daSStefan Achatz /*
8cb7cf3daSStefan Achatz  * This program is free software; you can redistribute it and/or modify it
9cb7cf3daSStefan Achatz  * under the terms of the GNU General Public License as published by the Free
10cb7cf3daSStefan Achatz  * Software Foundation; either version 2 of the License, or (at your option)
11cb7cf3daSStefan Achatz  * any later version.
12cb7cf3daSStefan Achatz  */
13cb7cf3daSStefan Achatz 
14cb7cf3daSStefan Achatz /*
15cb7cf3daSStefan Achatz  * Roccat Pyra is a mobile gamer mouse which comes in wired and wireless
16cb7cf3daSStefan Achatz  * variant. Wireless variant is not tested.
17cb7cf3daSStefan Achatz  * Userland tools can be found at http://sourceforge.net/projects/roccat
18cb7cf3daSStefan Achatz  */
19cb7cf3daSStefan Achatz 
20cb7cf3daSStefan Achatz #include <linux/device.h>
21cb7cf3daSStefan Achatz #include <linux/input.h>
22cb7cf3daSStefan Achatz #include <linux/hid.h>
23cb7cf3daSStefan Achatz #include <linux/module.h>
24cb7cf3daSStefan Achatz #include <linux/slab.h>
255dc0c983SStefan Achatz #include <linux/hid-roccat.h>
26cb7cf3daSStefan Achatz #include "hid-ids.h"
275772f636SStefan Achatz #include "hid-roccat-common.h"
28cb7cf3daSStefan Achatz #include "hid-roccat-pyra.h"
29cb7cf3daSStefan Achatz 
3014a057f8SStefan Achatz static uint profile_numbers[5] = {0, 1, 2, 3, 4};
3114a057f8SStefan Achatz 
325012aadaSStefan Achatz /* pyra_class is used for creating sysfs attributes via roccat char device */
335012aadaSStefan Achatz static struct class *pyra_class;
345012aadaSStefan Achatz 
35cb7cf3daSStefan Achatz static void profile_activated(struct pyra_device *pyra,
36cb7cf3daSStefan Achatz 		unsigned int new_profile)
37cb7cf3daSStefan Achatz {
38cb7cf3daSStefan Achatz 	pyra->actual_profile = new_profile;
39cb7cf3daSStefan Achatz 	pyra->actual_cpi = pyra->profile_settings[pyra->actual_profile].y_cpi;
40cb7cf3daSStefan Achatz }
41cb7cf3daSStefan Achatz 
42cb7cf3daSStefan Achatz static int pyra_send_control(struct usb_device *usb_dev, int value,
43cb7cf3daSStefan Achatz 		enum pyra_control_requests request)
44cb7cf3daSStefan Achatz {
45cb7cf3daSStefan Achatz 	struct pyra_control control;
46cb7cf3daSStefan Achatz 
47cb7cf3daSStefan Achatz 	if ((request == PYRA_CONTROL_REQUEST_PROFILE_SETTINGS ||
48cb7cf3daSStefan Achatz 			request == PYRA_CONTROL_REQUEST_PROFILE_BUTTONS) &&
49cb7cf3daSStefan Achatz 			(value < 0 || value > 4))
50cb7cf3daSStefan Achatz 		return -EINVAL;
51cb7cf3daSStefan Achatz 
52cb7cf3daSStefan Achatz 	control.command = PYRA_COMMAND_CONTROL;
53cb7cf3daSStefan Achatz 	control.value = value;
54cb7cf3daSStefan Achatz 	control.request = request;
55cb7cf3daSStefan Achatz 
561edd5b42SStefan Achatz 	return roccat_common_send(usb_dev, PYRA_COMMAND_CONTROL,
575772f636SStefan Achatz 			&control, sizeof(struct pyra_control));
58cb7cf3daSStefan Achatz }
59cb7cf3daSStefan Achatz 
60cb7cf3daSStefan Achatz static int pyra_receive_control_status(struct usb_device *usb_dev)
61cb7cf3daSStefan Achatz {
625772f636SStefan Achatz 	int retval;
63cb7cf3daSStefan Achatz 	struct pyra_control control;
64cb7cf3daSStefan Achatz 
65cb7cf3daSStefan Achatz 	do {
66cb7cf3daSStefan Achatz 		msleep(10);
671edd5b42SStefan Achatz 		retval = roccat_common_receive(usb_dev, PYRA_COMMAND_CONTROL,
685772f636SStefan Achatz 				&control, sizeof(struct pyra_control));
69cb7cf3daSStefan Achatz 
70cb7cf3daSStefan Achatz 		/* requested too early, try again */
715772f636SStefan Achatz 	} while (retval == -EPROTO);
72cb7cf3daSStefan Achatz 
735772f636SStefan Achatz 	if (!retval && control.command == PYRA_COMMAND_CONTROL &&
74cb7cf3daSStefan Achatz 			control.request == PYRA_CONTROL_REQUEST_STATUS &&
75cb7cf3daSStefan Achatz 			control.value == 1)
76cb7cf3daSStefan Achatz 		return 0;
77cb7cf3daSStefan Achatz 	else {
784291ee30SJoe Perches 		hid_err(usb_dev, "receive control status: unknown response 0x%x 0x%x\n",
79cb7cf3daSStefan Achatz 			control.request, control.value);
805772f636SStefan Achatz 		return retval ? retval : -EINVAL;
81cb7cf3daSStefan Achatz 	}
82cb7cf3daSStefan Achatz }
83cb7cf3daSStefan Achatz 
84cb7cf3daSStefan Achatz static int pyra_get_profile_settings(struct usb_device *usb_dev,
85cb7cf3daSStefan Achatz 		struct pyra_profile_settings *buf, int number)
86cb7cf3daSStefan Achatz {
87cb7cf3daSStefan Achatz 	int retval;
88cb7cf3daSStefan Achatz 	retval = pyra_send_control(usb_dev, number,
89cb7cf3daSStefan Achatz 			PYRA_CONTROL_REQUEST_PROFILE_SETTINGS);
90cb7cf3daSStefan Achatz 	if (retval)
91cb7cf3daSStefan Achatz 		return retval;
921edd5b42SStefan Achatz 	return roccat_common_receive(usb_dev, PYRA_COMMAND_PROFILE_SETTINGS,
935772f636SStefan Achatz 			buf, sizeof(struct pyra_profile_settings));
94cb7cf3daSStefan Achatz }
95cb7cf3daSStefan Achatz 
96cb7cf3daSStefan Achatz static int pyra_get_profile_buttons(struct usb_device *usb_dev,
97cb7cf3daSStefan Achatz 		struct pyra_profile_buttons *buf, int number)
98cb7cf3daSStefan Achatz {
99cb7cf3daSStefan Achatz 	int retval;
100cb7cf3daSStefan Achatz 	retval = pyra_send_control(usb_dev, number,
101cb7cf3daSStefan Achatz 			PYRA_CONTROL_REQUEST_PROFILE_BUTTONS);
102cb7cf3daSStefan Achatz 	if (retval)
103cb7cf3daSStefan Achatz 		return retval;
1041edd5b42SStefan Achatz 	return roccat_common_receive(usb_dev, PYRA_COMMAND_PROFILE_BUTTONS,
1055772f636SStefan Achatz 			buf, sizeof(struct pyra_profile_buttons));
106cb7cf3daSStefan Achatz }
107cb7cf3daSStefan Achatz 
108cb7cf3daSStefan Achatz static int pyra_get_settings(struct usb_device *usb_dev,
109cb7cf3daSStefan Achatz 		struct pyra_settings *buf)
110cb7cf3daSStefan Achatz {
1111edd5b42SStefan Achatz 	return roccat_common_receive(usb_dev, PYRA_COMMAND_SETTINGS,
1125772f636SStefan Achatz 			buf, sizeof(struct pyra_settings));
113cb7cf3daSStefan Achatz }
114cb7cf3daSStefan Achatz 
115cb7cf3daSStefan Achatz static int pyra_get_info(struct usb_device *usb_dev, struct pyra_info *buf)
116cb7cf3daSStefan Achatz {
1171edd5b42SStefan Achatz 	return roccat_common_receive(usb_dev, PYRA_COMMAND_INFO,
1185772f636SStefan Achatz 			buf, sizeof(struct pyra_info));
1195772f636SStefan Achatz }
1205772f636SStefan Achatz 
1215772f636SStefan Achatz static int pyra_send(struct usb_device *usb_dev, uint command,
1225772f636SStefan Achatz 		void const *buf, uint size)
1235772f636SStefan Achatz {
1245772f636SStefan Achatz 	int retval;
1255772f636SStefan Achatz 	retval = roccat_common_send(usb_dev, command, buf, size);
1265772f636SStefan Achatz 	if (retval)
1275772f636SStefan Achatz 		return retval;
1285772f636SStefan Achatz 	return pyra_receive_control_status(usb_dev);
129cb7cf3daSStefan Achatz }
130cb7cf3daSStefan Achatz 
131cb7cf3daSStefan Achatz static int pyra_set_profile_settings(struct usb_device *usb_dev,
132cb7cf3daSStefan Achatz 		struct pyra_profile_settings const *settings)
133cb7cf3daSStefan Achatz {
1341edd5b42SStefan Achatz 	return pyra_send(usb_dev, PYRA_COMMAND_PROFILE_SETTINGS, settings,
1355772f636SStefan Achatz 			sizeof(struct pyra_profile_settings));
136cb7cf3daSStefan Achatz }
137cb7cf3daSStefan Achatz 
138cb7cf3daSStefan Achatz static int pyra_set_profile_buttons(struct usb_device *usb_dev,
139cb7cf3daSStefan Achatz 		struct pyra_profile_buttons const *buttons)
140cb7cf3daSStefan Achatz {
1411edd5b42SStefan Achatz 	return pyra_send(usb_dev, PYRA_COMMAND_PROFILE_BUTTONS, buttons,
1425772f636SStefan Achatz 			sizeof(struct pyra_profile_buttons));
143cb7cf3daSStefan Achatz }
144cb7cf3daSStefan Achatz 
145cb7cf3daSStefan Achatz static int pyra_set_settings(struct usb_device *usb_dev,
146cb7cf3daSStefan Achatz 		struct pyra_settings const *settings)
147cb7cf3daSStefan Achatz {
1481edd5b42SStefan Achatz 	return pyra_send(usb_dev, PYRA_COMMAND_SETTINGS, settings,
1495772f636SStefan Achatz 			sizeof(struct pyra_settings));
150cb7cf3daSStefan Achatz }
151cb7cf3daSStefan Achatz 
152cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_read_profilex_settings(struct file *fp,
153cb7cf3daSStefan Achatz 		struct kobject *kobj, struct bin_attribute *attr, char *buf,
15414a057f8SStefan Achatz 		loff_t off, size_t count)
155cb7cf3daSStefan Achatz {
1565012aadaSStefan Achatz 	struct device *dev =
1575012aadaSStefan Achatz 			container_of(kobj, struct device, kobj)->parent->parent;
158cb7cf3daSStefan Achatz 	struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
159cb7cf3daSStefan Achatz 
160cb7cf3daSStefan Achatz 	if (off >= sizeof(struct pyra_profile_settings))
161cb7cf3daSStefan Achatz 		return 0;
162cb7cf3daSStefan Achatz 
163cb7cf3daSStefan Achatz 	if (off + count > sizeof(struct pyra_profile_settings))
164cb7cf3daSStefan Achatz 		count = sizeof(struct pyra_profile_settings) - off;
165cb7cf3daSStefan Achatz 
166cb7cf3daSStefan Achatz 	mutex_lock(&pyra->pyra_lock);
16714a057f8SStefan Achatz 	memcpy(buf, ((char const *)&pyra->profile_settings[*(uint *)(attr->private)]) + off,
168cb7cf3daSStefan Achatz 			count);
169cb7cf3daSStefan Achatz 	mutex_unlock(&pyra->pyra_lock);
170cb7cf3daSStefan Achatz 
171cb7cf3daSStefan Achatz 	return count;
172cb7cf3daSStefan Achatz }
173cb7cf3daSStefan Achatz 
174cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_read_profilex_buttons(struct file *fp,
175cb7cf3daSStefan Achatz 		struct kobject *kobj, struct bin_attribute *attr, char *buf,
17614a057f8SStefan Achatz 		loff_t off, size_t count)
177cb7cf3daSStefan Achatz {
1785012aadaSStefan Achatz 	struct device *dev =
1795012aadaSStefan Achatz 			container_of(kobj, struct device, kobj)->parent->parent;
180cb7cf3daSStefan Achatz 	struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
181cb7cf3daSStefan Achatz 
182cb7cf3daSStefan Achatz 	if (off >= sizeof(struct pyra_profile_buttons))
183cb7cf3daSStefan Achatz 		return 0;
184cb7cf3daSStefan Achatz 
185cb7cf3daSStefan Achatz 	if (off + count > sizeof(struct pyra_profile_buttons))
186cb7cf3daSStefan Achatz 		count = sizeof(struct pyra_profile_buttons) - off;
187cb7cf3daSStefan Achatz 
188cb7cf3daSStefan Achatz 	mutex_lock(&pyra->pyra_lock);
18914a057f8SStefan Achatz 	memcpy(buf, ((char const *)&pyra->profile_buttons[*(uint *)(attr->private)]) + off,
190cb7cf3daSStefan Achatz 			count);
191cb7cf3daSStefan Achatz 	mutex_unlock(&pyra->pyra_lock);
192cb7cf3daSStefan Achatz 
193cb7cf3daSStefan Achatz 	return count;
194cb7cf3daSStefan Achatz }
195cb7cf3daSStefan Achatz 
196cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_write_profile_settings(struct file *fp,
197cb7cf3daSStefan Achatz 		struct kobject *kobj, struct bin_attribute *attr, char *buf,
198cb7cf3daSStefan Achatz 		loff_t off, size_t count)
199cb7cf3daSStefan Achatz {
2005012aadaSStefan Achatz 	struct device *dev =
2015012aadaSStefan Achatz 			container_of(kobj, struct device, kobj)->parent->parent;
202cb7cf3daSStefan Achatz 	struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
203cb7cf3daSStefan Achatz 	struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
204cb7cf3daSStefan Achatz 	int retval = 0;
205cb7cf3daSStefan Achatz 	int difference;
206cb7cf3daSStefan Achatz 	int profile_number;
207cb7cf3daSStefan Achatz 	struct pyra_profile_settings *profile_settings;
208cb7cf3daSStefan Achatz 
209cb7cf3daSStefan Achatz 	if (off != 0 || count != sizeof(struct pyra_profile_settings))
210cb7cf3daSStefan Achatz 		return -EINVAL;
211cb7cf3daSStefan Achatz 
212cb7cf3daSStefan Achatz 	profile_number = ((struct pyra_profile_settings const *)buf)->number;
213cb7cf3daSStefan Achatz 	profile_settings = &pyra->profile_settings[profile_number];
214cb7cf3daSStefan Achatz 
215cb7cf3daSStefan Achatz 	mutex_lock(&pyra->pyra_lock);
216cb7cf3daSStefan Achatz 	difference = memcmp(buf, profile_settings,
217cb7cf3daSStefan Achatz 			sizeof(struct pyra_profile_settings));
218cb7cf3daSStefan Achatz 	if (difference) {
219cb7cf3daSStefan Achatz 		retval = pyra_set_profile_settings(usb_dev,
220cb7cf3daSStefan Achatz 				(struct pyra_profile_settings const *)buf);
221cb7cf3daSStefan Achatz 		if (!retval)
222cb7cf3daSStefan Achatz 			memcpy(profile_settings, buf,
223cb7cf3daSStefan Achatz 					sizeof(struct pyra_profile_settings));
224cb7cf3daSStefan Achatz 	}
225cb7cf3daSStefan Achatz 	mutex_unlock(&pyra->pyra_lock);
226cb7cf3daSStefan Achatz 
227cb7cf3daSStefan Achatz 	if (retval)
228cb7cf3daSStefan Achatz 		return retval;
229cb7cf3daSStefan Achatz 
230cb7cf3daSStefan Achatz 	return sizeof(struct pyra_profile_settings);
231cb7cf3daSStefan Achatz }
232cb7cf3daSStefan Achatz 
233cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_write_profile_buttons(struct file *fp,
234cb7cf3daSStefan Achatz 		struct kobject *kobj, struct bin_attribute *attr, char *buf,
235cb7cf3daSStefan Achatz 		loff_t off, size_t count)
236cb7cf3daSStefan Achatz {
2375012aadaSStefan Achatz 	struct device *dev =
2385012aadaSStefan Achatz 			container_of(kobj, struct device, kobj)->parent->parent;
239cb7cf3daSStefan Achatz 	struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
240cb7cf3daSStefan Achatz 	struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
241cb7cf3daSStefan Achatz 	int retval = 0;
242cb7cf3daSStefan Achatz 	int difference;
243cb7cf3daSStefan Achatz 	int profile_number;
244cb7cf3daSStefan Achatz 	struct pyra_profile_buttons *profile_buttons;
245cb7cf3daSStefan Achatz 
246cb7cf3daSStefan Achatz 	if (off != 0 || count != sizeof(struct pyra_profile_buttons))
247cb7cf3daSStefan Achatz 		return -EINVAL;
248cb7cf3daSStefan Achatz 
249cb7cf3daSStefan Achatz 	profile_number = ((struct pyra_profile_buttons const *)buf)->number;
250cb7cf3daSStefan Achatz 	profile_buttons = &pyra->profile_buttons[profile_number];
251cb7cf3daSStefan Achatz 
252cb7cf3daSStefan Achatz 	mutex_lock(&pyra->pyra_lock);
253cb7cf3daSStefan Achatz 	difference = memcmp(buf, profile_buttons,
254cb7cf3daSStefan Achatz 			sizeof(struct pyra_profile_buttons));
255cb7cf3daSStefan Achatz 	if (difference) {
256cb7cf3daSStefan Achatz 		retval = pyra_set_profile_buttons(usb_dev,
257cb7cf3daSStefan Achatz 				(struct pyra_profile_buttons const *)buf);
258cb7cf3daSStefan Achatz 		if (!retval)
259cb7cf3daSStefan Achatz 			memcpy(profile_buttons, buf,
260cb7cf3daSStefan Achatz 					sizeof(struct pyra_profile_buttons));
261cb7cf3daSStefan Achatz 	}
262cb7cf3daSStefan Achatz 	mutex_unlock(&pyra->pyra_lock);
263cb7cf3daSStefan Achatz 
264cb7cf3daSStefan Achatz 	if (retval)
265cb7cf3daSStefan Achatz 		return retval;
266cb7cf3daSStefan Achatz 
267cb7cf3daSStefan Achatz 	return sizeof(struct pyra_profile_buttons);
268cb7cf3daSStefan Achatz }
269cb7cf3daSStefan Achatz 
270cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_read_settings(struct file *fp,
271cb7cf3daSStefan Achatz 		struct kobject *kobj, struct bin_attribute *attr, char *buf,
272cb7cf3daSStefan Achatz 		loff_t off, size_t count)
273cb7cf3daSStefan Achatz {
2745012aadaSStefan Achatz 	struct device *dev =
2755012aadaSStefan Achatz 			container_of(kobj, struct device, kobj)->parent->parent;
276cb7cf3daSStefan Achatz 	struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
277cb7cf3daSStefan Achatz 
278cb7cf3daSStefan Achatz 	if (off >= sizeof(struct pyra_settings))
279cb7cf3daSStefan Achatz 		return 0;
280cb7cf3daSStefan Achatz 
281cb7cf3daSStefan Achatz 	if (off + count > sizeof(struct pyra_settings))
282cb7cf3daSStefan Achatz 		count = sizeof(struct pyra_settings) - off;
283cb7cf3daSStefan Achatz 
284cb7cf3daSStefan Achatz 	mutex_lock(&pyra->pyra_lock);
285cb7cf3daSStefan Achatz 	memcpy(buf, ((char const *)&pyra->settings) + off, count);
286cb7cf3daSStefan Achatz 	mutex_unlock(&pyra->pyra_lock);
287cb7cf3daSStefan Achatz 
288cb7cf3daSStefan Achatz 	return count;
289cb7cf3daSStefan Achatz }
290cb7cf3daSStefan Achatz 
291cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_write_settings(struct file *fp,
292cb7cf3daSStefan Achatz 		struct kobject *kobj, struct bin_attribute *attr, char *buf,
293cb7cf3daSStefan Achatz 		loff_t off, size_t count)
294cb7cf3daSStefan Achatz {
2955012aadaSStefan Achatz 	struct device *dev =
2965012aadaSStefan Achatz 			container_of(kobj, struct device, kobj)->parent->parent;
297cb7cf3daSStefan Achatz 	struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
298cb7cf3daSStefan Achatz 	struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
299cb7cf3daSStefan Achatz 	int retval = 0;
300cb7cf3daSStefan Achatz 	int difference;
301dc186b66SStefan Achatz 	struct pyra_roccat_report roccat_report;
302cb7cf3daSStefan Achatz 
303cb7cf3daSStefan Achatz 	if (off != 0 || count != sizeof(struct pyra_settings))
304cb7cf3daSStefan Achatz 		return -EINVAL;
305cb7cf3daSStefan Achatz 
306cb7cf3daSStefan Achatz 	mutex_lock(&pyra->pyra_lock);
307cb7cf3daSStefan Achatz 	difference = memcmp(buf, &pyra->settings, sizeof(struct pyra_settings));
308cb7cf3daSStefan Achatz 	if (difference) {
309cb7cf3daSStefan Achatz 		retval = pyra_set_settings(usb_dev,
310cb7cf3daSStefan Achatz 				(struct pyra_settings const *)buf);
311dc186b66SStefan Achatz 		if (retval) {
312dc186b66SStefan Achatz 			mutex_unlock(&pyra->pyra_lock);
313dc186b66SStefan Achatz 			return retval;
314dc186b66SStefan Achatz 		}
315dc186b66SStefan Achatz 
316cb7cf3daSStefan Achatz 		memcpy(&pyra->settings, buf,
317cb7cf3daSStefan Achatz 				sizeof(struct pyra_settings));
318cb7cf3daSStefan Achatz 
319cb7cf3daSStefan Achatz 		profile_activated(pyra, pyra->settings.startup_profile);
320cb7cf3daSStefan Achatz 
321dc186b66SStefan Achatz 		roccat_report.type = PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2;
322dc186b66SStefan Achatz 		roccat_report.value = pyra->settings.startup_profile + 1;
323dc186b66SStefan Achatz 		roccat_report.key = 0;
324dc186b66SStefan Achatz 		roccat_report_event(pyra->chrdev_minor,
325dc186b66SStefan Achatz 				(uint8_t const *)&roccat_report);
326dc186b66SStefan Achatz 	}
327dc186b66SStefan Achatz 	mutex_unlock(&pyra->pyra_lock);
328cb7cf3daSStefan Achatz 	return sizeof(struct pyra_settings);
329cb7cf3daSStefan Achatz }
330cb7cf3daSStefan Achatz 
331cb7cf3daSStefan Achatz 
332cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_show_actual_cpi(struct device *dev,
333cb7cf3daSStefan Achatz 		struct device_attribute *attr, char *buf)
334cb7cf3daSStefan Achatz {
3355012aadaSStefan Achatz 	struct pyra_device *pyra =
3365012aadaSStefan Achatz 			hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
337cb7cf3daSStefan Achatz 	return snprintf(buf, PAGE_SIZE, "%d\n", pyra->actual_cpi);
338cb7cf3daSStefan Achatz }
339cb7cf3daSStefan Achatz 
340cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_show_actual_profile(struct device *dev,
341cb7cf3daSStefan Achatz 		struct device_attribute *attr, char *buf)
342cb7cf3daSStefan Achatz {
3435012aadaSStefan Achatz 	struct pyra_device *pyra =
3445012aadaSStefan Achatz 			hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
345cb7cf3daSStefan Achatz 	return snprintf(buf, PAGE_SIZE, "%d\n", pyra->actual_profile);
346cb7cf3daSStefan Achatz }
347cb7cf3daSStefan Achatz 
348cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_show_firmware_version(struct device *dev,
349cb7cf3daSStefan Achatz 		struct device_attribute *attr, char *buf)
350cb7cf3daSStefan Achatz {
3515012aadaSStefan Achatz 	struct pyra_device *pyra =
3525012aadaSStefan Achatz 			hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
353cb7cf3daSStefan Achatz 	return snprintf(buf, PAGE_SIZE, "%d\n", pyra->firmware_version);
354cb7cf3daSStefan Achatz }
355cb7cf3daSStefan Achatz 
356cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_show_startup_profile(struct device *dev,
357cb7cf3daSStefan Achatz 		struct device_attribute *attr, char *buf)
358cb7cf3daSStefan Achatz {
3595012aadaSStefan Achatz 	struct pyra_device *pyra =
3605012aadaSStefan Achatz 			hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
361cb7cf3daSStefan Achatz 	return snprintf(buf, PAGE_SIZE, "%d\n", pyra->settings.startup_profile);
362cb7cf3daSStefan Achatz }
363cb7cf3daSStefan Achatz 
3645012aadaSStefan Achatz static struct device_attribute pyra_attributes[] = {
3655012aadaSStefan Achatz 	__ATTR(actual_cpi, 0440, pyra_sysfs_show_actual_cpi, NULL),
3665012aadaSStefan Achatz 	__ATTR(actual_profile, 0440, pyra_sysfs_show_actual_profile, NULL),
3675012aadaSStefan Achatz 	__ATTR(firmware_version, 0440,
3685012aadaSStefan Achatz 			pyra_sysfs_show_firmware_version, NULL),
3695012aadaSStefan Achatz 	__ATTR(startup_profile, 0440,
3705012aadaSStefan Achatz 			pyra_sysfs_show_startup_profile, NULL),
3715012aadaSStefan Achatz 	__ATTR_NULL
372cb7cf3daSStefan Achatz };
373cb7cf3daSStefan Achatz 
3745012aadaSStefan Achatz static struct bin_attribute pyra_bin_attributes[] = {
3755012aadaSStefan Achatz 	{
376cb7cf3daSStefan Achatz 		.attr = { .name = "profile_settings", .mode = 0220 },
377cb7cf3daSStefan Achatz 		.size = sizeof(struct pyra_profile_settings),
378cb7cf3daSStefan Achatz 		.write = pyra_sysfs_write_profile_settings
3795012aadaSStefan Achatz 	},
3805012aadaSStefan Achatz 	{
381cb7cf3daSStefan Achatz 		.attr = { .name = "profile1_settings", .mode = 0440 },
382cb7cf3daSStefan Achatz 		.size = sizeof(struct pyra_profile_settings),
38314a057f8SStefan Achatz 		.read = pyra_sysfs_read_profilex_settings,
38414a057f8SStefan Achatz 		.private = &profile_numbers[0]
3855012aadaSStefan Achatz 	},
3865012aadaSStefan Achatz 	{
387cb7cf3daSStefan Achatz 		.attr = { .name = "profile2_settings", .mode = 0440 },
388cb7cf3daSStefan Achatz 		.size = sizeof(struct pyra_profile_settings),
38914a057f8SStefan Achatz 		.read = pyra_sysfs_read_profilex_settings,
39014a057f8SStefan Achatz 		.private = &profile_numbers[1]
3915012aadaSStefan Achatz 	},
3925012aadaSStefan Achatz 	{
393cb7cf3daSStefan Achatz 		.attr = { .name = "profile3_settings", .mode = 0440 },
394cb7cf3daSStefan Achatz 		.size = sizeof(struct pyra_profile_settings),
39514a057f8SStefan Achatz 		.read = pyra_sysfs_read_profilex_settings,
39614a057f8SStefan Achatz 		.private = &profile_numbers[2]
3975012aadaSStefan Achatz 	},
3985012aadaSStefan Achatz 	{
399cb7cf3daSStefan Achatz 		.attr = { .name = "profile4_settings", .mode = 0440 },
400cb7cf3daSStefan Achatz 		.size = sizeof(struct pyra_profile_settings),
40114a057f8SStefan Achatz 		.read = pyra_sysfs_read_profilex_settings,
40214a057f8SStefan Achatz 		.private = &profile_numbers[3]
4035012aadaSStefan Achatz 	},
4045012aadaSStefan Achatz 	{
405cb7cf3daSStefan Achatz 		.attr = { .name = "profile5_settings", .mode = 0440 },
406cb7cf3daSStefan Achatz 		.size = sizeof(struct pyra_profile_settings),
40714a057f8SStefan Achatz 		.read = pyra_sysfs_read_profilex_settings,
40814a057f8SStefan Achatz 		.private = &profile_numbers[4]
4095012aadaSStefan Achatz 	},
4105012aadaSStefan Achatz 	{
411cb7cf3daSStefan Achatz 		.attr = { .name = "profile_buttons", .mode = 0220 },
412cb7cf3daSStefan Achatz 		.size = sizeof(struct pyra_profile_buttons),
413cb7cf3daSStefan Achatz 		.write = pyra_sysfs_write_profile_buttons
4145012aadaSStefan Achatz 	},
4155012aadaSStefan Achatz 	{
416cb7cf3daSStefan Achatz 		.attr = { .name = "profile1_buttons", .mode = 0440 },
417cb7cf3daSStefan Achatz 		.size = sizeof(struct pyra_profile_buttons),
41814a057f8SStefan Achatz 		.read = pyra_sysfs_read_profilex_buttons,
41914a057f8SStefan Achatz 		.private = &profile_numbers[0]
4205012aadaSStefan Achatz 	},
4215012aadaSStefan Achatz 	{
422cb7cf3daSStefan Achatz 		.attr = { .name = "profile2_buttons", .mode = 0440 },
423cb7cf3daSStefan Achatz 		.size = sizeof(struct pyra_profile_buttons),
42414a057f8SStefan Achatz 		.read = pyra_sysfs_read_profilex_buttons,
42514a057f8SStefan Achatz 		.private = &profile_numbers[1]
4265012aadaSStefan Achatz 	},
4275012aadaSStefan Achatz 	{
428cb7cf3daSStefan Achatz 		.attr = { .name = "profile3_buttons", .mode = 0440 },
429cb7cf3daSStefan Achatz 		.size = sizeof(struct pyra_profile_buttons),
43014a057f8SStefan Achatz 		.read = pyra_sysfs_read_profilex_buttons,
43114a057f8SStefan Achatz 		.private = &profile_numbers[2]
4325012aadaSStefan Achatz 	},
4335012aadaSStefan Achatz 	{
434cb7cf3daSStefan Achatz 		.attr = { .name = "profile4_buttons", .mode = 0440 },
435cb7cf3daSStefan Achatz 		.size = sizeof(struct pyra_profile_buttons),
43614a057f8SStefan Achatz 		.read = pyra_sysfs_read_profilex_buttons,
43714a057f8SStefan Achatz 		.private = &profile_numbers[3]
4385012aadaSStefan Achatz 	},
4395012aadaSStefan Achatz 	{
440cb7cf3daSStefan Achatz 		.attr = { .name = "profile5_buttons", .mode = 0440 },
441cb7cf3daSStefan Achatz 		.size = sizeof(struct pyra_profile_buttons),
44214a057f8SStefan Achatz 		.read = pyra_sysfs_read_profilex_buttons,
44314a057f8SStefan Achatz 		.private = &profile_numbers[4]
4445012aadaSStefan Achatz 	},
4455012aadaSStefan Achatz 	{
446cb7cf3daSStefan Achatz 		.attr = { .name = "settings", .mode = 0660 },
447cb7cf3daSStefan Achatz 		.size = sizeof(struct pyra_settings),
448cb7cf3daSStefan Achatz 		.read = pyra_sysfs_read_settings,
449cb7cf3daSStefan Achatz 		.write = pyra_sysfs_write_settings
4505012aadaSStefan Achatz 	},
4515012aadaSStefan Achatz 	__ATTR_NULL
452cb7cf3daSStefan Achatz };
453cb7cf3daSStefan Achatz 
454cb7cf3daSStefan Achatz static int pyra_init_pyra_device_struct(struct usb_device *usb_dev,
455cb7cf3daSStefan Achatz 		struct pyra_device *pyra)
456cb7cf3daSStefan Achatz {
4575772f636SStefan Achatz 	struct pyra_info info;
458cb7cf3daSStefan Achatz 	int retval, i;
459cb7cf3daSStefan Achatz 
460cb7cf3daSStefan Achatz 	mutex_init(&pyra->pyra_lock);
461cb7cf3daSStefan Achatz 
4625772f636SStefan Achatz 	retval = pyra_get_info(usb_dev, &info);
4635772f636SStefan Achatz 	if (retval)
464cb7cf3daSStefan Achatz 		return retval;
4655772f636SStefan Achatz 
4665772f636SStefan Achatz 	pyra->firmware_version = info.firmware_version;
467cb7cf3daSStefan Achatz 
468cb7cf3daSStefan Achatz 	retval = pyra_get_settings(usb_dev, &pyra->settings);
469cb7cf3daSStefan Achatz 	if (retval)
470cb7cf3daSStefan Achatz 		return retval;
471cb7cf3daSStefan Achatz 
472cb7cf3daSStefan Achatz 	for (i = 0; i < 5; ++i) {
473cb7cf3daSStefan Achatz 		retval = pyra_get_profile_settings(usb_dev,
474cb7cf3daSStefan Achatz 				&pyra->profile_settings[i], i);
475cb7cf3daSStefan Achatz 		if (retval)
476cb7cf3daSStefan Achatz 			return retval;
477cb7cf3daSStefan Achatz 
478cb7cf3daSStefan Achatz 		retval = pyra_get_profile_buttons(usb_dev,
479cb7cf3daSStefan Achatz 				&pyra->profile_buttons[i], i);
480cb7cf3daSStefan Achatz 		if (retval)
481cb7cf3daSStefan Achatz 			return retval;
482cb7cf3daSStefan Achatz 	}
483cb7cf3daSStefan Achatz 
484cb7cf3daSStefan Achatz 	profile_activated(pyra, pyra->settings.startup_profile);
485cb7cf3daSStefan Achatz 
486cb7cf3daSStefan Achatz 	return 0;
487cb7cf3daSStefan Achatz }
488cb7cf3daSStefan Achatz 
489cb7cf3daSStefan Achatz static int pyra_init_specials(struct hid_device *hdev)
490cb7cf3daSStefan Achatz {
491cb7cf3daSStefan Achatz 	struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
492cb7cf3daSStefan Achatz 	struct usb_device *usb_dev = interface_to_usbdev(intf);
493cb7cf3daSStefan Achatz 	struct pyra_device *pyra;
494cb7cf3daSStefan Achatz 	int retval;
495cb7cf3daSStefan Achatz 
496cb7cf3daSStefan Achatz 	if (intf->cur_altsetting->desc.bInterfaceProtocol
497cb7cf3daSStefan Achatz 			== USB_INTERFACE_PROTOCOL_MOUSE) {
498cb7cf3daSStefan Achatz 
499cb7cf3daSStefan Achatz 		pyra = kzalloc(sizeof(*pyra), GFP_KERNEL);
500cb7cf3daSStefan Achatz 		if (!pyra) {
5014291ee30SJoe Perches 			hid_err(hdev, "can't alloc device descriptor\n");
502cb7cf3daSStefan Achatz 			return -ENOMEM;
503cb7cf3daSStefan Achatz 		}
504cb7cf3daSStefan Achatz 		hid_set_drvdata(hdev, pyra);
505cb7cf3daSStefan Achatz 
506cb7cf3daSStefan Achatz 		retval = pyra_init_pyra_device_struct(usb_dev, pyra);
507cb7cf3daSStefan Achatz 		if (retval) {
5084291ee30SJoe Perches 			hid_err(hdev, "couldn't init struct pyra_device\n");
509cb7cf3daSStefan Achatz 			goto exit_free;
510cb7cf3daSStefan Achatz 		}
511cb7cf3daSStefan Achatz 
5128211e460SStefan Achatz 		retval = roccat_connect(pyra_class, hdev,
5138211e460SStefan Achatz 				sizeof(struct pyra_roccat_report));
514cb7cf3daSStefan Achatz 		if (retval < 0) {
5154291ee30SJoe Perches 			hid_err(hdev, "couldn't init char dev\n");
516cb7cf3daSStefan Achatz 		} else {
517cb7cf3daSStefan Achatz 			pyra->chrdev_minor = retval;
518cb7cf3daSStefan Achatz 			pyra->roccat_claimed = 1;
519cb7cf3daSStefan Achatz 		}
520cb7cf3daSStefan Achatz 	} else {
521cb7cf3daSStefan Achatz 		hid_set_drvdata(hdev, NULL);
522cb7cf3daSStefan Achatz 	}
523cb7cf3daSStefan Achatz 
524cb7cf3daSStefan Achatz 	return 0;
525cb7cf3daSStefan Achatz exit_free:
526cb7cf3daSStefan Achatz 	kfree(pyra);
527cb7cf3daSStefan Achatz 	return retval;
528cb7cf3daSStefan Achatz }
529cb7cf3daSStefan Achatz 
530cb7cf3daSStefan Achatz static void pyra_remove_specials(struct hid_device *hdev)
531cb7cf3daSStefan Achatz {
532cb7cf3daSStefan Achatz 	struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
533cb7cf3daSStefan Achatz 	struct pyra_device *pyra;
534cb7cf3daSStefan Achatz 
535cb7cf3daSStefan Achatz 	if (intf->cur_altsetting->desc.bInterfaceProtocol
536cb7cf3daSStefan Achatz 			== USB_INTERFACE_PROTOCOL_MOUSE) {
537cb7cf3daSStefan Achatz 		pyra = hid_get_drvdata(hdev);
538cb7cf3daSStefan Achatz 		if (pyra->roccat_claimed)
539cb7cf3daSStefan Achatz 			roccat_disconnect(pyra->chrdev_minor);
540cb7cf3daSStefan Achatz 		kfree(hid_get_drvdata(hdev));
541cb7cf3daSStefan Achatz 	}
542cb7cf3daSStefan Achatz }
543cb7cf3daSStefan Achatz 
544cb7cf3daSStefan Achatz static int pyra_probe(struct hid_device *hdev, const struct hid_device_id *id)
545cb7cf3daSStefan Achatz {
546cb7cf3daSStefan Achatz 	int retval;
547cb7cf3daSStefan Achatz 
548cb7cf3daSStefan Achatz 	retval = hid_parse(hdev);
549cb7cf3daSStefan Achatz 	if (retval) {
5504291ee30SJoe Perches 		hid_err(hdev, "parse failed\n");
551cb7cf3daSStefan Achatz 		goto exit;
552cb7cf3daSStefan Achatz 	}
553cb7cf3daSStefan Achatz 
554cb7cf3daSStefan Achatz 	retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
555cb7cf3daSStefan Achatz 	if (retval) {
5564291ee30SJoe Perches 		hid_err(hdev, "hw start failed\n");
557cb7cf3daSStefan Achatz 		goto exit;
558cb7cf3daSStefan Achatz 	}
559cb7cf3daSStefan Achatz 
560cb7cf3daSStefan Achatz 	retval = pyra_init_specials(hdev);
561cb7cf3daSStefan Achatz 	if (retval) {
5624291ee30SJoe Perches 		hid_err(hdev, "couldn't install mouse\n");
563cb7cf3daSStefan Achatz 		goto exit_stop;
564cb7cf3daSStefan Achatz 	}
565cb7cf3daSStefan Achatz 	return 0;
566cb7cf3daSStefan Achatz 
567cb7cf3daSStefan Achatz exit_stop:
568cb7cf3daSStefan Achatz 	hid_hw_stop(hdev);
569cb7cf3daSStefan Achatz exit:
570cb7cf3daSStefan Achatz 	return retval;
571cb7cf3daSStefan Achatz }
572cb7cf3daSStefan Achatz 
573cb7cf3daSStefan Achatz static void pyra_remove(struct hid_device *hdev)
574cb7cf3daSStefan Achatz {
575cb7cf3daSStefan Achatz 	pyra_remove_specials(hdev);
576cb7cf3daSStefan Achatz 	hid_hw_stop(hdev);
577cb7cf3daSStefan Achatz }
578cb7cf3daSStefan Achatz 
579cb7cf3daSStefan Achatz static void pyra_keep_values_up_to_date(struct pyra_device *pyra,
580cb7cf3daSStefan Achatz 		u8 const *data)
581cb7cf3daSStefan Achatz {
582cb7cf3daSStefan Achatz 	struct pyra_mouse_event_button const *button_event;
583cb7cf3daSStefan Achatz 
584cb7cf3daSStefan Achatz 	switch (data[0]) {
585cb7cf3daSStefan Achatz 	case PYRA_MOUSE_REPORT_NUMBER_BUTTON:
586cb7cf3daSStefan Achatz 		button_event = (struct pyra_mouse_event_button const *)data;
587cb7cf3daSStefan Achatz 		switch (button_event->type) {
588cb7cf3daSStefan Achatz 		case PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2:
589cb7cf3daSStefan Achatz 			profile_activated(pyra, button_event->data1 - 1);
590cb7cf3daSStefan Achatz 			break;
591cb7cf3daSStefan Achatz 		case PYRA_MOUSE_EVENT_BUTTON_TYPE_CPI:
592cb7cf3daSStefan Achatz 			pyra->actual_cpi = button_event->data1;
593cb7cf3daSStefan Achatz 			break;
594cb7cf3daSStefan Achatz 		}
595cb7cf3daSStefan Achatz 		break;
596cb7cf3daSStefan Achatz 	}
597cb7cf3daSStefan Achatz }
598cb7cf3daSStefan Achatz 
599cb7cf3daSStefan Achatz static void pyra_report_to_chrdev(struct pyra_device const *pyra,
600cb7cf3daSStefan Achatz 		u8 const *data)
601cb7cf3daSStefan Achatz {
602cb7cf3daSStefan Achatz 	struct pyra_roccat_report roccat_report;
603cb7cf3daSStefan Achatz 	struct pyra_mouse_event_button const *button_event;
604cb7cf3daSStefan Achatz 
605cb7cf3daSStefan Achatz 	if (data[0] != PYRA_MOUSE_REPORT_NUMBER_BUTTON)
606cb7cf3daSStefan Achatz 		return;
607cb7cf3daSStefan Achatz 
608cb7cf3daSStefan Achatz 	button_event = (struct pyra_mouse_event_button const *)data;
609cb7cf3daSStefan Achatz 
610cb7cf3daSStefan Achatz 	switch (button_event->type) {
611cb7cf3daSStefan Achatz 	case PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2:
612cb7cf3daSStefan Achatz 	case PYRA_MOUSE_EVENT_BUTTON_TYPE_CPI:
613cb7cf3daSStefan Achatz 		roccat_report.type = button_event->type;
614cb7cf3daSStefan Achatz 		roccat_report.value = button_event->data1;
615cb7cf3daSStefan Achatz 		roccat_report.key = 0;
616cb7cf3daSStefan Achatz 		roccat_report_event(pyra->chrdev_minor,
6178211e460SStefan Achatz 				(uint8_t const *)&roccat_report);
618cb7cf3daSStefan Achatz 		break;
619cb7cf3daSStefan Achatz 	case PYRA_MOUSE_EVENT_BUTTON_TYPE_MACRO:
620cb7cf3daSStefan Achatz 	case PYRA_MOUSE_EVENT_BUTTON_TYPE_SHORTCUT:
621cb7cf3daSStefan Achatz 	case PYRA_MOUSE_EVENT_BUTTON_TYPE_QUICKLAUNCH:
622cb7cf3daSStefan Achatz 		if (button_event->data2 == PYRA_MOUSE_EVENT_BUTTON_PRESS) {
623cb7cf3daSStefan Achatz 			roccat_report.type = button_event->type;
624cb7cf3daSStefan Achatz 			roccat_report.key = button_event->data1;
625d2b570a5SStefan Achatz 			/*
626d2b570a5SStefan Achatz 			 * pyra reports profile numbers with range 1-5.
627d2b570a5SStefan Achatz 			 * Keeping this behaviour.
628d2b570a5SStefan Achatz 			 */
629d2b570a5SStefan Achatz 			roccat_report.value = pyra->actual_profile + 1;
630cb7cf3daSStefan Achatz 			roccat_report_event(pyra->chrdev_minor,
6318211e460SStefan Achatz 					(uint8_t const *)&roccat_report);
632cb7cf3daSStefan Achatz 		}
633cb7cf3daSStefan Achatz 		break;
634cb7cf3daSStefan Achatz 	}
635cb7cf3daSStefan Achatz }
636cb7cf3daSStefan Achatz 
637cb7cf3daSStefan Achatz static int pyra_raw_event(struct hid_device *hdev, struct hid_report *report,
638cb7cf3daSStefan Achatz 		u8 *data, int size)
639cb7cf3daSStefan Achatz {
640cb7cf3daSStefan Achatz 	struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
641cb7cf3daSStefan Achatz 	struct pyra_device *pyra = hid_get_drvdata(hdev);
642cb7cf3daSStefan Achatz 
643cb7cf3daSStefan Achatz 	if (intf->cur_altsetting->desc.bInterfaceProtocol
644cb7cf3daSStefan Achatz 			!= USB_INTERFACE_PROTOCOL_MOUSE)
645cb7cf3daSStefan Achatz 		return 0;
646cb7cf3daSStefan Achatz 
647901e64dbSStefan Achatz 	if (pyra == NULL)
648901e64dbSStefan Achatz 		return 0;
649901e64dbSStefan Achatz 
650cb7cf3daSStefan Achatz 	pyra_keep_values_up_to_date(pyra, data);
651cb7cf3daSStefan Achatz 
652cb7cf3daSStefan Achatz 	if (pyra->roccat_claimed)
653cb7cf3daSStefan Achatz 		pyra_report_to_chrdev(pyra, data);
654cb7cf3daSStefan Achatz 
655cb7cf3daSStefan Achatz 	return 0;
656cb7cf3daSStefan Achatz }
657cb7cf3daSStefan Achatz 
658cb7cf3daSStefan Achatz static const struct hid_device_id pyra_devices[] = {
659cb7cf3daSStefan Achatz 	{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT,
660cb7cf3daSStefan Achatz 			USB_DEVICE_ID_ROCCAT_PYRA_WIRED) },
6613fce2246SStefan Achatz 	{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT,
6623fce2246SStefan Achatz 			USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS) },
663cb7cf3daSStefan Achatz 	{ }
664cb7cf3daSStefan Achatz };
665cb7cf3daSStefan Achatz 
666cb7cf3daSStefan Achatz MODULE_DEVICE_TABLE(hid, pyra_devices);
667cb7cf3daSStefan Achatz 
668cb7cf3daSStefan Achatz static struct hid_driver pyra_driver = {
669cb7cf3daSStefan Achatz 		.name = "pyra",
670cb7cf3daSStefan Achatz 		.id_table = pyra_devices,
671cb7cf3daSStefan Achatz 		.probe = pyra_probe,
672cb7cf3daSStefan Achatz 		.remove = pyra_remove,
673cb7cf3daSStefan Achatz 		.raw_event = pyra_raw_event
674cb7cf3daSStefan Achatz };
675cb7cf3daSStefan Achatz 
676cb7cf3daSStefan Achatz static int __init pyra_init(void)
677cb7cf3daSStefan Achatz {
6785012aadaSStefan Achatz 	int retval;
6795012aadaSStefan Achatz 
6805012aadaSStefan Achatz 	/* class name has to be same as driver name */
6815012aadaSStefan Achatz 	pyra_class = class_create(THIS_MODULE, "pyra");
6825012aadaSStefan Achatz 	if (IS_ERR(pyra_class))
6835012aadaSStefan Achatz 		return PTR_ERR(pyra_class);
6845012aadaSStefan Achatz 	pyra_class->dev_attrs = pyra_attributes;
6855012aadaSStefan Achatz 	pyra_class->dev_bin_attrs = pyra_bin_attributes;
6865012aadaSStefan Achatz 
6875012aadaSStefan Achatz 	retval = hid_register_driver(&pyra_driver);
6885012aadaSStefan Achatz 	if (retval)
6895012aadaSStefan Achatz 		class_destroy(pyra_class);
6905012aadaSStefan Achatz 	return retval;
691cb7cf3daSStefan Achatz }
692cb7cf3daSStefan Achatz 
693cb7cf3daSStefan Achatz static void __exit pyra_exit(void)
694cb7cf3daSStefan Achatz {
695cb7cf3daSStefan Achatz 	hid_unregister_driver(&pyra_driver);
69674b643daSStefan Achatz 	class_destroy(pyra_class);
697cb7cf3daSStefan Achatz }
698cb7cf3daSStefan Achatz 
699cb7cf3daSStefan Achatz module_init(pyra_init);
700cb7cf3daSStefan Achatz module_exit(pyra_exit);
701cb7cf3daSStefan Achatz 
702cb7cf3daSStefan Achatz MODULE_AUTHOR("Stefan Achatz");
703cb7cf3daSStefan Achatz MODULE_DESCRIPTION("USB Roccat Pyra driver");
704cb7cf3daSStefan Achatz MODULE_LICENSE("GPL v2");
705