Blame view

drivers/hid/hid-roccat-pyra.c 16.3 KB
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
  /*
   * Roccat Pyra driver for Linux
   *
   * Copyright (c) 2010 Stefan Achatz <erazor_de@users.sourceforge.net>
   */
  
  /*
   * This program is free software; you can redistribute it and/or modify it
   * under the terms of the GNU General Public License as published by the Free
   * Software Foundation; either version 2 of the License, or (at your option)
   * any later version.
   */
  
  /*
   * 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 ...
23
24
  #include <linux/module.h>
  #include <linux/slab.h>
5dc0c9835   Stefan Achatz   HID: roccat: Rena...
25
  #include <linux/hid-roccat.h>
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
26
  #include "hid-ids.h"
5772f6361   Stefan Achatz   HID: roccat: Intr...
27
  #include "hid-roccat-common.h"
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
28
  #include "hid-roccat-pyra.h"
14a057f80   Stefan Achatz   HID: roccat: redu...
29
  static uint profile_numbers[5] = {0, 1, 2, 3, 4};
5012aada5   Stefan Achatz   HID: roccat: use ...
30
31
  /* pyra_class is used for creating sysfs attributes via roccat char device */
  static struct class *pyra_class;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
32
33
34
  static void profile_activated(struct pyra_device *pyra,
  		unsigned int new_profile)
  {
606185b20   Dan Carpenter   HID: roccat: pote...
35
36
  	if (new_profile >= ARRAY_SIZE(pyra->profile_settings))
  		return;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
37
38
39
40
41
42
43
  	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...
44
  	struct roccat_common2_control control;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
45
46
47
48
49
  
  	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...
50
  	control.command = ROCCAT_COMMON_COMMAND_CONTROL;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
51
52
  	control.value = value;
  	control.request = request;
7392d73be   Stefan Achatz   HID: roccat: rena...
53
54
  	return roccat_common2_send(usb_dev, ROCCAT_COMMON_COMMAND_CONTROL,
  			&control, sizeof(struct roccat_common2_control));
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
55
56
57
58
59
60
  }
  
  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 ...
61
62
  	retval = pyra_send_control(usb_dev, number,
  			PYRA_CONTROL_REQUEST_PROFILE_SETTINGS);
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
63
64
  	if (retval)
  		return retval;
7392d73be   Stefan Achatz   HID: roccat: rena...
65
  	return roccat_common2_receive(usb_dev, PYRA_COMMAND_PROFILE_SETTINGS,
be34380ef   Stefan Achatz   HID: roccat: clea...
66
  			buf, PYRA_SIZE_PROFILE_SETTINGS);
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
67
68
69
70
71
  }
  
  static int pyra_get_settings(struct usb_device *usb_dev,
  		struct pyra_settings *buf)
  {
7392d73be   Stefan Achatz   HID: roccat: rena...
72
  	return roccat_common2_receive(usb_dev, PYRA_COMMAND_SETTINGS,
be34380ef   Stefan Achatz   HID: roccat: clea...
73
  			buf, PYRA_SIZE_SETTINGS);
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
74
75
76
77
78
  }
  
  static int pyra_set_settings(struct usb_device *usb_dev,
  		struct pyra_settings const *settings)
  {
7392d73be   Stefan Achatz   HID: roccat: rena...
79
  	return roccat_common2_send_with_status(usb_dev,
4728f2dc9   Stefan Achatz   HID: roccat: move...
80
  			PYRA_COMMAND_SETTINGS, settings,
be34380ef   Stefan Achatz   HID: roccat: clea...
81
  			PYRA_SIZE_SETTINGS);
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
82
  }
be34380ef   Stefan Achatz   HID: roccat: clea...
83
84
85
  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 ...
86
  {
2cf83833f   Geliang Tang   HID: use kobj_to_...
87
  	struct device *dev = kobj_to_dev(kobj)->parent->parent;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
88
  	struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
be34380ef   Stefan Achatz   HID: roccat: clea...
89
90
  	struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
  	int retval;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
91

be34380ef   Stefan Achatz   HID: roccat: clea...
92
  	if (off >= real_size)
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
93
  		return 0;
be34380ef   Stefan Achatz   HID: roccat: clea...
94
95
  	if (off != 0 || count != real_size)
  		return -EINVAL;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
96
97
  
  	mutex_lock(&pyra->pyra_lock);
be34380ef   Stefan Achatz   HID: roccat: clea...
98
  	retval = roccat_common2_receive(usb_dev, command, buf, real_size);
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
99
  	mutex_unlock(&pyra->pyra_lock);
be34380ef   Stefan Achatz   HID: roccat: clea...
100
101
102
103
  	if (retval)
  		return retval;
  
  	return real_size;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
104
  }
be34380ef   Stefan Achatz   HID: roccat: clea...
105
106
107
  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 ...
