Blame view

drivers/hid/hid-roccat-pyra.c 19 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
35
36
37
38
39
40
41
  static void profile_activated(struct pyra_device *pyra,
  		unsigned int new_profile)
  {
  	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)
  {
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
42
43
44
45
46
47
48
49
50
51
  	struct pyra_control control;
  
  	if ((request == PYRA_CONTROL_REQUEST_PROFILE_SETTINGS ||
  			request == PYRA_CONTROL_REQUEST_PROFILE_BUTTONS) &&
  			(value < 0 || value > 4))
  		return -EINVAL;
  
  	control.command = PYRA_COMMAND_CONTROL;
  	control.value = value;
  	control.request = request;
1edd5b42a   Stefan Achatz   HID: roccat: corr...
52
  	return roccat_common_send(usb_dev, PYRA_COMMAND_CONTROL,
5772f6361   Stefan Achatz   HID: roccat: Intr...
53
  			&control, sizeof(struct pyra_control));
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
54
55
56
57
  }
  
  static int pyra_receive_control_status(struct usb_device *usb_dev)
  {
5772f6361   Stefan Achatz   HID: roccat: Intr...
58
  	int retval;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
59
60
61
62
  	struct pyra_control control;
  
  	do {
  		msleep(10);
1edd5b42a   Stefan Achatz   HID: roccat: corr...
63
  		retval = roccat_common_receive(usb_dev, PYRA_COMMAND_CONTROL,
5772f6361   Stefan Achatz   HID: roccat: Intr...
64
  				&control, sizeof(struct pyra_control));
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
65
66
  
  		/* requested too early, try again */
5772f6361   Stefan Achatz   HID: roccat: Intr...
67
  	} while (retval == -EPROTO);
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
68

5772f6361   Stefan Achatz   HID: roccat: Intr...
69
  	if (!retval && control.command == PYRA_COMMAND_CONTROL &&
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
70
71
  			control.request == PYRA_CONTROL_REQUEST_STATUS &&
  			control.value == 1)
5772f6361   Stefan Achatz   HID: roccat: Intr...
72
  		return 0;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
73
  	else {
4291ee305   Joe Perches   HID: Add and use ...
74
75
76
  		hid_err(usb_dev, "receive control status: unknown response 0x%x 0x%x
  ",
  			control.request, control.value);
5772f6361   Stefan Achatz   HID: roccat: Intr...
77
  		return retval ? retval : -EINVAL;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
78
79
80
81
82
83
84
  	}
  }
  
  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 ...
85
86
  	retval = pyra_send_control(usb_dev, number,
  			PYRA_CONTROL_REQUEST_PROFILE_SETTINGS);
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
87
88
  	if (retval)
  		return retval;
