diff --git a/drivers/hid/hid-roccat.c b/drivers/hid/hid-roccat.c index 4f15eb951039..bdf885750929 100644 --- a/drivers/hid/hid-roccat.c +++ b/drivers/hid/hid-roccat.c @@ -40,7 +40,7 @@ struct roccat_device { unsigned int minor; int report_size; int open; - int exist; + struct kref ref; wait_queue_head_t wait; struct device *dev; struct hid_device *hid; @@ -70,12 +70,25 @@ 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 bool roccat_device_available(struct roccat_device *device) { + return device->dev ? device_is_registered(device->dev) : false; +} + +static void roccat_free_device(struct kref *ref) +{ + struct roccat_device *device; int i; + WARN_ON(!mutex_is_locked(&devices_lock)); + + device = container_of(ref, struct roccat_device, ref); for (i = 0; i < ROCCAT_CBUF_SIZE; i++) kfree(device->cbuf[i].value); + + devices[device->minor] = NULL; + mutex_destroy(&device->readers_lock); + mutex_destroy(&device->cbuf_lock); kfree(device); } @@ -105,7 +118,7 @@ static ssize_t roccat_read(struct file *file, char __user *buffer, retval = -ERESTARTSYS; break; } - if (!device->exist) { + if (!roccat_device_available(device)) { retval = -EIO; break; } @@ -149,7 +162,7 @@ static __poll_t roccat_poll(struct file *file, poll_table *wait) poll_wait(file, &reader->device->wait, wait); if (reader->cbuf_start != reader->device->cbuf_end) return EPOLLIN | EPOLLRDNORM; - if (!reader->device->exist) + if (!roccat_device_available(reader->device)) return EPOLLERR | EPOLLHUP; return 0; } @@ -169,7 +182,7 @@ static int roccat_open(struct inode *inode, struct file *file) device = devices[minor]; - if (!device) { + if (!device || !roccat_device_available(device)) { pr_emerg("roccat device with minor %d doesn't exist\n", minor); error = -ENODEV; goto exit_err_devices; @@ -191,6 +204,7 @@ static int roccat_open(struct inode *inode, struct file *file) --device->open; goto exit_err_readers; } + kref_get(&device->ref); } reader->device = device; @@ -211,32 +225,26 @@ static int roccat_open(struct inode *inode, struct file *file) static int roccat_release(struct inode *inode, struct file *file) { - unsigned int minor = iminor(inode); struct roccat_reader *reader = file->private_data; - struct roccat_device *device; - - mutex_lock(&devices_lock); + struct roccat_device *device = reader->device; - device = devices[minor]; - if (!device) { - mutex_unlock(&devices_lock); - pr_emerg("roccat device with minor %d doesn't exist\n", minor); + if (WARN_ON(!device)) return -ENODEV; - } mutex_lock(&device->readers_lock); list_del(&reader->node); mutex_unlock(&device->readers_lock); kfree(reader); + mutex_lock(&devices_lock); + if (!--device->open) { /* removing last reader */ - if (device->exist) { + if (roccat_device_available(device)) { hid_hw_power(device->hid, PM_HINT_NORMAL); hid_hw_close(device->hid); - } else { - roccat_free_device(device); } + kref_put(&device->ref, roccat_free_device); } mutex_unlock(&devices_lock); @@ -344,18 +352,17 @@ int roccat_connect(const struct class *klass, struct hid_device *hid, int report return temp; } - mutex_unlock(&devices_lock); - init_waitqueue_head(&device->wait); 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; device->cbuf_end = 0; device->report_size = report_size; + mutex_unlock(&devices_lock); return minor; } EXPORT_SYMBOL_GPL(roccat_connect); @@ -369,22 +376,21 @@ void roccat_disconnect(int minor) mutex_lock(&devices_lock); device = devices[minor]; - mutex_unlock(&devices_lock); - - device->exist = 0; /* TODO exist maybe not needed */ - - device_destroy(device->dev->class, MKDEV(roccat_major, minor)); + if (WARN_ON(!device)) + goto out; - mutex_lock(&devices_lock); - devices[minor] = NULL; - mutex_unlock(&devices_lock); + if (!WARN_ON(!roccat_device_available(device))) { + device_destroy(device->dev->class, MKDEV(roccat_major, minor)); + device->dev = NULL; + } if (device->open) { hid_hw_close(device->hid); wake_up_interruptible(&device->wait); - } else { - roccat_free_device(device); } + kref_put(&device->ref, roccat_free_device); +out: + mutex_unlock(&devices_lock); } EXPORT_SYMBOL_GPL(roccat_disconnect); @@ -398,7 +404,7 @@ static long roccat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) mutex_lock(&devices_lock); device = devices[minor]; - if (!device) { + if (!device || !roccat_device_available(device)) { retval = -ENODEV; goto out; }