xref: /openbmc/linux/drivers/media/rc/lirc_dev.c (revision 463015dd)
132cf86f6SMauro Carvalho Chehab /*
232cf86f6SMauro Carvalho Chehab  * LIRC base driver
332cf86f6SMauro Carvalho Chehab  *
432cf86f6SMauro Carvalho Chehab  * by Artur Lipowski <alipowski@interia.pl>
532cf86f6SMauro Carvalho Chehab  *
632cf86f6SMauro Carvalho Chehab  *  This program is free software; you can redistribute it and/or modify
732cf86f6SMauro Carvalho Chehab  *  it under the terms of the GNU General Public License as published by
832cf86f6SMauro Carvalho Chehab  *  the Free Software Foundation; either version 2 of the License, or
932cf86f6SMauro Carvalho Chehab  *  (at your option) any later version.
1032cf86f6SMauro Carvalho Chehab  *
1132cf86f6SMauro Carvalho Chehab  *  This program is distributed in the hope that it will be useful,
1232cf86f6SMauro Carvalho Chehab  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
1332cf86f6SMauro Carvalho Chehab  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1432cf86f6SMauro Carvalho Chehab  *  GNU General Public License for more details.
1532cf86f6SMauro Carvalho Chehab  *
1632cf86f6SMauro Carvalho Chehab  */
1732cf86f6SMauro Carvalho Chehab 
183fac0314SAndi Shyti #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
193fac0314SAndi Shyti 
2032cf86f6SMauro Carvalho Chehab #include <linux/module.h>
21174cd4b1SIngo Molnar #include <linux/sched/signal.h>
2232cf86f6SMauro Carvalho Chehab #include <linux/ioctl.h>
2332cf86f6SMauro Carvalho Chehab #include <linux/poll.h>
2432cf86f6SMauro Carvalho Chehab #include <linux/mutex.h>
2532cf86f6SMauro Carvalho Chehab #include <linux/device.h>
2632cf86f6SMauro Carvalho Chehab #include <linux/cdev.h>
2732cf86f6SMauro Carvalho Chehab 
28ca7a722dSSrinivas Kandagatla #include <media/rc-core.h>
2932cf86f6SMauro Carvalho Chehab #include <media/lirc.h>
3032cf86f6SMauro Carvalho Chehab #include <media/lirc_dev.h>
3132cf86f6SMauro Carvalho Chehab 
3232cf86f6SMauro Carvalho Chehab #define NOPLUG		-1
3332cf86f6SMauro Carvalho Chehab #define LOGHEAD		"lirc_dev (%s[%d]): "
3432cf86f6SMauro Carvalho Chehab 
3532cf86f6SMauro Carvalho Chehab static dev_t lirc_base_dev;
3632cf86f6SMauro Carvalho Chehab 
3732cf86f6SMauro Carvalho Chehab struct irctl {
3832cf86f6SMauro Carvalho Chehab 	struct lirc_driver d;
3932cf86f6SMauro Carvalho Chehab 	int attached;
4032cf86f6SMauro Carvalho Chehab 	int open;
4132cf86f6SMauro Carvalho Chehab 
4232cf86f6SMauro Carvalho Chehab 	struct mutex irctl_lock;
4332cf86f6SMauro Carvalho Chehab 	struct lirc_buffer *buf;
440f7c4063SDavid Härdeman 	bool buf_internal;
4532cf86f6SMauro Carvalho Chehab 	unsigned int chunk_size;
4632cf86f6SMauro Carvalho Chehab 
4774c839b2SSean Young 	struct device dev;
4874c839b2SSean Young 	struct cdev cdev;
4932cf86f6SMauro Carvalho Chehab };
5032cf86f6SMauro Carvalho Chehab 
5132cf86f6SMauro Carvalho Chehab static DEFINE_MUTEX(lirc_dev_lock);
5232cf86f6SMauro Carvalho Chehab 
5332cf86f6SMauro Carvalho Chehab static struct irctl *irctls[MAX_IRCTL_DEVICES];
5432cf86f6SMauro Carvalho Chehab 
5532cf86f6SMauro Carvalho Chehab /* Only used for sysfs but defined to void otherwise */
5632cf86f6SMauro Carvalho Chehab static struct class *lirc_class;
5732cf86f6SMauro Carvalho Chehab 
5874c839b2SSean Young static void lirc_release(struct device *ld)
5932cf86f6SMauro Carvalho Chehab {
6074c839b2SSean Young 	struct irctl *ir = container_of(ld, struct irctl, dev);
6174c839b2SSean Young 
620f7c4063SDavid Härdeman 	if (ir->buf_internal) {
6332cf86f6SMauro Carvalho Chehab 		lirc_buffer_free(ir->buf);
6432cf86f6SMauro Carvalho Chehab 		kfree(ir->buf);
6532cf86f6SMauro Carvalho Chehab 	}
6674c839b2SSean Young 
6774c839b2SSean Young 	mutex_lock(&lirc_dev_lock);
6874c839b2SSean Young 	irctls[ir->d.minor] = NULL;
6974c839b2SSean Young 	mutex_unlock(&lirc_dev_lock);
7074c839b2SSean Young 	kfree(ir);
7132cf86f6SMauro Carvalho Chehab }
7232cf86f6SMauro Carvalho Chehab 
736fa99e1aSAndi Shyti static int lirc_allocate_buffer(struct irctl *ir)
746fa99e1aSAndi Shyti {
7570143984SAndi Shyti 	int err = 0;
766fa99e1aSAndi Shyti 	int bytes_in_key;
776fa99e1aSAndi Shyti 	unsigned int chunk_size;
786fa99e1aSAndi Shyti 	unsigned int buffer_size;
796fa99e1aSAndi Shyti 	struct lirc_driver *d = &ir->d;
806fa99e1aSAndi Shyti 
816fa99e1aSAndi Shyti 	bytes_in_key = BITS_TO_LONGS(d->code_length) +
826fa99e1aSAndi Shyti 						(d->code_length % 8 ? 1 : 0);
836fa99e1aSAndi Shyti 	buffer_size = d->buffer_size ? d->buffer_size : BUFLEN / bytes_in_key;
846fa99e1aSAndi Shyti 	chunk_size  = d->chunk_size  ? d->chunk_size  : bytes_in_key;
856fa99e1aSAndi Shyti 
866fa99e1aSAndi Shyti 	if (d->rbuf) {
876fa99e1aSAndi Shyti 		ir->buf = d->rbuf;
880f7c4063SDavid Härdeman 		ir->buf_internal = false;
896fa99e1aSAndi Shyti 	} else {
906fa99e1aSAndi Shyti 		ir->buf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
9170143984SAndi Shyti 		if (!ir->buf) {
9270143984SAndi Shyti 			err = -ENOMEM;
9370143984SAndi Shyti 			goto out;
9470143984SAndi Shyti 		}
956fa99e1aSAndi Shyti 
966fa99e1aSAndi Shyti 		err = lirc_buffer_init(ir->buf, chunk_size, buffer_size);
976fa99e1aSAndi Shyti 		if (err) {
986fa99e1aSAndi Shyti 			kfree(ir->buf);
990f7c4063SDavid Härdeman 			ir->buf = NULL;
10070143984SAndi Shyti 			goto out;
1016fa99e1aSAndi Shyti 		}
1020f7c4063SDavid Härdeman 
1030f7c4063SDavid Härdeman 		ir->buf_internal = true;
10456481f00SDavid Härdeman 		d->rbuf = ir->buf;
1056fa99e1aSAndi Shyti 	}
1066fa99e1aSAndi Shyti 	ir->chunk_size = ir->buf->chunk_size;
1076fa99e1aSAndi Shyti 
10870143984SAndi Shyti out:
10970143984SAndi Shyti 	return err;
1106fa99e1aSAndi Shyti }
1116fa99e1aSAndi Shyti 
11256481f00SDavid Härdeman int lirc_register_driver(struct lirc_driver *d)
11332cf86f6SMauro Carvalho Chehab {
11432cf86f6SMauro Carvalho Chehab 	struct irctl *ir;
11532cf86f6SMauro Carvalho Chehab 	int minor;
11632cf86f6SMauro Carvalho Chehab 	int err;
11732cf86f6SMauro Carvalho Chehab 
11832cf86f6SMauro Carvalho Chehab 	if (!d) {
1193fac0314SAndi Shyti 		pr_err("driver pointer must be not NULL!\n");
12054fcecafSAndi Shyti 		return -EBADRQC;
12132cf86f6SMauro Carvalho Chehab 	}
12232cf86f6SMauro Carvalho Chehab 
12332cf86f6SMauro Carvalho Chehab 	if (!d->dev) {
1243fac0314SAndi Shyti 		pr_err("dev pointer not filled in!\n");
12554fcecafSAndi Shyti 		return -EINVAL;
12632cf86f6SMauro Carvalho Chehab 	}
12732cf86f6SMauro Carvalho Chehab 
128712551f0SDavid Härdeman 	if (!d->fops) {
129712551f0SDavid Härdeman 		pr_err("fops pointer not filled in!\n");
130712551f0SDavid Härdeman 		return -EINVAL;
131712551f0SDavid Härdeman 	}
132712551f0SDavid Härdeman 
1339675ee5aSAndi Shyti 	if (d->minor >= MAX_IRCTL_DEVICES) {
1343fac0314SAndi Shyti 		dev_err(d->dev, "minor must be between 0 and %d!\n",
1353fac0314SAndi Shyti 						MAX_IRCTL_DEVICES - 1);
13654fcecafSAndi Shyti 		return -EBADRQC;
13732cf86f6SMauro Carvalho Chehab 	}
13832cf86f6SMauro Carvalho Chehab 
1399675ee5aSAndi Shyti 	if (d->code_length < 1 || d->code_length > (BUFLEN * 8)) {
1403fac0314SAndi Shyti 		dev_err(d->dev, "code length must be less than %d bits\n",
1413fac0314SAndi Shyti 								BUFLEN * 8);
14254fcecafSAndi Shyti 		return -EBADRQC;
14332cf86f6SMauro Carvalho Chehab 	}
14432cf86f6SMauro Carvalho Chehab 
145c3104e1bSDavid Härdeman 	if (!d->rbuf && !(d->fops && d->fops->read &&
14614db9fc2SAndi Shyti 			  d->fops->poll && d->fops->unlocked_ioctl)) {
1473fac0314SAndi Shyti 		dev_err(d->dev, "undefined read, poll, ioctl\n");
14854fcecafSAndi Shyti 		return -EBADRQC;
14932cf86f6SMauro Carvalho Chehab 	}
15032cf86f6SMauro Carvalho Chehab 
15132cf86f6SMauro Carvalho Chehab 	mutex_lock(&lirc_dev_lock);
15232cf86f6SMauro Carvalho Chehab 
15332cf86f6SMauro Carvalho Chehab 	minor = d->minor;
15432cf86f6SMauro Carvalho Chehab 
15532cf86f6SMauro Carvalho Chehab 	if (minor < 0) {
15632cf86f6SMauro Carvalho Chehab 		/* find first free slot for driver */
15732cf86f6SMauro Carvalho Chehab 		for (minor = 0; minor < MAX_IRCTL_DEVICES; minor++)
15832cf86f6SMauro Carvalho Chehab 			if (!irctls[minor])
15932cf86f6SMauro Carvalho Chehab 				break;
1609675ee5aSAndi Shyti 		if (minor == MAX_IRCTL_DEVICES) {
1613fac0314SAndi Shyti 			dev_err(d->dev, "no free slots for drivers!\n");
16232cf86f6SMauro Carvalho Chehab 			err = -ENOMEM;
16332cf86f6SMauro Carvalho Chehab 			goto out_lock;
16432cf86f6SMauro Carvalho Chehab 		}
16532cf86f6SMauro Carvalho Chehab 	} else if (irctls[minor]) {
1663fac0314SAndi Shyti 		dev_err(d->dev, "minor (%d) just registered!\n", minor);
16732cf86f6SMauro Carvalho Chehab 		err = -EBUSY;
16832cf86f6SMauro Carvalho Chehab 		goto out_lock;
16932cf86f6SMauro Carvalho Chehab 	}
17032cf86f6SMauro Carvalho Chehab 
17132cf86f6SMauro Carvalho Chehab 	ir = kzalloc(sizeof(struct irctl), GFP_KERNEL);
17232cf86f6SMauro Carvalho Chehab 	if (!ir) {
17332cf86f6SMauro Carvalho Chehab 		err = -ENOMEM;
17432cf86f6SMauro Carvalho Chehab 		goto out_lock;
17532cf86f6SMauro Carvalho Chehab 	}
176712551f0SDavid Härdeman 
177712551f0SDavid Härdeman 	mutex_init(&ir->irctl_lock);
17832cf86f6SMauro Carvalho Chehab 	irctls[minor] = ir;
17932cf86f6SMauro Carvalho Chehab 	d->minor = minor;
18032cf86f6SMauro Carvalho Chehab 
18132cf86f6SMauro Carvalho Chehab 	/* some safety check 8-) */
18232cf86f6SMauro Carvalho Chehab 	d->name[sizeof(d->name)-1] = '\0';
18332cf86f6SMauro Carvalho Chehab 
18432cf86f6SMauro Carvalho Chehab 	if (d->features == 0)
18532cf86f6SMauro Carvalho Chehab 		d->features = LIRC_CAN_REC_LIRCCODE;
18632cf86f6SMauro Carvalho Chehab 
18732cf86f6SMauro Carvalho Chehab 	ir->d = *d;
18832cf86f6SMauro Carvalho Chehab 
18956481f00SDavid Härdeman 	if (LIRC_CAN_REC(d->features)) {
19056481f00SDavid Härdeman 		err = lirc_allocate_buffer(irctls[minor]);
19156481f00SDavid Härdeman 		if (err) {
19256481f00SDavid Härdeman 			kfree(ir);
19356481f00SDavid Härdeman 			goto out_lock;
19456481f00SDavid Härdeman 		}
19556481f00SDavid Härdeman 		d->rbuf = ir->buf;
19656481f00SDavid Härdeman 	}
19756481f00SDavid Härdeman 
19825823226SDavid Härdeman 	device_initialize(&ir->dev);
19974c839b2SSean Young 	ir->dev.devt = MKDEV(MAJOR(lirc_base_dev), ir->d.minor);
20074c839b2SSean Young 	ir->dev.class = lirc_class;
20174c839b2SSean Young 	ir->dev.parent = d->dev;
20274c839b2SSean Young 	ir->dev.release = lirc_release;
20374c839b2SSean Young 	dev_set_name(&ir->dev, "lirc%d", ir->d.minor);
20432cf86f6SMauro Carvalho Chehab 
205712551f0SDavid Härdeman 	cdev_init(&ir->cdev, d->fops);
206712551f0SDavid Härdeman 	ir->cdev.owner = ir->d.owner;
207712551f0SDavid Härdeman 	ir->cdev.kobj.parent = &ir->dev.kobj;
208712551f0SDavid Härdeman 
209712551f0SDavid Härdeman 	err = cdev_add(&ir->cdev, ir->dev.devt, 1);
21032cf86f6SMauro Carvalho Chehab 	if (err)
21125823226SDavid Härdeman 		goto out_free_dev;
21232cf86f6SMauro Carvalho Chehab 
21332cf86f6SMauro Carvalho Chehab 	ir->attached = 1;
21474c839b2SSean Young 
21574c839b2SSean Young 	err = device_add(&ir->dev);
21674c839b2SSean Young 	if (err)
21774c839b2SSean Young 		goto out_cdev;
21874c839b2SSean Young 
21932cf86f6SMauro Carvalho Chehab 	mutex_unlock(&lirc_dev_lock);
22032cf86f6SMauro Carvalho Chehab 
22132cf86f6SMauro Carvalho Chehab 	dev_info(ir->d.dev, "lirc_dev: driver %s registered at minor = %d\n",
22232cf86f6SMauro Carvalho Chehab 		 ir->d.name, ir->d.minor);
22356481f00SDavid Härdeman 
22432cf86f6SMauro Carvalho Chehab 	return minor;
22556481f00SDavid Härdeman 
22674c839b2SSean Young out_cdev:
22774c839b2SSean Young 	cdev_del(&ir->cdev);
22825823226SDavid Härdeman out_free_dev:
22974c839b2SSean Young 	put_device(&ir->dev);
23032cf86f6SMauro Carvalho Chehab out_lock:
23132cf86f6SMauro Carvalho Chehab 	mutex_unlock(&lirc_dev_lock);
23254fcecafSAndi Shyti 
23332cf86f6SMauro Carvalho Chehab 	return err;
23432cf86f6SMauro Carvalho Chehab }
23532cf86f6SMauro Carvalho Chehab EXPORT_SYMBOL(lirc_register_driver);
23632cf86f6SMauro Carvalho Chehab 
23732cf86f6SMauro Carvalho Chehab int lirc_unregister_driver(int minor)
23832cf86f6SMauro Carvalho Chehab {
23932cf86f6SMauro Carvalho Chehab 	struct irctl *ir;
24032cf86f6SMauro Carvalho Chehab 
24132cf86f6SMauro Carvalho Chehab 	if (minor < 0 || minor >= MAX_IRCTL_DEVICES) {
2423fac0314SAndi Shyti 		pr_err("minor (%d) must be between 0 and %d!\n",
2433fac0314SAndi Shyti 					minor, MAX_IRCTL_DEVICES - 1);
24432cf86f6SMauro Carvalho Chehab 		return -EBADRQC;
24532cf86f6SMauro Carvalho Chehab 	}
24632cf86f6SMauro Carvalho Chehab 
24732cf86f6SMauro Carvalho Chehab 	ir = irctls[minor];
24832cf86f6SMauro Carvalho Chehab 	if (!ir) {
2493fac0314SAndi Shyti 		pr_err("failed to get irctl\n");
25032cf86f6SMauro Carvalho Chehab 		return -ENOENT;
25132cf86f6SMauro Carvalho Chehab 	}
25232cf86f6SMauro Carvalho Chehab 
25332cf86f6SMauro Carvalho Chehab 	mutex_lock(&lirc_dev_lock);
25432cf86f6SMauro Carvalho Chehab 
25532cf86f6SMauro Carvalho Chehab 	if (ir->d.minor != minor) {
2563fac0314SAndi Shyti 		dev_err(ir->d.dev, "lirc_dev: minor %d device not registered\n",
2573fac0314SAndi Shyti 									minor);
25832cf86f6SMauro Carvalho Chehab 		mutex_unlock(&lirc_dev_lock);
25932cf86f6SMauro Carvalho Chehab 		return -ENOENT;
26032cf86f6SMauro Carvalho Chehab 	}
26132cf86f6SMauro Carvalho Chehab 
26232cf86f6SMauro Carvalho Chehab 	dev_dbg(ir->d.dev, "lirc_dev: driver %s unregistered from minor = %d\n",
26332cf86f6SMauro Carvalho Chehab 		ir->d.name, ir->d.minor);
26432cf86f6SMauro Carvalho Chehab 
26532cf86f6SMauro Carvalho Chehab 	ir->attached = 0;
26632cf86f6SMauro Carvalho Chehab 	if (ir->open) {
26732cf86f6SMauro Carvalho Chehab 		dev_dbg(ir->d.dev, LOGHEAD "releasing opened driver\n",
26832cf86f6SMauro Carvalho Chehab 			ir->d.name, ir->d.minor);
26932cf86f6SMauro Carvalho Chehab 		wake_up_interruptible(&ir->buf->wait_poll);
27074c839b2SSean Young 	}
27174c839b2SSean Young 
27232cf86f6SMauro Carvalho Chehab 	mutex_unlock(&lirc_dev_lock);
27332cf86f6SMauro Carvalho Chehab 
27474c839b2SSean Young 	device_del(&ir->dev);
27574c839b2SSean Young 	cdev_del(&ir->cdev);
27674c839b2SSean Young 	put_device(&ir->dev);
27774c839b2SSean Young 
27832cf86f6SMauro Carvalho Chehab 	return 0;
27932cf86f6SMauro Carvalho Chehab }
28032cf86f6SMauro Carvalho Chehab EXPORT_SYMBOL(lirc_unregister_driver);
28132cf86f6SMauro Carvalho Chehab 
28232cf86f6SMauro Carvalho Chehab int lirc_dev_fop_open(struct inode *inode, struct file *file)
28332cf86f6SMauro Carvalho Chehab {
28432cf86f6SMauro Carvalho Chehab 	struct irctl *ir;
28532cf86f6SMauro Carvalho Chehab 	int retval = 0;
28632cf86f6SMauro Carvalho Chehab 
28732cf86f6SMauro Carvalho Chehab 	if (iminor(inode) >= MAX_IRCTL_DEVICES) {
2883fac0314SAndi Shyti 		pr_err("open result for %d is -ENODEV\n", iminor(inode));
28932cf86f6SMauro Carvalho Chehab 		return -ENODEV;
29032cf86f6SMauro Carvalho Chehab 	}
29132cf86f6SMauro Carvalho Chehab 
29232cf86f6SMauro Carvalho Chehab 	if (mutex_lock_interruptible(&lirc_dev_lock))
29332cf86f6SMauro Carvalho Chehab 		return -ERESTARTSYS;
29432cf86f6SMauro Carvalho Chehab 
29532cf86f6SMauro Carvalho Chehab 	ir = irctls[iminor(inode)];
296db5b15b7SSean Young 	mutex_unlock(&lirc_dev_lock);
297db5b15b7SSean Young 
29832cf86f6SMauro Carvalho Chehab 	if (!ir) {
29932cf86f6SMauro Carvalho Chehab 		retval = -ENODEV;
30032cf86f6SMauro Carvalho Chehab 		goto error;
30132cf86f6SMauro Carvalho Chehab 	}
30232cf86f6SMauro Carvalho Chehab 
30332cf86f6SMauro Carvalho Chehab 	dev_dbg(ir->d.dev, LOGHEAD "open called\n", ir->d.name, ir->d.minor);
30432cf86f6SMauro Carvalho Chehab 
30532cf86f6SMauro Carvalho Chehab 	if (ir->d.minor == NOPLUG) {
30632cf86f6SMauro Carvalho Chehab 		retval = -ENODEV;
30732cf86f6SMauro Carvalho Chehab 		goto error;
30832cf86f6SMauro Carvalho Chehab 	}
30932cf86f6SMauro Carvalho Chehab 
31032cf86f6SMauro Carvalho Chehab 	if (ir->open) {
31132cf86f6SMauro Carvalho Chehab 		retval = -EBUSY;
31232cf86f6SMauro Carvalho Chehab 		goto error;
31332cf86f6SMauro Carvalho Chehab 	}
31432cf86f6SMauro Carvalho Chehab 
315ca7a722dSSrinivas Kandagatla 	if (ir->d.rdev) {
316ca7a722dSSrinivas Kandagatla 		retval = rc_open(ir->d.rdev);
317ca7a722dSSrinivas Kandagatla 		if (retval)
318ca7a722dSSrinivas Kandagatla 			goto error;
319ca7a722dSSrinivas Kandagatla 	}
320ca7a722dSSrinivas Kandagatla 
32174c839b2SSean Young 	if (ir->buf)
32232cf86f6SMauro Carvalho Chehab 		lirc_buffer_clear(ir->buf);
3232c5a1f44SDavid Härdeman 
3242c5a1f44SDavid Härdeman 	ir->open++;
32532cf86f6SMauro Carvalho Chehab 
32632cf86f6SMauro Carvalho Chehab error:
32732cf86f6SMauro Carvalho Chehab 	nonseekable_open(inode, file);
32832cf86f6SMauro Carvalho Chehab 
32932cf86f6SMauro Carvalho Chehab 	return retval;
33032cf86f6SMauro Carvalho Chehab }
33132cf86f6SMauro Carvalho Chehab EXPORT_SYMBOL(lirc_dev_fop_open);
33232cf86f6SMauro Carvalho Chehab 
33332cf86f6SMauro Carvalho Chehab int lirc_dev_fop_close(struct inode *inode, struct file *file)
33432cf86f6SMauro Carvalho Chehab {
33532cf86f6SMauro Carvalho Chehab 	struct irctl *ir = irctls[iminor(inode)];
336b64e10f3SMauro Carvalho Chehab 	int ret;
33732cf86f6SMauro Carvalho Chehab 
33832cf86f6SMauro Carvalho Chehab 	if (!ir) {
3393fac0314SAndi Shyti 		pr_err("called with invalid irctl\n");
34032cf86f6SMauro Carvalho Chehab 		return -EINVAL;
34132cf86f6SMauro Carvalho Chehab 	}
34232cf86f6SMauro Carvalho Chehab 
343b64e10f3SMauro Carvalho Chehab 	ret = mutex_lock_killable(&lirc_dev_lock);
344b64e10f3SMauro Carvalho Chehab 	WARN_ON(ret);
34532cf86f6SMauro Carvalho Chehab 
346ca7a722dSSrinivas Kandagatla 	rc_close(ir->d.rdev);
347ca7a722dSSrinivas Kandagatla 
34832cf86f6SMauro Carvalho Chehab 	ir->open--;
349b64e10f3SMauro Carvalho Chehab 	if (!ret)
35032cf86f6SMauro Carvalho Chehab 		mutex_unlock(&lirc_dev_lock);
35132cf86f6SMauro Carvalho Chehab 
35232cf86f6SMauro Carvalho Chehab 	return 0;
35332cf86f6SMauro Carvalho Chehab }
35432cf86f6SMauro Carvalho Chehab EXPORT_SYMBOL(lirc_dev_fop_close);
35532cf86f6SMauro Carvalho Chehab 
35632cf86f6SMauro Carvalho Chehab unsigned int lirc_dev_fop_poll(struct file *file, poll_table *wait)
35732cf86f6SMauro Carvalho Chehab {
358496ad9aaSAl Viro 	struct irctl *ir = irctls[iminor(file_inode(file))];
35932cf86f6SMauro Carvalho Chehab 	unsigned int ret;
36032cf86f6SMauro Carvalho Chehab 
36132cf86f6SMauro Carvalho Chehab 	if (!ir) {
3623fac0314SAndi Shyti 		pr_err("called with invalid irctl\n");
36332cf86f6SMauro Carvalho Chehab 		return POLLERR;
36432cf86f6SMauro Carvalho Chehab 	}
36532cf86f6SMauro Carvalho Chehab 
36632cf86f6SMauro Carvalho Chehab 	if (!ir->attached)
36729debf3dSDavid Härdeman 		return POLLHUP | POLLERR;
36832cf86f6SMauro Carvalho Chehab 
3693656cdddSAndy Shevchenko 	if (ir->buf) {
37032cf86f6SMauro Carvalho Chehab 		poll_wait(file, &ir->buf->wait_poll, wait);
37132cf86f6SMauro Carvalho Chehab 
37232cf86f6SMauro Carvalho Chehab 		if (lirc_buffer_empty(ir->buf))
37332cf86f6SMauro Carvalho Chehab 			ret = 0;
37432cf86f6SMauro Carvalho Chehab 		else
37532cf86f6SMauro Carvalho Chehab 			ret = POLLIN | POLLRDNORM;
3763656cdddSAndy Shevchenko 	} else
37732cf86f6SMauro Carvalho Chehab 		ret = POLLERR;
37832cf86f6SMauro Carvalho Chehab 
37932cf86f6SMauro Carvalho Chehab 	dev_dbg(ir->d.dev, LOGHEAD "poll result = %d\n",
38032cf86f6SMauro Carvalho Chehab 		ir->d.name, ir->d.minor, ret);
38132cf86f6SMauro Carvalho Chehab 
38232cf86f6SMauro Carvalho Chehab 	return ret;
38332cf86f6SMauro Carvalho Chehab }
38432cf86f6SMauro Carvalho Chehab EXPORT_SYMBOL(lirc_dev_fop_poll);
38532cf86f6SMauro Carvalho Chehab 
38632cf86f6SMauro Carvalho Chehab long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
38732cf86f6SMauro Carvalho Chehab {
38832cf86f6SMauro Carvalho Chehab 	__u32 mode;
38932cf86f6SMauro Carvalho Chehab 	int result = 0;
390496ad9aaSAl Viro 	struct irctl *ir = irctls[iminor(file_inode(file))];
39132cf86f6SMauro Carvalho Chehab 
39232cf86f6SMauro Carvalho Chehab 	if (!ir) {
3933fac0314SAndi Shyti 		pr_err("no irctl found!\n");
39432cf86f6SMauro Carvalho Chehab 		return -ENODEV;
39532cf86f6SMauro Carvalho Chehab 	}
39632cf86f6SMauro Carvalho Chehab 
39732cf86f6SMauro Carvalho Chehab 	dev_dbg(ir->d.dev, LOGHEAD "ioctl called (0x%x)\n",
39832cf86f6SMauro Carvalho Chehab 		ir->d.name, ir->d.minor, cmd);
39932cf86f6SMauro Carvalho Chehab 
40032cf86f6SMauro Carvalho Chehab 	if (ir->d.minor == NOPLUG || !ir->attached) {
4013fac0314SAndi Shyti 		dev_err(ir->d.dev, LOGHEAD "ioctl result = -ENODEV\n",
40232cf86f6SMauro Carvalho Chehab 			ir->d.name, ir->d.minor);
40332cf86f6SMauro Carvalho Chehab 		return -ENODEV;
40432cf86f6SMauro Carvalho Chehab 	}
40532cf86f6SMauro Carvalho Chehab 
40632cf86f6SMauro Carvalho Chehab 	mutex_lock(&ir->irctl_lock);
40732cf86f6SMauro Carvalho Chehab 
40832cf86f6SMauro Carvalho Chehab 	switch (cmd) {
40932cf86f6SMauro Carvalho Chehab 	case LIRC_GET_FEATURES:
41060519af3SHans Verkuil 		result = put_user(ir->d.features, (__u32 __user *)arg);
41132cf86f6SMauro Carvalho Chehab 		break;
41232cf86f6SMauro Carvalho Chehab 	case LIRC_GET_REC_MODE:
413bd291208SSean Young 		if (!LIRC_CAN_REC(ir->d.features)) {
414b4088094SAndi Shyti 			result = -ENOTTY;
41532cf86f6SMauro Carvalho Chehab 			break;
41632cf86f6SMauro Carvalho Chehab 		}
41732cf86f6SMauro Carvalho Chehab 
41832cf86f6SMauro Carvalho Chehab 		result = put_user(LIRC_REC2MODE
41932cf86f6SMauro Carvalho Chehab 				  (ir->d.features & LIRC_CAN_REC_MASK),
42060519af3SHans Verkuil 				  (__u32 __user *)arg);
42132cf86f6SMauro Carvalho Chehab 		break;
42232cf86f6SMauro Carvalho Chehab 	case LIRC_SET_REC_MODE:
423bd291208SSean Young 		if (!LIRC_CAN_REC(ir->d.features)) {
424b4088094SAndi Shyti 			result = -ENOTTY;
42532cf86f6SMauro Carvalho Chehab 			break;
42632cf86f6SMauro Carvalho Chehab 		}
42732cf86f6SMauro Carvalho Chehab 
42860519af3SHans Verkuil 		result = get_user(mode, (__u32 __user *)arg);
42932cf86f6SMauro Carvalho Chehab 		if (!result && !(LIRC_MODE2REC(mode) & ir->d.features))
43032cf86f6SMauro Carvalho Chehab 			result = -EINVAL;
43132cf86f6SMauro Carvalho Chehab 		/*
43232cf86f6SMauro Carvalho Chehab 		 * FIXME: We should actually set the mode somehow but
43332cf86f6SMauro Carvalho Chehab 		 * for now, lirc_serial doesn't support mode changing either
43432cf86f6SMauro Carvalho Chehab 		 */
43532cf86f6SMauro Carvalho Chehab 		break;
43632cf86f6SMauro Carvalho Chehab 	case LIRC_GET_LENGTH:
43760519af3SHans Verkuil 		result = put_user(ir->d.code_length, (__u32 __user *)arg);
43832cf86f6SMauro Carvalho Chehab 		break;
43932cf86f6SMauro Carvalho Chehab 	case LIRC_GET_MIN_TIMEOUT:
44032cf86f6SMauro Carvalho Chehab 		if (!(ir->d.features & LIRC_CAN_SET_REC_TIMEOUT) ||
44132cf86f6SMauro Carvalho Chehab 		    ir->d.min_timeout == 0) {
442b4088094SAndi Shyti 			result = -ENOTTY;
44332cf86f6SMauro Carvalho Chehab 			break;
44432cf86f6SMauro Carvalho Chehab 		}
44532cf86f6SMauro Carvalho Chehab 
44660519af3SHans Verkuil 		result = put_user(ir->d.min_timeout, (__u32 __user *)arg);
44732cf86f6SMauro Carvalho Chehab 		break;
44832cf86f6SMauro Carvalho Chehab 	case LIRC_GET_MAX_TIMEOUT:
44932cf86f6SMauro Carvalho Chehab 		if (!(ir->d.features & LIRC_CAN_SET_REC_TIMEOUT) ||
45032cf86f6SMauro Carvalho Chehab 		    ir->d.max_timeout == 0) {
451b4088094SAndi Shyti 			result = -ENOTTY;
45232cf86f6SMauro Carvalho Chehab 			break;
45332cf86f6SMauro Carvalho Chehab 		}
45432cf86f6SMauro Carvalho Chehab 
45560519af3SHans Verkuil 		result = put_user(ir->d.max_timeout, (__u32 __user *)arg);
45632cf86f6SMauro Carvalho Chehab 		break;
45732cf86f6SMauro Carvalho Chehab 	default:
4585c862758SSean Young 		result = -ENOTTY;
45932cf86f6SMauro Carvalho Chehab 	}
46032cf86f6SMauro Carvalho Chehab 
46132cf86f6SMauro Carvalho Chehab 	mutex_unlock(&ir->irctl_lock);
46232cf86f6SMauro Carvalho Chehab 
46332cf86f6SMauro Carvalho Chehab 	return result;
46432cf86f6SMauro Carvalho Chehab }
46532cf86f6SMauro Carvalho Chehab EXPORT_SYMBOL(lirc_dev_fop_ioctl);
46632cf86f6SMauro Carvalho Chehab 
46732cf86f6SMauro Carvalho Chehab ssize_t lirc_dev_fop_read(struct file *file,
4680e835087SDan Carpenter 			  char __user *buffer,
46932cf86f6SMauro Carvalho Chehab 			  size_t length,
47032cf86f6SMauro Carvalho Chehab 			  loff_t *ppos)
47132cf86f6SMauro Carvalho Chehab {
472496ad9aaSAl Viro 	struct irctl *ir = irctls[iminor(file_inode(file))];
47332cf86f6SMauro Carvalho Chehab 	unsigned char *buf;
47432cf86f6SMauro Carvalho Chehab 	int ret = 0, written = 0;
47532cf86f6SMauro Carvalho Chehab 	DECLARE_WAITQUEUE(wait, current);
47632cf86f6SMauro Carvalho Chehab 
47732cf86f6SMauro Carvalho Chehab 	if (!ir) {
4783fac0314SAndi Shyti 		pr_err("called with invalid irctl\n");
47932cf86f6SMauro Carvalho Chehab 		return -ENODEV;
48032cf86f6SMauro Carvalho Chehab 	}
48132cf86f6SMauro Carvalho Chehab 
48232002f72SSean Young 	if (!LIRC_CAN_REC(ir->d.features))
48332002f72SSean Young 		return -EINVAL;
48432002f72SSean Young 
48532cf86f6SMauro Carvalho Chehab 	dev_dbg(ir->d.dev, LOGHEAD "read called\n", ir->d.name, ir->d.minor);
48632cf86f6SMauro Carvalho Chehab 
48732cf86f6SMauro Carvalho Chehab 	buf = kzalloc(ir->chunk_size, GFP_KERNEL);
48832cf86f6SMauro Carvalho Chehab 	if (!buf)
48932cf86f6SMauro Carvalho Chehab 		return -ENOMEM;
49032cf86f6SMauro Carvalho Chehab 
49132cf86f6SMauro Carvalho Chehab 	if (mutex_lock_interruptible(&ir->irctl_lock)) {
49232cf86f6SMauro Carvalho Chehab 		ret = -ERESTARTSYS;
49332cf86f6SMauro Carvalho Chehab 		goto out_unlocked;
49432cf86f6SMauro Carvalho Chehab 	}
49532cf86f6SMauro Carvalho Chehab 	if (!ir->attached) {
49632cf86f6SMauro Carvalho Chehab 		ret = -ENODEV;
49732cf86f6SMauro Carvalho Chehab 		goto out_locked;
49832cf86f6SMauro Carvalho Chehab 	}
49932cf86f6SMauro Carvalho Chehab 
50032cf86f6SMauro Carvalho Chehab 	if (length % ir->chunk_size) {
50132cf86f6SMauro Carvalho Chehab 		ret = -EINVAL;
50232cf86f6SMauro Carvalho Chehab 		goto out_locked;
50332cf86f6SMauro Carvalho Chehab 	}
50432cf86f6SMauro Carvalho Chehab 
50532cf86f6SMauro Carvalho Chehab 	/*
50632cf86f6SMauro Carvalho Chehab 	 * we add ourselves to the task queue before buffer check
50732cf86f6SMauro Carvalho Chehab 	 * to avoid losing scan code (in case when queue is awaken somewhere
50832cf86f6SMauro Carvalho Chehab 	 * between while condition checking and scheduling)
50932cf86f6SMauro Carvalho Chehab 	 */
51032cf86f6SMauro Carvalho Chehab 	add_wait_queue(&ir->buf->wait_poll, &wait);
51132cf86f6SMauro Carvalho Chehab 
51232cf86f6SMauro Carvalho Chehab 	/*
51332cf86f6SMauro Carvalho Chehab 	 * while we didn't provide 'length' bytes, device is opened in blocking
51432cf86f6SMauro Carvalho Chehab 	 * mode and 'copy_to_user' is happy, wait for data.
51532cf86f6SMauro Carvalho Chehab 	 */
51632cf86f6SMauro Carvalho Chehab 	while (written < length && ret == 0) {
51732cf86f6SMauro Carvalho Chehab 		if (lirc_buffer_empty(ir->buf)) {
51832cf86f6SMauro Carvalho Chehab 			/* According to the read(2) man page, 'written' can be
51932cf86f6SMauro Carvalho Chehab 			 * returned as less than 'length', instead of blocking
52032cf86f6SMauro Carvalho Chehab 			 * again, returning -EWOULDBLOCK, or returning
52162e92682SAndi Shyti 			 * -ERESTARTSYS
52262e92682SAndi Shyti 			 */
52332cf86f6SMauro Carvalho Chehab 			if (written)
52432cf86f6SMauro Carvalho Chehab 				break;
52532cf86f6SMauro Carvalho Chehab 			if (file->f_flags & O_NONBLOCK) {
52632cf86f6SMauro Carvalho Chehab 				ret = -EWOULDBLOCK;
52732cf86f6SMauro Carvalho Chehab 				break;
52832cf86f6SMauro Carvalho Chehab 			}
52932cf86f6SMauro Carvalho Chehab 			if (signal_pending(current)) {
53032cf86f6SMauro Carvalho Chehab 				ret = -ERESTARTSYS;
53132cf86f6SMauro Carvalho Chehab 				break;
53232cf86f6SMauro Carvalho Chehab 			}
53332cf86f6SMauro Carvalho Chehab 
53432cf86f6SMauro Carvalho Chehab 			mutex_unlock(&ir->irctl_lock);
53532cf86f6SMauro Carvalho Chehab 			set_current_state(TASK_INTERRUPTIBLE);
53612accdcbSSean Young 			schedule();
53712accdcbSSean Young 			set_current_state(TASK_RUNNING);
53832cf86f6SMauro Carvalho Chehab 
53932cf86f6SMauro Carvalho Chehab 			if (mutex_lock_interruptible(&ir->irctl_lock)) {
54032cf86f6SMauro Carvalho Chehab 				ret = -ERESTARTSYS;
54132cf86f6SMauro Carvalho Chehab 				remove_wait_queue(&ir->buf->wait_poll, &wait);
54232cf86f6SMauro Carvalho Chehab 				goto out_unlocked;
54332cf86f6SMauro Carvalho Chehab 			}
54432cf86f6SMauro Carvalho Chehab 
54532cf86f6SMauro Carvalho Chehab 			if (!ir->attached) {
54632cf86f6SMauro Carvalho Chehab 				ret = -ENODEV;
547c77d17c0SSean Young 				goto out_locked;
54832cf86f6SMauro Carvalho Chehab 			}
54932cf86f6SMauro Carvalho Chehab 		} else {
55032cf86f6SMauro Carvalho Chehab 			lirc_buffer_read(ir->buf, buf);
55160519af3SHans Verkuil 			ret = copy_to_user((void __user *)buffer+written, buf,
55232cf86f6SMauro Carvalho Chehab 					   ir->buf->chunk_size);
55332cf86f6SMauro Carvalho Chehab 			if (!ret)
55432cf86f6SMauro Carvalho Chehab 				written += ir->buf->chunk_size;
55532cf86f6SMauro Carvalho Chehab 			else
55632cf86f6SMauro Carvalho Chehab 				ret = -EFAULT;
55732cf86f6SMauro Carvalho Chehab 		}
55832cf86f6SMauro Carvalho Chehab 	}
55932cf86f6SMauro Carvalho Chehab 
56032cf86f6SMauro Carvalho Chehab 	remove_wait_queue(&ir->buf->wait_poll, &wait);
56132cf86f6SMauro Carvalho Chehab 
56232cf86f6SMauro Carvalho Chehab out_locked:
56332cf86f6SMauro Carvalho Chehab 	mutex_unlock(&ir->irctl_lock);
56432cf86f6SMauro Carvalho Chehab 
56532cf86f6SMauro Carvalho Chehab out_unlocked:
56632cf86f6SMauro Carvalho Chehab 	kfree(buf);
56732cf86f6SMauro Carvalho Chehab 
56832cf86f6SMauro Carvalho Chehab 	return ret ? ret : written;
56932cf86f6SMauro Carvalho Chehab }
57032cf86f6SMauro Carvalho Chehab EXPORT_SYMBOL(lirc_dev_fop_read);
57132cf86f6SMauro Carvalho Chehab 
57232cf86f6SMauro Carvalho Chehab void *lirc_get_pdata(struct file *file)
57332cf86f6SMauro Carvalho Chehab {
5740990a97aSAl Viro 	return irctls[iminor(file_inode(file))]->d.data;
57532cf86f6SMauro Carvalho Chehab }
57632cf86f6SMauro Carvalho Chehab EXPORT_SYMBOL(lirc_get_pdata);
57732cf86f6SMauro Carvalho Chehab 
57832cf86f6SMauro Carvalho Chehab 
57932cf86f6SMauro Carvalho Chehab static int __init lirc_dev_init(void)
58032cf86f6SMauro Carvalho Chehab {
58132cf86f6SMauro Carvalho Chehab 	int retval;
58232cf86f6SMauro Carvalho Chehab 
58332cf86f6SMauro Carvalho Chehab 	lirc_class = class_create(THIS_MODULE, "lirc");
58432cf86f6SMauro Carvalho Chehab 	if (IS_ERR(lirc_class)) {
5853fac0314SAndi Shyti 		pr_err("class_create failed\n");
58654fcecafSAndi Shyti 		return PTR_ERR(lirc_class);
58732cf86f6SMauro Carvalho Chehab 	}
58832cf86f6SMauro Carvalho Chehab 
58932cf86f6SMauro Carvalho Chehab 	retval = alloc_chrdev_region(&lirc_base_dev, 0, MAX_IRCTL_DEVICES,
590463015ddSDavid Härdeman 				     "BaseRemoteCtl");
59132cf86f6SMauro Carvalho Chehab 	if (retval) {
59232cf86f6SMauro Carvalho Chehab 		class_destroy(lirc_class);
5933fac0314SAndi Shyti 		pr_err("alloc_chrdev_region failed\n");
59454fcecafSAndi Shyti 		return retval;
59532cf86f6SMauro Carvalho Chehab 	}
59632cf86f6SMauro Carvalho Chehab 
5973fac0314SAndi Shyti 	pr_info("IR Remote Control driver registered, major %d\n",
5983fac0314SAndi Shyti 						MAJOR(lirc_base_dev));
59932cf86f6SMauro Carvalho Chehab 
60054fcecafSAndi Shyti 	return 0;
60132cf86f6SMauro Carvalho Chehab }
60232cf86f6SMauro Carvalho Chehab 
60332cf86f6SMauro Carvalho Chehab static void __exit lirc_dev_exit(void)
60432cf86f6SMauro Carvalho Chehab {
60532cf86f6SMauro Carvalho Chehab 	class_destroy(lirc_class);
60632cf86f6SMauro Carvalho Chehab 	unregister_chrdev_region(lirc_base_dev, MAX_IRCTL_DEVICES);
6073fac0314SAndi Shyti 	pr_info("module unloaded\n");
60832cf86f6SMauro Carvalho Chehab }
60932cf86f6SMauro Carvalho Chehab 
61032cf86f6SMauro Carvalho Chehab module_init(lirc_dev_init);
61132cf86f6SMauro Carvalho Chehab module_exit(lirc_dev_exit);
61232cf86f6SMauro Carvalho Chehab 
61332cf86f6SMauro Carvalho Chehab MODULE_DESCRIPTION("LIRC base driver module");
61432cf86f6SMauro Carvalho Chehab MODULE_AUTHOR("Artur Lipowski");
61532cf86f6SMauro Carvalho Chehab MODULE_LICENSE("GPL");
616