提交 74c839b2 编写于 作者: S Sean Young 提交者: Mauro Carvalho Chehab

[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: NSean Young <sean@mess.org>
Signed-off-by: NMauro Carvalho Chehab <mchehab@s-opensource.com>
上级 069f3b10
...@@ -54,7 +54,8 @@ struct irctl { ...@@ -54,7 +54,8 @@ struct irctl {
struct lirc_buffer *buf; struct lirc_buffer *buf;
unsigned int chunk_size; unsigned int chunk_size;
struct cdev *cdev; struct device dev;
struct cdev cdev;
struct task_struct *task; struct task_struct *task;
long jiffies_to_wait; long jiffies_to_wait;
...@@ -76,15 +77,21 @@ static void lirc_irctl_init(struct irctl *ir) ...@@ -76,15 +77,21 @@ static void lirc_irctl_init(struct irctl *ir)
ir->d.minor = NOPLUG; 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) { if (ir->buf != ir->d.rbuf) {
lirc_buffer_free(ir->buf); lirc_buffer_free(ir->buf);
kfree(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 /* helper function
...@@ -157,32 +164,21 @@ static int lirc_cdev_add(struct irctl *ir) ...@@ -157,32 +164,21 @@ static int lirc_cdev_add(struct irctl *ir)
struct cdev *cdev; struct cdev *cdev;
int retval; int retval;
cdev = cdev_alloc(); cdev = &ir->cdev;
if (!cdev)
return -ENOMEM;
if (d->fops) { if (d->fops) {
cdev->ops = d->fops; cdev_init(cdev, d->fops);
cdev->owner = d->owner; cdev->owner = d->owner;
} else { } else {
cdev->ops = &lirc_dev_fops; cdev_init(cdev, &lirc_dev_fops);
cdev->owner = THIS_MODULE; cdev->owner = THIS_MODULE;
} }
retval = kobject_set_name(&cdev->kobj, "lirc%d", d->minor); retval = kobject_set_name(&cdev->kobj, "lirc%d", d->minor);
if (retval) if (retval)
goto err_out; return retval;
retval = cdev_add(cdev, MKDEV(MAJOR(lirc_base_dev), d->minor), 1);
if (retval)
goto err_out;
ir->cdev = cdev;
return 0;
err_out: cdev->kobj.parent = &ir->dev.kobj;
cdev_del(cdev); return cdev_add(cdev, ir->dev.devt, 1);
return retval;
} }
static int lirc_allocate_buffer(struct irctl *ir) static int lirc_allocate_buffer(struct irctl *ir)
...@@ -304,9 +300,12 @@ static int lirc_allocate_driver(struct lirc_driver *d) ...@@ -304,9 +300,12 @@ static int lirc_allocate_driver(struct lirc_driver *d)
ir->d = *d; ir->d = *d;
device_create(lirc_class, ir->d.dev, ir->dev.devt = MKDEV(MAJOR(lirc_base_dev), ir->d.minor);
MKDEV(MAJOR(lirc_base_dev), ir->d.minor), NULL, ir->dev.class = lirc_class;
"lirc%u", ir->d.minor); 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) { if (d->sample_rate) {
ir->jiffies_to_wait = HZ / d->sample_rate; ir->jiffies_to_wait = HZ / d->sample_rate;
...@@ -329,14 +328,22 @@ static int lirc_allocate_driver(struct lirc_driver *d) ...@@ -329,14 +328,22 @@ static int lirc_allocate_driver(struct lirc_driver *d)
goto out_sysfs; goto out_sysfs;
ir->attached = 1; ir->attached = 1;
err = device_add(&ir->dev);
if (err)
goto out_cdev;
mutex_unlock(&lirc_dev_lock); mutex_unlock(&lirc_dev_lock);
get_device(ir->dev.parent);
dev_info(ir->d.dev, "lirc_dev: driver %s registered at minor = %d\n", dev_info(ir->d.dev, "lirc_dev: driver %s registered at minor = %d\n",
ir->d.name, ir->d.minor); ir->d.name, ir->d.minor);
return minor; return minor;
out_cdev:
cdev_del(&ir->cdev);
out_sysfs: out_sysfs:
device_destroy(lirc_class, MKDEV(MAJOR(lirc_base_dev), ir->d.minor)); put_device(&ir->dev);
out_lock: out_lock:
mutex_unlock(&lirc_dev_lock); mutex_unlock(&lirc_dev_lock);
...@@ -364,7 +371,6 @@ EXPORT_SYMBOL(lirc_register_driver); ...@@ -364,7 +371,6 @@ EXPORT_SYMBOL(lirc_register_driver);
int lirc_unregister_driver(int minor) int lirc_unregister_driver(int minor)
{ {
struct irctl *ir; struct irctl *ir;
struct cdev *cdev;
if (minor < 0 || minor >= MAX_IRCTL_DEVICES) { if (minor < 0 || minor >= MAX_IRCTL_DEVICES) {
pr_err("minor (%d) must be between 0 and %d!\n", pr_err("minor (%d) must be between 0 and %d!\n",
...@@ -378,8 +384,6 @@ int lirc_unregister_driver(int minor) ...@@ -378,8 +384,6 @@ int lirc_unregister_driver(int minor)
return -ENOENT; return -ENOENT;
} }
cdev = ir->cdev;
mutex_lock(&lirc_dev_lock); mutex_lock(&lirc_dev_lock);
if (ir->d.minor != minor) { if (ir->d.minor != minor) {
...@@ -401,22 +405,20 @@ int lirc_unregister_driver(int minor) ...@@ -401,22 +405,20 @@ int lirc_unregister_driver(int minor)
dev_dbg(ir->d.dev, LOGHEAD "releasing opened driver\n", dev_dbg(ir->d.dev, LOGHEAD "releasing opened driver\n",
ir->d.name, ir->d.minor); ir->d.name, ir->d.minor);
wake_up_interruptible(&ir->buf->wait_poll); wake_up_interruptible(&ir->buf->wait_poll);
mutex_lock(&ir->irctl_lock); }
if (ir->d.set_use_dec) mutex_lock(&ir->irctl_lock);
ir->d.set_use_dec(ir->d.data);
module_put(cdev->owner); if (ir->d.set_use_dec)
mutex_unlock(&ir->irctl_lock); ir->d.set_use_dec(ir->d.data);
} else {
lirc_irctl_cleanup(ir);
cdev_del(cdev);
kfree(ir);
irctls[minor] = NULL;
}
mutex_unlock(&ir->irctl_lock);
mutex_unlock(&lirc_dev_lock); mutex_unlock(&lirc_dev_lock);
device_del(&ir->dev);
cdev_del(&ir->cdev);
put_device(&ir->dev);
return 0; return 0;
} }
EXPORT_SYMBOL(lirc_unregister_driver); EXPORT_SYMBOL(lirc_unregister_driver);
...@@ -424,7 +426,6 @@ 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) int lirc_dev_fop_open(struct inode *inode, struct file *file)
{ {
struct irctl *ir; struct irctl *ir;
struct cdev *cdev;
int retval = 0; int retval = 0;
if (iminor(inode) >= MAX_IRCTL_DEVICES) { if (iminor(inode) >= MAX_IRCTL_DEVICES) {
...@@ -459,18 +460,14 @@ int lirc_dev_fop_open(struct inode *inode, struct file *file) ...@@ -459,18 +460,14 @@ int lirc_dev_fop_open(struct inode *inode, struct file *file)
goto error; goto error;
} }
cdev = ir->cdev; ir->open++;
if (try_module_get(cdev->owner)) { if (ir->d.set_use_inc)
ir->open++; retval = ir->d.set_use_inc(ir->d.data);
if (ir->d.set_use_inc) if (retval) {
retval = ir->d.set_use_inc(ir->d.data); ir->open--;
} else {
if (retval) { if (ir->buf)
module_put(cdev->owner);
ir->open--;
} else if (ir->buf) {
lirc_buffer_clear(ir->buf); lirc_buffer_clear(ir->buf);
}
if (ir->task) if (ir->task)
wake_up_process(ir->task); wake_up_process(ir->task);
} }
...@@ -487,7 +484,6 @@ EXPORT_SYMBOL(lirc_dev_fop_open); ...@@ -487,7 +484,6 @@ EXPORT_SYMBOL(lirc_dev_fop_open);
int lirc_dev_fop_close(struct inode *inode, struct file *file) int lirc_dev_fop_close(struct inode *inode, struct file *file)
{ {
struct irctl *ir = irctls[iminor(inode)]; struct irctl *ir = irctls[iminor(inode)];
struct cdev *cdev;
int ret; int ret;
if (!ir) { if (!ir) {
...@@ -495,25 +491,14 @@ int lirc_dev_fop_close(struct inode *inode, struct file *file) ...@@ -495,25 +491,14 @@ int lirc_dev_fop_close(struct inode *inode, struct file *file)
return -EINVAL; return -EINVAL;
} }
cdev = ir->cdev;
ret = mutex_lock_killable(&lirc_dev_lock); ret = mutex_lock_killable(&lirc_dev_lock);
WARN_ON(ret); WARN_ON(ret);
rc_close(ir->d.rdev); rc_close(ir->d.rdev);
ir->open--; ir->open--;
if (ir->attached) { if (ir->d.set_use_dec)
if (ir->d.set_use_dec) ir->d.set_use_dec(ir->d.data);
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 (!ret) if (!ret)
mutex_unlock(&lirc_dev_lock); mutex_unlock(&lirc_dev_lock);
...@@ -780,15 +765,12 @@ static int __init lirc_dev_init(void) ...@@ -780,15 +765,12 @@ static int __init lirc_dev_init(void)
return retval; return retval;
} }
pr_info("IR Remote Control driver registered, major %d\n", pr_info("IR Remote Control driver registered, major %d\n",
MAJOR(lirc_base_dev)); MAJOR(lirc_base_dev));
return 0; return 0;
} }
static void __exit lirc_dev_exit(void) static void __exit lirc_dev_exit(void)
{ {
class_destroy(lirc_class); class_destroy(lirc_class);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册