xref: /OK3568_Linux_fs/kernel/drivers/hid/hid-roccat.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * Roccat driver for Linux
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright (c) 2010 Stefan Achatz <erazor_de@users.sourceforge.net>
6*4882a593Smuzhiyun  */
7*4882a593Smuzhiyun 
8*4882a593Smuzhiyun /*
9*4882a593Smuzhiyun  */
10*4882a593Smuzhiyun 
11*4882a593Smuzhiyun /*
12*4882a593Smuzhiyun  * Module roccat is a char device used to report special events of roccat
13*4882a593Smuzhiyun  * hardware to userland. These events include requests for on-screen-display of
14*4882a593Smuzhiyun  * profile or dpi settings or requests for execution of macro sequences that are
15*4882a593Smuzhiyun  * not stored in device. The information in these events depends on hid device
16*4882a593Smuzhiyun  * implementation and contains data that is not available in a single hid event
17*4882a593Smuzhiyun  * or else hidraw could have been used.
18*4882a593Smuzhiyun  * It is inspired by hidraw, but uses only one circular buffer for all readers.
19*4882a593Smuzhiyun  */
20*4882a593Smuzhiyun 
21*4882a593Smuzhiyun #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
22*4882a593Smuzhiyun 
23*4882a593Smuzhiyun #include <linux/cdev.h>
24*4882a593Smuzhiyun #include <linux/poll.h>
25*4882a593Smuzhiyun #include <linux/sched/signal.h>
26*4882a593Smuzhiyun #include <linux/hid-roccat.h>
27*4882a593Smuzhiyun #include <linux/module.h>
28*4882a593Smuzhiyun 
29*4882a593Smuzhiyun #define ROCCAT_FIRST_MINOR 0
30*4882a593Smuzhiyun #define ROCCAT_MAX_DEVICES 8
31*4882a593Smuzhiyun 
32*4882a593Smuzhiyun /* should be a power of 2 for performance reason */
33*4882a593Smuzhiyun #define ROCCAT_CBUF_SIZE 16
34*4882a593Smuzhiyun 
35*4882a593Smuzhiyun struct roccat_report {
36*4882a593Smuzhiyun 	uint8_t *value;
37*4882a593Smuzhiyun };
38*4882a593Smuzhiyun 
39*4882a593Smuzhiyun struct roccat_device {
40*4882a593Smuzhiyun 	unsigned int minor;
41*4882a593Smuzhiyun 	int report_size;
42*4882a593Smuzhiyun 	int open;
43*4882a593Smuzhiyun 	int exist;
44*4882a593Smuzhiyun 	wait_queue_head_t wait;
45*4882a593Smuzhiyun 	struct device *dev;
46*4882a593Smuzhiyun 	struct hid_device *hid;
47*4882a593Smuzhiyun 	struct list_head readers;
48*4882a593Smuzhiyun 	/* protects modifications of readers list */
49*4882a593Smuzhiyun 	struct mutex readers_lock;
50*4882a593Smuzhiyun 
51*4882a593Smuzhiyun 	/*
52*4882a593Smuzhiyun 	 * circular_buffer has one writer and multiple readers with their own
53*4882a593Smuzhiyun 	 * read pointers
54*4882a593Smuzhiyun 	 */
55*4882a593Smuzhiyun 	struct roccat_report cbuf[ROCCAT_CBUF_SIZE];
56*4882a593Smuzhiyun 	int cbuf_end;
57*4882a593Smuzhiyun 	struct mutex cbuf_lock;
58*4882a593Smuzhiyun };
59*4882a593Smuzhiyun 
60*4882a593Smuzhiyun struct roccat_reader {
61*4882a593Smuzhiyun 	struct list_head node;
62*4882a593Smuzhiyun 	struct roccat_device *device;
63*4882a593Smuzhiyun 	int cbuf_start;
64*4882a593Smuzhiyun };
65*4882a593Smuzhiyun 
66*4882a593Smuzhiyun static int roccat_major;
67*4882a593Smuzhiyun static struct cdev roccat_cdev;
68*4882a593Smuzhiyun 
69*4882a593Smuzhiyun static struct roccat_device *devices[ROCCAT_MAX_DEVICES];
70*4882a593Smuzhiyun /* protects modifications of devices array */
71*4882a593Smuzhiyun static DEFINE_MUTEX(devices_lock);
72*4882a593Smuzhiyun 
roccat_read(struct file * file,char __user * buffer,size_t count,loff_t * ppos)73*4882a593Smuzhiyun static ssize_t roccat_read(struct file *file, char __user *buffer,
74*4882a593Smuzhiyun 		size_t count, loff_t *ppos)
75*4882a593Smuzhiyun {
76*4882a593Smuzhiyun 	struct roccat_reader *reader = file->private_data;
77*4882a593Smuzhiyun 	struct roccat_device *device = reader->device;
78*4882a593Smuzhiyun 	struct roccat_report *report;
79*4882a593Smuzhiyun 	ssize_t retval = 0, len;
80*4882a593Smuzhiyun 	DECLARE_WAITQUEUE(wait, current);
81*4882a593Smuzhiyun 
82*4882a593Smuzhiyun 	mutex_lock(&device->cbuf_lock);
83*4882a593Smuzhiyun 
84*4882a593Smuzhiyun 	/* no data? */
85*4882a593Smuzhiyun 	if (reader->cbuf_start == device->cbuf_end) {
86*4882a593Smuzhiyun 		add_wait_queue(&device->wait, &wait);
87*4882a593Smuzhiyun 		set_current_state(TASK_INTERRUPTIBLE);
88*4882a593Smuzhiyun 
89*4882a593Smuzhiyun 		/* wait for data */
90*4882a593Smuzhiyun 		while (reader->cbuf_start == device->cbuf_end) {
91*4882a593Smuzhiyun 			if (file->f_flags & O_NONBLOCK) {
92*4882a593Smuzhiyun 				retval = -EAGAIN;
93*4882a593Smuzhiyun 				break;
94*4882a593Smuzhiyun 			}
95*4882a593Smuzhiyun 			if (signal_pending(current)) {
96*4882a593Smuzhiyun 				retval = -ERESTARTSYS;
97*4882a593Smuzhiyun 				break;
98*4882a593Smuzhiyun 			}
99*4882a593Smuzhiyun 			if (!device->exist) {
100*4882a593Smuzhiyun 				retval = -EIO;
101*4882a593Smuzhiyun 				break;
102*4882a593Smuzhiyun 			}
103*4882a593Smuzhiyun 
104*4882a593Smuzhiyun 			mutex_unlock(&device->cbuf_lock);
105*4882a593Smuzhiyun 			schedule();
106*4882a593Smuzhiyun 			mutex_lock(&device->cbuf_lock);
107*4882a593Smuzhiyun 			set_current_state(TASK_INTERRUPTIBLE);
108*4882a593Smuzhiyun 		}
109*4882a593Smuzhiyun 
110*4882a593Smuzhiyun 		set_current_state(TASK_RUNNING);
111*4882a593Smuzhiyun 		remove_wait_queue(&device->wait, &wait);
112*4882a593Smuzhiyun 	}
113*4882a593Smuzhiyun 
114*4882a593Smuzhiyun 	/* here we either have data or a reason to return if retval is set */
115*4882a593Smuzhiyun 	if (retval)
116*4882a593Smuzhiyun 		goto exit_unlock;
117*4882a593Smuzhiyun 
118*4882a593Smuzhiyun 	report = &device->cbuf[reader->cbuf_start];
119*4882a593Smuzhiyun 	/*
120*4882a593Smuzhiyun 	 * If report is larger than requested amount of data, rest of report
121*4882a593Smuzhiyun 	 * is lost!
122*4882a593Smuzhiyun 	 */
123*4882a593Smuzhiyun 	len = device->report_size > count ? count : device->report_size;
124*4882a593Smuzhiyun 
125*4882a593Smuzhiyun 	if (copy_to_user(buffer, report->value, len)) {
126*4882a593Smuzhiyun 		retval = -EFAULT;
127*4882a593Smuzhiyun 		goto exit_unlock;
128*4882a593Smuzhiyun 	}
129*4882a593Smuzhiyun 	retval += len;
130*4882a593Smuzhiyun 	reader->cbuf_start = (reader->cbuf_start + 1) % ROCCAT_CBUF_SIZE;
131*4882a593Smuzhiyun 
132*4882a593Smuzhiyun exit_unlock:
133*4882a593Smuzhiyun 	mutex_unlock(&device->cbuf_lock);
134*4882a593Smuzhiyun 	return retval;
135*4882a593Smuzhiyun }
136*4882a593Smuzhiyun 
roccat_poll(struct file * file,poll_table * wait)137*4882a593Smuzhiyun static __poll_t roccat_poll(struct file *file, poll_table *wait)
138*4882a593Smuzhiyun {
139*4882a593Smuzhiyun 	struct roccat_reader *reader = file->private_data;
140*4882a593Smuzhiyun 	poll_wait(file, &reader->device->wait, wait);
141*4882a593Smuzhiyun 	if (reader->cbuf_start != reader->device->cbuf_end)
142*4882a593Smuzhiyun 		return EPOLLIN | EPOLLRDNORM;
143*4882a593Smuzhiyun 	if (!reader->device->exist)
144*4882a593Smuzhiyun 		return EPOLLERR | EPOLLHUP;
145*4882a593Smuzhiyun 	return 0;
146*4882a593Smuzhiyun }
147*4882a593Smuzhiyun 
roccat_open(struct inode * inode,struct file * file)148*4882a593Smuzhiyun static int roccat_open(struct inode *inode, struct file *file)
149*4882a593Smuzhiyun {
150*4882a593Smuzhiyun 	unsigned int minor = iminor(inode);
151*4882a593Smuzhiyun 	struct roccat_reader *reader;
152*4882a593Smuzhiyun 	struct roccat_device *device;
153*4882a593Smuzhiyun 	int error = 0;
154*4882a593Smuzhiyun 
155*4882a593Smuzhiyun 	reader = kzalloc(sizeof(struct roccat_reader), GFP_KERNEL);
156*4882a593Smuzhiyun 	if (!reader)
157*4882a593Smuzhiyun 		return -ENOMEM;
158*4882a593Smuzhiyun 
159*4882a593Smuzhiyun 	mutex_lock(&devices_lock);
160*4882a593Smuzhiyun 
161*4882a593Smuzhiyun 	device = devices[minor];
162*4882a593Smuzhiyun 
163*4882a593Smuzhiyun 	if (!device) {
164*4882a593Smuzhiyun 		pr_emerg("roccat device with minor %d doesn't exist\n", minor);
165*4882a593Smuzhiyun 		error = -ENODEV;
166*4882a593Smuzhiyun 		goto exit_err_devices;
167*4882a593Smuzhiyun 	}
168*4882a593Smuzhiyun 
169*4882a593Smuzhiyun 	mutex_lock(&device->readers_lock);
170*4882a593Smuzhiyun 
171*4882a593Smuzhiyun 	if (!device->open++) {
172*4882a593Smuzhiyun 		/* power on device on adding first reader */
173*4882a593Smuzhiyun 		error = hid_hw_power(device->hid, PM_HINT_FULLON);
174*4882a593Smuzhiyun 		if (error < 0) {
175*4882a593Smuzhiyun 			--device->open;
176*4882a593Smuzhiyun 			goto exit_err_readers;
177*4882a593Smuzhiyun 		}
178*4882a593Smuzhiyun 
179*4882a593Smuzhiyun 		error = hid_hw_open(device->hid);
180*4882a593Smuzhiyun 		if (error < 0) {
181*4882a593Smuzhiyun 			hid_hw_power(device->hid, PM_HINT_NORMAL);
182*4882a593Smuzhiyun 			--device->open;
183*4882a593Smuzhiyun 			goto exit_err_readers;
184*4882a593Smuzhiyun 		}
185*4882a593Smuzhiyun 	}
186*4882a593Smuzhiyun 
187*4882a593Smuzhiyun 	reader->device = device;
188*4882a593Smuzhiyun 	/* new reader doesn't get old events */
189*4882a593Smuzhiyun 	reader->cbuf_start = device->cbuf_end;
190*4882a593Smuzhiyun 
191*4882a593Smuzhiyun 	list_add_tail(&reader->node, &device->readers);
192*4882a593Smuzhiyun 	file->private_data = reader;
193*4882a593Smuzhiyun 
194*4882a593Smuzhiyun exit_err_readers:
195*4882a593Smuzhiyun 	mutex_unlock(&device->readers_lock);
196*4882a593Smuzhiyun exit_err_devices:
197*4882a593Smuzhiyun 	mutex_unlock(&devices_lock);
198*4882a593Smuzhiyun 	if (error)
199*4882a593Smuzhiyun 		kfree(reader);
200*4882a593Smuzhiyun 	return error;
201*4882a593Smuzhiyun }
202*4882a593Smuzhiyun 
roccat_release(struct inode * inode,struct file * file)203*4882a593Smuzhiyun static int roccat_release(struct inode *inode, struct file *file)
204*4882a593Smuzhiyun {
205*4882a593Smuzhiyun 	unsigned int minor = iminor(inode);
206*4882a593Smuzhiyun 	struct roccat_reader *reader = file->private_data;
207*4882a593Smuzhiyun 	struct roccat_device *device;
208*4882a593Smuzhiyun 
209*4882a593Smuzhiyun 	mutex_lock(&devices_lock);
210*4882a593Smuzhiyun 
211*4882a593Smuzhiyun 	device = devices[minor];
212*4882a593Smuzhiyun 	if (!device) {
213*4882a593Smuzhiyun 		mutex_unlock(&devices_lock);
214*4882a593Smuzhiyun 		pr_emerg("roccat device with minor %d doesn't exist\n", minor);
215*4882a593Smuzhiyun 		return -ENODEV;
216*4882a593Smuzhiyun 	}
217*4882a593Smuzhiyun 
218*4882a593Smuzhiyun 	mutex_lock(&device->readers_lock);
219*4882a593Smuzhiyun 	list_del(&reader->node);
220*4882a593Smuzhiyun 	mutex_unlock(&device->readers_lock);
221*4882a593Smuzhiyun 	kfree(reader);
222*4882a593Smuzhiyun 
223*4882a593Smuzhiyun 	if (!--device->open) {
224*4882a593Smuzhiyun 		/* removing last reader */
225*4882a593Smuzhiyun 		if (device->exist) {
226*4882a593Smuzhiyun 			hid_hw_power(device->hid, PM_HINT_NORMAL);
227*4882a593Smuzhiyun 			hid_hw_close(device->hid);
228*4882a593Smuzhiyun 		} else {
229*4882a593Smuzhiyun 			kfree(device);
230*4882a593Smuzhiyun 		}
231*4882a593Smuzhiyun 	}
232*4882a593Smuzhiyun 
233*4882a593Smuzhiyun 	mutex_unlock(&devices_lock);
234*4882a593Smuzhiyun 
235*4882a593Smuzhiyun 	return 0;
236*4882a593Smuzhiyun }
237*4882a593Smuzhiyun 
238*4882a593Smuzhiyun /*
239*4882a593Smuzhiyun  * roccat_report_event() - output data to readers
240*4882a593Smuzhiyun  * @minor: minor device number returned by roccat_connect()
241*4882a593Smuzhiyun  * @data: pointer to data
242*4882a593Smuzhiyun  *
243*4882a593Smuzhiyun  * Return value is zero on success, a negative error code on failure.
244*4882a593Smuzhiyun  *
245*4882a593Smuzhiyun  * This is called from interrupt handler.
246*4882a593Smuzhiyun  */
roccat_report_event(int minor,u8 const * data)247*4882a593Smuzhiyun int roccat_report_event(int minor, u8 const *data)
248*4882a593Smuzhiyun {
249*4882a593Smuzhiyun 	struct roccat_device *device;
250*4882a593Smuzhiyun 	struct roccat_reader *reader;
251*4882a593Smuzhiyun 	struct roccat_report *report;
252*4882a593Smuzhiyun 	uint8_t *new_value;
253*4882a593Smuzhiyun 
254*4882a593Smuzhiyun 	device = devices[minor];
255*4882a593Smuzhiyun 
256*4882a593Smuzhiyun 	new_value = kmemdup(data, device->report_size, GFP_ATOMIC);
257*4882a593Smuzhiyun 	if (!new_value)
258*4882a593Smuzhiyun 		return -ENOMEM;
259*4882a593Smuzhiyun 
260*4882a593Smuzhiyun 	mutex_lock(&device->cbuf_lock);
261*4882a593Smuzhiyun 
262*4882a593Smuzhiyun 	report = &device->cbuf[device->cbuf_end];
263*4882a593Smuzhiyun 
264*4882a593Smuzhiyun 	/* passing NULL is safe */
265*4882a593Smuzhiyun 	kfree(report->value);
266*4882a593Smuzhiyun 
267*4882a593Smuzhiyun 	report->value = new_value;
268*4882a593Smuzhiyun 	device->cbuf_end = (device->cbuf_end + 1) % ROCCAT_CBUF_SIZE;
269*4882a593Smuzhiyun 
270*4882a593Smuzhiyun 	list_for_each_entry(reader, &device->readers, node) {
271*4882a593Smuzhiyun 		/*
272*4882a593Smuzhiyun 		 * As we already inserted one element, the buffer can't be
273*4882a593Smuzhiyun 		 * empty. If start and end are equal, buffer is full and we
274*4882a593Smuzhiyun 		 * increase start, so that slow reader misses one event, but
275*4882a593Smuzhiyun 		 * gets the newer ones in the right order.
276*4882a593Smuzhiyun 		 */
277*4882a593Smuzhiyun 		if (reader->cbuf_start == device->cbuf_end)
278*4882a593Smuzhiyun 			reader->cbuf_start = (reader->cbuf_start + 1) % ROCCAT_CBUF_SIZE;
279*4882a593Smuzhiyun 	}
280*4882a593Smuzhiyun 
281*4882a593Smuzhiyun 	mutex_unlock(&device->cbuf_lock);
282*4882a593Smuzhiyun 
283*4882a593Smuzhiyun 	wake_up_interruptible(&device->wait);
284*4882a593Smuzhiyun 	return 0;
285*4882a593Smuzhiyun }
286*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(roccat_report_event);
287*4882a593Smuzhiyun 
288*4882a593Smuzhiyun /*
289*4882a593Smuzhiyun  * roccat_connect() - create a char device for special event output
290*4882a593Smuzhiyun  * @class: the class thats used to create the device. Meant to hold device
291*4882a593Smuzhiyun  * specific sysfs attributes.
292*4882a593Smuzhiyun  * @hid: the hid device the char device should be connected to.
293*4882a593Smuzhiyun  * @report_size: size of reports
294*4882a593Smuzhiyun  *
295*4882a593Smuzhiyun  * Return value is minor device number in Range [0, ROCCAT_MAX_DEVICES] on
296*4882a593Smuzhiyun  * success, a negative error code on failure.
297*4882a593Smuzhiyun  */
roccat_connect(struct class * klass,struct hid_device * hid,int report_size)298*4882a593Smuzhiyun int roccat_connect(struct class *klass, struct hid_device *hid, int report_size)
299*4882a593Smuzhiyun {
300*4882a593Smuzhiyun 	unsigned int minor;
301*4882a593Smuzhiyun 	struct roccat_device *device;
302*4882a593Smuzhiyun 	int temp;
303*4882a593Smuzhiyun 
304*4882a593Smuzhiyun 	device = kzalloc(sizeof(struct roccat_device), GFP_KERNEL);
305*4882a593Smuzhiyun 	if (!device)
306*4882a593Smuzhiyun 		return -ENOMEM;
307*4882a593Smuzhiyun 
308*4882a593Smuzhiyun 	mutex_lock(&devices_lock);
309*4882a593Smuzhiyun 
310*4882a593Smuzhiyun 	for (minor = 0; minor < ROCCAT_MAX_DEVICES; ++minor) {
311*4882a593Smuzhiyun 		if (devices[minor])
312*4882a593Smuzhiyun 			continue;
313*4882a593Smuzhiyun 		break;
314*4882a593Smuzhiyun 	}
315*4882a593Smuzhiyun 
316*4882a593Smuzhiyun 	if (minor < ROCCAT_MAX_DEVICES) {
317*4882a593Smuzhiyun 		devices[minor] = device;
318*4882a593Smuzhiyun 	} else {
319*4882a593Smuzhiyun 		mutex_unlock(&devices_lock);
320*4882a593Smuzhiyun 		kfree(device);
321*4882a593Smuzhiyun 		return -EINVAL;
322*4882a593Smuzhiyun 	}
323*4882a593Smuzhiyun 
324*4882a593Smuzhiyun 	device->dev = device_create(klass, &hid->dev,
325*4882a593Smuzhiyun 			MKDEV(roccat_major, minor), NULL,
326*4882a593Smuzhiyun 			"%s%s%d", "roccat", hid->driver->name, minor);
327*4882a593Smuzhiyun 
328*4882a593Smuzhiyun 	if (IS_ERR(device->dev)) {
329*4882a593Smuzhiyun 		devices[minor] = NULL;
330*4882a593Smuzhiyun 		mutex_unlock(&devices_lock);
331*4882a593Smuzhiyun 		temp = PTR_ERR(device->dev);
332*4882a593Smuzhiyun 		kfree(device);
333*4882a593Smuzhiyun 		return temp;
334*4882a593Smuzhiyun 	}
335*4882a593Smuzhiyun 
336*4882a593Smuzhiyun 	mutex_unlock(&devices_lock);
337*4882a593Smuzhiyun 
338*4882a593Smuzhiyun 	init_waitqueue_head(&device->wait);
339*4882a593Smuzhiyun 	INIT_LIST_HEAD(&device->readers);
340*4882a593Smuzhiyun 	mutex_init(&device->readers_lock);
341*4882a593Smuzhiyun 	mutex_init(&device->cbuf_lock);
342*4882a593Smuzhiyun 	device->minor = minor;
343*4882a593Smuzhiyun 	device->hid = hid;
344*4882a593Smuzhiyun 	device->exist = 1;
345*4882a593Smuzhiyun 	device->cbuf_end = 0;
346*4882a593Smuzhiyun 	device->report_size = report_size;
347*4882a593Smuzhiyun 
348*4882a593Smuzhiyun 	return minor;
349*4882a593Smuzhiyun }
350*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(roccat_connect);
351*4882a593Smuzhiyun 
352*4882a593Smuzhiyun /* roccat_disconnect() - remove char device from hid device
353*4882a593Smuzhiyun  * @minor: the minor device number returned by roccat_connect()
354*4882a593Smuzhiyun  */
roccat_disconnect(int minor)355*4882a593Smuzhiyun void roccat_disconnect(int minor)
356*4882a593Smuzhiyun {
357*4882a593Smuzhiyun 	struct roccat_device *device;
358*4882a593Smuzhiyun 
359*4882a593Smuzhiyun 	mutex_lock(&devices_lock);
360*4882a593Smuzhiyun 	device = devices[minor];
361*4882a593Smuzhiyun 	mutex_unlock(&devices_lock);
362*4882a593Smuzhiyun 
363*4882a593Smuzhiyun 	device->exist = 0; /* TODO exist maybe not needed */
364*4882a593Smuzhiyun 
365*4882a593Smuzhiyun 	device_destroy(device->dev->class, MKDEV(roccat_major, minor));
366*4882a593Smuzhiyun 
367*4882a593Smuzhiyun 	mutex_lock(&devices_lock);
368*4882a593Smuzhiyun 	devices[minor] = NULL;
369*4882a593Smuzhiyun 	mutex_unlock(&devices_lock);
370*4882a593Smuzhiyun 
371*4882a593Smuzhiyun 	if (device->open) {
372*4882a593Smuzhiyun 		hid_hw_close(device->hid);
373*4882a593Smuzhiyun 		wake_up_interruptible(&device->wait);
374*4882a593Smuzhiyun 	} else {
375*4882a593Smuzhiyun 		kfree(device);
376*4882a593Smuzhiyun 	}
377*4882a593Smuzhiyun }
378*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(roccat_disconnect);
379*4882a593Smuzhiyun 
roccat_ioctl(struct file * file,unsigned int cmd,unsigned long arg)380*4882a593Smuzhiyun static long roccat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
381*4882a593Smuzhiyun {
382*4882a593Smuzhiyun 	struct inode *inode = file_inode(file);
383*4882a593Smuzhiyun 	struct roccat_device *device;
384*4882a593Smuzhiyun 	unsigned int minor = iminor(inode);
385*4882a593Smuzhiyun 	long retval = 0;
386*4882a593Smuzhiyun 
387*4882a593Smuzhiyun 	mutex_lock(&devices_lock);
388*4882a593Smuzhiyun 
389*4882a593Smuzhiyun 	device = devices[minor];
390*4882a593Smuzhiyun 	if (!device) {
391*4882a593Smuzhiyun 		retval = -ENODEV;
392*4882a593Smuzhiyun 		goto out;
393*4882a593Smuzhiyun 	}
394*4882a593Smuzhiyun 
395*4882a593Smuzhiyun 	switch (cmd) {
396*4882a593Smuzhiyun 	case ROCCATIOCGREPSIZE:
397*4882a593Smuzhiyun 		if (put_user(device->report_size, (int __user *)arg))
398*4882a593Smuzhiyun 			retval = -EFAULT;
399*4882a593Smuzhiyun 		break;
400*4882a593Smuzhiyun 	default:
401*4882a593Smuzhiyun 		retval = -ENOTTY;
402*4882a593Smuzhiyun 	}
403*4882a593Smuzhiyun out:
404*4882a593Smuzhiyun 	mutex_unlock(&devices_lock);
405*4882a593Smuzhiyun 	return retval;
406*4882a593Smuzhiyun }
407*4882a593Smuzhiyun 
408*4882a593Smuzhiyun static const struct file_operations roccat_ops = {
409*4882a593Smuzhiyun 	.owner = THIS_MODULE,
410*4882a593Smuzhiyun 	.read = roccat_read,
411*4882a593Smuzhiyun 	.poll = roccat_poll,
412*4882a593Smuzhiyun 	.open = roccat_open,
413*4882a593Smuzhiyun 	.release = roccat_release,
414*4882a593Smuzhiyun 	.llseek = noop_llseek,
415*4882a593Smuzhiyun 	.unlocked_ioctl = roccat_ioctl,
416*4882a593Smuzhiyun };
417*4882a593Smuzhiyun 
roccat_init(void)418*4882a593Smuzhiyun static int __init roccat_init(void)
419*4882a593Smuzhiyun {
420*4882a593Smuzhiyun 	int retval;
421*4882a593Smuzhiyun 	dev_t dev_id;
422*4882a593Smuzhiyun 
423*4882a593Smuzhiyun 	retval = alloc_chrdev_region(&dev_id, ROCCAT_FIRST_MINOR,
424*4882a593Smuzhiyun 			ROCCAT_MAX_DEVICES, "roccat");
425*4882a593Smuzhiyun 	if (retval < 0) {
426*4882a593Smuzhiyun 		pr_warn("can't get major number\n");
427*4882a593Smuzhiyun 		goto error;
428*4882a593Smuzhiyun 	}
429*4882a593Smuzhiyun 
430*4882a593Smuzhiyun 	roccat_major = MAJOR(dev_id);
431*4882a593Smuzhiyun 
432*4882a593Smuzhiyun 	cdev_init(&roccat_cdev, &roccat_ops);
433*4882a593Smuzhiyun 	retval = cdev_add(&roccat_cdev, dev_id, ROCCAT_MAX_DEVICES);
434*4882a593Smuzhiyun 
435*4882a593Smuzhiyun 	if (retval < 0) {
436*4882a593Smuzhiyun 		pr_warn("cannot add cdev\n");
437*4882a593Smuzhiyun 		goto cleanup_alloc_chrdev_region;
438*4882a593Smuzhiyun 	}
439*4882a593Smuzhiyun 	return 0;
440*4882a593Smuzhiyun 
441*4882a593Smuzhiyun 
442*4882a593Smuzhiyun  cleanup_alloc_chrdev_region:
443*4882a593Smuzhiyun 	unregister_chrdev_region(dev_id, ROCCAT_MAX_DEVICES);
444*4882a593Smuzhiyun  error:
445*4882a593Smuzhiyun 	return retval;
446*4882a593Smuzhiyun }
447*4882a593Smuzhiyun 
roccat_exit(void)448*4882a593Smuzhiyun static void __exit roccat_exit(void)
449*4882a593Smuzhiyun {
450*4882a593Smuzhiyun 	dev_t dev_id = MKDEV(roccat_major, 0);
451*4882a593Smuzhiyun 
452*4882a593Smuzhiyun 	cdev_del(&roccat_cdev);
453*4882a593Smuzhiyun 	unregister_chrdev_region(dev_id, ROCCAT_MAX_DEVICES);
454*4882a593Smuzhiyun }
455*4882a593Smuzhiyun 
456*4882a593Smuzhiyun module_init(roccat_init);
457*4882a593Smuzhiyun module_exit(roccat_exit);
458*4882a593Smuzhiyun 
459*4882a593Smuzhiyun MODULE_AUTHOR("Stefan Achatz");
460*4882a593Smuzhiyun MODULE_DESCRIPTION("USB Roccat char device");
461*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
462