diff --git a/drivers/hid/hid-roccat.c b/drivers/hid/hid-roccat.c index 4f15eb951039..6e208e07e882 100644 --- a/drivers/hid/hid-roccat.c +++ b/drivers/hid/hid-roccat.c @@ -41,6 +41,7 @@ struct roccat_device { int report_size; int open; int exist; + struct kref ref; wait_queue_head_t wait; struct device *dev; struct hid_device *hid; @@ -70,10 +71,12 @@ static struct roccat_device *devices[ROCCAT_MAX_DEVICES]; /* protects modifications of devices array */ static DEFINE_MUTEX(devices_lock); -static void roccat_free_device(struct roccat_device *device) +static void roccat_free_device(struct kref *ref) { + struct roccat_device *device; int i; + device = container_of(ref, struct roccat_device, ref); for (i = 0; i < ROCCAT_CBUF_SIZE; i++) kfree(device->cbuf[i].value); kfree(device); @@ -193,6 +196,7 @@ static int roccat_open(struct inode *inode, struct file *file) } } + kref_get(&device->ref); reader->device = device; /* new reader doesn't get old events */ reader->cbuf_start = device->cbuf_end; @@ -235,7 +239,8 @@ static int roccat_release(struct inode *inode, struct file *file) hid_hw_power(device->hid, PM_HINT_NORMAL); hid_hw_close(device->hid); } else { - roccat_free_device(device); + if (kref_put(&device->ref, roccat_free_device)) + devices[minor] = NULL; } } @@ -350,6 +355,7 @@ int roccat_connect(const struct class *klass, struct hid_device *hid, int report INIT_LIST_HEAD(&device->readers); mutex_init(&device->readers_lock); mutex_init(&device->cbuf_lock); + kref_init(&device->ref); device->minor = minor; device->hid = hid; device->exist = 1; @@ -368,23 +374,23 @@ void roccat_disconnect(int minor) struct roccat_device *device; mutex_lock(&devices_lock); + device = devices[minor]; - mutex_unlock(&devices_lock); + if (!device) + goto out; device->exist = 0; /* TODO exist maybe not needed */ - device_destroy(device->dev->class, MKDEV(roccat_major, minor)); - mutex_lock(&devices_lock); - devices[minor] = NULL; - mutex_unlock(&devices_lock); - if (device->open) { hid_hw_close(device->hid); wake_up_interruptible(&device->wait); } else { - roccat_free_device(device); + if (kref_put(&device->ref, roccat_free_device)) + devices[minor] = NULL; } +out: + mutex_unlock(&devices_lock); } EXPORT_SYMBOL_GPL(roccat_disconnect);