Blame view

drivers/hid/hid-roccat-pyra.c 16.1 KB
2874c5fd2   Thomas Gleixner   treewide: Replace...
1
  // SPDX-License-Identifier: GPL-2.0-or-later
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
2
3
4
5
6
7
8
  /*
   * Roccat Pyra driver for Linux
   *
   * Copyright (c) 2010 Stefan Achatz <erazor_de@users.sourceforge.net>
   */
  
  /*
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
9
10
11
12
13
14
15
16
17
18
19
   */
  
  /*
   * Roccat Pyra is a mobile gamer mouse which comes in wired and wireless
   * variant. Wireless variant is not tested.
   * Userland tools can be found at http://sourceforge.net/projects/roccat
   */
  
  #include <linux/device.h>
  #include <linux/input.h>
  #include <linux/hid.h>
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
20
21
  #include <linux/module.h>
  #include <linux/slab.h>
5dc0c9835   Stefan Achatz   HID: roccat: Rena...
22
  #include <linux/hid-roccat.h>
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
23
  #include "hid-ids.h"
5772f6361   Stefan Achatz   HID: roccat: Intr...
24
  #include "hid-roccat-common.h"
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
25
  #include "hid-roccat-pyra.h"
14a057f80   Stefan Achatz   HID: roccat: redu...
26
  static uint profile_numbers[5] = {0, 1, 2, 3, 4};
5012aada5   Stefan Achatz   HID: roccat: use ...
27
28
  /* pyra_class is used for creating sysfs attributes via roccat char device */
  static struct class *pyra_class;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
29
30
31
  static void profile_activated(struct pyra_device *pyra,
  		unsigned int new_profile)
  {
606185b20   Dan Carpenter   HID: roccat: pote...
32
33
  	if (new_profile >= ARRAY_SIZE(pyra->profile_settings))
  		return;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
34
35
36
37
38
39
40
  	pyra->actual_profile = new_profile;
  	pyra->actual_cpi = pyra->profile_settings[pyra->actual_profile].y_cpi;
  }
  
  static int pyra_send_control(struct usb_device *usb_dev, int value,
  		enum pyra_control_requests request)
  {
7392d73be   Stefan Achatz   HID: roccat: rena...
41
  	struct roccat_common2_control control;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
42
43
44
45
46
  
  	if ((request == PYRA_CONTROL_REQUEST_PROFILE_SETTINGS ||
  			request == PYRA_CONTROL_REQUEST_PROFILE_BUTTONS) &&
  			(value < 0 || value > 4))
  		return -EINVAL;
4728f2dc9   Stefan Achatz   HID: roccat: move...
47
  	control.command = ROCCAT_COMMON_COMMAND_CONTROL;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
48
49
  	control.value = value;
  	control.request = request;
7392d73be   Stefan Achatz   HID: roccat: rena...
50
51
  	return roccat_common2_send(usb_dev, ROCCAT_COMMON_COMMAND_CONTROL,
  			&control, sizeof(struct roccat_common2_control));
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
52
53
54
55
56
57
  }
  
  static int pyra_get_profile_settings(struct usb_device *usb_dev,
  		struct pyra_profile_settings *buf, int number)
  {
  	int retval;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
58
59
  	retval = pyra_send_control(usb_dev, number,
  			PYRA_CONTROL_REQUEST_PROFILE_SETTINGS);
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
60
61
  	if (retval)
  		return retval;
7392d73be   Stefan Achatz   HID: roccat: rena...
62
  	return roccat_common2_receive(usb_dev, PYRA_COMMAND_PROFILE_SETTINGS,
be34380ef   Stefan Achatz   HID: roccat: clea...
63
  			buf, PYRA_SIZE_PROFILE_SETTINGS);
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
64
65
66
67
68
  }
  
  static int pyra_get_settings(struct usb_device *usb_dev,
  		struct pyra_settings *buf)
  {
7392d73be   Stefan Achatz   HID: roccat: rena...
69
  	return roccat_common2_receive(usb_dev, PYRA_COMMAND_SETTINGS,
be34380ef   Stefan Achatz   HID: roccat: clea...
70
  			buf, PYRA_SIZE_SETTINGS);
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
71
72
73
74
75
  }
  
  static int pyra_set_settings(struct usb_device *usb_dev,
  		struct pyra_settings const *settings)
  {
7392d73be   Stefan Achatz   HID: roccat: rena...
76
  	return roccat_common2_send_with_status(usb_dev,
4728f2dc9   Stefan Achatz   HID: roccat: move...
77
  			PYRA_COMMAND_SETTINGS, settings,
be34380ef   Stefan Achatz   HID: roccat: clea...
78
  			PYRA_SIZE_SETTINGS);
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
79
  }
