[go: nahoru, domu]

1206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz/*
2206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz * Roccat driver for Linux
3206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz *
4206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz * Copyright (c) 2010 Stefan Achatz <erazor_de@users.sourceforge.net>
5206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz */
6206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
7206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz/*
8206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz * This program is free software; you can redistribute it and/or modify it
9206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz * under the terms of the GNU General Public License as published by the Free
10206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz * Software Foundation; either version 2 of the License, or (at your option)
11206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz * any later version.
12206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz */
13206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
14206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz/*
15206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz * Module roccat is a char device used to report special events of roccat
16206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz * hardware to userland. These events include requests for on-screen-display of
17206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz * profile or dpi settings or requests for execution of macro sequences that are
18206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz * not stored in device. The information in these events depends on hid device
19206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz * implementation and contains data that is not available in a single hid event
20206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz * or else hidraw could have been used.
21206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz * It is inspired by hidraw, but uses only one circular buffer for all readers.
22206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz */
23206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
244291ee305e9bb0699504a66f0e2b7aefcf0512a5Joe Perches#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
254291ee305e9bb0699504a66f0e2b7aefcf0512a5Joe Perches
26206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz#include <linux/cdev.h>
27206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz#include <linux/poll.h>
28206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz#include <linux/sched.h>
295dc0c9835fb96c75c8dbf657393764bd0abbac04Stefan Achatz#include <linux/hid-roccat.h>
308f86a2c3cb90e8bb0733de2d2b0abbe7050bb536Paul Gortmaker#include <linux/module.h>
31206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
32206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz#define ROCCAT_FIRST_MINOR 0
33206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz#define ROCCAT_MAX_DEVICES 8
34206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
35206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz/* should be a power of 2 for performance reason */
36206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz#define ROCCAT_CBUF_SIZE 16
37206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
38206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatzstruct roccat_report {
39206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	uint8_t *value;
40206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz};
41206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
42206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatzstruct roccat_device {
43206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	unsigned int minor;
448211e46004518c977f70f2661da961d5ba617399Stefan Achatz	int report_size;
45206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	int open;
46206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	int exist;
47206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	wait_queue_head_t wait;
48206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	struct device *dev;
49206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	struct hid_device *hid;
50206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	struct list_head readers;
51206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	/* protects modifications of readers list */
52206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	struct mutex readers_lock;
53206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
54206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	/*
55206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	 * circular_buffer has one writer and multiple readers with their own
56206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	 * read pointers
57206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	 */
58206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	struct roccat_report cbuf[ROCCAT_CBUF_SIZE];
59206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	int cbuf_end;
60206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	struct mutex cbuf_lock;
61206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz};
62206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
63206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatzstruct roccat_reader {
64206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	struct list_head node;
65206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	struct roccat_device *device;
66206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	int cbuf_start;
67206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz};
68206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
69206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatzstatic int roccat_major;
70206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatzstatic struct cdev roccat_cdev;
71206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
72206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatzstatic struct roccat_device *devices[ROCCAT_MAX_DEVICES];
73206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz/* protects modifications of devices array */
74206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatzstatic DEFINE_MUTEX(devices_lock);
75206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
76206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatzstatic ssize_t roccat_read(struct file *file, char __user *buffer,
77206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz		size_t count, loff_t *ppos)
78206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz{
79206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	struct roccat_reader *reader = file->private_data;
80206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	struct roccat_device *device = reader->device;
81206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	struct roccat_report *report;
82206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	ssize_t retval = 0, len;
83206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	DECLARE_WAITQUEUE(wait, current);
84206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
85206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	mutex_lock(&device->cbuf_lock);
86206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
87206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	/* no data? */
88206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	if (reader->cbuf_start == device->cbuf_end) {
89206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz		add_wait_queue(&device->wait, &wait);
90206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz		set_current_state(TASK_INTERRUPTIBLE);
91206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
92206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz		/* wait for data */
93206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz		while (reader->cbuf_start == device->cbuf_end) {
94206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz			if (file->f_flags & O_NONBLOCK) {
95206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz				retval = -EAGAIN;
96206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz				break;
97206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz			}
98206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz			if (signal_pending(current)) {
99206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz				retval = -ERESTARTSYS;
100206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz				break;
101206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz			}
102206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz			if (!device->exist) {
103206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz				retval = -EIO;
104206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz				break;
105206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz			}
106206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
107206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz			mutex_unlock(&device->cbuf_lock);
108206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz			schedule();
109206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz			mutex_lock(&device->cbuf_lock);
110206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz			set_current_state(TASK_INTERRUPTIBLE);
111206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz		}
112206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
113206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz		set_current_state(TASK_RUNNING);
114206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz		remove_wait_queue(&device->wait, &wait);
115206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	}
116206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
117206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	/* here we either have data or a reason to return if retval is set */
118206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	if (retval)
119206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz		goto exit_unlock;
120206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
121206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	report = &device->cbuf[reader->cbuf_start];
122206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	/*
123206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	 * If report is larger than requested amount of data, rest of report
124206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	 * is lost!
125206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	 */
1268211e46004518c977f70f2661da961d5ba617399Stefan Achatz	len = device->report_size > count ? count : device->report_size;
127206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
128206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	if (copy_to_user(buffer, report->value, len)) {
129206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz		retval = -EFAULT;
130206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz		goto exit_unlock;
131206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	}
132206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	retval += len;
133206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	reader->cbuf_start = (reader->cbuf_start + 1) % ROCCAT_CBUF_SIZE;
134206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
135206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatzexit_unlock:
136206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	mutex_unlock(&device->cbuf_lock);
137206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	return retval;
138206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz}
139206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
140206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatzstatic unsigned int roccat_poll(struct file *file, poll_table *wait)
141206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz{
142206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	struct roccat_reader *reader = file->private_data;
143206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	poll_wait(file, &reader->device->wait, wait);
144206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	if (reader->cbuf_start != reader->device->cbuf_end)
145206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz		return POLLIN | POLLRDNORM;
146206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	if (!reader->device->exist)
147206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz		return POLLERR | POLLHUP;
148206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	return 0;
149206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz}
150206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
151206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatzstatic int roccat_open(struct inode *inode, struct file *file)
152206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz{
153206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	unsigned int minor = iminor(inode);
154206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	struct roccat_reader *reader;
155206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	struct roccat_device *device;
156206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	int error = 0;
157206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
158206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	reader = kzalloc(sizeof(struct roccat_reader), GFP_KERNEL);
159206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	if (!reader)
160206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz		return -ENOMEM;
161206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
162206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	mutex_lock(&devices_lock);
163206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
164206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	device = devices[minor];
165206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
166206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	if (!device) {
1674291ee305e9bb0699504a66f0e2b7aefcf0512a5Joe Perches		pr_emerg("roccat device with minor %d doesn't exist\n", minor);
168206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz		error = -ENODEV;
1698052ee5f5fd9be153129eaa06ced4a786415abc1Julia Lawall		goto exit_err_devices;
170206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	}
171206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
1728052ee5f5fd9be153129eaa06ced4a786415abc1Julia Lawall	mutex_lock(&device->readers_lock);
1738052ee5f5fd9be153129eaa06ced4a786415abc1Julia Lawall
174206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	if (!device->open++) {
175206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz		/* power on device on adding first reader */
1765bea7660bba973dc5e8e9d92b11fb1dd5b524ebfDmitry Torokhov		error = hid_hw_power(device->hid, PM_HINT_FULLON);
1775bea7660bba973dc5e8e9d92b11fb1dd5b524ebfDmitry Torokhov		if (error < 0) {
1785bea7660bba973dc5e8e9d92b11fb1dd5b524ebfDmitry Torokhov			--device->open;
1798052ee5f5fd9be153129eaa06ced4a786415abc1Julia Lawall			goto exit_err_readers;
180206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz		}
1815bea7660bba973dc5e8e9d92b11fb1dd5b524ebfDmitry Torokhov
1825bea7660bba973dc5e8e9d92b11fb1dd5b524ebfDmitry Torokhov		error = hid_hw_open(device->hid);
183206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz		if (error < 0) {
1845bea7660bba973dc5e8e9d92b11fb1dd5b524ebfDmitry Torokhov			hid_hw_power(device->hid, PM_HINT_NORMAL);
185206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz			--device->open;
1868052ee5f5fd9be153129eaa06ced4a786415abc1Julia Lawall			goto exit_err_readers;
187206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz		}
188206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	}
189206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
190206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	reader->device = device;
191206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	/* new reader doesn't get old events */
192206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	reader->cbuf_start = device->cbuf_end;
193206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
194206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	list_add_tail(&reader->node, &device->readers);
195206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	file->private_data = reader;
196206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
1978052ee5f5fd9be153129eaa06ced4a786415abc1Julia Lawallexit_err_readers:
198206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	mutex_unlock(&device->readers_lock);
1998052ee5f5fd9be153129eaa06ced4a786415abc1Julia Lawallexit_err_devices:
200206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	mutex_unlock(&devices_lock);
2018052ee5f5fd9be153129eaa06ced4a786415abc1Julia Lawall	if (error)
2028052ee5f5fd9be153129eaa06ced4a786415abc1Julia Lawall		kfree(reader);
203206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	return error;
204206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz}
205206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
206206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatzstatic int roccat_release(struct inode *inode, struct file *file)
207206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz{
208206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	unsigned int minor = iminor(inode);
209206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	struct roccat_reader *reader = file->private_data;
210206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	struct roccat_device *device;
211206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
212206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	mutex_lock(&devices_lock);
213206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
214206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	device = devices[minor];
215206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	if (!device) {
216206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz		mutex_unlock(&devices_lock);
2174291ee305e9bb0699504a66f0e2b7aefcf0512a5Joe Perches		pr_emerg("roccat device with minor %d doesn't exist\n", minor);
218206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz		return -ENODEV;
219206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	}
220206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
221206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	mutex_lock(&device->readers_lock);
222206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	list_del(&reader->node);
223206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	mutex_unlock(&device->readers_lock);
224206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	kfree(reader);
225206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
226206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	if (!--device->open) {
227206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz		/* removing last reader */
228206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz		if (device->exist) {
2295bea7660bba973dc5e8e9d92b11fb1dd5b524ebfDmitry Torokhov			hid_hw_power(device->hid, PM_HINT_NORMAL);
2305bea7660bba973dc5e8e9d92b11fb1dd5b524ebfDmitry Torokhov			hid_hw_close(device->hid);
231206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz		} else {
232206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz			kfree(device);
233206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz		}
234206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	}
235206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
236206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	mutex_unlock(&devices_lock);
237206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
238206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	return 0;
239206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz}
240206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
241206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz/*
242206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz * roccat_report_event() - output data to readers
243206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz * @minor: minor device number returned by roccat_connect()
244206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz * @data: pointer to data
245206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz *
246206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz * Return value is zero on success, a negative error code on failure.
247206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz *
248206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz * This is called from interrupt handler.
249206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz */
2508211e46004518c977f70f2661da961d5ba617399Stefan Achatzint roccat_report_event(int minor, u8 const *data)
251206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz{
252206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	struct roccat_device *device;
253206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	struct roccat_reader *reader;
254206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	struct roccat_report *report;
255206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	uint8_t *new_value;
256206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
2578211e46004518c977f70f2661da961d5ba617399Stefan Achatz	device = devices[minor];
2588211e46004518c977f70f2661da961d5ba617399Stefan Achatz
2598211e46004518c977f70f2661da961d5ba617399Stefan Achatz	new_value = kmemdup(data, device->report_size, GFP_ATOMIC);
260206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	if (!new_value)
261206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz		return -ENOMEM;
262206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
263206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	report = &device->cbuf[device->cbuf_end];
264206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
265206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	/* passing NULL is safe */
266206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	kfree(report->value);
267206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
268206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	report->value = new_value;
269206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	device->cbuf_end = (device->cbuf_end + 1) % ROCCAT_CBUF_SIZE;
270206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
271206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	list_for_each_entry(reader, &device->readers, node) {
272206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz		/*
273206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz		 * As we already inserted one element, the buffer can't be
274206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz		 * empty. If start and end are equal, buffer is full and we
275206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz		 * increase start, so that slow reader misses one event, but
276206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz		 * gets the newer ones in the right order.
277206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz		 */
278206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz		if (reader->cbuf_start == device->cbuf_end)
279206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz			reader->cbuf_start = (reader->cbuf_start + 1) % ROCCAT_CBUF_SIZE;
280206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	}
281206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
282206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	wake_up_interruptible(&device->wait);
283206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	return 0;
284206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz}
285206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan AchatzEXPORT_SYMBOL_GPL(roccat_report_event);
286206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
287206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz/*
288206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz * roccat_connect() - create a char device for special event output
2895012aada506cb8b570e46579077c0ec5b82ebd5dStefan Achatz * @class: the class thats used to create the device. Meant to hold device
2905012aada506cb8b570e46579077c0ec5b82ebd5dStefan Achatz * specific sysfs attributes.
291206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz * @hid: the hid device the char device should be connected to.
29202060045cd93b886381b02ec816e312f19d5e505Stefan Achatz * @report_size: size of reports
293206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz *
294206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz * Return value is minor device number in Range [0, ROCCAT_MAX_DEVICES] on
295206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz * success, a negative error code on failure.
296206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz */
2978211e46004518c977f70f2661da961d5ba617399Stefan Achatzint roccat_connect(struct class *klass, struct hid_device *hid, int report_size)
298206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz{
299206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	unsigned int minor;
300206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	struct roccat_device *device;
301206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	int temp;
302206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
303206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	device = kzalloc(sizeof(struct roccat_device), GFP_KERNEL);
304206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	if (!device)
305206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz		return -ENOMEM;
306206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
307206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	mutex_lock(&devices_lock);
308206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
309206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	for (minor = 0; minor < ROCCAT_MAX_DEVICES; ++minor) {
310206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz		if (devices[minor])
311206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz			continue;
312206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz		break;
313206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	}
314206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
315206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	if (minor < ROCCAT_MAX_DEVICES) {
316206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz		devices[minor] = device;
317206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	} else {
318206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz		mutex_unlock(&devices_lock);
319206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz		kfree(device);
320206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz		return -EINVAL;
321206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	}
322206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
3235012aada506cb8b570e46579077c0ec5b82ebd5dStefan Achatz	device->dev = device_create(klass, &hid->dev,
324206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz			MKDEV(roccat_major, minor), NULL,
325206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz			"%s%s%d", "roccat", hid->driver->name, minor);
326206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
327206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	if (IS_ERR(device->dev)) {
328206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz		devices[minor] = NULL;
329206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz		mutex_unlock(&devices_lock);
330206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz		temp = PTR_ERR(device->dev);
331206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz		kfree(device);
332206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz		return temp;
333206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	}
334206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
335206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	mutex_unlock(&devices_lock);
336206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
337206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	init_waitqueue_head(&device->wait);
338206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	INIT_LIST_HEAD(&device->readers);
339206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	mutex_init(&device->readers_lock);
340206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	mutex_init(&device->cbuf_lock);
341206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	device->minor = minor;
342206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	device->hid = hid;
343206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	device->exist = 1;
344206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	device->cbuf_end = 0;
3458211e46004518c977f70f2661da961d5ba617399Stefan Achatz	device->report_size = report_size;
346206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
347206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	return minor;
348206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz}
349206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan AchatzEXPORT_SYMBOL_GPL(roccat_connect);
350206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
351206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz/* roccat_disconnect() - remove char device from hid device
352206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz * @minor: the minor device number returned by roccat_connect()
353206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz */
354206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatzvoid roccat_disconnect(int minor)
355206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz{
356206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	struct roccat_device *device;
357206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
358206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	mutex_lock(&devices_lock);
359206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	device = devices[minor];
360206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	mutex_unlock(&devices_lock);
361206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
362206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	device->exist = 0; /* TODO exist maybe not needed */
363206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
3645012aada506cb8b570e46579077c0ec5b82ebd5dStefan Achatz	device_destroy(device->dev->class, MKDEV(roccat_major, minor));
365206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
366e6fa47af5542df3383a78d7cc78f5d5ef63c0d42Stefan Achatz	mutex_lock(&devices_lock);
367e6fa47af5542df3383a78d7cc78f5d5ef63c0d42Stefan Achatz	devices[minor] = NULL;
368e6fa47af5542df3383a78d7cc78f5d5ef63c0d42Stefan Achatz	mutex_unlock(&devices_lock);
369a0be10c2e0c822c0fdd6b207696012060147f84bMichael Rissi
370206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	if (device->open) {
3715bea7660bba973dc5e8e9d92b11fb1dd5b524ebfDmitry Torokhov		hid_hw_close(device->hid);
372206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz		wake_up_interruptible(&device->wait);
373206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	} else {
374206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz		kfree(device);
375206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	}
376206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz}
377206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan AchatzEXPORT_SYMBOL_GPL(roccat_disconnect);
378206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
3798211e46004518c977f70f2661da961d5ba617399Stefan Achatzstatic long roccat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
3808211e46004518c977f70f2661da961d5ba617399Stefan Achatz{
381496ad9aa8ef448058e36ca7a787c61f2e63f0f54Al Viro	struct inode *inode = file_inode(file);
3828211e46004518c977f70f2661da961d5ba617399Stefan Achatz	struct roccat_device *device;
3838211e46004518c977f70f2661da961d5ba617399Stefan Achatz	unsigned int minor = iminor(inode);
3848211e46004518c977f70f2661da961d5ba617399Stefan Achatz	long retval = 0;
3858211e46004518c977f70f2661da961d5ba617399Stefan Achatz
3868211e46004518c977f70f2661da961d5ba617399Stefan Achatz	mutex_lock(&devices_lock);
3878211e46004518c977f70f2661da961d5ba617399Stefan Achatz
3888211e46004518c977f70f2661da961d5ba617399Stefan Achatz	device = devices[minor];
3898211e46004518c977f70f2661da961d5ba617399Stefan Achatz	if (!device) {
3908211e46004518c977f70f2661da961d5ba617399Stefan Achatz		retval = -ENODEV;
3918211e46004518c977f70f2661da961d5ba617399Stefan Achatz		goto out;
3928211e46004518c977f70f2661da961d5ba617399Stefan Achatz	}
3938211e46004518c977f70f2661da961d5ba617399Stefan Achatz
3948211e46004518c977f70f2661da961d5ba617399Stefan Achatz	switch (cmd) {
3958211e46004518c977f70f2661da961d5ba617399Stefan Achatz	case ROCCATIOCGREPSIZE:
3968211e46004518c977f70f2661da961d5ba617399Stefan Achatz		if (put_user(device->report_size, (int __user *)arg))
3978211e46004518c977f70f2661da961d5ba617399Stefan Achatz			retval = -EFAULT;
3988211e46004518c977f70f2661da961d5ba617399Stefan Achatz		break;
3998211e46004518c977f70f2661da961d5ba617399Stefan Achatz	default:
4008211e46004518c977f70f2661da961d5ba617399Stefan Achatz		retval = -ENOTTY;
4018211e46004518c977f70f2661da961d5ba617399Stefan Achatz	}
4028211e46004518c977f70f2661da961d5ba617399Stefan Achatzout:
4038211e46004518c977f70f2661da961d5ba617399Stefan Achatz	mutex_unlock(&devices_lock);
4048211e46004518c977f70f2661da961d5ba617399Stefan Achatz	return retval;
4058211e46004518c977f70f2661da961d5ba617399Stefan Achatz}
4068211e46004518c977f70f2661da961d5ba617399Stefan Achatz
407206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatzstatic const struct file_operations roccat_ops = {
408206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	.owner = THIS_MODULE,
409206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	.read = roccat_read,
410206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	.poll = roccat_poll,
411206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	.open = roccat_open,
412206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	.release = roccat_release,
4136038f373a3dc1f1c26496e60b6c40b164716f07eArnd Bergmann	.llseek = noop_llseek,
4148211e46004518c977f70f2661da961d5ba617399Stefan Achatz	.unlocked_ioctl = roccat_ioctl,
415206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz};
416206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
417206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatzstatic int __init roccat_init(void)
418206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz{
419206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	int retval;
420206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	dev_t dev_id;
421206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
422206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	retval = alloc_chrdev_region(&dev_id, ROCCAT_FIRST_MINOR,
423206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz			ROCCAT_MAX_DEVICES, "roccat");
424206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
425206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	roccat_major = MAJOR(dev_id);
426206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
427206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	if (retval < 0) {
4284291ee305e9bb0699504a66f0e2b7aefcf0512a5Joe Perches		pr_warn("can't get major number\n");
429a0be10c2e0c822c0fdd6b207696012060147f84bMichael Rissi		goto error;
430206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	}
431206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
432206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	cdev_init(&roccat_cdev, &roccat_ops);
433a0be10c2e0c822c0fdd6b207696012060147f84bMichael Rissi	retval = cdev_add(&roccat_cdev, dev_id, ROCCAT_MAX_DEVICES);
434206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
435a0be10c2e0c822c0fdd6b207696012060147f84bMichael Rissi	if (retval < 0) {
436a0be10c2e0c822c0fdd6b207696012060147f84bMichael Rissi		pr_warn("cannot add cdev\n");
437a0be10c2e0c822c0fdd6b207696012060147f84bMichael Rissi		goto cleanup_alloc_chrdev_region;
438a0be10c2e0c822c0fdd6b207696012060147f84bMichael Rissi	}
439206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	return 0;
440a0be10c2e0c822c0fdd6b207696012060147f84bMichael Rissi
441a0be10c2e0c822c0fdd6b207696012060147f84bMichael Rissi
442a0be10c2e0c822c0fdd6b207696012060147f84bMichael Rissi cleanup_alloc_chrdev_region:
443a0be10c2e0c822c0fdd6b207696012060147f84bMichael Rissi	unregister_chrdev_region(dev_id, ROCCAT_MAX_DEVICES);
444a0be10c2e0c822c0fdd6b207696012060147f84bMichael Rissi error:
445a0be10c2e0c822c0fdd6b207696012060147f84bMichael Rissi	return retval;
446206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz}
447206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
448206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatzstatic void __exit roccat_exit(void)
449206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz{
450206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	dev_t dev_id = MKDEV(roccat_major, 0);
451206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
452206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	cdev_del(&roccat_cdev);
453206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz	unregister_chrdev_region(dev_id, ROCCAT_MAX_DEVICES);
454206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz}
455206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
456206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatzmodule_init(roccat_init);
457206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatzmodule_exit(roccat_exit);
458206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan Achatz
459206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan AchatzMODULE_AUTHOR("Stefan Achatz");
460206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan AchatzMODULE_DESCRIPTION("USB Roccat char device");
461206f5f2fcb5ff5bb0c60f9e9189937f3ca03e378Stefan AchatzMODULE_LICENSE("GPL v2");
462