xref: /openbmc/linux/drivers/hid/hid-roccat-pyra.c (revision 5772f636)
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>
25cb7cf3daSStefan Achatz #include "hid-ids.h"
26cb7cf3daSStefan Achatz #include "hid-roccat.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 
565772f636SStefan Achatz 	return roccat_common_send(usb_dev, PYRA_USB_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);
675772f636SStefan Achatz 		retval = roccat_common_receive(usb_dev, PYRA_USB_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;
925772f636SStefan Achatz 	return roccat_common_receive(usb_dev, PYRA_USB_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;
1045772f636SStefan Achatz 	return roccat_common_receive(usb_dev, PYRA_USB_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 {
1115772f636SStefan Achatz 	return roccat_common_receive(usb_dev, PYRA_USB_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 {
1175772f636SStefan Achatz 	return roccat_common_receive(usb_dev, PYRA_USB_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 {
1345772f636SStefan Achatz 	return pyra_send(usb_dev, PYRA_USB_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 {
1415772f636SStefan Achatz 	return pyra_send(usb_dev, PYRA_USB_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 {
1485772f636SStefan Achatz 	int retval;
1495772f636SStefan Achatz 	retval = roccat_common_send(usb_dev, PYRA_USB_COMMAND_SETTINGS, settings,
1505772f636SStefan Achatz 			sizeof(struct pyra_settings));
1515772f636SStefan Achatz 	if (retval)
1525772f636SStefan Achatz 		return retval;
1535772f636SStefan Achatz 	return pyra_receive_control_status(usb_dev);
154cb7cf3daSStefan Achatz }
155cb7cf3daSStefan Achatz 
156cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_read_profilex_settings(struct file *fp,
157cb7cf3daSStefan Achatz 		struct kobject *kobj, struct bin_attribute *attr, char *buf,
15814a057f8SStefan Achatz 		loff_t off, size_t count)
159cb7cf3daSStefan Achatz {
1605012aadaSStefan Achatz 	struct device *dev =
1615012aadaSStefan Achatz 			container_of(kobj, struct device, kobj)->parent->parent;
162cb7cf3daSStefan Achatz 	struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
163cb7cf3daSStefan Achatz 
164cb7cf3daSStefan Achatz 	if (off >= sizeof(struct pyra_profile_settings))
165cb7cf3daSStefan Achatz 		return 0;
166cb7cf3daSStefan Achatz 
167cb7cf3daSStefan Achatz 	if (off + count > sizeof(struct pyra_profile_settings))
168cb7cf3daSStefan Achatz 		count = sizeof(struct pyra_profile_settings) - off;
169cb7cf3daSStefan Achatz 
170cb7cf3daSStefan Achatz 	mutex_lock(&pyra->pyra_lock);
17114a057f8SStefan Achatz 	memcpy(buf, ((char const *)&pyra->profile_settings[*(uint *)(attr->private)]) + off,
172cb7cf3daSStefan Achatz 			count);
173cb7cf3daSStefan Achatz 	mutex_unlock(&pyra->pyra_lock);
174cb7cf3daSStefan Achatz 
175cb7cf3daSStefan Achatz 	return count;
176cb7cf3daSStefan Achatz }
177cb7cf3daSStefan Achatz 
178cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_read_profilex_buttons(struct file *fp,
179cb7cf3daSStefan Achatz 		struct kobject *kobj, struct bin_attribute *attr, char *buf,
18014a057f8SStefan Achatz 		loff_t off, size_t count)
181cb7cf3daSStefan Achatz {
1825012aadaSStefan Achatz 	struct device *dev =
1835012aadaSStefan Achatz 			container_of(kobj, struct device, kobj)->parent->parent;
184cb7cf3daSStefan Achatz 	struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
185cb7cf3daSStefan Achatz 
186cb7cf3daSStefan Achatz 	if (off >= sizeof(struct pyra_profile_buttons))
187cb7cf3daSStefan Achatz 		return 0;
188cb7cf3daSStefan Achatz 
189cb7cf3daSStefan Achatz 	if (off + count > sizeof(struct pyra_profile_buttons))
190cb7cf3daSStefan Achatz 		count = sizeof(struct pyra_profile_buttons) - off;
191cb7cf3daSStefan Achatz 
192cb7cf3daSStefan Achatz 	mutex_lock(&pyra->pyra_lock);
19314a057f8SStefan Achatz 	memcpy(buf, ((char const *)&pyra->profile_buttons[*(uint *)(attr->private)]) + off,
194cb7cf3daSStefan Achatz 			count);
195cb7cf3daSStefan Achatz 	mutex_unlock(&pyra->pyra_lock);
196cb7cf3daSStefan Achatz 
197cb7cf3daSStefan Achatz 	return count;
198cb7cf3daSStefan Achatz }
199cb7cf3daSStefan Achatz 
200cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_write_profile_settings(struct file *fp,
201cb7cf3daSStefan Achatz 		struct kobject *kobj, struct bin_attribute *attr, char *buf,
202cb7cf3daSStefan Achatz 		loff_t off, size_t count)
203cb7cf3daSStefan Achatz {
2045012aadaSStefan Achatz 	struct device *dev =
2055012aadaSStefan Achatz 			container_of(kobj, struct device, kobj)->parent->parent;
206cb7cf3daSStefan Achatz 	struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
207cb7cf3daSStefan Achatz 	struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
208cb7cf3daSStefan Achatz 	int retval = 0;
209cb7cf3daSStefan Achatz 	int difference;
210cb7cf3daSStefan Achatz 	int profile_number;
211cb7cf3daSStefan Achatz 	struct pyra_profile_settings *profile_settings;
212cb7cf3daSStefan Achatz 
213cb7cf3daSStefan Achatz 	if (off != 0 || count != sizeof(struct pyra_profile_settings))
214cb7cf3daSStefan Achatz 		return -EINVAL;
215cb7cf3daSStefan Achatz 
216cb7cf3daSStefan Achatz 	profile_number = ((struct pyra_profile_settings const *)buf)->number;
217cb7cf3daSStefan Achatz 	profile_settings = &pyra->profile_settings[profile_number];
218cb7cf3daSStefan Achatz 
219cb7cf3daSStefan Achatz 	mutex_lock(&pyra->pyra_lock);
220cb7cf3daSStefan Achatz 	difference = memcmp(buf, profile_settings,
221cb7cf3daSStefan Achatz 			sizeof(struct pyra_profile_settings));
222cb7cf3daSStefan Achatz 	if (difference) {
223cb7cf3daSStefan Achatz 		retval = pyra_set_profile_settings(usb_dev,
224cb7cf3daSStefan Achatz 				(struct pyra_profile_settings const *)buf);
225cb7cf3daSStefan Achatz 		if (!retval)
226cb7cf3daSStefan Achatz 			memcpy(profile_settings, buf,
227cb7cf3daSStefan Achatz 					sizeof(struct pyra_profile_settings));
228cb7cf3daSStefan Achatz 	}
229cb7cf3daSStefan Achatz 	mutex_unlock(&pyra->pyra_lock);
230cb7cf3daSStefan Achatz 
231cb7cf3daSStefan Achatz 	if (retval)
232cb7cf3daSStefan Achatz 		return retval;
233cb7cf3daSStefan Achatz 
234cb7cf3daSStefan Achatz 	return sizeof(struct pyra_profile_settings);
235cb7cf3daSStefan Achatz }
236cb7cf3daSStefan Achatz 
237cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_write_profile_buttons(struct file *fp,
238cb7cf3daSStefan Achatz 		struct kobject *kobj, struct bin_attribute *attr, char *buf,
239cb7cf3daSStefan Achatz 		loff_t off, size_t count)
240cb7cf3daSStefan Achatz {
2415012aadaSStefan Achatz 	struct device *dev =
2425012aadaSStefan Achatz 			container_of(kobj, struct device, kobj)->parent->parent;
243cb7cf3daSStefan Achatz 	struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
244cb7cf3daSStefan Achatz 	struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
245cb7cf3daSStefan Achatz 	int retval = 0;
246cb7cf3daSStefan Achatz 	int difference;
247cb7cf3daSStefan Achatz 	int profile_number;
248cb7cf3daSStefan Achatz 	struct pyra_profile_buttons *profile_buttons;
249cb7cf3daSStefan Achatz 
250cb7cf3daSStefan Achatz 	if (off != 0 || count != sizeof(struct pyra_profile_buttons))
251cb7cf3daSStefan Achatz 		return -EINVAL;
252cb7cf3daSStefan Achatz 
253cb7cf3daSStefan Achatz 	profile_number = ((struct pyra_profile_buttons const *)buf)->number;
254cb7cf3daSStefan Achatz 	profile_buttons = &pyra->profile_buttons[profile_number];
255cb7cf3daSStefan Achatz 
256cb7cf3daSStefan Achatz 	mutex_lock(&pyra->pyra_lock);
257cb7cf3daSStefan Achatz 	difference = memcmp(buf, profile_buttons,
258cb7cf3daSStefan Achatz 			sizeof(struct pyra_profile_buttons));
259cb7cf3daSStefan Achatz 	if (difference) {
260cb7cf3daSStefan Achatz 		retval = pyra_set_profile_buttons(usb_dev,
261cb7cf3daSStefan Achatz 				(struct pyra_profile_buttons const *)buf);
262cb7cf3daSStefan Achatz 		if (!retval)
263cb7cf3daSStefan Achatz 			memcpy(profile_buttons, buf,
264cb7cf3daSStefan Achatz 					sizeof(struct pyra_profile_buttons));
265cb7cf3daSStefan Achatz 	}
266cb7cf3daSStefan Achatz 	mutex_unlock(&pyra->pyra_lock);
267cb7cf3daSStefan Achatz 
268cb7cf3daSStefan Achatz 	if (retval)
269cb7cf3daSStefan Achatz 		return retval;
270cb7cf3daSStefan Achatz 
271cb7cf3daSStefan Achatz 	return sizeof(struct pyra_profile_buttons);
272cb7cf3daSStefan Achatz }
273cb7cf3daSStefan Achatz 
274cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_read_settings(struct file *fp,
275cb7cf3daSStefan Achatz 		struct kobject *kobj, struct bin_attribute *attr, char *buf,
276cb7cf3daSStefan Achatz 		loff_t off, size_t count)
277cb7cf3daSStefan Achatz {
2785012aadaSStefan Achatz 	struct device *dev =
2795012aadaSStefan Achatz 			container_of(kobj, struct device, kobj)->parent->parent;
280cb7cf3daSStefan Achatz 	struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
281cb7cf3daSStefan Achatz 
282cb7cf3daSStefan Achatz 	if (off >= sizeof(struct pyra_settings))
283cb7cf3daSStefan Achatz 		return 0;
284cb7cf3daSStefan Achatz 
285cb7cf3daSStefan Achatz 	if (off + count > sizeof(struct pyra_settings))
286cb7cf3daSStefan Achatz 		count = sizeof(struct pyra_settings) - off;
287cb7cf3daSStefan Achatz 
288cb7cf3daSStefan Achatz 	mutex_lock(&pyra->pyra_lock);
289cb7cf3daSStefan Achatz 	memcpy(buf, ((char const *)&pyra->settings) + off, count);
290cb7cf3daSStefan Achatz 	mutex_unlock(&pyra->pyra_lock);
291cb7cf3daSStefan Achatz 
292cb7cf3daSStefan Achatz 	return count;
293cb7cf3daSStefan Achatz }
294cb7cf3daSStefan Achatz 
295cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_write_settings(struct file *fp,
296cb7cf3daSStefan Achatz 		struct kobject *kobj, struct bin_attribute *attr, char *buf,
297cb7cf3daSStefan Achatz 		loff_t off, size_t count)
298cb7cf3daSStefan Achatz {
2995012aadaSStefan Achatz 	struct device *dev =
3005012aadaSStefan Achatz 			container_of(kobj, struct device, kobj)->parent->parent;
301cb7cf3daSStefan Achatz 	struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
302cb7cf3daSStefan Achatz 	struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
303cb7cf3daSStefan Achatz 	int retval = 0;
304cb7cf3daSStefan Achatz 	int difference;
305cb7cf3daSStefan Achatz 
306cb7cf3daSStefan Achatz 	if (off != 0 || count != sizeof(struct pyra_settings))
307cb7cf3daSStefan Achatz 		return -EINVAL;
308cb7cf3daSStefan Achatz 
309cb7cf3daSStefan Achatz 	mutex_lock(&pyra->pyra_lock);
310cb7cf3daSStefan Achatz 	difference = memcmp(buf, &pyra->settings, sizeof(struct pyra_settings));
311cb7cf3daSStefan Achatz 	if (difference) {
312cb7cf3daSStefan Achatz 		retval = pyra_set_settings(usb_dev,
313cb7cf3daSStefan Achatz 				(struct pyra_settings const *)buf);
314cb7cf3daSStefan Achatz 		if (!retval)
315cb7cf3daSStefan Achatz 			memcpy(&pyra->settings, buf,
316cb7cf3daSStefan Achatz 					sizeof(struct pyra_settings));
317cb7cf3daSStefan Achatz 	}
318cb7cf3daSStefan Achatz 	mutex_unlock(&pyra->pyra_lock);
319cb7cf3daSStefan Achatz 
320cb7cf3daSStefan Achatz 	if (retval)
321cb7cf3daSStefan Achatz 		return retval;
322cb7cf3daSStefan Achatz 
323cb7cf3daSStefan Achatz 	profile_activated(pyra, pyra->settings.startup_profile);
324cb7cf3daSStefan Achatz 
325cb7cf3daSStefan Achatz 	return sizeof(struct pyra_settings);
326cb7cf3daSStefan Achatz }
327cb7cf3daSStefan Achatz 
328cb7cf3daSStefan Achatz 
329cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_show_actual_cpi(struct device *dev,
330cb7cf3daSStefan Achatz 		struct device_attribute *attr, char *buf)
331cb7cf3daSStefan Achatz {
3325012aadaSStefan Achatz 	struct pyra_device *pyra =
3335012aadaSStefan Achatz 			hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
334cb7cf3daSStefan Achatz 	return snprintf(buf, PAGE_SIZE, "%d\n", pyra->actual_cpi);
335cb7cf3daSStefan Achatz }
336cb7cf3daSStefan Achatz 
337cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_show_actual_profile(struct device *dev,
338cb7cf3daSStefan Achatz 		struct device_attribute *attr, char *buf)
339cb7cf3daSStefan Achatz {
3405012aadaSStefan Achatz 	struct pyra_device *pyra =
3415012aadaSStefan Achatz 			hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
342cb7cf3daSStefan Achatz 	return snprintf(buf, PAGE_SIZE, "%d\n", pyra->actual_profile);
343cb7cf3daSStefan Achatz }
344cb7cf3daSStefan Achatz 
345cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_show_firmware_version(struct device *dev,
346cb7cf3daSStefan Achatz 		struct device_attribute *attr, char *buf)
347cb7cf3daSStefan Achatz {
3485012aadaSStefan Achatz 	struct pyra_device *pyra =
3495012aadaSStefan Achatz 			hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
350cb7cf3daSStefan Achatz 	return snprintf(buf, PAGE_SIZE, "%d\n", pyra->firmware_version);
351cb7cf3daSStefan Achatz }
352cb7cf3daSStefan Achatz 
353cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_show_startup_profile(struct device *dev,
354cb7cf3daSStefan Achatz 		struct device_attribute *attr, char *buf)
355cb7cf3daSStefan Achatz {
3565012aadaSStefan Achatz 	struct pyra_device *pyra =
3575012aadaSStefan Achatz 			hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
358cb7cf3daSStefan Achatz 	return snprintf(buf, PAGE_SIZE, "%d\n", pyra->settings.startup_profile);
359cb7cf3daSStefan Achatz }
360cb7cf3daSStefan Achatz 
3615012aadaSStefan Achatz static struct device_attribute pyra_attributes[] = {
3625012aadaSStefan Achatz 	__ATTR(actual_cpi, 0440, pyra_sysfs_show_actual_cpi, NULL),
3635012aadaSStefan Achatz 	__ATTR(actual_profile, 0440, pyra_sysfs_show_actual_profile, NULL),
3645012aadaSStefan Achatz 	__ATTR(firmware_version, 0440,
3655012aadaSStefan Achatz 			pyra_sysfs_show_firmware_version, NULL),
3665012aadaSStefan Achatz 	__ATTR(startup_profile, 0440,
3675012aadaSStefan Achatz 			pyra_sysfs_show_startup_profile, NULL),
3685012aadaSStefan Achatz 	__ATTR_NULL
369cb7cf3daSStefan Achatz };
370cb7cf3daSStefan Achatz 
3715012aadaSStefan Achatz static struct bin_attribute pyra_bin_attributes[] = {
3725012aadaSStefan Achatz 	{
373cb7cf3daSStefan Achatz 		.attr = { .name = "profile_settings", .mode = 0220 },
374cb7cf3daSStefan Achatz 		.size = sizeof(struct pyra_profile_settings),
375cb7cf3daSStefan Achatz 		.write = pyra_sysfs_write_profile_settings
3765012aadaSStefan Achatz 	},
3775012aadaSStefan Achatz 	{
378cb7cf3daSStefan Achatz 		.attr = { .name = "profile1_settings", .mode = 0440 },
379cb7cf3daSStefan Achatz 		.size = sizeof(struct pyra_profile_settings),
38014a057f8SStefan Achatz 		.read = pyra_sysfs_read_profilex_settings,
38114a057f8SStefan Achatz 		.private = &profile_numbers[0]
3825012aadaSStefan Achatz 	},
3835012aadaSStefan Achatz 	{
384cb7cf3daSStefan Achatz 		.attr = { .name = "profile2_settings", .mode = 0440 },
385cb7cf3daSStefan Achatz 		.size = sizeof(struct pyra_profile_settings),
38614a057f8SStefan Achatz 		.read = pyra_sysfs_read_profilex_settings,
38714a057f8SStefan Achatz 		.private = &profile_numbers[1]
3885012aadaSStefan Achatz 	},
3895012aadaSStefan Achatz 	{
390cb7cf3daSStefan Achatz 		.attr = { .name = "profile3_settings", .mode = 0440 },
391cb7cf3daSStefan Achatz 		.size = sizeof(struct pyra_profile_settings),
39214a057f8SStefan Achatz 		.read = pyra_sysfs_read_profilex_settings,
39314a057f8SStefan Achatz 		.private = &profile_numbers[2]
3945012aadaSStefan Achatz 	},
3955012aadaSStefan Achatz 	{
396cb7cf3daSStefan Achatz 		.attr = { .name = "profile4_settings", .mode = 0440 },
397cb7cf3daSStefan Achatz 		.size = sizeof(struct pyra_profile_settings),
39814a057f8SStefan Achatz 		.read = pyra_sysfs_read_profilex_settings,
39914a057f8SStefan Achatz 		.private = &profile_numbers[3]
4005012aadaSStefan Achatz 	},
4015012aadaSStefan Achatz 	{
402cb7cf3daSStefan Achatz 		.attr = { .name = "profile5_settings", .mode = 0440 },
403cb7cf3daSStefan Achatz 		.size = sizeof(struct pyra_profile_settings),
40414a057f8SStefan Achatz 		.read = pyra_sysfs_read_profilex_settings,
40514a057f8SStefan Achatz 		.private = &profile_numbers[4]
4065012aadaSStefan Achatz 	},
4075012aadaSStefan Achatz 	{
408cb7cf3daSStefan Achatz 		.attr = { .name = "profile_buttons", .mode = 0220 },
409cb7cf3daSStefan Achatz 		.size = sizeof(struct pyra_profile_buttons),
410cb7cf3daSStefan Achatz 		.write = pyra_sysfs_write_profile_buttons
4115012aadaSStefan Achatz 	},
4125012aadaSStefan Achatz 	{
413cb7cf3daSStefan Achatz 		.attr = { .name = "profile1_buttons", .mode = 0440 },
414cb7cf3daSStefan Achatz 		.size = sizeof(struct pyra_profile_buttons),
41514a057f8SStefan Achatz 		.read = pyra_sysfs_read_profilex_buttons,
41614a057f8SStefan Achatz 		.private = &profile_numbers[0]
4175012aadaSStefan Achatz 	},
4185012aadaSStefan Achatz 	{
419cb7cf3daSStefan Achatz 		.attr = { .name = "profile2_buttons", .mode = 0440 },
420cb7cf3daSStefan Achatz 		.size = sizeof(struct pyra_profile_buttons),
42114a057f8SStefan Achatz 		.read = pyra_sysfs_read_profilex_buttons,
42214a057f8SStefan Achatz 		.private = &profile_numbers[1]
4235012aadaSStefan Achatz 	},
4245012aadaSStefan Achatz 	{
425cb7cf3daSStefan Achatz 		.attr = { .name = "profile3_buttons", .mode = 0440 },
426cb7cf3daSStefan Achatz 		.size = sizeof(struct pyra_profile_buttons),
42714a057f8SStefan Achatz 		.read = pyra_sysfs_read_profilex_buttons,
42814a057f8SStefan Achatz 		.private = &profile_numbers[2]
4295012aadaSStefan Achatz 	},
4305012aadaSStefan Achatz 	{
431cb7cf3daSStefan Achatz 		.attr = { .name = "profile4_buttons", .mode = 0440 },
432cb7cf3daSStefan Achatz 		.size = sizeof(struct pyra_profile_buttons),
43314a057f8SStefan Achatz 		.read = pyra_sysfs_read_profilex_buttons,
43414a057f8SStefan Achatz 		.private = &profile_numbers[3]
4355012aadaSStefan Achatz 	},
4365012aadaSStefan Achatz 	{
437cb7cf3daSStefan Achatz 		.attr = { .name = "profile5_buttons", .mode = 0440 },
438cb7cf3daSStefan Achatz 		.size = sizeof(struct pyra_profile_buttons),
43914a057f8SStefan Achatz 		.read = pyra_sysfs_read_profilex_buttons,
44014a057f8SStefan Achatz 		.private = &profile_numbers[4]
4415012aadaSStefan Achatz 	},
4425012aadaSStefan Achatz 	{
443cb7cf3daSStefan Achatz 		.attr = { .name = "settings", .mode = 0660 },
444cb7cf3daSStefan Achatz 		.size = sizeof(struct pyra_settings),
445cb7cf3daSStefan Achatz 		.read = pyra_sysfs_read_settings,
446cb7cf3daSStefan Achatz 		.write = pyra_sysfs_write_settings
4475012aadaSStefan Achatz 	},
4485012aadaSStefan Achatz 	__ATTR_NULL
449cb7cf3daSStefan Achatz };
450cb7cf3daSStefan Achatz 
451cb7cf3daSStefan Achatz static int pyra_init_pyra_device_struct(struct usb_device *usb_dev,
452cb7cf3daSStefan Achatz 		struct pyra_device *pyra)
453cb7cf3daSStefan Achatz {
4545772f636SStefan Achatz 	struct pyra_info info;
455cb7cf3daSStefan Achatz 	int retval, i;
456cb7cf3daSStefan Achatz 
457cb7cf3daSStefan Achatz 	mutex_init(&pyra->pyra_lock);
458cb7cf3daSStefan Achatz 
4595772f636SStefan Achatz 	retval = pyra_get_info(usb_dev, &info);
4605772f636SStefan Achatz 	if (retval)
461cb7cf3daSStefan Achatz 		return retval;
4625772f636SStefan Achatz 
4635772f636SStefan Achatz 	pyra->firmware_version = info.firmware_version;
464cb7cf3daSStefan Achatz 
465cb7cf3daSStefan Achatz 	retval = pyra_get_settings(usb_dev, &pyra->settings);
466cb7cf3daSStefan Achatz 	if (retval)
467cb7cf3daSStefan Achatz 		return retval;
468cb7cf3daSStefan Achatz 
469cb7cf3daSStefan Achatz 	for (i = 0; i < 5; ++i) {
470cb7cf3daSStefan Achatz 		retval = pyra_get_profile_settings(usb_dev,
471cb7cf3daSStefan Achatz 				&pyra->profile_settings[i], i);
472cb7cf3daSStefan Achatz 		if (retval)
473cb7cf3daSStefan Achatz 			return retval;
474cb7cf3daSStefan Achatz 
475cb7cf3daSStefan Achatz 		retval = pyra_get_profile_buttons(usb_dev,
476cb7cf3daSStefan Achatz 				&pyra->profile_buttons[i], i);
477cb7cf3daSStefan Achatz 		if (retval)
478cb7cf3daSStefan Achatz 			return retval;
479cb7cf3daSStefan Achatz 	}
480cb7cf3daSStefan Achatz 
481cb7cf3daSStefan Achatz 	profile_activated(pyra, pyra->settings.startup_profile);
482cb7cf3daSStefan Achatz 
483cb7cf3daSStefan Achatz 	return 0;
484cb7cf3daSStefan Achatz }
485cb7cf3daSStefan Achatz 
486cb7cf3daSStefan Achatz static int pyra_init_specials(struct hid_device *hdev)
487cb7cf3daSStefan Achatz {
488cb7cf3daSStefan Achatz 	struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
489cb7cf3daSStefan Achatz 	struct usb_device *usb_dev = interface_to_usbdev(intf);
490cb7cf3daSStefan Achatz 	struct pyra_device *pyra;
491cb7cf3daSStefan Achatz 	int retval;
492cb7cf3daSStefan Achatz 
493cb7cf3daSStefan Achatz 	if (intf->cur_altsetting->desc.bInterfaceProtocol
494cb7cf3daSStefan Achatz 			== USB_INTERFACE_PROTOCOL_MOUSE) {
495cb7cf3daSStefan Achatz 
496cb7cf3daSStefan Achatz 		pyra = kzalloc(sizeof(*pyra), GFP_KERNEL);
497cb7cf3daSStefan Achatz 		if (!pyra) {
4984291ee30SJoe Perches 			hid_err(hdev, "can't alloc device descriptor\n");
499cb7cf3daSStefan Achatz 			return -ENOMEM;
500cb7cf3daSStefan Achatz 		}
501cb7cf3daSStefan Achatz 		hid_set_drvdata(hdev, pyra);
502cb7cf3daSStefan Achatz 
503cb7cf3daSStefan Achatz 		retval = pyra_init_pyra_device_struct(usb_dev, pyra);
504cb7cf3daSStefan Achatz 		if (retval) {
5054291ee30SJoe Perches 			hid_err(hdev, "couldn't init struct pyra_device\n");
506cb7cf3daSStefan Achatz 			goto exit_free;
507cb7cf3daSStefan Achatz 		}
508cb7cf3daSStefan Achatz 
5095012aadaSStefan Achatz 		retval = roccat_connect(pyra_class, hdev);
510cb7cf3daSStefan Achatz 		if (retval < 0) {
5114291ee30SJoe Perches 			hid_err(hdev, "couldn't init char dev\n");
512cb7cf3daSStefan Achatz 		} else {
513cb7cf3daSStefan Achatz 			pyra->chrdev_minor = retval;
514cb7cf3daSStefan Achatz 			pyra->roccat_claimed = 1;
515cb7cf3daSStefan Achatz 		}
516cb7cf3daSStefan Achatz 	} else {
517cb7cf3daSStefan Achatz 		hid_set_drvdata(hdev, NULL);
518cb7cf3daSStefan Achatz 	}
519cb7cf3daSStefan Achatz 
520cb7cf3daSStefan Achatz 	return 0;
521cb7cf3daSStefan Achatz exit_free:
522cb7cf3daSStefan Achatz 	kfree(pyra);
523cb7cf3daSStefan Achatz 	return retval;
524cb7cf3daSStefan Achatz }
525cb7cf3daSStefan Achatz 
526cb7cf3daSStefan Achatz static void pyra_remove_specials(struct hid_device *hdev)
527cb7cf3daSStefan Achatz {
528cb7cf3daSStefan Achatz 	struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
529cb7cf3daSStefan Achatz 	struct pyra_device *pyra;
530cb7cf3daSStefan Achatz 
531cb7cf3daSStefan Achatz 	if (intf->cur_altsetting->desc.bInterfaceProtocol
532cb7cf3daSStefan Achatz 			== USB_INTERFACE_PROTOCOL_MOUSE) {
533cb7cf3daSStefan Achatz 		pyra = hid_get_drvdata(hdev);
534cb7cf3daSStefan Achatz 		if (pyra->roccat_claimed)
535cb7cf3daSStefan Achatz 			roccat_disconnect(pyra->chrdev_minor);
536cb7cf3daSStefan Achatz 		kfree(hid_get_drvdata(hdev));
537cb7cf3daSStefan Achatz 	}
538cb7cf3daSStefan Achatz }
539cb7cf3daSStefan Achatz 
540cb7cf3daSStefan Achatz static int pyra_probe(struct hid_device *hdev, const struct hid_device_id *id)
541cb7cf3daSStefan Achatz {
542cb7cf3daSStefan Achatz 	int retval;
543cb7cf3daSStefan Achatz 
544cb7cf3daSStefan Achatz 	retval = hid_parse(hdev);
545cb7cf3daSStefan Achatz 	if (retval) {
5464291ee30SJoe Perches 		hid_err(hdev, "parse failed\n");
547cb7cf3daSStefan Achatz 		goto exit;
548cb7cf3daSStefan Achatz 	}
549cb7cf3daSStefan Achatz 
550cb7cf3daSStefan Achatz 	retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
551cb7cf3daSStefan Achatz 	if (retval) {
5524291ee30SJoe Perches 		hid_err(hdev, "hw start failed\n");
553cb7cf3daSStefan Achatz 		goto exit;
554cb7cf3daSStefan Achatz 	}
555cb7cf3daSStefan Achatz 
556cb7cf3daSStefan Achatz 	retval = pyra_init_specials(hdev);
557cb7cf3daSStefan Achatz 	if (retval) {
5584291ee30SJoe Perches 		hid_err(hdev, "couldn't install mouse\n");
559cb7cf3daSStefan Achatz 		goto exit_stop;
560cb7cf3daSStefan Achatz 	}
561cb7cf3daSStefan Achatz 	return 0;
562cb7cf3daSStefan Achatz 
563cb7cf3daSStefan Achatz exit_stop:
564cb7cf3daSStefan Achatz 	hid_hw_stop(hdev);
565cb7cf3daSStefan Achatz exit:
566cb7cf3daSStefan Achatz 	return retval;
567cb7cf3daSStefan Achatz }
568cb7cf3daSStefan Achatz 
569cb7cf3daSStefan Achatz static void pyra_remove(struct hid_device *hdev)
570cb7cf3daSStefan Achatz {
571cb7cf3daSStefan Achatz 	pyra_remove_specials(hdev);
572cb7cf3daSStefan Achatz 	hid_hw_stop(hdev);
573cb7cf3daSStefan Achatz }
574cb7cf3daSStefan Achatz 
575cb7cf3daSStefan Achatz static void pyra_keep_values_up_to_date(struct pyra_device *pyra,
576cb7cf3daSStefan Achatz 		u8 const *data)
577cb7cf3daSStefan Achatz {
578cb7cf3daSStefan Achatz 	struct pyra_mouse_event_button const *button_event;
579cb7cf3daSStefan Achatz 
580cb7cf3daSStefan Achatz 	switch (data[0]) {
581cb7cf3daSStefan Achatz 	case PYRA_MOUSE_REPORT_NUMBER_BUTTON:
582cb7cf3daSStefan Achatz 		button_event = (struct pyra_mouse_event_button const *)data;
583cb7cf3daSStefan Achatz 		switch (button_event->type) {
584cb7cf3daSStefan Achatz 		case PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2:
585cb7cf3daSStefan Achatz 			profile_activated(pyra, button_event->data1 - 1);
586cb7cf3daSStefan Achatz 			break;
587cb7cf3daSStefan Achatz 		case PYRA_MOUSE_EVENT_BUTTON_TYPE_CPI:
588cb7cf3daSStefan Achatz 			pyra->actual_cpi = button_event->data1;
589cb7cf3daSStefan Achatz 			break;
590cb7cf3daSStefan Achatz 		}
591cb7cf3daSStefan Achatz 		break;
592cb7cf3daSStefan Achatz 	}
593cb7cf3daSStefan Achatz }
594cb7cf3daSStefan Achatz 
595cb7cf3daSStefan Achatz static void pyra_report_to_chrdev(struct pyra_device const *pyra,
596cb7cf3daSStefan Achatz 		u8 const *data)
597cb7cf3daSStefan Achatz {
598cb7cf3daSStefan Achatz 	struct pyra_roccat_report roccat_report;
599cb7cf3daSStefan Achatz 	struct pyra_mouse_event_button const *button_event;
600cb7cf3daSStefan Achatz 
601cb7cf3daSStefan Achatz 	if (data[0] != PYRA_MOUSE_REPORT_NUMBER_BUTTON)
602cb7cf3daSStefan Achatz 		return;
603cb7cf3daSStefan Achatz 
604cb7cf3daSStefan Achatz 	button_event = (struct pyra_mouse_event_button const *)data;
605cb7cf3daSStefan Achatz 
606cb7cf3daSStefan Achatz 	switch (button_event->type) {
607cb7cf3daSStefan Achatz 	case PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2:
608cb7cf3daSStefan Achatz 	case PYRA_MOUSE_EVENT_BUTTON_TYPE_CPI:
609cb7cf3daSStefan Achatz 		roccat_report.type = button_event->type;
610cb7cf3daSStefan Achatz 		roccat_report.value = button_event->data1;
611cb7cf3daSStefan Achatz 		roccat_report.key = 0;
612cb7cf3daSStefan Achatz 		roccat_report_event(pyra->chrdev_minor,
613cb7cf3daSStefan Achatz 				(uint8_t const *)&roccat_report,
614cb7cf3daSStefan Achatz 				sizeof(struct pyra_roccat_report));
615cb7cf3daSStefan Achatz 		break;
616cb7cf3daSStefan Achatz 	case PYRA_MOUSE_EVENT_BUTTON_TYPE_MACRO:
617cb7cf3daSStefan Achatz 	case PYRA_MOUSE_EVENT_BUTTON_TYPE_SHORTCUT:
618cb7cf3daSStefan Achatz 	case PYRA_MOUSE_EVENT_BUTTON_TYPE_QUICKLAUNCH:
619cb7cf3daSStefan Achatz 		if (button_event->data2 == PYRA_MOUSE_EVENT_BUTTON_PRESS) {
620cb7cf3daSStefan Achatz 			roccat_report.type = button_event->type;
621cb7cf3daSStefan Achatz 			roccat_report.key = button_event->data1;
622d2b570a5SStefan Achatz 			/*
623d2b570a5SStefan Achatz 			 * pyra reports profile numbers with range 1-5.
624d2b570a5SStefan Achatz 			 * Keeping this behaviour.
625d2b570a5SStefan Achatz 			 */
626d2b570a5SStefan Achatz 			roccat_report.value = pyra->actual_profile + 1;
627cb7cf3daSStefan Achatz 			roccat_report_event(pyra->chrdev_minor,
628cb7cf3daSStefan Achatz 					(uint8_t const *)&roccat_report,
629cb7cf3daSStefan Achatz 					sizeof(struct pyra_roccat_report));
630cb7cf3daSStefan Achatz 		}
631cb7cf3daSStefan Achatz 		break;
632cb7cf3daSStefan Achatz 	}
633cb7cf3daSStefan Achatz }
634cb7cf3daSStefan Achatz 
635cb7cf3daSStefan Achatz static int pyra_raw_event(struct hid_device *hdev, struct hid_report *report,
636cb7cf3daSStefan Achatz 		u8 *data, int size)
637cb7cf3daSStefan Achatz {
638cb7cf3daSStefan Achatz 	struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
639cb7cf3daSStefan Achatz 	struct pyra_device *pyra = hid_get_drvdata(hdev);
640cb7cf3daSStefan Achatz 
641cb7cf3daSStefan Achatz 	if (intf->cur_altsetting->desc.bInterfaceProtocol
642cb7cf3daSStefan Achatz 			!= USB_INTERFACE_PROTOCOL_MOUSE)
643cb7cf3daSStefan Achatz 		return 0;
644cb7cf3daSStefan Achatz 
645cb7cf3daSStefan Achatz 	pyra_keep_values_up_to_date(pyra, data);
646cb7cf3daSStefan Achatz 
647cb7cf3daSStefan Achatz 	if (pyra->roccat_claimed)
648cb7cf3daSStefan Achatz 		pyra_report_to_chrdev(pyra, data);
649cb7cf3daSStefan Achatz 
650cb7cf3daSStefan Achatz 	return 0;
651cb7cf3daSStefan Achatz }
652cb7cf3daSStefan Achatz 
653cb7cf3daSStefan Achatz static const struct hid_device_id pyra_devices[] = {
654cb7cf3daSStefan Achatz 	{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT,
655cb7cf3daSStefan Achatz 			USB_DEVICE_ID_ROCCAT_PYRA_WIRED) },
656cb7cf3daSStefan Achatz 	/* TODO add USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS after testing */
657cb7cf3daSStefan Achatz 	{ }
658cb7cf3daSStefan Achatz };
659cb7cf3daSStefan Achatz 
660cb7cf3daSStefan Achatz MODULE_DEVICE_TABLE(hid, pyra_devices);
661cb7cf3daSStefan Achatz 
662cb7cf3daSStefan Achatz static struct hid_driver pyra_driver = {
663cb7cf3daSStefan Achatz 		.name = "pyra",
664cb7cf3daSStefan Achatz 		.id_table = pyra_devices,
665cb7cf3daSStefan Achatz 		.probe = pyra_probe,
666cb7cf3daSStefan Achatz 		.remove = pyra_remove,
667cb7cf3daSStefan Achatz 		.raw_event = pyra_raw_event
668cb7cf3daSStefan Achatz };
669cb7cf3daSStefan Achatz 
670cb7cf3daSStefan Achatz static int __init pyra_init(void)
671cb7cf3daSStefan Achatz {
6725012aadaSStefan Achatz 	int retval;
6735012aadaSStefan Achatz 
6745012aadaSStefan Achatz 	/* class name has to be same as driver name */
6755012aadaSStefan Achatz 	pyra_class = class_create(THIS_MODULE, "pyra");
6765012aadaSStefan Achatz 	if (IS_ERR(pyra_class))
6775012aadaSStefan Achatz 		return PTR_ERR(pyra_class);
6785012aadaSStefan Achatz 	pyra_class->dev_attrs = pyra_attributes;
6795012aadaSStefan Achatz 	pyra_class->dev_bin_attrs = pyra_bin_attributes;
6805012aadaSStefan Achatz 
6815012aadaSStefan Achatz 	retval = hid_register_driver(&pyra_driver);
6825012aadaSStefan Achatz 	if (retval)
6835012aadaSStefan Achatz 		class_destroy(pyra_class);
6845012aadaSStefan Achatz 	return retval;
685cb7cf3daSStefan Achatz }
686cb7cf3daSStefan Achatz 
687cb7cf3daSStefan Achatz static void __exit pyra_exit(void)
688cb7cf3daSStefan Achatz {
6895012aadaSStefan Achatz 	class_destroy(pyra_class);
690cb7cf3daSStefan Achatz 	hid_unregister_driver(&pyra_driver);
691cb7cf3daSStefan Achatz }
692cb7cf3daSStefan Achatz 
693cb7cf3daSStefan Achatz module_init(pyra_init);
694cb7cf3daSStefan Achatz module_exit(pyra_exit);
695cb7cf3daSStefan Achatz 
696cb7cf3daSStefan Achatz MODULE_AUTHOR("Stefan Achatz");
697cb7cf3daSStefan Achatz MODULE_DESCRIPTION("USB Roccat Pyra driver");
698cb7cf3daSStefan Achatz MODULE_LICENSE("GPL v2");
699