summaryrefslogtreecommitdiff
path: root/drivers/media/rc
diff options
context:
space:
mode:
authorSean Young <sean@mess.org>2017-01-30 13:49:58 -0200
committerMauro Carvalho Chehab <mchehab@s-opensource.com>2017-03-24 08:27:07 -0300
commit74c839b2f5544fd77fdb34a99b577965d4812edf (patch)
treec3eccff4c63588d72c16b31ba185e9dfb856fe0a /drivers/media/rc
parent069f3b10aed966b2da6bb1161af41da0e8880724 (diff)
[media] lirc: use refcounting for lirc devices
If a lirc device is unplugged, the struct rc_dev is freed even though userspace can still have a file descriptor open on the lirc chardev. The rc_dev structure can be used in a subsequent, or even currently executing ioctl, read or write. Signed-off-by: Sean Young <sean@mess.org> Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
Diffstat (limited to 'drivers/media/rc')
-rw-r--r--drivers/media/rc/lirc_dev.c120
1 files changed, 51 insertions, 69 deletions
diff --git a/drivers/media/rc/lirc_dev.c b/drivers/media/rc/lirc_dev.c
index e930c0598d3f..c8f8edd5b7de 100644
--- a/drivers/media/rc/lirc_dev.c
+++ b/drivers/media/rc/lirc_dev.c
@@ -54,7 +54,8 @@ struct irctl {
struct lirc_buffer *buf;
unsigned int chunk_size;
- struct cdev *cdev;
+ struct device dev;
+ struct cdev cdev;
struct task_struct *task;
long jiffies_to_wait;
@@ -76,15 +77,21 @@ static void lirc_irctl_init(struct irctl *ir)
ir->d.minor = NOPLUG;
}
-static void lirc_irctl_cleanup(struct irctl *ir)
+static void lirc_release(struct device *ld)
{
- device_destroy(lirc_class, MKDEV(MAJOR(lirc_base_dev), ir->d.minor));
+ struct irctl *ir = container_of(ld, struct irctl, dev);
+
+ put_device(ir->dev.parent);
if (ir->buf != ir->d.rbuf) {
lirc_buffer_free(ir->buf);
kfree(ir->buf);
}
- ir->buf = NULL;
+
+ mutex_lock(&lirc_dev_lock);
+ irctls[ir->d.minor] = NULL;
+ mutex_unlock(&lirc_dev_lock);
+ kfree(ir);
}
/* helper function
@@ -157,32 +164,21 @@ static int lirc_cdev_add(struct irctl *ir)
struct cdev *cdev;
int retval;
- cdev = cdev_alloc();
- if (!cdev)
- return -ENOMEM;
+ cdev = &ir->cdev;
if (d->fops) {
- cdev->ops = d->fops;
+ cdev_init(cdev, d->fops);
cdev->owner = d->owner;
} else {
- cdev->ops = &lirc_dev_fops;
+ cdev_init(cdev, &lirc_dev_fops);
cdev->owner = THIS_MODULE;
}
retval = kobject_set_name(&cdev->kobj, "lirc%d", d->minor);
if (retval)
- goto err_out;
-
- retval = cdev_add(cdev, MKDEV(MAJOR(lirc_base_dev), d->minor), 1);
- if (retval)
- goto err_out;
-
- ir->cdev = cdev;
-
- return 0;
+ return retval;
-err_out:
- cdev_del(cdev);
- return retval;
+ cdev->kobj.parent = &ir->dev.kobj;
+ return cdev_add(cdev, ir->dev.devt, 1);
}
static int lirc_allocate_buffer(struct irctl *ir)
@@ -304,9 +300,12 @@ static int lirc_allocate_driver(struct lirc_driver *d)
ir->d = *d;
- device_create(lirc_class, ir->d.dev,
- MKDEV(MAJOR(lirc_base_dev), ir->d.minor), NULL,
- "lirc%u", ir->d.minor);
+ ir->dev.devt = MKDEV(MAJOR(lirc_base_dev), ir->d.minor);
+ ir->dev.class = lirc_class;
+ ir->dev.parent = d->dev;
+ ir->dev.release = lirc_release;
+ dev_set_name(&ir->dev, "lirc%d", ir->d.minor);
+ device_initialize(&ir->dev);
if (d->sample_rate) {
ir->jiffies_to_wait = HZ / d->sample_rate;
@@ -329,14 +328,22 @@ static int lirc_allocate_driver(struct lirc_driver *d)
goto out_sysfs;
ir->attached = 1;
+
+ err = device_add(&ir->dev);
+ if (err)
+ goto out_cdev;
+
mutex_unlock(&lirc_dev_lock);
+ get_device(ir->dev.parent);
+
dev_info(ir->d.dev, "lirc_dev: driver %s registered at minor = %d\n",
ir->d.name, ir->d.minor);
return minor;
-
+out_cdev:
+ cdev_del(&ir->cdev);
out_sysfs:
- device_destroy(lirc_class, MKDEV(MAJOR(lirc_base_dev), ir->d.minor));
+ put_device(&ir->dev);
out_lock:
mutex_unlock(&lirc_dev_lock);
@@ -364,7 +371,6 @@ EXPORT_SYMBOL(lirc_register_driver);
int lirc_unregister_driver(int minor)
{
struct irctl *ir;
- struct cdev *cdev;
if (minor < 0 || minor >= MAX_IRCTL_DEVICES) {
pr_err("minor (%d) must be between 0 and %d!\n",
@@ -378,8 +384,6 @@ int lirc_unregister_driver(int minor)
return -ENOENT;
}
- cdev = ir->cdev;
-
mutex_lock(&lirc_dev_lock);
if (ir->d.minor != minor) {
@@ -401,22 +405,20 @@ int lirc_unregister_driver(int minor)
dev_dbg(ir->d.dev, LOGHEAD "releasing opened driver\n",
ir->d.name, ir->d.minor);
wake_up_interruptible(&ir->buf->wait_poll);
- mutex_lock(&ir->irctl_lock);
+ }
- if (ir->d.set_use_dec)
- ir->d.set_use_dec(ir->d.data);
+ mutex_lock(&ir->irctl_lock);
- module_put(cdev->owner);
- mutex_unlock(&ir->irctl_lock);
- } else {
- lirc_irctl_cleanup(ir);
- cdev_del(cdev);
- kfree(ir);
- irctls[minor] = NULL;
- }
+ if (ir->d.set_use_dec)
+ ir->d.set_use_dec(ir->d.data);
+ mutex_unlock(&ir->irctl_lock);
mutex_unlock(&lirc_dev_lock);
+ device_del(&ir->dev);
+ cdev_del(&ir->cdev);
+ put_device(&ir->dev);
+
return 0;
}
EXPORT_SYMBOL(lirc_unregister_driver);
@@ -424,7 +426,6 @@ EXPORT_SYMBOL(lirc_unregister_driver);
int lirc_dev_fop_open(struct inode *inode, struct file *file)
{
struct irctl *ir;
- struct cdev *cdev;
int retval = 0;
if (iminor(inode) >= MAX_IRCTL_DEVICES) {
@@ -459,18 +460,14 @@ int lirc_dev_fop_open(struct inode *inode, struct file *file)
goto error;
}
- cdev = ir->cdev;
- if (try_module_get(cdev->owner)) {
- ir->open++;
- if (ir->d.set_use_inc)
- retval = ir->d.set_use_inc(ir->d.data);
-
- if (retval) {
- module_put(cdev->owner);
- ir->open--;
- } else if (ir->buf) {
+ ir->open++;
+ if (ir->d.set_use_inc)
+ retval = ir->d.set_use_inc(ir->d.data);
+ if (retval) {
+ ir->open--;
+ } else {
+ if (ir->buf)
lirc_buffer_clear(ir->buf);
- }
if (ir->task)
wake_up_process(ir->task);
}
@@ -487,7 +484,6 @@ EXPORT_SYMBOL(lirc_dev_fop_open);
int lirc_dev_fop_close(struct inode *inode, struct file *file)
{
struct irctl *ir = irctls[iminor(inode)];
- struct cdev *cdev;
int ret;
if (!ir) {
@@ -495,25 +491,14 @@ int lirc_dev_fop_close(struct inode *inode, struct file *file)
return -EINVAL;
}
- cdev = ir->cdev;
-
ret = mutex_lock_killable(&lirc_dev_lock);
WARN_ON(ret);
rc_close(ir->d.rdev);
ir->open--;
- if (ir->attached) {
- if (ir->d.set_use_dec)
- ir->d.set_use_dec(ir->d.data);
- module_put(cdev->owner);
- } else {
- lirc_irctl_cleanup(ir);
- cdev_del(cdev);
- irctls[ir->d.minor] = NULL;
- kfree(ir);
- }
-
+ if (ir->d.set_use_dec)
+ ir->d.set_use_dec(ir->d.data);
if (!ret)
mutex_unlock(&lirc_dev_lock);
@@ -780,15 +765,12 @@ static int __init lirc_dev_init(void)
return retval;
}
-
pr_info("IR Remote Control driver registered, major %d\n",
MAJOR(lirc_base_dev));
return 0;
}
-
-
static void __exit lirc_dev_exit(void)
{
class_destroy(lirc_class);