108
  {
2cf83833f   Geliang Tang   HID: use kobj_to_...
109
  	struct device *dev = kobj_to_dev(kobj)->parent->parent;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
110
  	struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
be34380ef   Stefan Achatz   HID: roccat: clea...
111
112
  	struct usb_device *usb_dev = interface_to_usbdev(to_usb_interface(dev));
  	int retval;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
113

be34380ef   Stefan Achatz   HID: roccat: clea...
114
115
  	if (off != 0 || count != real_size)
  		return -EINVAL;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
116
117
  
  	mutex_lock(&pyra->pyra_lock);
be34380ef   Stefan Achatz   HID: roccat: clea...
118
  	retval = roccat_common2_send_with_status(usb_dev, command, (void *)buf, real_size);
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
119
  	mutex_unlock(&pyra->pyra_lock);
be34380ef   Stefan Achatz   HID: roccat: clea...
120
121
122
123
  	if (retval)
  		return retval;
  
  	return real_size;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
124
  }
be34380ef   Stefan Achatz   HID: roccat: clea...
125
126
127
128
129
130
131
132
  #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 ...
133

be34380ef   Stefan Achatz   HID: roccat: clea...
134
135
136
137
138
139
140
141
  #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 ...
142

be34380ef   Stefan Achatz   HID: roccat: clea...
143
144
145
  #define PYRA_SYSFS_RW(thingy, THINGY) \
  PYRA_SYSFS_W(thingy, THINGY) \
  PYRA_SYSFS_R(thingy, THINGY)
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
146

be34380ef   Stefan Achatz   HID: roccat: clea...
147
  #define PYRA_BIN_ATTRIBUTE_RW(thingy, THINGY) \
a74924516   Greg Kroah-Hartman   hid: roccat-pyra:...
148
149
  PYRA_SYSFS_RW(thingy, THINGY); \
  static struct bin_attribute bin_attr_##thingy = { \
be34380ef   Stefan Achatz   HID: roccat: clea...
150
151
152
153
154
  	.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 ...
155

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

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

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

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

be34380ef   Stefan Achatz   HID: roccat: clea...
205
206
207
  	return pyra_sysfs_read(fp, kobj, buf, off, count,
  			PYRA_SIZE_PROFILE_BUTTONS,
  			PYRA_COMMAND_PROFILE_BUTTONS);
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
208
  }
a74924516   Greg Kroah-Hartman   hid: roccat-pyra:...
209
210
  #define PROFILE_ATTR(number)						\
  static struct bin_attribute bin_attr_profile##number##_settings = {	\
550dbf478   Stefan Achatz   HID: roccat: Fix ...
211
  	.attr = { .name = "profile" #number "_settings", .mode = 0440 },	\
a74924516   Greg Kroah-Hartman   hid: roccat-pyra:...
212
213
214
215
216
  	.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 ...
217
  	.attr = { .name = "profile" #number "_buttons", .mode = 0440 },	\
a74924516   Greg Kroah-Hartman   hid: roccat-pyra:...
218
219
220
221
222
223
224
225
226
  	.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 ...
227
228
229
230
  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_...
231
  	struct device *dev = kobj_to_dev(kobj)->parent->parent;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
232
233
234
  	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...
235
  	struct pyra_roccat_report roccat_report;
be34380ef   Stefan Achatz   HID: roccat: clea...
236
  	struct pyra_settings const *settings;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
237

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

be34380ef   Stefan Achatz   HID: roccat: clea...
246
247
248
249
  	retval = pyra_set_settings(usb_dev, settings);
  	if (retval) {
  		mutex_unlock(&pyra->pyra_lock);
  		return retval;
dc186b661   Stefan Achatz   HID: roccat: Pyra...
250
  	}
be34380ef   Stefan Achatz   HID: roccat: clea...
251
252
253
254
255
256
257
258
  
  	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...
259
  	mutex_unlock(&pyra->pyra_lock);
be34380ef   Stefan Achatz   HID: roccat: clea...
260
  	return PYRA_SIZE_SETTINGS;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
261
  }
a74924516   Greg Kroah-Hartman   hid: roccat-pyra:...
262
263
264
265
266
  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 ...
267
268
269
270
  
  static ssize_t pyra_sysfs_show_actual_cpi(struct device *dev,
  		struct device_attribute *attr, char *buf)
  {
5012aada5   Stefan Achatz   HID: roccat: use ...
271
272
  	struct pyra_device *pyra =
  			hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
273
274
275
  	return snprintf(buf, PAGE_SIZE, "%d
  ", pyra->actual_cpi);
  }
46a58c44c   Greg Kroah-Hartman   HID: roccat: conv...
276
  static DEVICE_ATTR(actual_cpi, 0440, pyra_sysfs_show_actual_cpi, NULL);
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
277
278
279
280
  
  static ssize_t pyra_sysfs_show_actual_profile(struct device *dev,
  		struct device_attribute *attr, char *buf)
  {
5012aada5   Stefan Achatz   HID: roccat: use ...
281
282
  	struct pyra_device *pyra =
  			hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
be34380ef   Stefan Achatz   HID: roccat: clea...
283
284
285
286
287
288
289
290
291
292
  	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 ...
293
  }
46a58c44c   Greg Kroah-Hartman   HID: roccat: conv...
294
295
  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 ...
296
297
298
299
  
  static ssize_t pyra_sysfs_show_firmware_version(struct device *dev,
  		struct device_attribute *attr, char *buf)
  {
be34380ef   Stefan Achatz   HID: roccat: clea...
300
301
302
  	struct pyra_device *pyra;
  	struct usb_device *usb_dev;
  	struct pyra_info info;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
303

be34380ef   Stefan Achatz   HID: roccat: clea...
304
305
306
307
308
309
310
311
312
313
314
  	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 ...
315
  }
46a58c44c   Greg Kroah-Hartman   HID: roccat: conv...
316
317
318
319
320
321
322
323
324
  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 ...
325
  };
a74924516   Greg Kroah-Hartman   hid: roccat-pyra:...
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
351
352
353
  
  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 ...
354
  };
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
355
356
357
  static int pyra_init_pyra_device_struct(struct usb_device *usb_dev,
  		struct pyra_device *pyra)
  {
be34380ef   Stefan Achatz   HID: roccat: clea...
358
  	struct pyra_settings settings;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
359
360
361
  	int retval, i;
  
  	mutex_init(&pyra->pyra_lock);
be34380ef   Stefan Achatz   HID: roccat: clea...
362
  	retval = pyra_get_settings(usb_dev, &settings);
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
363
364
365
366
367
368
369
370
  	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 ...
371
  	}