1edd5b42a   Stefan Achatz   HID: roccat: corr...
89
  	return roccat_common_receive(usb_dev, PYRA_COMMAND_PROFILE_SETTINGS,
5772f6361   Stefan Achatz   HID: roccat: Intr...
90
  			buf, sizeof(struct pyra_profile_settings));
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
91
92
93
94
95
96
  }
  
  static int pyra_get_profile_buttons(struct usb_device *usb_dev,
  		struct pyra_profile_buttons *buf, int number)
  {
  	int retval;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
97
98
  	retval = pyra_send_control(usb_dev, number,
  			PYRA_CONTROL_REQUEST_PROFILE_BUTTONS);
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
99
100
  	if (retval)
  		return retval;
1edd5b42a   Stefan Achatz   HID: roccat: corr...
101
  	return roccat_common_receive(usb_dev, PYRA_COMMAND_PROFILE_BUTTONS,
5772f6361   Stefan Achatz   HID: roccat: Intr...
102
  			buf, sizeof(struct pyra_profile_buttons));
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
103
104
105
106
107
  }
  
  static int pyra_get_settings(struct usb_device *usb_dev,
  		struct pyra_settings *buf)
  {
1edd5b42a   Stefan Achatz   HID: roccat: corr...
108
  	return roccat_common_receive(usb_dev, PYRA_COMMAND_SETTINGS,
5772f6361   Stefan Achatz   HID: roccat: Intr...
109
  			buf, sizeof(struct pyra_settings));
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
110
111
112
113
  }
  
  static int pyra_get_info(struct usb_device *usb_dev, struct pyra_info *buf)
  {
1edd5b42a   Stefan Achatz   HID: roccat: corr...
114
  	return roccat_common_receive(usb_dev, PYRA_COMMAND_INFO,
5772f6361   Stefan Achatz   HID: roccat: Intr...
115
116
117
118
119
120
121
122
123
124
125
  			buf, sizeof(struct pyra_info));
  }
  
  static int pyra_send(struct usb_device *usb_dev, uint command,
  		void const *buf, uint size)
  {
  	int retval;
  	retval = roccat_common_send(usb_dev, command, buf, size);
  	if (retval)
  		return retval;
  	return pyra_receive_control_status(usb_dev);
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
126
127
128
129
130
  }
  
  static int pyra_set_profile_settings(struct usb_device *usb_dev,
  		struct pyra_profile_settings const *settings)
  {
1edd5b42a   Stefan Achatz   HID: roccat: corr...
131
  	return pyra_send(usb_dev, PYRA_COMMAND_PROFILE_SETTINGS, settings,
5772f6361   Stefan Achatz   HID: roccat: Intr...
132
  			sizeof(struct pyra_profile_settings));
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
133
134
135
136
137
  }
  
  static int pyra_set_profile_buttons(struct usb_device *usb_dev,
  		struct pyra_profile_buttons const *buttons)
  {
1edd5b42a   Stefan Achatz   HID: roccat: corr...
138
  	return pyra_send(usb_dev, PYRA_COMMAND_PROFILE_BUTTONS, buttons,
5772f6361   Stefan Achatz   HID: roccat: Intr...
139
  			sizeof(struct pyra_profile_buttons));
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
140
141
142
143
144
  }
  
  static int pyra_set_settings(struct usb_device *usb_dev,
  		struct pyra_settings const *settings)
  {
1edd5b42a   Stefan Achatz   HID: roccat: corr...
145
  	return pyra_send(usb_dev, PYRA_COMMAND_SETTINGS, settings,
5772f6361   Stefan Achatz   HID: roccat: Intr...
146
  			sizeof(struct pyra_settings));
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
147
148
149
150
  }
  
  static ssize_t pyra_sysfs_read_profilex_settings(struct file *fp,
  		struct kobject *kobj, struct bin_attribute *attr, char *buf,
14a057f80   Stefan Achatz   HID: roccat: redu...
151
  		loff_t off, size_t count)
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
152
  {
5012aada5   Stefan Achatz   HID: roccat: use ...
153
154
  	struct device *dev =
  			container_of(kobj, struct device, kobj)->parent->parent;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
155
156
157
158
159
160
161
162
163
  	struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
  
  	if (off >= sizeof(struct pyra_profile_settings))
  		return 0;
  
  	if (off + count > sizeof(struct pyra_profile_settings))
  		count = sizeof(struct pyra_profile_settings) - off;
  
  	mutex_lock(&pyra->pyra_lock);
14a057f80   Stefan Achatz   HID: roccat: redu...
164
  	memcpy(buf, ((char const *)&pyra->profile_settings[*(uint *)(attr->private)]) + off,
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
165
166
167
168
169
  			count);
  	mutex_unlock(&pyra->pyra_lock);
  
  	return count;
  }
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
170
171
  static ssize_t pyra_sysfs_read_profilex_buttons(struct file *fp,
  		struct kobject *kobj, struct bin_attribute *attr, char *buf,
14a057f80   Stefan Achatz   HID: roccat: redu...
172
  		loff_t off, size_t count)
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
173
  {
5012aada5   Stefan Achatz   HID: roccat: use ...
174
175
  	struct device *dev =
  			container_of(kobj, struct device, kobj)->parent->parent;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
176
177
178
179
180
181
182
183
184
  	struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
  
  	if (off >= sizeof(struct pyra_profile_buttons))
  		return 0;
  
  	if (off + count > sizeof(struct pyra_profile_buttons))
  		count = sizeof(struct pyra_profile_buttons) - off;
  
  	mutex_lock(&pyra->pyra_lock);
14a057f80   Stefan Achatz   HID: roccat: redu...
185
  	memcpy(buf, ((char const *)&pyra->profile_buttons[*(uint *)(attr->private)]) + off,
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
186
187
188
189
190
  			count);
  	mutex_unlock(&pyra->pyra_lock);
  
  	return count;
  }
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
191
192
193
194
  static ssize_t pyra_sysfs_write_profile_settings(struct file *fp,
  		struct kobject *kobj, struct bin_attribute *attr, char *buf,
  		loff_t off, size_t count)
  {
5012aada5   Stefan Achatz   HID: roccat: use ...
195
196
  	struct device *dev =
  			container_of(kobj, struct device, kobj)->parent->parent;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
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;
  	int difference;
  	int profile_number;
  	struct pyra_profile_settings *profile_settings;
  
  	if (off != 0 || count != sizeof(struct pyra_profile_settings))
  		return -EINVAL;
  
  	profile_number = ((struct pyra_profile_settings const *)buf)->number;
  	profile_settings = &pyra->profile_settings[profile_number];
  
  	mutex_lock(&pyra->pyra_lock);
  	difference = memcmp(buf, profile_settings,
  			sizeof(struct pyra_profile_settings));
  	if (difference) {
  		retval = pyra_set_profile_settings(usb_dev,
  				(struct pyra_profile_settings const *)buf);
  		if (!retval)
  			memcpy(profile_settings, buf,
  					sizeof(struct pyra_profile_settings));
  	}
  	mutex_unlock(&pyra->pyra_lock);
  
  	if (retval)
  		return retval;
  
  	return sizeof(struct pyra_profile_settings);
  }
  
  static ssize_t pyra_sysfs_write_profile_buttons(struct file *fp,
  		struct kobject *kobj, struct bin_attribute *attr, char *buf,
  		loff_t off, size_t count)
  {
5012aada5   Stefan Achatz   HID: roccat: use ...
232
233
  	struct device *dev =
  			container_of(kobj, struct device, kobj)->parent->parent;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
  	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;
  	int difference;
  	int profile_number;
  	struct pyra_profile_buttons *profile_buttons;
  
  	if (off != 0 || count != sizeof(struct pyra_profile_buttons))
  		return -EINVAL;
  
  	profile_number = ((struct pyra_profile_buttons const *)buf)->number;
  	profile_buttons = &pyra->profile_buttons[profile_number];
  
  	mutex_lock(&pyra->pyra_lock);
  	difference = memcmp(buf, profile_buttons,
  			sizeof(struct pyra_profile_buttons));
  	if (difference) {
  		retval = pyra_set_profile_buttons(usb_dev,
  				(struct pyra_profile_buttons const *)buf);
  		if (!retval)
  			memcpy(profile_buttons, buf,
  					sizeof(struct pyra_profile_buttons));
  	}
  	mutex_unlock(&pyra->pyra_lock);
  
  	if (retval)
  		return retval;
  
  	return sizeof(struct pyra_profile_buttons);
  }
  
  static ssize_t pyra_sysfs_read_settings(struct file *fp,
  		struct kobject *kobj, struct bin_attribute *attr, char *buf,
  		loff_t off, size_t count)
  {
5012aada5   Stefan Achatz   HID: roccat: use ...
269
270
  	struct device *dev =
  			container_of(kobj, struct device, kobj)->parent->parent;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
  	struct pyra_device *pyra = hid_get_drvdata(dev_get_drvdata(dev));
  
  	if (off >= sizeof(struct pyra_settings))
  		return 0;
  
  	if (off + count > sizeof(struct pyra_settings))
  		count = sizeof(struct pyra_settings) - off;
  
  	mutex_lock(&pyra->pyra_lock);
  	memcpy(buf, ((char const *)&pyra->settings) + off, count);
  	mutex_unlock(&pyra->pyra_lock);
  
  	return count;
  }
  
  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)
  {
5012aada5   Stefan Achatz   HID: roccat: use ...
290
291
  	struct device *dev =
  			container_of(kobj, struct device, kobj)->parent->parent;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
292
293
294
295
  	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;
  	int difference;
dc186b661   Stefan Achatz   HID: roccat: Pyra...
296
  	struct pyra_roccat_report roccat_report;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
297
298
299
300
301
302
303
304
305
  
  	if (off != 0 || count != sizeof(struct pyra_settings))
  		return -EINVAL;
  
  	mutex_lock(&pyra->pyra_lock);
  	difference = memcmp(buf, &pyra->settings, sizeof(struct pyra_settings));
  	if (difference) {
  		retval = pyra_set_settings(usb_dev,
  				(struct pyra_settings const *)buf);
dc186b661   Stefan Achatz   HID: roccat: Pyra...
306
307
308
309
  		if (retval) {
  			mutex_unlock(&pyra->pyra_lock);
  			return retval;
  		}
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
310

dc186b661   Stefan Achatz   HID: roccat: Pyra...
311
312
  		memcpy(&pyra->settings, buf,
  				sizeof(struct pyra_settings));
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
313

dc186b661   Stefan Achatz   HID: roccat: Pyra...
314
  		profile_activated(pyra, pyra->settings.startup_profile);
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
315

dc186b661   Stefan Achatz   HID: roccat: Pyra...
316
317
318
319
320
321
322
  		roccat_report.type = PYRA_MOUSE_EVENT_BUTTON_TYPE_PROFILE_2;
  		roccat_report.value = pyra->settings.startup_profile + 1;
  		roccat_report.key = 0;
  		roccat_report_event(pyra->chrdev_minor,
  				(uint8_t const *)&roccat_report);
  	}
  	mutex_unlock(&pyra->pyra_lock);
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
323
324
325
326
327
328
329
  	return sizeof(struct pyra_settings);
  }
  
  
  static ssize_t pyra_sysfs_show_actual_cpi(struct device *dev,
  		struct device_attribute *attr, char *buf)
  {
5012aada5   Stefan Achatz   HID: roccat: use ...
330
331
  	struct pyra_device *pyra =
  			hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
332
333
334
335
336
337
338
  	return snprintf(buf, PAGE_SIZE, "%d
  ", pyra->actual_cpi);
  }
  
  static ssize_t pyra_sysfs_show_actual_profile(struct device *dev,
  		struct device_attribute *attr, char *buf)
  {
5012aada5   Stefan Achatz   HID: roccat: use ...
339
340
  	struct pyra_device *pyra =
  			hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
341
342
343
344
345
346
347
  	return snprintf(buf, PAGE_SIZE, "%d
  ", pyra->actual_profile);
  }
  
  static ssize_t pyra_sysfs_show_firmware_version(struct device *dev,
  		struct device_attribute *attr, char *buf)
  {
5012aada5   Stefan Achatz   HID: roccat: use ...
348
349
  	struct pyra_device *pyra =
  			hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
350
351
352
353
354
355
356
  	return snprintf(buf, PAGE_SIZE, "%d
  ", pyra->firmware_version);
  }
  
  static ssize_t pyra_sysfs_show_startup_profile(struct device *dev,
  		struct device_attribute *attr, char *buf)
  {
5012aada5   Stefan Achatz   HID: roccat: use ...
357
358
  	struct pyra_device *pyra =
  			hid_get_drvdata(dev_get_drvdata(dev->parent->parent));
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
359
360
361
  	return snprintf(buf, PAGE_SIZE, "%d
  ", pyra->settings.startup_profile);
  }
5012aada5   Stefan Achatz   HID: roccat: use ...
362
363
364
365
366
367
368
369
  static struct device_attribute pyra_attributes[] = {
  	__ATTR(actual_cpi, 0440, pyra_sysfs_show_actual_cpi, NULL),
  	__ATTR(actual_profile, 0440, pyra_sysfs_show_actual_profile, NULL),
  	__ATTR(firmware_version, 0440,
  			pyra_sysfs_show_firmware_version, NULL),
  	__ATTR(startup_profile, 0440,
  			pyra_sysfs_show_startup_profile, NULL),
  	__ATTR_NULL
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
370
  };
5012aada5   Stefan Achatz   HID: roccat: use ...
371
372
  static struct bin_attribute pyra_bin_attributes[] = {
  	{
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
373
374
375
  		.attr = { .name = "profile_settings", .mode = 0220 },
  		.size = sizeof(struct pyra_profile_settings),
  		.write = pyra_sysfs_write_profile_settings
5012aada5   Stefan Achatz   HID: roccat: use ...
376
377
  	},
  	{
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
378
379
  		.attr = { .name = "profile1_settings", .mode = 0440 },
  		.size = sizeof(struct pyra_profile_settings),
14a057f80   Stefan Achatz   HID: roccat: redu...
380
381
  		.read = pyra_sysfs_read_profilex_settings,
  		.private = &profile_numbers[0]
5012aada5   Stefan Achatz   HID: roccat: use ...
382
383
  	},
  	{
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
384
385
  		.attr = { .name = "profile2_settings", .mode = 0440 },
  		.size = sizeof(struct pyra_profile_settings),
14a057f80   Stefan Achatz   HID: roccat: redu...
386
387
  		.read = pyra_sysfs_read_profilex_settings,
  		.private = &profile_numbers[1]
5012aada5   Stefan Achatz   HID: roccat: use ...
388
389
  	},
  	{
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
390
391
  		.attr = { .name = "profile3_settings", .mode = 0440 },
  		.size = sizeof(struct pyra_profile_settings),
14a057f80   Stefan Achatz   HID: roccat: redu...
392
393
  		.read = pyra_sysfs_read_profilex_settings,
  		.private = &profile_numbers[2]
5012aada5   Stefan Achatz   HID: roccat: use ...
394
395
  	},
  	{
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
396
397
  		.attr = { .name = "profile4_settings", .mode = 0440 },
  		.size = sizeof(struct pyra_profile_settings),
14a057f80   Stefan Achatz   HID: roccat: redu...
398
399
  		.read = pyra_sysfs_read_profilex_settings,
  		.private = &profile_numbers[3]
5012aada5   Stefan Achatz   HID: roccat: use ...
400
401
  	},
  	{
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
402
403
  		.attr = { .name = "profile5_settings", .mode = 0440 },
  		.size = sizeof(struct pyra_profile_settings),
14a057f80   Stefan Achatz   HID: roccat: redu...
404
405
  		.read = pyra_sysfs_read_profilex_settings,
  		.private = &profile_numbers[4]
5012aada5   Stefan Achatz   HID: roccat: use ...
406
407
  	},
  	{
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
408
409
410
  		.attr = { .name = "profile_buttons", .mode = 0220 },
  		.size = sizeof(struct pyra_profile_buttons),
  		.write = pyra_sysfs_write_profile_buttons
5012aada5   Stefan Achatz   HID: roccat: use ...
411
412
  	},
  	{
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
413
414
  		.attr = { .name = "profile1_buttons", .mode = 0440 },
  		.size = sizeof(struct pyra_profile_buttons),
14a057f80   Stefan Achatz   HID: roccat: redu...
415
416
  		.read = pyra_sysfs_read_profilex_buttons,
  		.private = &profile_numbers[0]
5012aada5   Stefan Achatz   HID: roccat: use ...
417
418
  	},
  	{
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
419
420
  		.attr = { .name = "profile2_buttons", .mode = 0440 },
  		.size = sizeof(struct pyra_profile_buttons),
14a057f80   Stefan Achatz   HID: roccat: redu...
421
422
  		.read = pyra_sysfs_read_profilex_buttons,
  		.private = &profile_numbers[1]
5012aada5   Stefan Achatz   HID: roccat: use ...
423
424
  	},
  	{
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
425
426
  		.attr = { .name = "profile3_buttons", .mode = 0440 },
  		.size = sizeof(struct pyra_profile_buttons),
14a057f80   Stefan Achatz   HID: roccat: redu...
427
428
  		.read = pyra_sysfs_read_profilex_buttons,
  		.private = &profile_numbers[2]
5012aada5   Stefan Achatz   HID: roccat: use ...
429
430
  	},
  	{
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
431
432
  		.attr = { .name = "profile4_buttons", .mode = 0440 },
  		.size = sizeof(struct pyra_profile_buttons),
14a057f80   Stefan Achatz   HID: roccat: redu...
433
434
  		.read = pyra_sysfs_read_profilex_buttons,
  		.private = &profile_numbers[3]
5012aada5   Stefan Achatz   HID: roccat: use ...
435
436
  	},
  	{
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
437
438
  		.attr = { .name = "profile5_buttons", .mode = 0440 },
  		.size = sizeof(struct pyra_profile_buttons),
14a057f80   Stefan Achatz   HID: roccat: redu...
439
440
  		.read = pyra_sysfs_read_profilex_buttons,
  		.private = &profile_numbers[4]
5012aada5   Stefan Achatz   HID: roccat: use ...
441
442
  	},
  	{
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
443
444
445
446
  		.attr = { .name = "settings", .mode = 0660 },
  		.size = sizeof(struct pyra_settings),
  		.read = pyra_sysfs_read_settings,
  		.write = pyra_sysfs_write_settings
5012aada5   Stefan Achatz   HID: roccat: use ...
447
448
  	},
  	__ATTR_NULL
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
449
  };
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
450
451
452
  static int pyra_init_pyra_device_struct(struct usb_device *usb_dev,
  		struct pyra_device *pyra)
  {
5772f6361   Stefan Achatz   HID: roccat: Intr...
453
  	struct pyra_info info;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
454
455
456
  	int retval, i;
  
  	mutex_init(&pyra->pyra_lock);
5772f6361   Stefan Achatz   HID: roccat: Intr...
457
458
  	retval = pyra_get_info(usb_dev, &info);
  	if (retval)
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
459
  		return retval;
5772f6361   Stefan Achatz   HID: roccat: Intr...
460
461
  
  	pyra->firmware_version = info.firmware_version;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
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
  
  	retval = pyra_get_settings(usb_dev, &pyra->settings);
  	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;
  
  		retval = pyra_get_profile_buttons(usb_dev,
  				&pyra->profile_buttons[i], i);
  		if (retval)
  			return retval;
  	}
  
  	profile_activated(pyra, pyra->settings.startup_profile);
  
  	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 ...
496
497
  			hid_err(hdev, "can't alloc device descriptor
  ");
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
498
499
500
501
502
503
  			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 ...
504
505
  			hid_err(hdev, "couldn't init struct pyra_device
  ");
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
506
507
  			goto exit_free;
  		}
8211e4600   Stefan Achatz   HID: roccat: Add ...
508
509
  		retval = roccat_connect(pyra_class, hdev,
  				sizeof(struct pyra_roccat_report));
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
510
  		if (retval < 0) {
4291ee305   Joe Perches   HID: Add and use ...
511
512
  			hid_err(hdev, "couldn't init char dev
  ");
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
513
514
515
516
  		} else {
  			pyra->chrdev_minor = retval;
  			pyra->roccat_claimed = 1;
  		}
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
  	} 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 ...
534
535
536
537
538
539
540
541
542
543
544
545
546
  		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 ...
547
548
  		hid_err(hdev, "parse failed
  ");
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
549
550
551
552
553
  		goto exit;
  	}
  
  	retval = hid_hw_start(hdev, HID_CONNECT_DEFAULT);
  	if (retval) {
4291ee305   Joe Perches   HID: Add and use ...
554
555
  		hid_err(hdev, "hw start failed
  ");
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
556
557
558
559
560
  		goto exit;
  	}
  
  	retval = pyra_init_specials(hdev);
  	if (retval) {
4291ee305   Joe Perches   HID: Add and use ...
561
562
  		hid_err(hdev, "couldn't install mouse
  ");
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
  		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 ...
617
  				(uint8_t const *)&roccat_report);
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
618
619
620
621
622
623
624
  		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...
625
626
627
628
629
  			/*
  			 * pyra reports profile numbers with range 1-5.
  			 * Keeping this behaviour.
  			 */
  			roccat_report.value = pyra->actual_profile + 1;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
630
  			roccat_report_event(pyra->chrdev_minor,
8211e4600   Stefan Achatz   HID: roccat: Add ...
631
  					(uint8_t const *)&roccat_report);
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
632
633
634
635
636
637
638
639
640
641
642
643
644
645
  		}
  		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 ...
646
647
  	if (pyra == NULL)
  		return 0;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
648
649
650
651
652
653
654
655
656
657
658
  	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 ...
659
660
  	{ HID_USB_DEVICE(USB_VENDOR_ID_ROCCAT,
  			USB_DEVICE_ID_ROCCAT_PYRA_WIRELESS) },
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
  	{ }
  };
  
  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 ...
676
677
678
679
680
681
682
683
684
685
686
687
688
  	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);
  	pyra_class->dev_attrs = pyra_attributes;
  	pyra_class->dev_bin_attrs = pyra_bin_attributes;
  
  	retval = hid_register_driver(&pyra_driver);
  	if (retval)
  		class_destroy(pyra_class);
  	return retval;
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
689
690
691
692
693
  }
  
  static void __exit pyra_exit(void)
  {
  	hid_unregister_driver(&pyra_driver);
74b643dac   Stefan Achatz   HID: roccat: Fix ...
694
  	class_destroy(pyra_class);
cb7cf3da0   Stefan Achatz   HID: roccat: add ...
695
696
697
698
699
700
701
702
  }
  
  module_init(pyra_init);
  module_exit(pyra_exit);
  
  MODULE_AUTHOR("Stefan Achatz");
  MODULE_DESCRIPTION("USB Roccat Pyra driver");
  MODULE_LICENSE("GPL v2");