summaryrefslogtreecommitdiffstats
path: root/drivers/media/IR/lirc_dev.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/IR/lirc_dev.c')
-rw-r--r--drivers/media/IR/lirc_dev.c133
1 files changed, 89 insertions, 44 deletions
diff --git a/drivers/media/IR/lirc_dev.c b/drivers/media/IR/lirc_dev.c
index 202581808bd..8418b14ee4d 100644
--- a/drivers/media/IR/lirc_dev.c
+++ b/drivers/media/IR/lirc_dev.c
@@ -57,13 +57,12 @@ struct irctl {
struct task_struct *task;
long jiffies_to_wait;
-
- struct cdev cdev;
};
static DEFINE_MUTEX(lirc_dev_lock);
static struct irctl *irctls[MAX_IRCTL_DEVICES];
+static struct cdev cdevs[MAX_IRCTL_DEVICES];
/* Only used for sysfs but defined to void otherwise */
static struct class *lirc_class;
@@ -71,15 +70,13 @@ static struct class *lirc_class;
/* helper function
* initializes the irctl structure
*/
-static void init_irctl(struct irctl *ir)
+static void lirc_irctl_init(struct irctl *ir)
{
- dev_dbg(ir->d.dev, LOGHEAD "initializing irctl\n",
- ir->d.name, ir->d.minor);
mutex_init(&ir->irctl_lock);
ir->d.minor = NOPLUG;
}
-static void cleanup(struct irctl *ir)
+static void lirc_irctl_cleanup(struct irctl *ir)
{
dev_dbg(ir->d.dev, LOGHEAD "cleaning up\n", ir->d.name, ir->d.minor);
@@ -96,7 +93,7 @@ static void cleanup(struct irctl *ir)
* reads key codes from driver and puts them into buffer
* returns 0 on success
*/
-static int add_to_buf(struct irctl *ir)
+static int lirc_add_to_buf(struct irctl *ir)
{
if (ir->d.add_to_buf) {
int res = -ENODATA;
@@ -139,7 +136,7 @@ static int lirc_thread(void *irctl)
}
if (kthread_should_stop())
break;
- if (!add_to_buf(ir))
+ if (!lirc_add_to_buf(ir))
wake_up_interruptible(&ir->buf->wait_poll);
} else {
set_current_state(TASK_INTERRUPTIBLE);
@@ -154,12 +151,15 @@ static int lirc_thread(void *irctl)
}
-static struct file_operations fops = {
+static struct file_operations lirc_dev_fops = {
.owner = THIS_MODULE,
.read = lirc_dev_fop_read,
.write = lirc_dev_fop_write,
.poll = lirc_dev_fop_poll,
.unlocked_ioctl = lirc_dev_fop_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = lirc_dev_fop_ioctl,
+#endif
.open = lirc_dev_fop_open,
.release = lirc_dev_fop_close,
.llseek = noop_llseek,
@@ -169,19 +169,20 @@ static int lirc_cdev_add(struct irctl *ir)
{
int retval;
struct lirc_driver *d = &ir->d;
+ struct cdev *cdev = &cdevs[d->minor];
if (d->fops) {
- cdev_init(&ir->cdev, d->fops);
- ir->cdev.owner = d->owner;
+ cdev_init(cdev, d->fops);
+ cdev->owner = d->owner;
} else {
- cdev_init(&ir->cdev, &fops);
- ir->cdev.owner = THIS_MODULE;
+ cdev_init(cdev, &lirc_dev_fops);
+ cdev->owner = THIS_MODULE;
}
- kobject_set_name(&ir->cdev.kobj, "lirc%d", d->minor);
+ kobject_set_name(&cdev->kobj, "lirc%d", d->minor);
- retval = cdev_add(&ir->cdev, MKDEV(MAJOR(lirc_base_dev), d->minor), 1);
+ retval = cdev_add(cdev, MKDEV(MAJOR(lirc_base_dev), d->minor), 1);
if (retval)
- kobject_put(&ir->cdev.kobj);
+ kobject_put(&cdev->kobj);
return retval;
}
@@ -202,6 +203,12 @@ int lirc_register_driver(struct lirc_driver *d)
goto out;
}
+ if (!d->dev) {
+ printk(KERN_ERR "%s: dev pointer not filled in!\n", __func__);
+ err = -EINVAL;
+ goto out;
+ }
+
if (MAX_IRCTL_DEVICES <= d->minor) {
dev_err(d->dev, "lirc_dev: lirc_register_driver: "
"\"minor\" must be between 0 and %d (%d)!\n",
@@ -277,7 +284,7 @@ int lirc_register_driver(struct lirc_driver *d)
err = -ENOMEM;
goto out_lock;
}
- init_irctl(ir);
+ lirc_irctl_init(ir);
irctls[minor] = ir;
d->minor = minor;
@@ -316,7 +323,6 @@ int lirc_register_driver(struct lirc_driver *d)
d->features = LIRC_CAN_REC_LIRCCODE;
ir->d = *d;
- ir->d.minor = minor;
device_create(lirc_class, ir->d.dev,
MKDEV(MAJOR(lirc_base_dev), ir->d.minor), NULL,
@@ -357,21 +363,28 @@ EXPORT_SYMBOL(lirc_register_driver);
int lirc_unregister_driver(int minor)
{
struct irctl *ir;
+ struct cdev *cdev;
if (minor < 0 || minor >= MAX_IRCTL_DEVICES) {
- printk(KERN_ERR "lirc_dev: lirc_unregister_driver: "
- "\"minor (%d)\" must be between 0 and %d!\n",
- minor, MAX_IRCTL_DEVICES-1);
+ printk(KERN_ERR "lirc_dev: %s: minor (%d) must be between "
+ "0 and %d!\n", __func__, minor, MAX_IRCTL_DEVICES-1);
return -EBADRQC;
}
ir = irctls[minor];
+ if (!ir) {
+ printk(KERN_ERR "lirc_dev: %s: failed to get irctl struct "
+ "for minor %d!\n", __func__, minor);
+ return -ENOENT;
+ }
+
+ cdev = &cdevs[minor];
mutex_lock(&lirc_dev_lock);
if (ir->d.minor != minor) {
- printk(KERN_ERR "lirc_dev: lirc_unregister_driver: "
- "minor (%d) device not registered!", minor);
+ printk(KERN_ERR "lirc_dev: %s: minor (%d) device not "
+ "registered!\n", __func__, minor);
mutex_unlock(&lirc_dev_lock);
return -ENOENT;
}
@@ -390,12 +403,11 @@ int lirc_unregister_driver(int minor)
wake_up_interruptible(&ir->buf->wait_poll);
mutex_lock(&ir->irctl_lock);
ir->d.set_use_dec(ir->d.data);
- module_put(ir->d.owner);
+ module_put(cdev->owner);
mutex_unlock(&ir->irctl_lock);
- cdev_del(&ir->cdev);
} else {
- cleanup(ir);
- cdev_del(&ir->cdev);
+ lirc_irctl_cleanup(ir);
+ cdev_del(cdev);
kfree(ir);
irctls[minor] = NULL;
}
@@ -409,6 +421,7 @@ 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) {
@@ -425,7 +438,6 @@ int lirc_dev_fop_open(struct inode *inode, struct file *file)
retval = -ENODEV;
goto error;
}
- file->private_data = ir;
dev_dbg(ir->d.dev, LOGHEAD "open called\n", ir->d.name, ir->d.minor);
@@ -439,13 +451,14 @@ int lirc_dev_fop_open(struct inode *inode, struct file *file)
goto error;
}
- if (try_module_get(ir->d.owner)) {
- ++ir->open;
+ cdev = &cdevs[iminor(inode)];
+ if (try_module_get(cdev->owner)) {
+ ir->open++;
retval = ir->d.set_use_inc(ir->d.data);
if (retval) {
- module_put(ir->d.owner);
- --ir->open;
+ module_put(cdev->owner);
+ ir->open--;
} else {
lirc_buffer_clear(ir->buf);
}
@@ -469,17 +482,24 @@ 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 = &cdevs[iminor(inode)];
+
+ if (!ir) {
+ printk(KERN_ERR "%s: called with invalid irctl\n", __func__);
+ return -EINVAL;
+ }
dev_dbg(ir->d.dev, LOGHEAD "close called\n", ir->d.name, ir->d.minor);
WARN_ON(mutex_lock_killable(&lirc_dev_lock));
- --ir->open;
+ ir->open--;
if (ir->attached) {
ir->d.set_use_dec(ir->d.data);
- module_put(ir->d.owner);
+ module_put(cdev->owner);
} else {
- cleanup(ir);
+ lirc_irctl_cleanup(ir);
+ cdev_del(cdev);
irctls[ir->d.minor] = NULL;
kfree(ir);
}
@@ -495,6 +515,11 @@ unsigned int lirc_dev_fop_poll(struct file *file, poll_table *wait)
struct irctl *ir = irctls[iminor(file->f_dentry->d_inode)];
unsigned int ret;
+ if (!ir) {
+ printk(KERN_ERR "%s: called with invalid irctl\n", __func__);
+ return POLLERR;
+ }
+
dev_dbg(ir->d.dev, LOGHEAD "poll called\n", ir->d.name, ir->d.minor);
if (!ir->attached) {
@@ -521,9 +546,14 @@ EXPORT_SYMBOL(lirc_dev_fop_poll);
long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
- unsigned long mode;
+ __u32 mode;
int result = 0;
- struct irctl *ir = file->private_data;
+ struct irctl *ir = irctls[iminor(file->f_dentry->d_inode)];
+
+ if (!ir) {
+ printk(KERN_ERR "lirc_dev: %s: no irctl found!\n", __func__);
+ return -ENODEV;
+ }
dev_dbg(ir->d.dev, LOGHEAD "ioctl called (0x%x)\n",
ir->d.name, ir->d.minor, cmd);
@@ -538,7 +568,7 @@ long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
switch (cmd) {
case LIRC_GET_FEATURES:
- result = put_user(ir->d.features, (unsigned long *)arg);
+ result = put_user(ir->d.features, (__u32 *)arg);
break;
case LIRC_GET_REC_MODE:
if (!(ir->d.features & LIRC_CAN_REC_MASK)) {
@@ -548,7 +578,7 @@ long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
result = put_user(LIRC_REC2MODE
(ir->d.features & LIRC_CAN_REC_MASK),
- (unsigned long *)arg);
+ (__u32 *)arg);
break;
case LIRC_SET_REC_MODE:
if (!(ir->d.features & LIRC_CAN_REC_MASK)) {
@@ -556,7 +586,7 @@ long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
break;
}
- result = get_user(mode, (unsigned long *)arg);
+ result = get_user(mode, (__u32 *)arg);
if (!result && !(LIRC_MODE2REC(mode) & ir->d.features))
result = -EINVAL;
/*
@@ -565,7 +595,7 @@ long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
*/
break;
case LIRC_GET_LENGTH:
- result = put_user(ir->d.code_length, (unsigned long *)arg);
+ result = put_user(ir->d.code_length, (__u32 *)arg);
break;
case LIRC_GET_MIN_TIMEOUT:
if (!(ir->d.features & LIRC_CAN_SET_REC_TIMEOUT) ||
@@ -574,7 +604,7 @@ long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
break;
}
- result = put_user(ir->d.min_timeout, (unsigned long *)arg);
+ result = put_user(ir->d.min_timeout, (__u32 *)arg);
break;
case LIRC_GET_MAX_TIMEOUT:
if (!(ir->d.features & LIRC_CAN_SET_REC_TIMEOUT) ||
@@ -583,7 +613,7 @@ long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
break;
}
- result = put_user(ir->d.max_timeout, (unsigned long *)arg);
+ result = put_user(ir->d.max_timeout, (__u32 *)arg);
break;
default:
result = -EINVAL;
@@ -604,12 +634,21 @@ ssize_t lirc_dev_fop_read(struct file *file,
loff_t *ppos)
{
struct irctl *ir = irctls[iminor(file->f_dentry->d_inode)];
- unsigned char buf[ir->chunk_size];
+ unsigned char *buf;
int ret = 0, written = 0;
DECLARE_WAITQUEUE(wait, current);
+ if (!ir) {
+ printk(KERN_ERR "%s: called with invalid irctl\n", __func__);
+ return -ENODEV;
+ }
+
dev_dbg(ir->d.dev, LOGHEAD "read called\n", ir->d.name, ir->d.minor);
+ buf = kzalloc(ir->chunk_size, GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
if (mutex_lock_interruptible(&ir->irctl_lock))
return -ERESTARTSYS;
if (!ir->attached) {
@@ -681,6 +720,7 @@ ssize_t lirc_dev_fop_read(struct file *file,
mutex_unlock(&ir->irctl_lock);
out_unlocked:
+ kfree(buf);
dev_dbg(ir->d.dev, LOGHEAD "read result = %s (%d)\n",
ir->d.name, ir->d.minor, ret ? "-EFAULT" : "OK", ret);
@@ -709,6 +749,11 @@ ssize_t lirc_dev_fop_write(struct file *file, const char *buffer,
{
struct irctl *ir = irctls[iminor(file->f_dentry->d_inode)];
+ if (!ir) {
+ printk(KERN_ERR "%s: called with invalid irctl\n", __func__);
+ return -ENODEV;
+ }
+
dev_dbg(ir->d.dev, LOGHEAD "write called\n", ir->d.name, ir->d.minor);
if (!ir->attached)