diff options
author | Jarod Wilson <jarod@redhat.com> | 2010-10-18 17:39:20 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2010-10-22 18:05:36 -0400 |
commit | c1cbb7029e81894c056680d61c64741bd2ff246f (patch) | |
tree | 99a96186dae129398300db241fbf5eff6bfd7d44 /drivers/media | |
parent | 09c8dd8de67cf781be95d809cd45af22f40c37df (diff) |
[media] lirc_dev: rework storage for cdev data
Fixes an oops when an lirc driver that doesn't provide its own fops is
unplugged while the lirc cdev is open. Tested with lirc_igorplugusb,
with a special thanks to Timo Boettcher for providing the test hardware.
Signed-off-by: Jarod Wilson <jarod@redhat.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
Diffstat (limited to 'drivers/media')
-rw-r--r-- | drivers/media/IR/lirc_dev.c | 40 |
1 files changed, 23 insertions, 17 deletions
diff --git a/drivers/media/IR/lirc_dev.c b/drivers/media/IR/lirc_dev.c index 8fdb7e11a138..3ef15625fa84 100644 --- a/drivers/media/IR/lirc_dev.c +++ b/drivers/media/IR/lirc_dev.c | |||
@@ -58,13 +58,12 @@ struct irctl { | |||
58 | 58 | ||
59 | struct task_struct *task; | 59 | struct task_struct *task; |
60 | long jiffies_to_wait; | 60 | long jiffies_to_wait; |
61 | |||
62 | struct cdev cdev; | ||
63 | }; | 61 | }; |
64 | 62 | ||
65 | static DEFINE_MUTEX(lirc_dev_lock); | 63 | static DEFINE_MUTEX(lirc_dev_lock); |
66 | 64 | ||
67 | static struct irctl *irctls[MAX_IRCTL_DEVICES]; | 65 | static struct irctl *irctls[MAX_IRCTL_DEVICES]; |
66 | static struct cdev cdevs[MAX_IRCTL_DEVICES]; | ||
68 | 67 | ||
69 | /* Only used for sysfs but defined to void otherwise */ | 68 | /* Only used for sysfs but defined to void otherwise */ |
70 | static struct class *lirc_class; | 69 | static struct class *lirc_class; |
@@ -170,19 +169,20 @@ static int lirc_cdev_add(struct irctl *ir) | |||
170 | { | 169 | { |
171 | int retval; | 170 | int retval; |
172 | struct lirc_driver *d = &ir->d; | 171 | struct lirc_driver *d = &ir->d; |
172 | struct cdev *cdev = &cdevs[d->minor]; | ||
173 | 173 | ||
174 | if (d->fops) { | 174 | if (d->fops) { |
175 | cdev_init(&ir->cdev, d->fops); | 175 | cdev_init(cdev, d->fops); |
176 | ir->cdev.owner = d->owner; | 176 | cdev->owner = d->owner; |
177 | } else { | 177 | } else { |
178 | cdev_init(&ir->cdev, &lirc_dev_fops); | 178 | cdev_init(cdev, &lirc_dev_fops); |
179 | ir->cdev.owner = THIS_MODULE; | 179 | cdev->owner = THIS_MODULE; |
180 | } | 180 | } |
181 | kobject_set_name(&ir->cdev.kobj, "lirc%d", d->minor); | 181 | kobject_set_name(&cdev->kobj, "lirc%d", d->minor); |
182 | 182 | ||
183 | retval = cdev_add(&ir->cdev, MKDEV(MAJOR(lirc_base_dev), d->minor), 1); | 183 | retval = cdev_add(cdev, MKDEV(MAJOR(lirc_base_dev), d->minor), 1); |
184 | if (retval) | 184 | if (retval) |
185 | kobject_put(&ir->cdev.kobj); | 185 | kobject_put(&cdev->kobj); |
186 | 186 | ||
187 | return retval; | 187 | return retval; |
188 | } | 188 | } |
@@ -363,6 +363,7 @@ EXPORT_SYMBOL(lirc_register_driver); | |||
363 | int lirc_unregister_driver(int minor) | 363 | int lirc_unregister_driver(int minor) |
364 | { | 364 | { |
365 | struct irctl *ir; | 365 | struct irctl *ir; |
366 | struct cdev *cdev; | ||
366 | 367 | ||
367 | if (minor < 0 || minor >= MAX_IRCTL_DEVICES) { | 368 | if (minor < 0 || minor >= MAX_IRCTL_DEVICES) { |
368 | printk(KERN_ERR "lirc_dev: %s: minor (%d) must be between " | 369 | printk(KERN_ERR "lirc_dev: %s: minor (%d) must be between " |
@@ -377,6 +378,8 @@ int lirc_unregister_driver(int minor) | |||
377 | return -ENOENT; | 378 | return -ENOENT; |
378 | } | 379 | } |
379 | 380 | ||
381 | cdev = &cdevs[minor]; | ||
382 | |||
380 | mutex_lock(&lirc_dev_lock); | 383 | mutex_lock(&lirc_dev_lock); |
381 | 384 | ||
382 | if (ir->d.minor != minor) { | 385 | if (ir->d.minor != minor) { |
@@ -400,11 +403,11 @@ int lirc_unregister_driver(int minor) | |||
400 | wake_up_interruptible(&ir->buf->wait_poll); | 403 | wake_up_interruptible(&ir->buf->wait_poll); |
401 | mutex_lock(&ir->irctl_lock); | 404 | mutex_lock(&ir->irctl_lock); |
402 | ir->d.set_use_dec(ir->d.data); | 405 | ir->d.set_use_dec(ir->d.data); |
403 | module_put(ir->cdev.owner); | 406 | module_put(cdev->owner); |
404 | mutex_unlock(&ir->irctl_lock); | 407 | mutex_unlock(&ir->irctl_lock); |
405 | } else { | 408 | } else { |
406 | lirc_irctl_cleanup(ir); | 409 | lirc_irctl_cleanup(ir); |
407 | cdev_del(&ir->cdev); | 410 | cdev_del(cdev); |
408 | kfree(ir); | 411 | kfree(ir); |
409 | irctls[minor] = NULL; | 412 | irctls[minor] = NULL; |
410 | } | 413 | } |
@@ -418,6 +421,7 @@ EXPORT_SYMBOL(lirc_unregister_driver); | |||
418 | int lirc_dev_fop_open(struct inode *inode, struct file *file) | 421 | int lirc_dev_fop_open(struct inode *inode, struct file *file) |
419 | { | 422 | { |
420 | struct irctl *ir; | 423 | struct irctl *ir; |
424 | struct cdev *cdev; | ||
421 | int retval = 0; | 425 | int retval = 0; |
422 | 426 | ||
423 | if (iminor(inode) >= MAX_IRCTL_DEVICES) { | 427 | if (iminor(inode) >= MAX_IRCTL_DEVICES) { |
@@ -447,13 +451,14 @@ int lirc_dev_fop_open(struct inode *inode, struct file *file) | |||
447 | goto error; | 451 | goto error; |
448 | } | 452 | } |
449 | 453 | ||
450 | if (try_module_get(ir->cdev.owner)) { | 454 | cdev = &cdevs[iminor(inode)]; |
451 | ++ir->open; | 455 | if (try_module_get(cdev->owner)) { |
456 | ir->open++; | ||
452 | retval = ir->d.set_use_inc(ir->d.data); | 457 | retval = ir->d.set_use_inc(ir->d.data); |
453 | 458 | ||
454 | if (retval) { | 459 | if (retval) { |
455 | module_put(ir->cdev.owner); | 460 | module_put(cdev->owner); |
456 | --ir->open; | 461 | ir->open--; |
457 | } else { | 462 | } else { |
458 | lirc_buffer_clear(ir->buf); | 463 | lirc_buffer_clear(ir->buf); |
459 | } | 464 | } |
@@ -475,6 +480,7 @@ EXPORT_SYMBOL(lirc_dev_fop_open); | |||
475 | int lirc_dev_fop_close(struct inode *inode, struct file *file) | 480 | int lirc_dev_fop_close(struct inode *inode, struct file *file) |
476 | { | 481 | { |
477 | struct irctl *ir = irctls[iminor(inode)]; | 482 | struct irctl *ir = irctls[iminor(inode)]; |
483 | struct cdev *cdev = &cdevs[iminor(inode)]; | ||
478 | 484 | ||
479 | if (!ir) { | 485 | if (!ir) { |
480 | printk(KERN_ERR "%s: called with invalid irctl\n", __func__); | 486 | printk(KERN_ERR "%s: called with invalid irctl\n", __func__); |
@@ -488,10 +494,10 @@ int lirc_dev_fop_close(struct inode *inode, struct file *file) | |||
488 | ir->open--; | 494 | ir->open--; |
489 | if (ir->attached) { | 495 | if (ir->attached) { |
490 | ir->d.set_use_dec(ir->d.data); | 496 | ir->d.set_use_dec(ir->d.data); |
491 | module_put(ir->cdev.owner); | 497 | module_put(cdev->owner); |
492 | } else { | 498 | } else { |
493 | lirc_irctl_cleanup(ir); | 499 | lirc_irctl_cleanup(ir); |
494 | cdev_del(&ir->cdev); | 500 | cdev_del(cdev); |
495 | irctls[ir->d.minor] = NULL; | 501 | irctls[ir->d.minor] = NULL; |
496 | kfree(ir); | 502 | kfree(ir); |
497 | } | 503 | } |