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