xref: /openbmc/linux/drivers/hid/hid-roccat-pyra.c (revision cb7cf3da)
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/usb.h>
24cb7cf3daSStefan Achatz #include <linux/module.h>
25cb7cf3daSStefan Achatz #include <linux/slab.h>
26cb7cf3daSStefan Achatz #include "hid-ids.h"
27cb7cf3daSStefan Achatz #include "hid-roccat.h"
28cb7cf3daSStefan Achatz #include "hid-roccat-pyra.h"
29cb7cf3daSStefan Achatz 
30cb7cf3daSStefan Achatz static void profile_activated(struct pyra_device *pyra,
31cb7cf3daSStefan Achatz 		unsigned int new_profile)
32cb7cf3daSStefan Achatz {
33cb7cf3daSStefan Achatz 	pyra->actual_profile = new_profile;
34cb7cf3daSStefan Achatz 	pyra->actual_cpi = pyra->profile_settings[pyra->actual_profile].y_cpi;
35cb7cf3daSStefan Achatz }
36cb7cf3daSStefan Achatz 
37cb7cf3daSStefan Achatz static int pyra_send_control(struct usb_device *usb_dev, int value,
38cb7cf3daSStefan Achatz 		enum pyra_control_requests request)
39cb7cf3daSStefan Achatz {
40cb7cf3daSStefan Achatz 	int len;
41cb7cf3daSStefan Achatz 	struct pyra_control control;
42cb7cf3daSStefan Achatz 
43cb7cf3daSStefan Achatz 	if ((request == PYRA_CONTROL_REQUEST_PROFILE_SETTINGS ||
44cb7cf3daSStefan Achatz 			request == PYRA_CONTROL_REQUEST_PROFILE_BUTTONS) &&
45cb7cf3daSStefan Achatz 			(value < 0 || value > 4))
46cb7cf3daSStefan Achatz 		return -EINVAL;
47cb7cf3daSStefan Achatz 
48cb7cf3daSStefan Achatz 	control.command = PYRA_COMMAND_CONTROL;
49cb7cf3daSStefan Achatz 	control.value = value;
50cb7cf3daSStefan Achatz 	control.request = request;
51cb7cf3daSStefan Achatz 
52cb7cf3daSStefan Achatz 	len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
53cb7cf3daSStefan Achatz 			USB_REQ_SET_CONFIGURATION,
54cb7cf3daSStefan Achatz 			USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
55cb7cf3daSStefan Achatz 			PYRA_USB_COMMAND_CONTROL, 0, (char *)&control,
56cb7cf3daSStefan Achatz 			sizeof(struct pyra_control),
57cb7cf3daSStefan Achatz 			USB_CTRL_SET_TIMEOUT);
58cb7cf3daSStefan Achatz 
59cb7cf3daSStefan Achatz 	if (len != sizeof(struct pyra_control))
60cb7cf3daSStefan Achatz 		return len;
61cb7cf3daSStefan Achatz 
62cb7cf3daSStefan Achatz 	return 0;
63cb7cf3daSStefan Achatz }
64cb7cf3daSStefan Achatz 
65cb7cf3daSStefan Achatz static int pyra_receive_control_status(struct usb_device *usb_dev)
66cb7cf3daSStefan Achatz {
67cb7cf3daSStefan Achatz 	int len;
68cb7cf3daSStefan Achatz 	struct pyra_control control;
69cb7cf3daSStefan Achatz 
70cb7cf3daSStefan Achatz 	do {
71cb7cf3daSStefan Achatz 		msleep(10);
72cb7cf3daSStefan Achatz 
73cb7cf3daSStefan Achatz 		len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
74cb7cf3daSStefan Achatz 				USB_REQ_CLEAR_FEATURE,
75cb7cf3daSStefan Achatz 				USB_TYPE_CLASS | USB_RECIP_INTERFACE |
76cb7cf3daSStefan Achatz 				USB_DIR_IN,
77cb7cf3daSStefan Achatz 				PYRA_USB_COMMAND_CONTROL, 0, (char *)&control,
78cb7cf3daSStefan Achatz 				sizeof(struct pyra_control),
79cb7cf3daSStefan Achatz 				USB_CTRL_SET_TIMEOUT);
80cb7cf3daSStefan Achatz 
81cb7cf3daSStefan Achatz 		/* requested too early, try again */
82cb7cf3daSStefan Achatz 	} while (len == -EPROTO);
83cb7cf3daSStefan Achatz 
84cb7cf3daSStefan Achatz 	if (len == sizeof(struct pyra_control) &&
85cb7cf3daSStefan Achatz 			control.command == PYRA_COMMAND_CONTROL &&
86cb7cf3daSStefan Achatz 			control.request == PYRA_CONTROL_REQUEST_STATUS &&
87cb7cf3daSStefan Achatz 			control.value == 1)
88cb7cf3daSStefan Achatz 			return 0;
89cb7cf3daSStefan Achatz 	else {
90cb7cf3daSStefan Achatz 		dev_err(&usb_dev->dev, "receive control status: "
91cb7cf3daSStefan Achatz 				"unknown response 0x%x 0x%x\n",
92cb7cf3daSStefan Achatz 				control.request, control.value);
93cb7cf3daSStefan Achatz 		return -EINVAL;
94cb7cf3daSStefan Achatz 	}
95cb7cf3daSStefan Achatz }
96cb7cf3daSStefan Achatz 
97cb7cf3daSStefan Achatz static int pyra_get_profile_settings(struct usb_device *usb_dev,
98cb7cf3daSStefan Achatz 		struct pyra_profile_settings *buf, int number)
99cb7cf3daSStefan Achatz {
100cb7cf3daSStefan Achatz 	int retval;
101cb7cf3daSStefan Achatz 
102cb7cf3daSStefan Achatz 	retval = pyra_send_control(usb_dev, number,
103cb7cf3daSStefan Achatz 			PYRA_CONTROL_REQUEST_PROFILE_SETTINGS);
104cb7cf3daSStefan Achatz 
105cb7cf3daSStefan Achatz 	if (retval)
106cb7cf3daSStefan Achatz 		return retval;
107cb7cf3daSStefan Achatz 
108cb7cf3daSStefan Achatz 	retval = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
109cb7cf3daSStefan Achatz 			USB_REQ_CLEAR_FEATURE,
110cb7cf3daSStefan Achatz 			USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
111cb7cf3daSStefan Achatz 			PYRA_USB_COMMAND_PROFILE_SETTINGS, 0, (char *)buf,
112cb7cf3daSStefan Achatz 			sizeof(struct pyra_profile_settings),
113cb7cf3daSStefan Achatz 			USB_CTRL_SET_TIMEOUT);
114cb7cf3daSStefan Achatz 
115cb7cf3daSStefan Achatz 	if (retval != sizeof(struct pyra_profile_settings))
116cb7cf3daSStefan Achatz 		return retval;
117cb7cf3daSStefan Achatz 
118cb7cf3daSStefan Achatz 	return 0;
119cb7cf3daSStefan Achatz }
120cb7cf3daSStefan Achatz 
121cb7cf3daSStefan Achatz static int pyra_get_profile_buttons(struct usb_device *usb_dev,
122cb7cf3daSStefan Achatz 		struct pyra_profile_buttons *buf, int number)
123cb7cf3daSStefan Achatz {
124cb7cf3daSStefan Achatz 	int retval;
125cb7cf3daSStefan Achatz 
126cb7cf3daSStefan Achatz 	retval = pyra_send_control(usb_dev, number,
127cb7cf3daSStefan Achatz 			PYRA_CONTROL_REQUEST_PROFILE_BUTTONS);
128cb7cf3daSStefan Achatz 
129cb7cf3daSStefan Achatz 	if (retval)
130cb7cf3daSStefan Achatz 		return retval;
131cb7cf3daSStefan Achatz 
132cb7cf3daSStefan Achatz 	retval = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
133cb7cf3daSStefan Achatz 			USB_REQ_CLEAR_FEATURE,
134cb7cf3daSStefan Achatz 			USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
135cb7cf3daSStefan Achatz 			PYRA_USB_COMMAND_PROFILE_BUTTONS, 0, (char *)buf,
136cb7cf3daSStefan Achatz 			sizeof(struct pyra_profile_buttons),
137cb7cf3daSStefan Achatz 			USB_CTRL_SET_TIMEOUT);
138cb7cf3daSStefan Achatz 
139cb7cf3daSStefan Achatz 	if (retval != sizeof(struct pyra_profile_buttons))
140cb7cf3daSStefan Achatz 		return retval;
141cb7cf3daSStefan Achatz 
142cb7cf3daSStefan Achatz 	return 0;
143cb7cf3daSStefan Achatz }
144cb7cf3daSStefan Achatz 
145cb7cf3daSStefan Achatz static int pyra_get_settings(struct usb_device *usb_dev,
146cb7cf3daSStefan Achatz 		struct pyra_settings *buf)
147cb7cf3daSStefan Achatz {
148cb7cf3daSStefan Achatz 	int len;
149cb7cf3daSStefan Achatz 	len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
150cb7cf3daSStefan Achatz 			USB_REQ_CLEAR_FEATURE,
151cb7cf3daSStefan Achatz 			USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
152cb7cf3daSStefan Achatz 			PYRA_USB_COMMAND_SETTINGS, 0, buf,
153cb7cf3daSStefan Achatz 			sizeof(struct pyra_settings), USB_CTRL_SET_TIMEOUT);
154cb7cf3daSStefan Achatz 	if (len != sizeof(struct pyra_settings))
155cb7cf3daSStefan Achatz 		return -EIO;
156cb7cf3daSStefan Achatz 	return 0;
157cb7cf3daSStefan Achatz }
158cb7cf3daSStefan Achatz 
159cb7cf3daSStefan Achatz static int pyra_get_info(struct usb_device *usb_dev, struct pyra_info *buf)
160cb7cf3daSStefan Achatz {
161cb7cf3daSStefan Achatz 	int len;
162cb7cf3daSStefan Achatz 	len = usb_control_msg(usb_dev, usb_rcvctrlpipe(usb_dev, 0),
163cb7cf3daSStefan Achatz 			USB_REQ_CLEAR_FEATURE,
164cb7cf3daSStefan Achatz 			USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_IN,
165cb7cf3daSStefan Achatz 			PYRA_USB_COMMAND_INFO, 0, buf,
166cb7cf3daSStefan Achatz 			sizeof(struct pyra_info), USB_CTRL_SET_TIMEOUT);
167cb7cf3daSStefan Achatz 	if (len != sizeof(struct pyra_info))
168cb7cf3daSStefan Achatz 		return -EIO;
169cb7cf3daSStefan Achatz 	return 0;
170cb7cf3daSStefan Achatz }
171cb7cf3daSStefan Achatz 
172cb7cf3daSStefan Achatz static int pyra_set_profile_settings(struct usb_device *usb_dev,
173cb7cf3daSStefan Achatz 		struct pyra_profile_settings const *settings)
174cb7cf3daSStefan Achatz {
175cb7cf3daSStefan Achatz 	int len;
176cb7cf3daSStefan Achatz 	len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
177cb7cf3daSStefan Achatz 			USB_REQ_SET_CONFIGURATION,
178cb7cf3daSStefan Achatz 			USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
179cb7cf3daSStefan Achatz 			PYRA_USB_COMMAND_PROFILE_SETTINGS, 0, (char *)settings,
180cb7cf3daSStefan Achatz 			sizeof(struct pyra_profile_settings),
181cb7cf3daSStefan Achatz 			USB_CTRL_SET_TIMEOUT);
182cb7cf3daSStefan Achatz 	if (len != sizeof(struct pyra_profile_settings))
183cb7cf3daSStefan Achatz 		return -EIO;
184cb7cf3daSStefan Achatz 	if (pyra_receive_control_status(usb_dev))
185cb7cf3daSStefan Achatz 		return -EIO;
186cb7cf3daSStefan Achatz 	return 0;
187cb7cf3daSStefan Achatz }
188cb7cf3daSStefan Achatz 
189cb7cf3daSStefan Achatz static int pyra_set_profile_buttons(struct usb_device *usb_dev,
190cb7cf3daSStefan Achatz 		struct pyra_profile_buttons const *buttons)
191cb7cf3daSStefan Achatz {
192cb7cf3daSStefan Achatz 	int len;
193cb7cf3daSStefan Achatz 	len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
194cb7cf3daSStefan Achatz 			USB_REQ_SET_CONFIGURATION,
195cb7cf3daSStefan Achatz 			USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
196cb7cf3daSStefan Achatz 			PYRA_USB_COMMAND_PROFILE_BUTTONS, 0, (char *)buttons,
197cb7cf3daSStefan Achatz 			sizeof(struct pyra_profile_buttons),
198cb7cf3daSStefan Achatz 			USB_CTRL_SET_TIMEOUT);
199cb7cf3daSStefan Achatz 	if (len != sizeof(struct pyra_profile_buttons))
200cb7cf3daSStefan Achatz 		return -EIO;
201cb7cf3daSStefan Achatz 	if (pyra_receive_control_status(usb_dev))
202cb7cf3daSStefan Achatz 		return -EIO;
203cb7cf3daSStefan Achatz 	return 0;
204cb7cf3daSStefan Achatz }
205cb7cf3daSStefan Achatz 
206cb7cf3daSStefan Achatz static int pyra_set_settings(struct usb_device *usb_dev,
207cb7cf3daSStefan Achatz 		struct pyra_settings const *settings)
208cb7cf3daSStefan Achatz {
209cb7cf3daSStefan Achatz 	int len;
210cb7cf3daSStefan Achatz 	len = usb_control_msg(usb_dev, usb_sndctrlpipe(usb_dev, 0),
211cb7cf3daSStefan Achatz 			USB_REQ_SET_CONFIGURATION,
212cb7cf3daSStefan Achatz 			USB_TYPE_CLASS | USB_RECIP_INTERFACE | USB_DIR_OUT,
213cb7cf3daSStefan Achatz 			PYRA_USB_COMMAND_SETTINGS, 0, (char *)settings,
214cb7cf3daSStefan Achatz 			sizeof(struct pyra_settings), USB_CTRL_SET_TIMEOUT);
215cb7cf3daSStefan Achatz 	if (len != sizeof(struct pyra_settings))
216cb7cf3daSStefan Achatz 		return -EIO;
217cb7cf3daSStefan Achatz 	if (pyra_receive_control_status(usb_dev))
218cb7cf3daSStefan Achatz 		return -EIO;
219cb7cf3daSStefan Achatz 	return 0;
220cb7cf3daSStefan Achatz }
221cb7cf3daSStefan Achatz 
222cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_read_profilex_settings(struct file *fp,
223cb7cf3daSStefan Achatz 		struct kobject *kobj, struct bin_attribute *attr, char *buf,
224cb7cf3daSStefan Achatz 		loff_t off, size_t count, int number)
225cb7cf3daSStefan Achatz {
226cb7cf3daSStefan Achatz 	struct device *dev = container_of(kobj, struct device, kobj);
227cb7cf3daSStefan Achatz 	struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
228cb7cf3daSStefan Achatz 
229cb7cf3daSStefan Achatz 	if (off >= sizeof(struct pyra_profile_settings))
230cb7cf3daSStefan Achatz 		return 0;
231cb7cf3daSStefan Achatz 
232cb7cf3daSStefan Achatz 	if (off + count > sizeof(struct pyra_profile_settings))
233cb7cf3daSStefan Achatz 		count = sizeof(struct pyra_profile_settings) - off;
234cb7cf3daSStefan Achatz 
235cb7cf3daSStefan Achatz 	mutex_lock(&pyra->pyra_lock);
236cb7cf3daSStefan Achatz 	memcpy(buf, ((char const *)&pyra->profile_settings[number]) + off,
237cb7cf3daSStefan Achatz 			count);
238cb7cf3daSStefan Achatz 	mutex_unlock(&pyra->pyra_lock);
239cb7cf3daSStefan Achatz 
240cb7cf3daSStefan Achatz 	return count;
241cb7cf3daSStefan Achatz }
242cb7cf3daSStefan Achatz 
243cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_read_profile1_settings(struct file *fp,
244cb7cf3daSStefan Achatz 		struct kobject *kobj, struct bin_attribute *attr, char *buf,
245cb7cf3daSStefan Achatz 		loff_t off, size_t count)
246cb7cf3daSStefan Achatz {
247cb7cf3daSStefan Achatz 	return pyra_sysfs_read_profilex_settings(fp, kobj,
248cb7cf3daSStefan Achatz 			attr, buf, off, count, 0);
249cb7cf3daSStefan Achatz }
250cb7cf3daSStefan Achatz 
251cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_read_profile2_settings(struct file *fp,
252cb7cf3daSStefan Achatz 		struct kobject *kobj, struct bin_attribute *attr, char *buf,
253cb7cf3daSStefan Achatz 		loff_t off, size_t count)
254cb7cf3daSStefan Achatz {
255cb7cf3daSStefan Achatz 	return pyra_sysfs_read_profilex_settings(fp, kobj,
256cb7cf3daSStefan Achatz 			attr, buf, off, count, 1);
257cb7cf3daSStefan Achatz }
258cb7cf3daSStefan Achatz 
259cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_read_profile3_settings(struct file *fp,
260cb7cf3daSStefan Achatz 		struct kobject *kobj, struct bin_attribute *attr, char *buf,
261cb7cf3daSStefan Achatz 		loff_t off, size_t count)
262cb7cf3daSStefan Achatz {
263cb7cf3daSStefan Achatz 	return pyra_sysfs_read_profilex_settings(fp, kobj,
264cb7cf3daSStefan Achatz 			attr, buf, off, count, 2);
265cb7cf3daSStefan Achatz }
266cb7cf3daSStefan Achatz 
267cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_read_profile4_settings(struct file *fp,
268cb7cf3daSStefan Achatz 		struct kobject *kobj, struct bin_attribute *attr, char *buf,
269cb7cf3daSStefan Achatz 		loff_t off, size_t count)
270cb7cf3daSStefan Achatz {
271cb7cf3daSStefan Achatz 	return pyra_sysfs_read_profilex_settings(fp, kobj,
272cb7cf3daSStefan Achatz 			attr, buf, off, count, 3);
273cb7cf3daSStefan Achatz }
274cb7cf3daSStefan Achatz 
275cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_read_profile5_settings(struct file *fp,
276cb7cf3daSStefan Achatz 		struct kobject *kobj, struct bin_attribute *attr, char *buf,
277cb7cf3daSStefan Achatz 		loff_t off, size_t count)
278cb7cf3daSStefan Achatz {
279cb7cf3daSStefan Achatz 	return pyra_sysfs_read_profilex_settings(fp, kobj,
280cb7cf3daSStefan Achatz 			attr, buf, off, count, 4);
281cb7cf3daSStefan Achatz }
282cb7cf3daSStefan Achatz 
283cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_read_profilex_buttons(struct file *fp,
284cb7cf3daSStefan Achatz 		struct kobject *kobj, struct bin_attribute *attr, char *buf,
285cb7cf3daSStefan Achatz 		loff_t off, size_t count, int number)
286cb7cf3daSStefan Achatz {
287cb7cf3daSStefan Achatz 	struct device *dev = container_of(kobj, struct device, kobj);
288cb7cf3daSStefan Achatz 	struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
289cb7cf3daSStefan Achatz 
290cb7cf3daSStefan Achatz 	if (off >= sizeof(struct pyra_profile_buttons))
291cb7cf3daSStefan Achatz 		return 0;
292cb7cf3daSStefan Achatz 
293cb7cf3daSStefan Achatz 	if (off + count > sizeof(struct pyra_profile_buttons))
294cb7cf3daSStefan Achatz 		count = sizeof(struct pyra_profile_buttons) - off;
295cb7cf3daSStefan Achatz 
296cb7cf3daSStefan Achatz 	mutex_lock(&pyra->pyra_lock);
297cb7cf3daSStefan Achatz 	memcpy(buf, ((char const *)&pyra->profile_buttons[number]) + off,
298cb7cf3daSStefan Achatz 			count);
299cb7cf3daSStefan Achatz 	mutex_unlock(&pyra->pyra_lock);
300cb7cf3daSStefan Achatz 
301cb7cf3daSStefan Achatz 	return count;
302cb7cf3daSStefan Achatz }
303cb7cf3daSStefan Achatz 
304cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_read_profile1_buttons(struct file *fp,
305cb7cf3daSStefan Achatz 		struct kobject *kobj, struct bin_attribute *attr, char *buf,
306cb7cf3daSStefan Achatz 		loff_t off, size_t count)
307cb7cf3daSStefan Achatz {
308cb7cf3daSStefan Achatz 	return pyra_sysfs_read_profilex_buttons(fp, kobj,
309cb7cf3daSStefan Achatz 			attr, buf, off, count, 0);
310cb7cf3daSStefan Achatz }
311cb7cf3daSStefan Achatz 
312cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_read_profile2_buttons(struct file *fp,
313cb7cf3daSStefan Achatz 		struct kobject *kobj, struct bin_attribute *attr, char *buf,
314cb7cf3daSStefan Achatz 		loff_t off, size_t count)
315cb7cf3daSStefan Achatz {
316cb7cf3daSStefan Achatz 	return pyra_sysfs_read_profilex_buttons(fp, kobj,
317cb7cf3daSStefan Achatz 			attr, buf, off, count, 1);
318cb7cf3daSStefan Achatz }
319cb7cf3daSStefan Achatz 
320cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_read_profile3_buttons(struct file *fp,
321cb7cf3daSStefan Achatz 		struct kobject *kobj, struct bin_attribute *attr, char *buf,
322cb7cf3daSStefan Achatz 		loff_t off, size_t count)
323cb7cf3daSStefan Achatz {
324cb7cf3daSStefan Achatz 	return pyra_sysfs_read_profilex_buttons(fp, kobj,
325cb7cf3daSStefan Achatz 			attr, buf, off, count, 2);
326cb7cf3daSStefan Achatz }
327cb7cf3daSStefan Achatz 
328cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_read_profile4_buttons(struct file *fp,
329cb7cf3daSStefan Achatz 		struct kobject *kobj, struct bin_attribute *attr, char *buf,
330cb7cf3daSStefan Achatz 		loff_t off, size_t count)
331cb7cf3daSStefan Achatz {
332cb7cf3daSStefan Achatz 	return pyra_sysfs_read_profilex_buttons(fp, kobj,
333cb7cf3daSStefan Achatz 			attr, buf, off, count, 3);
334cb7cf3daSStefan Achatz }
335cb7cf3daSStefan Achatz 
336cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_read_profile5_buttons(struct file *fp,
337cb7cf3daSStefan Achatz 		struct kobject *kobj, struct bin_attribute *attr, char *buf,
338cb7cf3daSStefan Achatz 		loff_t off, size_t count)
339cb7cf3daSStefan Achatz {
340cb7cf3daSStefan Achatz 	return pyra_sysfs_read_profilex_buttons(fp, kobj,
341cb7cf3daSStefan Achatz 			attr, buf, off, count, 4);
342cb7cf3daSStefan Achatz }
343cb7cf3daSStefan Achatz 
344cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_write_profile_settings(struct file *fp,
345cb7cf3daSStefan Achatz 		struct kobject *kobj, struct bin_attribute *attr, char *buf,
346cb7cf3daSStefan Achatz 		loff_t off, size_t count)
347cb7cf3daSStefan Achatz {
348cb7cf3daSStefan Achatz 	struct device *dev = container_of(kobj, struct device, kobj);
349cb7cf3daSStefan Achatz 	struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
350cb7cf3daSStefan Achatz 	struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
351cb7cf3daSStefan Achatz 	int retval = 0;
352cb7cf3daSStefan Achatz 	int difference;
353cb7cf3daSStefan Achatz 	int profile_number;
354cb7cf3daSStefan Achatz 	struct pyra_profile_settings *profile_settings;
355cb7cf3daSStefan Achatz 
356cb7cf3daSStefan Achatz 	if (off != 0 || count != sizeof(struct pyra_profile_settings))
357cb7cf3daSStefan Achatz 		return -EINVAL;
358cb7cf3daSStefan Achatz 
359cb7cf3daSStefan Achatz 	profile_number = ((struct pyra_profile_settings const *)buf)->number;
360cb7cf3daSStefan Achatz 	profile_settings = &pyra->profile_settings[profile_number];
361cb7cf3daSStefan Achatz 
362cb7cf3daSStefan Achatz 	mutex_lock(&pyra->pyra_lock);
363cb7cf3daSStefan Achatz 	difference = memcmp(buf, profile_settings,
364cb7cf3daSStefan Achatz 			sizeof(struct pyra_profile_settings));
365cb7cf3daSStefan Achatz 	if (difference) {
366cb7cf3daSStefan Achatz 		retval = pyra_set_profile_settings(usb_dev,
367cb7cf3daSStefan Achatz 				(struct pyra_profile_settings const *)buf);
368cb7cf3daSStefan Achatz 		if (!retval)
369cb7cf3daSStefan Achatz 			memcpy(profile_settings, buf,
370cb7cf3daSStefan Achatz 					sizeof(struct pyra_profile_settings));
371cb7cf3daSStefan Achatz 	}
372cb7cf3daSStefan Achatz 	mutex_unlock(&pyra->pyra_lock);
373cb7cf3daSStefan Achatz 
374cb7cf3daSStefan Achatz 	if (retval)
375cb7cf3daSStefan Achatz 		return retval;
376cb7cf3daSStefan Achatz 
377cb7cf3daSStefan Achatz 	return sizeof(struct pyra_profile_settings);
378cb7cf3daSStefan Achatz }
379cb7cf3daSStefan Achatz 
380cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_write_profile_buttons(struct file *fp,
381cb7cf3daSStefan Achatz 		struct kobject *kobj, struct bin_attribute *attr, char *buf,
382cb7cf3daSStefan Achatz 		loff_t off, size_t count)
383cb7cf3daSStefan Achatz {
384cb7cf3daSStefan Achatz 	struct device *dev = container_of(kobj, struct device, kobj);
385cb7cf3daSStefan Achatz 	struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
386cb7cf3daSStefan Achatz 	struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
387cb7cf3daSStefan Achatz 	int retval = 0;
388cb7cf3daSStefan Achatz 	int difference;
389cb7cf3daSStefan Achatz 	int profile_number;
390cb7cf3daSStefan Achatz 	struct pyra_profile_buttons *profile_buttons;
391cb7cf3daSStefan Achatz 
392cb7cf3daSStefan Achatz 	if (off != 0 || count != sizeof(struct pyra_profile_buttons))
393cb7cf3daSStefan Achatz 		return -EINVAL;
394cb7cf3daSStefan Achatz 
395cb7cf3daSStefan Achatz 	profile_number = ((struct pyra_profile_buttons const *)buf)->number;
396cb7cf3daSStefan Achatz 	profile_buttons = &pyra->profile_buttons[profile_number];
397cb7cf3daSStefan Achatz 
398cb7cf3daSStefan Achatz 	mutex_lock(&pyra->pyra_lock);
399cb7cf3daSStefan Achatz 	difference = memcmp(buf, profile_buttons,
400cb7cf3daSStefan Achatz 			sizeof(struct pyra_profile_buttons));
401cb7cf3daSStefan Achatz 	if (difference) {
402cb7cf3daSStefan Achatz 		retval = pyra_set_profile_buttons(usb_dev,
403cb7cf3daSStefan Achatz 				(struct pyra_profile_buttons const *)buf);
404cb7cf3daSStefan Achatz 		if (!retval)
405cb7cf3daSStefan Achatz 			memcpy(profile_buttons, buf,
406cb7cf3daSStefan Achatz 					sizeof(struct pyra_profile_buttons));
407cb7cf3daSStefan Achatz 	}
408cb7cf3daSStefan Achatz 	mutex_unlock(&pyra->pyra_lock);
409cb7cf3daSStefan Achatz 
410cb7cf3daSStefan Achatz 	if (retval)
411cb7cf3daSStefan Achatz 		return retval;
412cb7cf3daSStefan Achatz 
413cb7cf3daSStefan Achatz 	return sizeof(struct pyra_profile_buttons);
414cb7cf3daSStefan Achatz }
415cb7cf3daSStefan Achatz 
416cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_read_settings(struct file *fp,
417cb7cf3daSStefan Achatz 		struct kobject *kobj, struct bin_attribute *attr, char *buf,
418cb7cf3daSStefan Achatz 		loff_t off, size_t count)
419cb7cf3daSStefan Achatz {
420cb7cf3daSStefan Achatz 	struct device *dev = container_of(kobj, struct device, kobj);
421cb7cf3daSStefan Achatz 	struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
422cb7cf3daSStefan Achatz 
423cb7cf3daSStefan Achatz 	if (off >= sizeof(struct pyra_settings))
424cb7cf3daSStefan Achatz 		return 0;
425cb7cf3daSStefan Achatz 
426cb7cf3daSStefan Achatz 	if (off + count > sizeof(struct pyra_settings))
427cb7cf3daSStefan Achatz 		count = sizeof(struct pyra_settings) - off;
428cb7cf3daSStefan Achatz 
429cb7cf3daSStefan Achatz 	mutex_lock(&pyra->pyra_lock);
430cb7cf3daSStefan Achatz 	memcpy(buf, ((char const *)&pyra->settings) + off, count);
431cb7cf3daSStefan Achatz 	mutex_unlock(&pyra->pyra_lock);
432cb7cf3daSStefan Achatz 
433cb7cf3daSStefan Achatz 	return count;
434cb7cf3daSStefan Achatz }
435cb7cf3daSStefan Achatz 
436cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_write_settings(struct file *fp,
437cb7cf3daSStefan Achatz 		struct kobject *kobj, struct bin_attribute *attr, char *buf,
438cb7cf3daSStefan Achatz 		loff_t off, size_t count)
439cb7cf3daSStefan Achatz {
440cb7cf3daSStefan Achatz 	struct device *dev = container_of(kobj, struct device, kobj);
441cb7cf3daSStefan Achatz 	struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
442cb7cf3daSStefan Achatz 	struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
443cb7cf3daSStefan Achatz 	int retval = 0;
444cb7cf3daSStefan Achatz 	int difference;
445cb7cf3daSStefan Achatz 
446cb7cf3daSStefan Achatz 	if (off != 0 || count != sizeof(struct pyra_settings))
447cb7cf3daSStefan Achatz 		return -EINVAL;
448cb7cf3daSStefan Achatz 
449cb7cf3daSStefan Achatz 	mutex_lock(&pyra->pyra_lock);
450cb7cf3daSStefan Achatz 	difference = memcmp(buf, &pyra->settings, sizeof(struct pyra_settings));
451cb7cf3daSStefan Achatz 	if (difference) {
452cb7cf3daSStefan Achatz 		retval = pyra_set_settings(usb_dev,
453cb7cf3daSStefan Achatz 				(struct pyra_settings const *)buf);
454cb7cf3daSStefan Achatz 		if (!retval)
455cb7cf3daSStefan Achatz 			memcpy(&pyra->settings, buf,
456cb7cf3daSStefan Achatz 					sizeof(struct pyra_settings));
457cb7cf3daSStefan Achatz 	}
458cb7cf3daSStefan Achatz 	mutex_unlock(&pyra->pyra_lock);
459cb7cf3daSStefan Achatz 
460cb7cf3daSStefan Achatz 	if (retval)
461cb7cf3daSStefan Achatz 		return retval;
462cb7cf3daSStefan Achatz 
463cb7cf3daSStefan Achatz 	profile_activated(pyra, pyra->settings.startup_profile);
464cb7cf3daSStefan Achatz 
465cb7cf3daSStefan Achatz 	return sizeof(struct pyra_settings);
466cb7cf3daSStefan Achatz }
467cb7cf3daSStefan Achatz 
468cb7cf3daSStefan Achatz 
469cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_show_actual_cpi(struct device *dev,
470cb7cf3daSStefan Achatz 		struct device_attribute *attr, char *buf)
471cb7cf3daSStefan Achatz {
472cb7cf3daSStefan Achatz 	struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
473cb7cf3daSStefan Achatz 	return snprintf(buf, PAGE_SIZE, "%d\n", pyra->actual_cpi);
474cb7cf3daSStefan Achatz }
475cb7cf3daSStefan Achatz 
476cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_show_actual_profile(struct device *dev,
477cb7cf3daSStefan Achatz 		struct device_attribute *attr, char *buf)
478cb7cf3daSStefan Achatz {
479cb7cf3daSStefan Achatz 	struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
480cb7cf3daSStefan Achatz 	return snprintf(buf, PAGE_SIZE, "%d\n", pyra->actual_profile);
481cb7cf3daSStefan Achatz }
482cb7cf3daSStefan Achatz 
483cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_show_firmware_version(struct device *dev,
484cb7cf3daSStefan Achatz 		struct device_attribute *attr, char *buf)
485cb7cf3daSStefan Achatz {
486cb7cf3daSStefan Achatz 	struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
487cb7cf3daSStefan Achatz 	return snprintf(buf, PAGE_SIZE, "%d\n", pyra->firmware_version);
488cb7cf3daSStefan Achatz }
489cb7cf3daSStefan Achatz 
490cb7cf3daSStefan Achatz static ssize_t pyra_sysfs_show_startup_profile(struct device *dev,
491cb7cf3daSStefan Achatz 		struct device_attribute *attr, char *buf)
492cb7cf3daSStefan Achatz {
493cb7cf3daSStefan Achatz 	struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
494cb7cf3daSStefan Achatz 	return snprintf(buf, PAGE_SIZE, "%d\n", pyra->settings.startup_profile);
495cb7cf3daSStefan Achatz }
496cb7cf3daSStefan Achatz 
497cb7cf3daSStefan Achatz static DEVICE_ATTR(actual_cpi, 0440, pyra_sysfs_show_actual_cpi, NULL);
498cb7cf3daSStefan Achatz 
499cb7cf3daSStefan Achatz static DEVICE_ATTR(actual_profile, 0440, pyra_sysfs_show_actual_profile, NULL);
500cb7cf3daSStefan Achatz 
501cb7cf3daSStefan Achatz static DEVICE_ATTR(firmware_version, 0440,
502cb7cf3daSStefan Achatz 		pyra_sysfs_show_firmware_version, NULL);
503cb7cf3daSStefan Achatz 
504cb7cf3daSStefan Achatz static DEVICE_ATTR(startup_profile, 0440,
505cb7cf3daSStefan Achatz 		pyra_sysfs_show_startup_profile, NULL);
506cb7cf3daSStefan Achatz 
507cb7cf3daSStefan Achatz static struct attribute *pyra_attributes[] = {
508cb7cf3daSStefan Achatz 		&dev_attr_actual_cpi.attr,
509cb7cf3daSStefan Achatz 		&dev_attr_actual_profile.attr,
510cb7cf3daSStefan Achatz 		&dev_attr_firmware_version.attr,
511cb7cf3daSStefan Achatz 		&dev_attr_startup_profile.attr,
512cb7cf3daSStefan Achatz 		NULL
513cb7cf3daSStefan Achatz };
514cb7cf3daSStefan Achatz 
515cb7cf3daSStefan Achatz static struct attribute_group pyra_attribute_group = {
516cb7cf3daSStefan Achatz 		.attrs = pyra_attributes
517cb7cf3daSStefan Achatz };
518cb7cf3daSStefan Achatz 
519cb7cf3daSStefan Achatz static struct bin_attribute pyra_profile_settings_attr = {
520cb7cf3daSStefan Achatz 		.attr = { .name = "profile_settings", .mode = 0220 },
521cb7cf3daSStefan Achatz 		.size = sizeof(struct pyra_profile_settings),
522cb7cf3daSStefan Achatz 		.write = pyra_sysfs_write_profile_settings
523cb7cf3daSStefan Achatz };
524cb7cf3daSStefan Achatz 
525cb7cf3daSStefan Achatz static struct bin_attribute pyra_profile1_settings_attr = {
526cb7cf3daSStefan Achatz 		.attr = { .name = "profile1_settings", .mode = 0440 },
527cb7cf3daSStefan Achatz 		.size = sizeof(struct pyra_profile_settings),
528cb7cf3daSStefan Achatz 		.read = pyra_sysfs_read_profile1_settings
529cb7cf3daSStefan Achatz };
530cb7cf3daSStefan Achatz 
531cb7cf3daSStefan Achatz static struct bin_attribute pyra_profile2_settings_attr = {
532cb7cf3daSStefan Achatz 		.attr = { .name = "profile2_settings", .mode = 0440 },
533cb7cf3daSStefan Achatz 		.size = sizeof(struct pyra_profile_settings),
534cb7cf3daSStefan Achatz 		.read = pyra_sysfs_read_profile2_settings
535cb7cf3daSStefan Achatz };
536cb7cf3daSStefan Achatz 
537cb7cf3daSStefan Achatz static struct bin_attribute pyra_profile3_settings_attr = {
538cb7cf3daSStefan Achatz 		.attr = { .name = "profile3_settings", .mode = 0440 },
539cb7cf3daSStefan Achatz 		.size = sizeof(struct pyra_profile_settings),
540cb7cf3daSStefan Achatz 		.read = pyra_sysfs_read_profile3_settings
541cb7cf3daSStefan Achatz };
542cb7cf3daSStefan Achatz 
543cb7cf3daSStefan Achatz static struct bin_attribute pyra_profile4_settings_attr = {
544cb7cf3daSStefan Achatz 		.attr = { .name = "profile4_settings", .mode = 0440 },
545cb7cf3daSStefan Achatz 		.size = sizeof(struct pyra_profile_settings),
546cb7cf3daSStefan Achatz 		.read = pyra_sysfs_read_profile4_settings
547cb7cf3daSStefan Achatz };
548cb7cf3daSStefan Achatz 
549cb7cf3daSStefan Achatz static struct bin_attribute pyra_profile5_settings_attr = {
550cb7cf3daSStefan Achatz 		.attr = { .name = "profile5_settings", .mode = 0440 },
551cb7cf3daSStefan Achatz 		.size = sizeof(struct pyra_profile_settings),
552cb7cf3daSStefan Achatz 		.read = pyra_sysfs_read_profile5_settings
553cb7cf3daSStefan Achatz };
554cb7cf3daSStefan Achatz 
555cb7cf3daSStefan Achatz static struct bin_attribute pyra_profile_buttons_attr = {
556cb7cf3daSStefan Achatz 		.attr = { .name = "profile_buttons", .mode = 0220 },
557cb7cf3daSStefan Achatz 		.size = sizeof(struct pyra_profile_buttons),
558cb7cf3daSStefan Achatz 		.write = pyra_sysfs_write_profile_buttons
559cb7cf3daSStefan Achatz };
560cb7cf3daSStefan Achatz 
561cb7cf3daSStefan Achatz static struct bin_attribute pyra_profile1_buttons_attr = {
562cb7cf3daSStefan Achatz 		.attr = { .name = "profile1_buttons", .mode = 0440 },
563cb7cf3daSStefan Achatz 		.size = sizeof(struct pyra_profile_buttons),
564cb7cf3daSStefan Achatz 		.read = pyra_sysfs_read_profile1_buttons
565cb7cf3daSStefan Achatz };
566cb7cf3daSStefan Achatz 
567cb7cf3daSStefan Achatz static struct bin_attribute pyra_profile2_buttons_attr = {
568cb7cf3daSStefan Achatz 		.attr = { .name = "profile2_buttons", .mode = 0440 },
569cb7cf3daSStefan Achatz 		.size = sizeof(struct pyra_profile_buttons),
570cb7cf3daSStefan Achatz 		.read = pyra_sysfs_read_profile2_buttons
571cb7cf3daSStefan Achatz };
572cb7cf3daSStefan Achatz 
573cb7cf3daSStefan Achatz static struct bin_attribute pyra_profile3_buttons_attr = {
574cb7cf3daSStefan Achatz 		.attr = { .name = "profile3_buttons", .mode = 0440 },
575cb7cf3daSStefan Achatz 		.size = sizeof(struct pyra_profile_buttons),
576cb7cf3daSStefan Achatz 		.read = pyra_sysfs_read_profile3_buttons
577cb7cf3daSStefan Achatz };
578cb7cf3daSStefan Achatz 
579cb7cf3daSStefan Achatz static struct bin_attribute pyra_profile4_buttons_attr = {
580cb7cf3daSStefan Achatz 		.attr = { .name = "profile4_buttons", .mode = 0440 },
581cb7cf3daSStefan Achatz 		.size = sizeof(struct pyra_profile_buttons),
582cb7cf3daSStefan Achatz 		.read = pyra_sysfs_read_profile4_buttons
583cb7cf3daSStefan Achatz };
584cb7cf3daSStefan Achatz 
585cb7cf3daSStefan Achatz static struct bin_attribute pyra_profile5_buttons_attr = {
586cb7cf3daSStefan Achatz 		.attr = { .name = "profile5_buttons", .mode = 0440 },
587cb7cf3daSStefan Achatz 		.size = sizeof(struct pyra_profile_buttons),
588cb7cf3daSStefan Achatz 		.read = pyra_sysfs_read_profile5_buttons
589cb7cf3daSStefan Achatz };
590cb7cf3daSStefan Achatz 
591cb7cf3daSStefan Achatz static struct bin_attribute pyra_settings_attr = {
592cb7cf3daSStefan Achatz 		.attr = { .name = "settings", .mode = 0660 },
593cb7cf3daSStefan Achatz 		.size = sizeof(struct pyra_settings),
594cb7cf3daSStefan Achatz 		.read = pyra_sysfs_read_settings,
595cb7cf3daSStefan Achatz 		.write = pyra_sysfs_write_settings
596cb7cf3daSStefan Achatz };
597cb7cf3daSStefan Achatz 
598cb7cf3daSStefan Achatz static int pyra_create_sysfs_attributes(struct usb_interface *intf)
599cb7cf3daSStefan Achatz {
600cb7cf3daSStefan Achatz 	int retval;
601cb7cf3daSStefan Achatz 
602cb7cf3daSStefan Achatz 	retval = sysfs_create_group(&intf->dev.kobj, &pyra_attribute_group);
603cb7cf3daSStefan Achatz 	if (retval)
604cb7cf3daSStefan Achatz 		goto exit_1;
605cb7cf3daSStefan Achatz 
606cb7cf3daSStefan Achatz 	retval = sysfs_create_bin_file(&intf->dev.kobj,
607cb7cf3daSStefan Achatz 			&pyra_profile_settings_attr);
608cb7cf3daSStefan Achatz 	if (retval)
609cb7cf3daSStefan Achatz 		goto exit_2;
610cb7cf3daSStefan Achatz 
611cb7cf3daSStefan Achatz 	retval = sysfs_create_bin_file(&intf->dev.kobj,
612cb7cf3daSStefan Achatz 			&pyra_profile1_settings_attr);
613cb7cf3daSStefan Achatz 	if (retval)
614cb7cf3daSStefan Achatz 		goto exit_3;
615cb7cf3daSStefan Achatz 
616cb7cf3daSStefan Achatz 	retval = sysfs_create_bin_file(&intf->dev.kobj,
617cb7cf3daSStefan Achatz 			&pyra_profile2_settings_attr);
618cb7cf3daSStefan Achatz 	if (retval)
619cb7cf3daSStefan Achatz 		goto exit_4;
620cb7cf3daSStefan Achatz 
621cb7cf3daSStefan Achatz 	retval = sysfs_create_bin_file(&intf->dev.kobj,
622cb7cf3daSStefan Achatz 			&pyra_profile3_settings_attr);
623cb7cf3daSStefan Achatz 	if (retval)
624cb7cf3daSStefan Achatz 		goto exit_5;
625cb7cf3daSStefan Achatz 
626cb7cf3daSStefan Achatz 	retval = sysfs_create_bin_file(&intf->dev.kobj,
627cb7cf3daSStefan Achatz 			&pyra_profile4_settings_attr);
628cb7cf3daSStefan Achatz 	if (retval)
629cb7cf3daSStefan Achatz 		goto exit_6;
630cb7cf3daSStefan Achatz 
631cb7cf3daSStefan Achatz 	retval = sysfs_create_bin_file(&intf->dev.kobj,
632cb7cf3daSStefan Achatz 			&pyra_profile5_settings_attr);
633cb7cf3daSStefan Achatz 	if (retval)
634cb7cf3daSStefan Achatz 		goto exit_7;
635cb7cf3daSStefan Achatz 
636cb7cf3daSStefan Achatz 	retval = sysfs_create_bin_file(&intf->dev.kobj,
637cb7cf3daSStefan Achatz 			&pyra_profile_buttons_attr);
638cb7cf3daSStefan Achatz 	if (retval)
639cb7cf3daSStefan Achatz 		goto exit_8;
640cb7cf3daSStefan Achatz 
641cb7cf3daSStefan Achatz 	retval = sysfs_create_bin_file(&intf->dev.kobj,
642cb7cf3daSStefan Achatz 			&pyra_profile1_buttons_attr);
643cb7cf3daSStefan Achatz 	if (retval)
644cb7cf3daSStefan Achatz 		goto exit_9;
645cb7cf3daSStefan Achatz 
646cb7cf3daSStefan Achatz 	retval = sysfs_create_bin_file(&intf->dev.kobj,
647cb7cf3daSStefan Achatz 			&pyra_profile2_buttons_attr);
648cb7cf3daSStefan Achatz 	if (retval)
649cb7cf3daSStefan Achatz 		goto exit_10;
650cb7cf3daSStefan Achatz 
651cb7cf3daSStefan Achatz 	retval = sysfs_create_bin_file(&intf->dev.kobj,
652cb7cf3daSStefan Achatz 			&pyra_profile3_buttons_attr);
653cb7cf3daSStefan Achatz 	if (retval)
654cb7cf3daSStefan Achatz 		goto exit_11;
655cb7cf3daSStefan Achatz 
656cb7cf3daSStefan Achatz 	retval = sysfs_create_bin_file(&intf->dev.kobj,
657cb7cf3daSStefan Achatz 			&pyra_profile4_buttons_attr);
658cb7cf3daSStefan Achatz 	if (retval)
659cb7cf3daSStefan Achatz 		goto exit_12;
660cb7cf3daSStefan Achatz 
661cb7cf3daSStefan Achatz 	retval = sysfs_create_bin_file(&intf->dev.kobj,
662cb7cf3daSStefan Achatz 			&pyra_profile5_buttons_attr);
663cb7cf3daSStefan Achatz 	if (retval)
664cb7cf3daSStefan Achatz 		goto exit_13;
665cb7cf3daSStefan Achatz 
666cb7cf3daSStefan Achatz 	retval = sysfs_create_bin_file(&intf->dev.kobj,
667cb7cf3daSStefan Achatz 			&pyra_settings_attr);
668cb7cf3daSStefan Achatz 	if (retval)
669cb7cf3daSStefan Achatz 		goto exit_14;
670cb7cf3daSStefan Achatz 
671cb7cf3daSStefan Achatz 	return 0;
672cb7cf3daSStefan Achatz 
673cb7cf3daSStefan Achatz exit_14:
674cb7cf3daSStefan Achatz 	sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile5_buttons_attr);
675cb7cf3daSStefan Achatz exit_13:
676cb7cf3daSStefan Achatz 	sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile4_buttons_attr);
677cb7cf3daSStefan Achatz exit_12:
678cb7cf3daSStefan Achatz 	sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile3_buttons_attr);
679cb7cf3daSStefan Achatz exit_11:
680cb7cf3daSStefan Achatz 	sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile2_buttons_attr);
681cb7cf3daSStefan Achatz exit_10:
682cb7cf3daSStefan Achatz 	sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile1_buttons_attr);
683cb7cf3daSStefan Achatz exit_9:
684cb7cf3daSStefan Achatz 	sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile_buttons_attr);
685cb7cf3daSStefan Achatz exit_8:
686cb7cf3daSStefan Achatz 	sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile5_settings_attr);
687cb7cf3daSStefan Achatz exit_7:
688cb7cf3daSStefan Achatz 	sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile4_settings_attr);
689cb7cf3daSStefan Achatz exit_6:
690cb7cf3daSStefan Achatz 	sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile3_settings_attr);
691cb7cf3daSStefan Achatz exit_5:
692cb7cf3daSStefan Achatz 	sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile2_settings_attr);
693cb7cf3daSStefan Achatz exit_4:
694cb7cf3daSStefan Achatz 	sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile1_settings_attr);
695cb7cf3daSStefan Achatz exit_3:
696cb7cf3daSStefan Achatz 	sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile_settings_attr);
697cb7cf3daSStefan Achatz exit_2:
698cb7cf3daSStefan Achatz 	sysfs_remove_group(&intf->dev.kobj, &pyra_attribute_group);
699cb7cf3daSStefan Achatz exit_1:
700cb7cf3daSStefan Achatz 	return retval;
701cb7cf3daSStefan Achatz }
702cb7cf3daSStefan Achatz 
703cb7cf3daSStefan Achatz static void pyra_remove_sysfs_attributes(struct usb_interface *intf)
704cb7cf3daSStefan Achatz {
705cb7cf3daSStefan Achatz 	sysfs_remove_bin_file(&intf->dev.kobj, &pyra_settings_attr);
706cb7cf3daSStefan Achatz 	sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile5_buttons_attr);
707cb7cf3daSStefan Achatz 	sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile4_buttons_attr);
708cb7cf3daSStefan Achatz 	sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile3_buttons_attr);
709cb7cf3daSStefan Achatz 	sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile2_buttons_attr);
710cb7cf3daSStefan Achatz 	sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile1_buttons_attr);
711cb7cf3daSStefan Achatz 	sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile_buttons_attr);
712cb7cf3daSStefan Achatz 	sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile5_settings_attr);
713cb7cf3daSStefan Achatz 	sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile4_settings_attr);
714cb7cf3daSStefan Achatz 	sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile3_settings_attr);
715cb7cf3daSStefan Achatz 	sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile2_settings_attr);
716cb7cf3daSStefan Achatz 	sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile1_settings_attr);
717cb7cf3daSStefan Achatz 	sysfs_remove_bin_file(&intf->dev.kobj, &pyra_profile_settings_attr);
718cb7cf3daSStefan Achatz 	sysfs_remove_group(&intf->dev.kobj, &pyra_attribute_group);
719cb7cf3daSStefan Achatz }
720cb7cf3daSStefan Achatz 
721cb7cf3daSStefan Achatz static int pyra_init_pyra_device_struct(struct usb_device *usb_dev,
722cb7cf3daSStefan Achatz 		struct pyra_device *pyra)
723cb7cf3daSStefan Achatz {
724cb7cf3daSStefan Achatz 	struct pyra_info *info;
725cb7cf3daSStefan Achatz 	int retval, i;
726cb7cf3daSStefan Achatz 
727cb7cf3daSStefan Achatz 	mutex_init(&pyra->pyra_lock);
728cb7cf3daSStefan Achatz 
729cb7cf3daSStefan Achatz 	info = kmalloc(sizeof(struct pyra_info), GFP_KERNEL);
730cb7cf3daSStefan Achatz 	if (!info)
731cb7cf3daSStefan Achatz 		return -ENOMEM;
732cb7cf3daSStefan Achatz 	retval = pyra_get_info(usb_dev, info);
733cb7cf3daSStefan Achatz 	if (retval) {
734cb7cf3daSStefan Achatz 		kfree(info);
735cb7cf3daSStefan Achatz 		return retval;
736cb7cf3daSStefan Achatz 	}
737cb7cf3daSStefan Achatz 	pyra->firmware_version = info->firmware_version;
738cb7cf3daSStefan Achatz 	kfree(info);
739cb7cf3daSStefan Achatz 
740cb7cf3daSStefan Achatz 	retval = pyra_get_settings(usb_dev, &pyra->settings);
741cb7cf3daSStefan Achatz 	if (retval)
742cb7cf3daSStefan Achatz 		return retval;
743cb7cf3daSStefan Achatz 
744cb7cf3daSStefan Achatz 	for (i = 0; i < 5; ++i) {
745cb7cf3daSStefan Achatz 		retval = pyra_get_profile_settings(usb_dev,
746cb7cf3daSStefan Achatz 				&pyra->profile_settings[i], i);
747cb7cf3daSStefan Achatz 		if (retval)
748cb7cf3daSStefan Achatz 			return retval;
749cb7cf3daSStefan Achatz 
750cb7cf3daSStefan Achatz 		retval = pyra_get_profile_buttons(usb_dev,
751cb7cf3daSStefan Achatz 				&pyra->profile_buttons[i], i);
752cb7cf3daSStefan Achatz 		if (retval)
753cb7cf3daSStefan Achatz 			return retval;
754cb7cf3daSStefan Achatz 	}
755cb7cf3daSStefan Achatz 
756cb7cf3daSStefan Achatz 	profile_activated(pyra, pyra->settings.startup_profile);
757cb7cf3daSStefan Achatz 
758cb7cf3daSStefan Achatz 	return 0;
759cb7cf3daSStefan Achatz }
760cb7cf3daSStefan Achatz 
761cb7cf3daSStefan Achatz static int pyra_init_specials(struct hid_device *hdev)
762cb7cf3daSStefan Achatz {
763cb7cf3daSStefan Achatz 	struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
764cb7cf3daSStefan Achatz 	struct usb_device *usb_dev = interface_to_usbdev(intf);
765cb7cf3daSStefan Achatz 	struct pyra_device *pyra;
766cb7cf3daSStefan Achatz 	int retval;
767cb7cf3daSStefan Achatz 
768cb7cf3daSStefan Achatz 	if (intf->cur_altsetting->desc.bInterfaceProtocol
769cb7cf3daSStefan Achatz 			== USB_INTERFACE_PROTOCOL_MOUSE) {
770cb7cf3daSStefan Achatz 
771cb7cf3daSStefan Achatz 		pyra = kzalloc(sizeof(*pyra), GFP_KERNEL);
772cb7cf3daSStefan Achatz 		if (!pyra) {
773cb7cf3daSStefan Achatz 			dev_err(&hdev->dev, "can't alloc device descriptor\n");
774cb7cf3daSStefan Achatz 			return -ENOMEM;
775cb7cf3daSStefan Achatz 		}
776cb7cf3daSStefan Achatz 		hid_set_drvdata(hdev, pyra);
777cb7cf3daSStefan Achatz 
778cb7cf3daSStefan Achatz 		retval = pyra_init_pyra_device_struct(usb_dev, pyra);
779cb7cf3daSStefan Achatz 		if (retval) {
780cb7cf3daSStefan Achatz 			dev_err(&hdev->dev,
781cb7cf3daSStefan Achatz 					"couldn't init struct pyra_device\n");
782cb7cf3daSStefan Achatz 			goto exit_free;
783cb7cf3daSStefan Achatz 		}
784cb7cf3daSStefan Achatz 
785cb7cf3daSStefan Achatz 		retval = roccat_connect(hdev);
786cb7cf3daSStefan Achatz 		if (retval < 0) {
787cb7cf3daSStefan Achatz 			dev_err(&hdev->dev, "couldn't init char dev\n");
788cb7cf3daSStefan Achatz 		} else {
789cb7cf3daSStefan Achatz 			pyra->chrdev_minor = retval;
790cb7cf3daSStefan Achatz 			pyra->roccat_claimed = 1;
791cb7cf3daSStefan Achatz 		}
792cb7cf3daSStefan Achatz 
793cb7cf3daSStefan Achatz 		retval = pyra_create_sysfs_attributes(intf);
794cb7cf3daSStefan Achatz 		if (retval) {
795cb7cf3daSStefan Achatz 			dev_err(&hdev->dev, "cannot create sysfs files\n");
796cb7cf3daSStefan Achatz 			goto exit_free;
797cb7cf3daSStefan Achatz 		}
798cb7cf3daSStefan Achatz 	} else {
799cb7cf3daSStefan Achatz 		hid_set_drvdata(hdev, NULL);
800cb7cf3daSStefan Achatz 	}
801cb7cf3daSStefan Achatz 
802cb7cf3daSStefan Achatz 	return 0;
803cb7cf3daSStefan Achatz exit_free:
804cb7cf3daSStefan Achatz 	kfree(pyra);
805cb7cf3daSStefan Achatz 	return retval;
806cb7cf3daSStefan Achatz }
807cb7cf3daSStefan Achatz 
808cb7cf3daSStefan Achatz static void pyra_remove_specials(struct hid_device *hdev)
809cb7cf3daSStefan Achatz {
810cb7cf3daSStefan Achatz 	struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
811cb7cf3daSStefan Achatz 	struct pyra_device *pyra;
812cb7cf3daSStefan Achatz 
813cb7cf3daSStefan Achatz 	if (intf->cur_altsetting->desc.bInterfaceProtocol
814cb7cf3daSStefan Achatz 			== USB_INTERFACE_PROTOCOL_MOUSE) {
815cb7cf3daSStefan Achatz 		pyra_remove_sysfs_attributes(intf);
816cb7cf3daSStefan Achatz 		pyra = hid_get_drvdata(hdev);
817cb7cf3daSStefan Achatz 		if (pyra->roccat_claimed)
818cb7cf3daSStefan Achatz 			roccat_disconnect(pyra->chrdev_minor);
819cb7cf3daSStefan Achatz 		kfree(hid_get_drvdata(hdev));
820cb7cf3daSStefan Achatz 	}
821cb7cf3daSStefan Achatz }
822cb7cf3daSStefan Achatz 
823cb7cf3daSStefan Achatz static int pyra_probe(struct hid_device *hdev, const struct hid_device_id *id)
824cb7cf3daSStefan Achatz {
825cb7cf3daSStefan Achatz 	int retval;
826cb7cf3daSStefan Achatz 
827cb7cf3daSStefan Achatz 	retval = hid_parse(hdev);
828cb7cf3daSStefan Achatz 	if (retval) {
829cb7cf3daSStefan Achatz 		dev_err(&hdev->dev, "parse failed\n");
830cb7cf3daSStefan Achatz 		goto exit;
831cb7cf3daSStefan Achatz 	}
832cb7cf3daSStefan Achatz 
833cb7cf3daSStefan Achatz 	retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
834cb7cf3daSStefan Achatz 	if (retval) {
835cb7cf3daSStefan Achatz 		dev_err(&hdev->dev, "hw start failed\n");
836cb7cf3daSStefan Achatz 		goto exit;
837cb7cf3daSStefan Achatz 	}
838cb7cf3daSStefan Achatz 
839cb7cf3daSStefan Achatz 	retval = pyra_init_specials(hdev);
840cb7cf3daSStefan Achatz 	if (retval) {
841cb7cf3daSStefan Achatz 		dev_err(&hdev->dev, "couldn't install mouse\n");
842cb7cf3daSStefan Achatz 		goto exit_stop;
843cb7cf3daSStefan Achatz 	}
844cb7cf3daSStefan Achatz 	return 0;
845cb7cf3daSStefan Achatz 
846cb7cf3daSStefan Achatz exit_stop:
847cb7cf3daSStefan Achatz 	hid_hw_stop(hdev);
848cb7cf3daSStefan Achatz exit:
849cb7cf3daSStefan Achatz 	return retval;
850cb7cf3daSStefan Achatz }
851cb7cf3daSStefan Achatz 
852cb7cf3daSStefan Achatz static void pyra_remove(struct hid_device *hdev)
853cb7cf3daSStefan Achatz {
854cb7cf3daSStefan Achatz 	pyra_remove_specials(hdev);
855cb7cf3daSStefan Achatz 	hid_hw_stop(hdev);
856cb7cf3daSStefan Achatz }
857cb7cf3daSStefan Achatz 
858cb7cf3daSStefan Achatz static void pyra_keep_values_up_to_date(struct pyra_device *pyra,
859cb7cf3daSStefan Achatz 		u8 const *data)
860cb7cf3daSStefan Achatz {
861cb7cf3daSStefan Achatz 	struct pyra_mouse_event_button const *button_event;
862cb7cf3daSStefan Achatz 
863cb7cf3daSStefan Achatz 	switch (data[0]) {
864cb7cf3daSStefan Achatz 	case PYRA_MOUSE_REPORT_NUMBER_BUTTON:
865cb7cf3daSStefan Achatz 		button_event = (struct pyra_mouse_event_button const *)data;
866cb7cf3daSStefan Achatz 		switch (button_event->type) {
867cb7cf3daSStefan Achatz 		case PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2:
868cb7cf3daSStefan Achatz 			profile_activated(pyra, button_event->data1 - 1);
869cb7cf3daSStefan Achatz 			break;
870cb7cf3daSStefan Achatz 		case PYRA_MOUSE_EVENT_BUTTON_TYPE_CPI:
871cb7cf3daSStefan Achatz 			pyra->actual_cpi = button_event->data1;
872cb7cf3daSStefan Achatz 			break;
873cb7cf3daSStefan Achatz 		}
874cb7cf3daSStefan Achatz 		break;
875cb7cf3daSStefan Achatz 	}
876cb7cf3daSStefan Achatz }
877cb7cf3daSStefan Achatz 
878cb7cf3daSStefan Achatz static void pyra_report_to_chrdev(struct pyra_device const *pyra,
879cb7cf3daSStefan Achatz 		u8 const *data)
880cb7cf3daSStefan Achatz {
881cb7cf3daSStefan Achatz 	struct pyra_roccat_report roccat_report;
882cb7cf3daSStefan Achatz 	struct pyra_mouse_event_button const *button_event;
883cb7cf3daSStefan Achatz 
884cb7cf3daSStefan Achatz 	if (data[0] != PYRA_MOUSE_REPORT_NUMBER_BUTTON)
885cb7cf3daSStefan Achatz 		return;
886cb7cf3daSStefan Achatz 
887cb7cf3daSStefan Achatz 	button_event = (struct pyra_mouse_event_button const *)data;
888cb7cf3daSStefan Achatz 
889cb7cf3daSStefan Achatz 	switch (button_event->type) {
890cb7cf3daSStefan Achatz 	case PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2:
891cb7cf3daSStefan Achatz 	case PYRA_MOUSE_EVENT_BUTTON_TYPE_CPI:
892cb7cf3daSStefan Achatz 		roccat_report.type = button_event->type;
893cb7cf3daSStefan Achatz 		roccat_report.value = button_event->data1;
894cb7cf3daSStefan Achatz 		roccat_report.key = 0;
895cb7cf3daSStefan Achatz 		roccat_report_event(pyra->chrdev_minor,
896cb7cf3daSStefan Achatz 				(uint8_t const *)&roccat_report,
897cb7cf3daSStefan Achatz 				sizeof(struct pyra_roccat_report));
898cb7cf3daSStefan Achatz 		break;
899cb7cf3daSStefan Achatz 	case PYRA_MOUSE_EVENT_BUTTON_TYPE_MACRO:
900cb7cf3daSStefan Achatz 	case PYRA_MOUSE_EVENT_BUTTON_TYPE_SHORTCUT:
901cb7cf3daSStefan Achatz 	case PYRA_MOUSE_EVENT_BUTTON_TYPE_QUICKLAUNCH:
902cb7cf3daSStefan Achatz 		if (button_event->data2 == PYRA_MOUSE_EVENT_BUTTON_PRESS) {
903cb7cf3daSStefan Achatz 			roccat_report.type = button_event->type;
904cb7cf3daSStefan Achatz 			roccat_report.key = button_event->data1;
905cb7cf3daSStefan Achatz 			roccat_report.value = pyra->actual_profile;
906cb7cf3daSStefan Achatz 			roccat_report_event(pyra->chrdev_minor,
907cb7cf3daSStefan Achatz 					(uint8_t const *)&roccat_report,
908cb7cf3daSStefan Achatz 					sizeof(struct pyra_roccat_report));
909cb7cf3daSStefan Achatz 		}
910cb7cf3daSStefan Achatz 		break;
911cb7cf3daSStefan Achatz 	}
912cb7cf3daSStefan Achatz }
913cb7cf3daSStefan Achatz 
914cb7cf3daSStefan Achatz static int pyra_raw_event(struct hid_device *hdev, struct hid_report *report,
915cb7cf3daSStefan Achatz 		u8 *data, int size)
916cb7cf3daSStefan Achatz {
917cb7cf3daSStefan Achatz 	struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
918cb7cf3daSStefan Achatz 	struct pyra_device *pyra = hid_get_drvdata(hdev);
919cb7cf3daSStefan Achatz 
920cb7cf3daSStefan Achatz 	if (intf->cur_altsetting->desc.bInterfaceProtocol
921cb7cf3daSStefan Achatz 			!= USB_INTERFACE_PROTOCOL_MOUSE)
922cb7cf3daSStefan Achatz 		return 0;
923cb7cf3daSStefan Achatz 
924cb7cf3daSStefan Achatz 	pyra_keep_values_up_to_date(pyra, data);
925cb7cf3daSStefan Achatz 
926cb7cf3daSStefan Achatz 	if (pyra->roccat_claimed)
927cb7cf3daSStefan Achatz 		pyra_report_to_chrdev(pyra, data);
928cb7cf3daSStefan Achatz 
929cb7cf3daSStefan Achatz 	return 0;
930cb7cf3daSStefan Achatz }
931cb7cf3daSStefan Achatz 
932cb7cf3daSStefan Achatz static const struct hid_device_id pyra_devices[] = {
933cb7cf3daSStefan Achatz 	{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT,
934cb7cf3daSStefan Achatz 			USB_DEVICE_ID_ROCCAT_PYRA_WIRED) },
935cb7cf3daSStefan Achatz 	/* TODO add USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS after testing */
936cb7cf3daSStefan Achatz 	{ }
937cb7cf3daSStefan Achatz };
938cb7cf3daSStefan Achatz 
939cb7cf3daSStefan Achatz MODULE_DEVICE_TABLE(hid, pyra_devices);
940cb7cf3daSStefan Achatz 
941cb7cf3daSStefan Achatz static struct hid_driver pyra_driver = {
942cb7cf3daSStefan Achatz 		.name = "pyra",
943cb7cf3daSStefan Achatz 		.id_table = pyra_devices,
944cb7cf3daSStefan Achatz 		.probe = pyra_probe,
945cb7cf3daSStefan Achatz 		.remove = pyra_remove,
946cb7cf3daSStefan Achatz 		.raw_event = pyra_raw_event
947cb7cf3daSStefan Achatz };
948cb7cf3daSStefan Achatz 
949cb7cf3daSStefan Achatz static int __init pyra_init(void)
950cb7cf3daSStefan Achatz {
951cb7cf3daSStefan Achatz 	return hid_register_driver(&pyra_driver);
952cb7cf3daSStefan Achatz }
953cb7cf3daSStefan Achatz 
954cb7cf3daSStefan Achatz static void __exit pyra_exit(void)
955cb7cf3daSStefan Achatz {
956cb7cf3daSStefan Achatz 	hid_unregister_driver(&pyra_driver);
957cb7cf3daSStefan Achatz }
958cb7cf3daSStefan Achatz 
959cb7cf3daSStefan Achatz module_init(pyra_init);
960cb7cf3daSStefan Achatz module_exit(pyra_exit);
961cb7cf3daSStefan Achatz 
962cb7cf3daSStefan Achatz MODULE_AUTHOR("Stefan Achatz");
963cb7cf3daSStefan Achatz MODULE_DESCRIPTION("USB Roccat Pyra driver");
964cb7cf3daSStefan Achatz MODULE_LICENSE("GPL v2");
965