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