be34380ef   Stefan Achatz   HID: roccat: clea...
80
81
82
  static ssize_t pyra_sysfs_read(struct file *fp, struct kobject *kobj,
  		char *buf, loff_t off, size_t count,
  		size_t real_size, uint command)
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
83
  {
2cf83833f   Geliang Tang   HID: use kobj_to_...
84
  	struct device *dev = kobj_to_dev(kobj)->parent->parent;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
85
  	struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
be34380ef   Stefan Achatz   HID: roccat: clea...
86
87
  	struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
  	int retval;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
88

be34380ef   Stefan Achatz   HID: roccat: clea...
89
  	if (off >= real_size)
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
90
  		return 0;
be34380ef   Stefan Achatz   HID: roccat: clea...
91
92
  	if (off != 0 || count != real_size)
  		return -EINVAL;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
93
94
  
  	mutex_lock(&pyra->pyra_lock);
be34380ef   Stefan Achatz   HID: roccat: clea...
95
  	retval = roccat_common2_receive(usb_dev, command, buf, real_size);
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
96
  	mutex_unlock(&pyra->pyra_lock);
be34380ef   Stefan Achatz   HID: roccat: clea...
97
98
99
100
  	if (retval)
  		return retval;
  
  	return real_size;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
101
  }
be34380ef   Stefan Achatz   HID: roccat: clea...
102
103
104
  static ssize_t pyra_sysfs_write(struct file *fp, struct kobject *kobj,
  		void const *buf, loff_t off, size_t count,
  		size_t real_size, uint command)
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
105
  {
2cf83833f   Geliang Tang   HID: use kobj_to_...
106
  	struct device *dev = kobj_to_dev(kobj)->parent->parent;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
107
  	struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
be34380ef   Stefan Achatz   HID: roccat: clea...
108
109
  	struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
  	int retval;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
110

be34380ef   Stefan Achatz   HID: roccat: clea...
111
112
  	if (off != 0 || count != real_size)
  		return -EINVAL;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
113
114
  
  	mutex_lock(&pyra->pyra_lock);
be34380ef   Stefan Achatz   HID: roccat: clea...
115
  	retval = roccat_common2_send_with_status(usb_dev, command, (void *)buf, real_size);
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
116
  	mutex_unlock(&pyra->pyra_lock);
be34380ef   Stefan Achatz   HID: roccat: clea...
117
118
119
120
  	if (retval)
  		return retval;
  
  	return real_size;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
121
  }