be34380ef   Stefan Achatz   HID: roccat: clea...
372
  	profile_activated(pyra, settings.startup_profile);
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
  
  	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 ...
389
390
  			hid_err(hdev, "can't alloc device descriptor
  ");
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
391
392
393
394
395
396
  			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 ...
397
398
  			hid_err(hdev, "couldn't init struct pyra_device
  ");
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
399
400
  			goto exit_free;
  		}
8211e4600   Stefan Achatz   HID: roccat: Add ...
401
402
  		retval = roccat_connect(pyra_class, hdev,
  				sizeof(struct pyra_roccat_report));
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
403
  		if (retval < 0) {
4291ee305   Joe Perches   HID: Add and use ...
404
405
  			hid_err(hdev, "couldn't init char dev
  ");
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
406
407
408
409
  		} else {
  			pyra->chrdev_minor = retval;
  			pyra->roccat_claimed = 1;
  		}
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
  	} 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 ...
427
428
429
430
431
432
433
434
435
436
437
438
439
  		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 ...
440
441
  		hid_err(hdev, "parse failed
  ");
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
442
443
444
445
446
  		goto exit;
  	}
  
  	retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
  	if (retval) {
4291ee305   Joe Perches   HID: Add and use ...
447
448
  		hid_err(hdev, "hw start failed
  ");
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
449
450
451
452
453
  		goto exit;
  	}
  
  	retval = pyra_init_specials(hdev);
  	if (retval) {
4291ee305   Joe Perches   HID: Add and use ...
454
455
  		hid_err(hdev, "couldn't install mouse
  ");
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
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
507
508
509
  		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 ...
510
  				(uint8_t const *)&roccat_report);
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
511
512
513
514
515
516
517
  		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...
518
519
520
521
522
  			/*
  			 * pyra reports profile numbers with range 1-5.
  			 * Keeping this behaviour.
  			 */
  			roccat_report.value = pyra->actual_profile + 1;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
523
  			roccat_report_event(pyra->chrdev_minor,
8211e4600   Stefan Achatz   HID: roccat: Add ...
524
  					(uint8_t const *)&roccat_report);
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
525
526
527
528
529
530
531
532
533
534
535
536
537
538
  		}
  		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 ...
539
540
  	if (pyra == NULL)
  		return 0;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
541
542
543
544
545
546
547
548
549
550
551
  	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 ...
552
553
  	{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT,
  			USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS) },
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
  	{ }
  };
  
  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 ...
569
570
571
572
573
574
  	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...
575
  	pyra_class->dev_groups = pyra_groups;
5012aada5   Stefan Achatz   HID: roccat: use ...
576
577
578
579
580
  
  	retval = hid_register_driver(&pyra_driver);
  	if (retval)
  		class_destroy(pyra_class);
  	return retval;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
581
582
583
584
585
  }
  
  static void __exit pyra_exit(void)
  {
  	hid_unregister_driver(&pyra_driver);
74b643dac   Stefan Achatz   HID: roccat: Fix ...
586
  	class_destroy(pyra_class);
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
587
588
589
590
591
592
593
594
  }
  
  module_init(pyra_init);
  module_exit(pyra_exit);
  
  MODULE_AUTHOR("Stefan Achatz");
  MODULE_DESCRIPTION("USB Roccat Pyra driver");
  MODULE_LICENSE("GPL v2");