be34380ef   Stefan Achatz   HID: roccat: clea...
122
123
124
125
126
127
128
129
  #define PYRA_SYSFS_W(thingy, THINGY) \
  static ssize_t pyra_sysfs_write_ ## thingy(struct file *fp, \
  		struct kobject *kobj, struct bin_attribute *attr, char *buf, \
  		loff_t off, size_t count) \
  { \
  	return pyra_sysfs_write(fp, kobj, buf, off, count, \
  			PYRA_SIZE_ ## THINGY, PYRA_COMMAND_ ## THINGY); \
  }
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
130

be34380ef   Stefan Achatz   HID: roccat: clea...
131
132
133
134
135
136
137
138
  #define PYRA_SYSFS_R(thingy, THINGY) \
  static ssize_t pyra_sysfs_read_ ## thingy(struct file *fp, \
  		struct kobject *kobj, struct bin_attribute *attr, char *buf, \
  		loff_t off, size_t count) \
  { \
  	return pyra_sysfs_read(fp, kobj, buf, off, count, \
  			PYRA_SIZE_ ## THINGY, PYRA_COMMAND_ ## THINGY); \
  }
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
139

be34380ef   Stefan Achatz   HID: roccat: clea...
140
141
142
  #define PYRA_SYSFS_RW(thingy, THINGY) \
  PYRA_SYSFS_W(thingy, THINGY) \
  PYRA_SYSFS_R(thingy, THINGY)
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
143

be34380ef   Stefan Achatz   HID: roccat: clea...
144
  #define PYRA_BIN_ATTRIBUTE_RW(thingy, THINGY) \
a74924516   Greg Kroah-Hartman   hid: roccat-pyra:...
145
146
  PYRA_SYSFS_RW(thingy, THINGY); \
  static struct bin_attribute bin_attr_##thingy = { \
be34380ef   Stefan Achatz   HID: roccat: clea...
147
148
149
150
151
  	.attr = { .name = #thingy, .mode = 0660 }, \
  	.size = PYRA_SIZE_ ## THINGY, \
  	.read = pyra_sysfs_read_ ## thingy, \
  	.write = pyra_sysfs_write_ ## thingy \
  }
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
152

be34380ef   Stefan Achatz   HID: roccat: clea...
153
  #define PYRA_BIN_ATTRIBUTE_R(thingy, THINGY) \
a74924516   Greg Kroah-Hartman   hid: roccat-pyra:...
154
155
  PYRA_SYSFS_R(thingy, THINGY); \
  static struct bin_attribute bin_attr_##thingy = { \
be34380ef   Stefan Achatz   HID: roccat: clea...
156
157
158
159
  	.attr = { .name = #thingy, .mode = 0440 }, \
  	.size = PYRA_SIZE_ ## THINGY, \
  	.read = pyra_sysfs_read_ ## thingy, \
  }
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
160

be34380ef   Stefan Achatz   HID: roccat: clea...
161
  #define PYRA_BIN_ATTRIBUTE_W(thingy, THINGY) \
a74924516   Greg Kroah-Hartman   hid: roccat-pyra:...
162
163
  PYRA_SYSFS_W(thingy, THINGY); \
  static struct bin_attribute bin_attr_##thingy = { \
be34380ef   Stefan Achatz   HID: roccat: clea...
164
165
166
  	.attr = { .name = #thingy, .mode = 0220 }, \
  	.size = PYRA_SIZE_ ## THINGY, \
  	.write = pyra_sysfs_write_ ## thingy \
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
167
  }
a74924516   Greg Kroah-Hartman   hid: roccat-pyra:...
168
169
170
171
  PYRA_BIN_ATTRIBUTE_W(control, CONTROL);
  PYRA_BIN_ATTRIBUTE_RW(info, INFO);
  PYRA_BIN_ATTRIBUTE_RW(profile_settings, PROFILE_SETTINGS);
  PYRA_BIN_ATTRIBUTE_RW(profile_buttons, PROFILE_BUTTONS);
be34380ef   Stefan Achatz   HID: roccat: clea...
172
173
  
  static ssize_t pyra_sysfs_read_profilex_settings(struct file *fp,
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
174
175
176
  		struct kobject *kobj, struct bin_attribute *attr, char *buf,
  		loff_t off, size_t count)
  {
2cf83833f   Geliang Tang   HID: use kobj_to_...
177
  	struct device *dev = kobj_to_dev(kobj)->parent->parent;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
178
  	struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
be34380ef   Stefan Achatz   HID: roccat: clea...
179
  	ssize_t retval;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
180

be34380ef   Stefan Achatz   HID: roccat: clea...
181
182
  	retval = pyra_send_control(usb_dev, *(uint *)(attr->private),
  			PYRA_CONTROL_REQUEST_PROFILE_SETTINGS);
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
183
184
  	if (retval)
  		return retval;
be34380ef   Stefan Achatz   HID: roccat: clea...
185
186
187
  	return pyra_sysfs_read(fp, kobj, buf, off, count,
  			PYRA_SIZE_PROFILE_SETTINGS,
  			PYRA_COMMAND_PROFILE_SETTINGS);
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
188
  }
be34380ef   Stefan Achatz   HID: roccat: clea...
189
  static ssize_t pyra_sysfs_read_profilex_buttons(struct file *fp,
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
190
191
192
  		struct kobject *kobj, struct bin_attribute *attr, char *buf,
  		loff_t off, size_t count)
  {
2cf83833f   Geliang Tang   HID: use kobj_to_...
193
  	struct device *dev = kobj_to_dev(kobj)->parent->parent;
be34380ef   Stefan Achatz   HID: roccat: clea...
194
195
  	struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
  	ssize_t retval;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
196

be34380ef   Stefan Achatz   HID: roccat: clea...
197
198
199
200
  	retval = pyra_send_control(usb_dev, *(uint *)(attr->private),
  			PYRA_CONTROL_REQUEST_PROFILE_BUTTONS);
  	if (retval)
  		return retval;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
201

be34380ef   Stefan Achatz   HID: roccat: clea...
202
203
204
  	return pyra_sysfs_read(fp, kobj, buf, off, count,
  			PYRA_SIZE_PROFILE_BUTTONS,
  			PYRA_COMMAND_PROFILE_BUTTONS);
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
205
  }
a74924516   Greg Kroah-Hartman   hid: roccat-pyra:...
206
207
  #define PROFILE_ATTR(number)						\
  static struct bin_attribute bin_attr_profile##number##_settings = {	\
550dbf478   Stefan Achatz   HID: roccat: Fix ...
208
  	.attr = { .name = "profile" #number "_settings", .mode = 0440 },	\
a74924516   Greg Kroah-Hartman   hid: roccat-pyra:...
209
210
211
212
213
  	.size = PYRA_SIZE_PROFILE_SETTINGS,				\
  	.read = pyra_sysfs_read_profilex_settings,			\
  	.private = &profile_numbers[number-1],				\
  };									\
  static struct bin_attribute bin_attr_profile##number##_buttons = {	\
550dbf478   Stefan Achatz   HID: roccat: Fix ...
214
  	.attr = { .name = "profile" #number "_buttons", .mode = 0440 },	\
a74924516   Greg Kroah-Hartman   hid: roccat-pyra:...
215
216
217
218
219
220
221
222
223
  	.size = PYRA_SIZE_PROFILE_BUTTONS,				\
  	.read = pyra_sysfs_read_profilex_buttons,			\
  	.private = &profile_numbers[number-1],				\
  };
  PROFILE_ATTR(1);
  PROFILE_ATTR(2);
  PROFILE_ATTR(3);
  PROFILE_ATTR(4);
  PROFILE_ATTR(5);
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
224
225
226
227
  static ssize_t pyra_sysfs_write_settings(struct file *fp,
  		struct kobject *kobj, struct bin_attribute *attr, char *buf,
  		loff_t off, size_t count)
  {
2cf83833f   Geliang Tang   HID: use kobj_to_...
228
  	struct device *dev = kobj_to_dev(kobj)->parent->parent;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
229
230
231
  	struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
  	struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
  	int retval = 0;
dc186b661   Stefan Achatz   HID: roccat: Pyra...
232
  	struct pyra_roccat_report roccat_report;
be34380ef   Stefan Achatz   HID: roccat: clea...
233
  	struct pyra_settings const *settings;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
234

be34380ef   Stefan Achatz   HID: roccat: clea...
235
  	if (off != 0 || count != PYRA_SIZE_SETTINGS)
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
236
  		return -EINVAL;
be34380ef   Stefan Achatz   HID: roccat: clea...
237
  	settings = (struct pyra_settings const *)buf;
606185b20   Dan Carpenter   HID: roccat: pote...
238
239
240
241
  	if (settings->startup_profile >= ARRAY_SIZE(pyra->profile_settings))
  		return -EINVAL;
  
  	mutex_lock(&pyra->pyra_lock);
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
242

be34380ef   Stefan Achatz   HID: roccat: clea...
243
244
245
246
  	retval = pyra_set_settings(usb_dev, settings);
  	if (retval) {
  		mutex_unlock(&pyra->pyra_lock);
  		return retval;
dc186b661   Stefan Achatz   HID: roccat: Pyra...
247
  	}
be34380ef   Stefan Achatz   HID: roccat: clea...
248
249
250
251
252
253
254
255
  
  	profile_activated(pyra, settings->startup_profile);
  
  	roccat_report.type = PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2;
  	roccat_report.value = settings->startup_profile + 1;
  	roccat_report.key = 0;
  	roccat_report_event(pyra->chrdev_minor,
  			(uint8_t const *)&roccat_report);
dc186b661   Stefan Achatz   HID: roccat: Pyra...
256
  	mutex_unlock(&pyra->pyra_lock);
be34380ef   Stefan Achatz   HID: roccat: clea...
257
  	return PYRA_SIZE_SETTINGS;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
258
  }
a74924516   Greg Kroah-Hartman   hid: roccat-pyra:...
259
260
261
262
263
  PYRA_SYSFS_R(settings, SETTINGS);
  static struct bin_attribute bin_attr_settings =
  	__BIN_ATTR(settings, (S_IWUSR | S_IRUGO),
  		   pyra_sysfs_read_settings, pyra_sysfs_write_settings,
  		   PYRA_SIZE_SETTINGS);
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
264
265
266
267
  
  static ssize_t pyra_sysfs_show_actual_cpi(struct device *dev,
  		struct device_attribute *attr, char *buf)
  {
5012aada5   Stefan Achatz   HID: roccat: use ...
268
269
  	struct pyra_device *pyra =
  			hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
270
271
272
  	return snprintf(buf, PAGE_SIZE, "%d
  ", pyra->actual_cpi);
  }
46a58c44c   Greg Kroah-Hartman   HID: roccat: conv...
273
  static DEVICE_ATTR(actual_cpi, 0440, pyra_sysfs_show_actual_cpi, NULL);
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
274
275
276
277
  
  static ssize_t pyra_sysfs_show_actual_profile(struct device *dev,
  		struct device_attribute *attr, char *buf)
  {
5012aada5   Stefan Achatz   HID: roccat: use ...
278
279
  	struct pyra_device *pyra =
  			hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
be34380ef   Stefan Achatz   HID: roccat: clea...
280
281
282
283
284
285
286
287
288
289
  	struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
  	struct pyra_settings settings;
  
  	mutex_lock(&pyra->pyra_lock);
  	roccat_common2_receive(usb_dev, PYRA_COMMAND_SETTINGS,
  			&settings, PYRA_SIZE_SETTINGS);
  	mutex_unlock(&pyra->pyra_lock);
  
  	return snprintf(buf, PAGE_SIZE, "%d
  ", settings.startup_profile);
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
290
  }
46a58c44c   Greg Kroah-Hartman   HID: roccat: conv...
291
292
  static DEVICE_ATTR(actual_profile, 0440, pyra_sysfs_show_actual_profile, NULL);
  static DEVICE_ATTR(startup_profile, 0440, pyra_sysfs_show_actual_profile, NULL);
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
293
294
295
296
  
  static ssize_t pyra_sysfs_show_firmware_version(struct device *dev,
  		struct device_attribute *attr, char *buf)
  {
be34380ef   Stefan Achatz   HID: roccat: clea...
297
298
299
  	struct pyra_device *pyra;
  	struct usb_device *usb_dev;
  	struct pyra_info info;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
300

be34380ef   Stefan Achatz   HID: roccat: clea...
301
302
303
304
305
306
307
308
309
310
311
  	dev = dev->parent->parent;
  	pyra = hid_get_drvdata(dev_get_drvdata(dev));
  	usb_dev = interface_to_usbdev(to_usb_interface(dev));
  
  	mutex_lock(&pyra->pyra_lock);
  	roccat_common2_receive(usb_dev, PYRA_COMMAND_INFO,
  			&info, PYRA_SIZE_INFO);
  	mutex_unlock(&pyra->pyra_lock);
  
  	return snprintf(buf, PAGE_SIZE, "%d
  ", info.firmware_version);
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
312
  }
46a58c44c   Greg Kroah-Hartman   HID: roccat: conv...
313
314
315
316
317
318
319
320
321
  static DEVICE_ATTR(firmware_version, 0440, pyra_sysfs_show_firmware_version,
  		   NULL);
  
  static struct attribute *pyra_attrs[] = {
  	&dev_attr_actual_cpi.attr,
  	&dev_attr_actual_profile.attr,
  	&dev_attr_firmware_version.attr,
  	&dev_attr_startup_profile.attr,
  	NULL,
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
322
  };
a74924516   Greg Kroah-Hartman   hid: roccat-pyra:...
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
  
  static struct bin_attribute *pyra_bin_attributes[] = {
  	&bin_attr_control,
  	&bin_attr_info,
  	&bin_attr_profile_settings,
  	&bin_attr_profile_buttons,
  	&bin_attr_settings,
  	&bin_attr_profile1_settings,
  	&bin_attr_profile2_settings,
  	&bin_attr_profile3_settings,
  	&bin_attr_profile4_settings,
  	&bin_attr_profile5_settings,
  	&bin_attr_profile1_buttons,
  	&bin_attr_profile2_buttons,
  	&bin_attr_profile3_buttons,
  	&bin_attr_profile4_buttons,
  	&bin_attr_profile5_buttons,
  	NULL,
  };
  
  static const struct attribute_group pyra_group = {
  	.attrs = pyra_attrs,
  	.bin_attrs = pyra_bin_attributes,
  };
  
  static const struct attribute_group *pyra_groups[] = {
  	&pyra_group,
  	NULL,
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
351
  };
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
352
353
354
  static int pyra_init_pyra_device_struct(struct usb_device *usb_dev,
  		struct pyra_device *pyra)
  {
be34380ef   Stefan Achatz   HID: roccat: clea...
355
  	struct pyra_settings settings;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
356
357
358
  	int retval, i;
  
  	mutex_init(&pyra->pyra_lock);
be34380ef   Stefan Achatz   HID: roccat: clea...
359
  	retval = pyra_get_settings(usb_dev, &settings);
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
360
361
362
363
364
365
366
367
  	if (retval)
  		return retval;
  
  	for (i = 0; i < 5; ++i) {
  		retval = pyra_get_profile_settings(usb_dev,
  				&pyra->profile_settings[i], i);
  		if (retval)
  			return retval;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
368
  	}
be34380ef   Stefan Achatz   HID: roccat: clea...
369
  	profile_activated(pyra, settings.startup_profile);
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
  
  	return 0;
  }
  
  static int pyra_init_specials(struct hid_device *hdev)
  {
  	struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
  	struct usb_device *usb_dev = interface_to_usbdev(intf);
  	struct pyra_device *pyra;
  	int retval;
  
  	if (intf->cur_altsetting->desc.bInterfaceProtocol
  			== USB_INTERFACE_PROTOCOL_MOUSE) {
  
  		pyra = kzalloc(sizeof(*pyra), GFP_KERNEL);
  		if (!pyra) {
4291ee305   Joe Perches   HID: Add and use ...
386
387
  			hid_err(hdev, "can't alloc device descriptor
  ");
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
388
389
390
391
392
393
  			return -ENOMEM;
  		}
  		hid_set_drvdata(hdev, pyra);
  
  		retval = pyra_init_pyra_device_struct(usb_dev, pyra);
  		if (retval) {
4291ee305   Joe Perches   HID: Add and use ...
394
395
  			hid_err(hdev, "couldn't init struct pyra_device
  ");
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
396
397
  			goto exit_free;
  		}
8211e4600   Stefan Achatz   HID: roccat: Add ...
398
399
  		retval = roccat_connect(pyra_class, hdev,
  				sizeof(struct pyra_roccat_report));
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
400
  		if (retval < 0) {
4291ee305   Joe Perches   HID: Add and use ...
401
402
  			hid_err(hdev, "couldn't init char dev
  ");
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
403
404
405
406
  		} else {
  			pyra->chrdev_minor = retval;
  			pyra->roccat_claimed = 1;
  		}
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
  	} else {
  		hid_set_drvdata(hdev, NULL);
  	}
  
  	return 0;
  exit_free:
  	kfree(pyra);
  	return retval;
  }
  
  static void pyra_remove_specials(struct hid_device *hdev)
  {
  	struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
  	struct pyra_device *pyra;
  
  	if (intf->cur_altsetting->desc.bInterfaceProtocol
  			== USB_INTERFACE_PROTOCOL_MOUSE) {
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
424
425
426
427
428
429
430
431
432
433
434
435
436
  		pyra = hid_get_drvdata(hdev);
  		if (pyra->roccat_claimed)
  			roccat_disconnect(pyra->chrdev_minor);
  		kfree(hid_get_drvdata(hdev));
  	}
  }
  
  static int pyra_probe(struct hid_device *hdev, const struct hid_device_id *id)
  {
  	int retval;
  
  	retval = hid_parse(hdev);
  	if (retval) {
4291ee305   Joe Perches   HID: Add and use ...
437
438
  		hid_err(hdev, "parse failed
  ");
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
439
440
441
442
443
  		goto exit;
  	}
  
  	retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
  	if (retval) {
4291ee305   Joe Perches   HID: Add and use ...
444
445
  		hid_err(hdev, "hw start failed
  ");
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
446
447
448
449
450
  		goto exit;
  	}
  
  	retval = pyra_init_specials(hdev);
  	if (retval) {
4291ee305   Joe Perches   HID: Add and use ...
451
452
  		hid_err(hdev, "couldn't install mouse
  ");
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
  		goto exit_stop;
  	}
  	return 0;
  
  exit_stop:
  	hid_hw_stop(hdev);
  exit:
  	return retval;
  }
  
  static void pyra_remove(struct hid_device *hdev)
  {
  	pyra_remove_specials(hdev);
  	hid_hw_stop(hdev);
  }
  
  static void pyra_keep_values_up_to_date(struct pyra_device *pyra,
  		u8 const *data)
  {
  	struct pyra_mouse_event_button const *button_event;
  
  	switch (data[0]) {
  	case PYRA_MOUSE_REPORT_NUMBER_BUTTON:
  		button_event = (struct pyra_mouse_event_button const *)data;
  		switch (button_event->type) {
  		case PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2:
  			profile_activated(pyra, button_event->data1 - 1);
  			break;
  		case PYRA_MOUSE_EVENT_BUTTON_TYPE_CPI:
  			pyra->actual_cpi = button_event->data1;
  			break;
  		}
  		break;
  	}
  }
  
  static void pyra_report_to_chrdev(struct pyra_device const *pyra,
  		u8 const *data)
  {
  	struct pyra_roccat_report roccat_report;
  	struct pyra_mouse_event_button const *button_event;
  
  	if (data[0] != PYRA_MOUSE_REPORT_NUMBER_BUTTON)
  		return;
  
  	button_event = (struct pyra_mouse_event_button const *)data;
  
  	switch (button_event->type) {
  	case PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2:
  	case PYRA_MOUSE_EVENT_BUTTON_TYPE_CPI:
  		roccat_report.type = button_event->type;
  		roccat_report.value = button_event->data1;
  		roccat_report.key = 0;
  		roccat_report_event(pyra->chrdev_minor,
8211e4600   Stefan Achatz   HID: roccat: Add ...
507
  				(uint8_t const *)&roccat_report);
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
508
509
510
511
512
513
514
  		break;
  	case PYRA_MOUSE_EVENT_BUTTON_TYPE_MACRO:
  	case PYRA_MOUSE_EVENT_BUTTON_TYPE_SHORTCUT:
  	case PYRA_MOUSE_EVENT_BUTTON_TYPE_QUICKLAUNCH:
  		if (button_event->data2 == PYRA_MOUSE_EVENT_BUTTON_PRESS) {
  			roccat_report.type = button_event->type;
  			roccat_report.key = button_event->data1;
d2b570a5d   Stefan Achatz   HID: roccat: Norm...
515
516
517
518
519
  			/*
  			 * pyra reports profile numbers with range 1-5.
  			 * Keeping this behaviour.
  			 */
  			roccat_report.value = pyra->actual_profile + 1;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
520
  			roccat_report_event(pyra->chrdev_minor,
8211e4600   Stefan Achatz   HID: roccat: Add ...
521
  					(uint8_t const *)&roccat_report);
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
522
523
524
525
526
527
528
529
530
531
532
533
534
535
  		}
  		break;
  	}
  }
  
  static int pyra_raw_event(struct hid_device *hdev, struct hid_report *report,
  		u8 *data, int size)
  {
  	struct usb_interface *intf = to_usb_interface(hdev->dev.parent);
  	struct pyra_device *pyra = hid_get_drvdata(hdev);
  
  	if (intf->cur_altsetting->desc.bInterfaceProtocol
  			!= USB_INTERFACE_PROTOCOL_MOUSE)
  		return 0;
901e64dbd   Stefan Achatz   HID: roccat: fix ...
536
537
  	if (pyra == NULL)
  		return 0;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
538
539
540
541
542
543
544
545
546
547
548
  	pyra_keep_values_up_to_date(pyra, data);
  
  	if (pyra->roccat_claimed)
  		pyra_report_to_chrdev(pyra, data);
  
  	return 0;
  }
  
  static const struct hid_device_id pyra_devices[] = {
  	{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT,
  			USB_DEVICE_ID_ROCCAT_PYRA_WIRED) },
3fce22460   Stefan Achatz   HID: roccat: Add ...
549
550
  	{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT,
  			USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS) },
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
  	{ }
  };
  
  MODULE_DEVICE_TABLE(hid, pyra_devices);
  
  static struct hid_driver pyra_driver = {
  		.name = "pyra",
  		.id_table = pyra_devices,
  		.probe = pyra_probe,
  		.remove = pyra_remove,
  		.raw_event = pyra_raw_event
  };
  
  static int __init pyra_init(void)
  {
5012aada5   Stefan Achatz   HID: roccat: use ...
566
567
568
569
570
571
  	int retval;
  
  	/* class name has to be same as driver name */
  	pyra_class = class_create(THIS_MODULE, "pyra");
  	if (IS_ERR(pyra_class))
  		return PTR_ERR(pyra_class);
46a58c44c   Greg Kroah-Hartman   HID: roccat: conv...
572
  	pyra_class->dev_groups = pyra_groups;
5012aada5   Stefan Achatz   HID: roccat: use ...
573
574
575
576
577
  
  	retval = hid_register_driver(&pyra_driver);
  	if (retval)
  		class_destroy(pyra_class);
  	return retval;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
578
579
580
581
582
  }
  
  static void __exit pyra_exit(void)
  {
  	hid_unregister_driver(&pyra_driver);
74b643dac   Stefan Achatz   HID: roccat: Fix ...
583
  	class_destroy(pyra_class);
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
584
585
586
587
588
589
590
591
  }
  
  module_init(pyra_init);
  module_exit(pyra_exit);
  
  MODULE_AUTHOR("Stefan Achatz");
  MODULE_DESCRIPTION("USB Roccat Pyra driver");
  MODULE_LICENSE("GPL v2");