14a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson/*
24a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson * LIRC base driver
34a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson *
44a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson * by Artur Lipowski <alipowski@interia.pl>
54a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson *
64a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson *  This program is free software; you can redistribute it and/or modify
74a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson *  it under the terms of the GNU General Public License as published by
84a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson *  the Free Software Foundation; either version 2 of the License, or
94a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson *  (at your option) any later version.
104a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson *
114a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson *  This program is distributed in the hope that it will be useful,
124a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson *  but WITHOUT ANY WARRANTY; without even the implied warranty of
134a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
144a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson *  GNU General Public License for more details.
154a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson *
164a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson *  You should have received a copy of the GNU General Public License
174a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson *  along with this program; if not, write to the Free Software
184a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
194a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson *
204a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson */
214a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
224a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson#include <linux/module.h>
234a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson#include <linux/kernel.h>
244a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson#include <linux/sched.h>
254a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson#include <linux/errno.h>
264a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson#include <linux/ioctl.h>
274a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson#include <linux/fs.h>
284a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson#include <linux/poll.h>
294a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson#include <linux/completion.h>
304a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson#include <linux/mutex.h>
314a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson#include <linux/wait.h>
324a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson#include <linux/unistd.h>
334a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson#include <linux/kthread.h>
344a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson#include <linux/bitops.h>
354a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson#include <linux/device.h>
364a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson#include <linux/cdev.h>
374a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
38ca7a722db1c90dfe0dba165ecef01d6ac8cfee0dSrinivas Kandagatla#include <media/rc-core.h>
394a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson#include <media/lirc.h>
405690085e7ba7f3081c6ab6db3a3b543444ad8a21Jarod Wilson#include <media/lirc_dev.h>
414a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
4290ab5ee94171b3e28de6bb42ee30b527014e0be7Rusty Russellstatic bool debug;
434a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
444a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson#define IRCTL_DEV_NAME	"BaseRemoteCtl"
454a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson#define NOPLUG		-1
464a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson#define LOGHEAD		"lirc_dev (%s[%d]): "
474a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
484a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilsonstatic dev_t lirc_base_dev;
494a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
504a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilsonstruct irctl {
514a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	struct lirc_driver d;
524a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	int attached;
534a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	int open;
544a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
554a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	struct mutex irctl_lock;
564a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	struct lirc_buffer *buf;
574a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	unsigned int chunk_size;
584a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
598de111e27688798623b9e9062235bb0cac29f599Jarod Wilson	struct cdev *cdev;
608de111e27688798623b9e9062235bb0cac29f599Jarod Wilson
614a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	struct task_struct *task;
624a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	long jiffies_to_wait;
634a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson};
644a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
654a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilsonstatic DEFINE_MUTEX(lirc_dev_lock);
664a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
674a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilsonstatic struct irctl *irctls[MAX_IRCTL_DEVICES];
684a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
694a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson/* Only used for sysfs but defined to void otherwise */
704a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilsonstatic struct class *lirc_class;
714a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
724a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson/*  helper function
734a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson *  initializes the irctl structure
744a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson */
75578fcb8e5f3046493932105c404792a2fe0e066fJarod Wilsonstatic void lirc_irctl_init(struct irctl *ir)
764a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson{
774a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	mutex_init(&ir->irctl_lock);
784a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	ir->d.minor = NOPLUG;
794a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson}
804a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
81578fcb8e5f3046493932105c404792a2fe0e066fJarod Wilsonstatic void lirc_irctl_cleanup(struct irctl *ir)
824a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson{
834a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	dev_dbg(ir->d.dev, LOGHEAD "cleaning up\n", ir->d.name, ir->d.minor);
844a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
854a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	device_destroy(lirc_class, MKDEV(MAJOR(lirc_base_dev), ir->d.minor));
864a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
874a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	if (ir->buf != ir->d.rbuf) {
884a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		lirc_buffer_free(ir->buf);
894a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		kfree(ir->buf);
904a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	}
914a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	ir->buf = NULL;
924a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson}
934a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
944a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson/*  helper function
954a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson *  reads key codes from driver and puts them into buffer
964a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson *  returns 0 on success
974a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson */
98578fcb8e5f3046493932105c404792a2fe0e066fJarod Wilsonstatic int lirc_add_to_buf(struct irctl *ir)
994a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson{
1004a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	if (ir->d.add_to_buf) {
1014a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		int res = -ENODATA;
1024a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		int got_data = 0;
1034a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
1044a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		/*
1054a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		 * service the device as long as it is returning
1064a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		 * data and we have space
1074a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		 */
1084a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilsonget_data:
1094a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		res = ir->d.add_to_buf(ir->d.data, ir->buf);
1104a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		if (res == 0) {
1114a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			got_data++;
1124a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			goto get_data;
1134a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		}
1144a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
1154a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		if (res == -ENODEV)
1164a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			kthread_stop(ir->task);
1174a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
1184a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		return got_data ? 0 : res;
1194a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	}
1204a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
1214a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	return 0;
1224a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson}
1234a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
1244a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson/* main function of the polling thread
1254a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson */
1264a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilsonstatic int lirc_thread(void *irctl)
1274a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson{
1284a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	struct irctl *ir = irctl;
1294a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
1304a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	dev_dbg(ir->d.dev, LOGHEAD "poll thread started\n",
1314a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		ir->d.name, ir->d.minor);
1324a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
1334a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	do {
1344a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		if (ir->open) {
1354a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			if (ir->jiffies_to_wait) {
1364a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson				set_current_state(TASK_INTERRUPTIBLE);
1374a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson				schedule_timeout(ir->jiffies_to_wait);
1384a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			}
1394a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			if (kthread_should_stop())
1404a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson				break;
141578fcb8e5f3046493932105c404792a2fe0e066fJarod Wilson			if (!lirc_add_to_buf(ir))
1424a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson				wake_up_interruptible(&ir->buf->wait_poll);
1434a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		} else {
1444a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			set_current_state(TASK_INTERRUPTIBLE);
1454a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			schedule();
1464a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		}
1474a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	} while (!kthread_should_stop());
1484a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
1494a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	dev_dbg(ir->d.dev, LOGHEAD "poll thread ended\n",
1504a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		ir->d.name, ir->d.minor);
1514a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
1524a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	return 0;
1534a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson}
1544a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
1554a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
15675ef9de1267ba171ecefafca35758e2be0db10dcAl Virostatic const struct file_operations lirc_dev_fops = {
1574a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	.owner		= THIS_MODULE,
1584a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	.read		= lirc_dev_fop_read,
1594a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	.write		= lirc_dev_fop_write,
1604a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	.poll		= lirc_dev_fop_poll,
161044e5878c2158d701e6f47a9604910589a384ee2Arnd Bergmann	.unlocked_ioctl	= lirc_dev_fop_ioctl,
1628be292cc035ebc3422f08e84682626dd8ed8334bJarod Wilson#ifdef CONFIG_COMPAT
1638be292cc035ebc3422f08e84682626dd8ed8334bJarod Wilson	.compat_ioctl	= lirc_dev_fop_ioctl,
1648be292cc035ebc3422f08e84682626dd8ed8334bJarod Wilson#endif
1654a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	.open		= lirc_dev_fop_open,
1664a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	.release	= lirc_dev_fop_close,
1676038f373a3dc1f1c26496e60b6c40b164716f07eArnd Bergmann	.llseek		= noop_llseek,
1684a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson};
1694a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
1704a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilsonstatic int lirc_cdev_add(struct irctl *ir)
1714a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson{
1728de111e27688798623b9e9062235bb0cac29f599Jarod Wilson	int retval = -ENOMEM;
1734a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	struct lirc_driver *d = &ir->d;
1748de111e27688798623b9e9062235bb0cac29f599Jarod Wilson	struct cdev *cdev;
1758de111e27688798623b9e9062235bb0cac29f599Jarod Wilson
1768de111e27688798623b9e9062235bb0cac29f599Jarod Wilson	cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);
1778de111e27688798623b9e9062235bb0cac29f599Jarod Wilson	if (!cdev)
1788de111e27688798623b9e9062235bb0cac29f599Jarod Wilson		goto err_out;
1794a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
1804a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	if (d->fops) {
181c1cbb7029e81894c056680d61c64741bd2ff246fJarod Wilson		cdev_init(cdev, d->fops);
182c1cbb7029e81894c056680d61c64741bd2ff246fJarod Wilson		cdev->owner = d->owner;
1834a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	} else {
184c1cbb7029e81894c056680d61c64741bd2ff246fJarod Wilson		cdev_init(cdev, &lirc_dev_fops);
185c1cbb7029e81894c056680d61c64741bd2ff246fJarod Wilson		cdev->owner = THIS_MODULE;
1864a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	}
187b395cbac36e58a55729fe7e6262a3f0b1691bcedVasiliy Kulikov	retval = kobject_set_name(&cdev->kobj, "lirc%d", d->minor);
188b395cbac36e58a55729fe7e6262a3f0b1691bcedVasiliy Kulikov	if (retval)
1898de111e27688798623b9e9062235bb0cac29f599Jarod Wilson		goto err_out;
1904a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
191c1cbb7029e81894c056680d61c64741bd2ff246fJarod Wilson	retval = cdev_add(cdev, MKDEV(MAJOR(lirc_base_dev), d->minor), 1);
1928de111e27688798623b9e9062235bb0cac29f599Jarod Wilson	if (retval) {
193c1cbb7029e81894c056680d61c64741bd2ff246fJarod Wilson		kobject_put(&cdev->kobj);
1948de111e27688798623b9e9062235bb0cac29f599Jarod Wilson		goto err_out;
1958de111e27688798623b9e9062235bb0cac29f599Jarod Wilson	}
1964a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
1978de111e27688798623b9e9062235bb0cac29f599Jarod Wilson	ir->cdev = cdev;
1988de111e27688798623b9e9062235bb0cac29f599Jarod Wilson
1998de111e27688798623b9e9062235bb0cac29f599Jarod Wilson	return 0;
2008de111e27688798623b9e9062235bb0cac29f599Jarod Wilson
2018de111e27688798623b9e9062235bb0cac29f599Jarod Wilsonerr_out:
2028de111e27688798623b9e9062235bb0cac29f599Jarod Wilson	kfree(cdev);
2034a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	return retval;
2044a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson}
2054a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
2064a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilsonint lirc_register_driver(struct lirc_driver *d)
2074a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson{
2084a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	struct irctl *ir;
2094a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	int minor;
2104a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	int bytes_in_key;
2114a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	unsigned int chunk_size;
2124a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	unsigned int buffer_size;
2134a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	int err;
2144a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
2154a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	if (!d) {
2164a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		printk(KERN_ERR "lirc_dev: lirc_register_driver: "
2174a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		       "driver pointer must be not NULL!\n");
2184a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		err = -EBADRQC;
2194a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		goto out;
2204a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	}
2214a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
222715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson	if (!d->dev) {
223715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson		printk(KERN_ERR "%s: dev pointer not filled in!\n", __func__);
224715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson		err = -EINVAL;
225715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson		goto out;
226715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson	}
227715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson
2284a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	if (MAX_IRCTL_DEVICES <= d->minor) {
2294a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		dev_err(d->dev, "lirc_dev: lirc_register_driver: "
2304a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			"\"minor\" must be between 0 and %d (%d)!\n",
2318de111e27688798623b9e9062235bb0cac29f599Jarod Wilson			MAX_IRCTL_DEVICES - 1, d->minor);
2324a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		err = -EBADRQC;
2334a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		goto out;
2344a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	}
2354a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
2364a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	if (1 > d->code_length || (BUFLEN * 8) < d->code_length) {
2374a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		dev_err(d->dev, "lirc_dev: lirc_register_driver: "
2384a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			"code length in bits for minor (%d) "
2394a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			"must be less than %d!\n",
2404a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			d->minor, BUFLEN * 8);
2414a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		err = -EBADRQC;
2424a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		goto out;
2434a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	}
2444a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
2454a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	dev_dbg(d->dev, "lirc_dev: lirc_register_driver: sample_rate: %d\n",
2464a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		d->sample_rate);
2474a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	if (d->sample_rate) {
2484a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		if (2 > d->sample_rate || HZ < d->sample_rate) {
2494a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			dev_err(d->dev, "lirc_dev: lirc_register_driver: "
2504a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson				"sample_rate must be between 2 and %d!\n", HZ);
2514a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			err = -EBADRQC;
2524a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			goto out;
2534a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		}
2544a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		if (!d->add_to_buf) {
2554a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			dev_err(d->dev, "lirc_dev: lirc_register_driver: "
2564a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson				"add_to_buf cannot be NULL when "
2574a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson				"sample_rate is set\n");
2584a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			err = -EBADRQC;
2594a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			goto out;
2604a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		}
2614a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	} else if (!(d->fops && d->fops->read) && !d->rbuf) {
2624a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		dev_err(d->dev, "lirc_dev: lirc_register_driver: "
2634a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			"fops->read and rbuf cannot all be NULL!\n");
2644a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		err = -EBADRQC;
2654a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		goto out;
2664a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	} else if (!d->rbuf) {
2674a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		if (!(d->fops && d->fops->read && d->fops->poll &&
268044e5878c2158d701e6f47a9604910589a384ee2Arnd Bergmann		      d->fops->unlocked_ioctl)) {
2694a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			dev_err(d->dev, "lirc_dev: lirc_register_driver: "
270044e5878c2158d701e6f47a9604910589a384ee2Arnd Bergmann				"neither read, poll nor unlocked_ioctl can be NULL!\n");
2714a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			err = -EBADRQC;
2724a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			goto out;
2734a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		}
2744a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	}
2754a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
2764a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	mutex_lock(&lirc_dev_lock);
2774a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
2784a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	minor = d->minor;
2794a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
2804a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	if (minor < 0) {
2814a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		/* find first free slot for driver */
2824a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		for (minor = 0; minor < MAX_IRCTL_DEVICES; minor++)
2834a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			if (!irctls[minor])
2844a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson				break;
2854a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		if (MAX_IRCTL_DEVICES == minor) {
2864a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			dev_err(d->dev, "lirc_dev: lirc_register_driver: "
2874a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson				"no free slots for drivers!\n");
2884a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			err = -ENOMEM;
2894a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			goto out_lock;
2904a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		}
2914a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	} else if (irctls[minor]) {
2924a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		dev_err(d->dev, "lirc_dev: lirc_register_driver: "
2934a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			"minor (%d) just registered!\n", minor);
2944a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		err = -EBUSY;
2954a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		goto out_lock;
2964a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	}
2974a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
2984a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	ir = kzalloc(sizeof(struct irctl), GFP_KERNEL);
2994a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	if (!ir) {
3004a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		err = -ENOMEM;
3014a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		goto out_lock;
3024a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	}
303578fcb8e5f3046493932105c404792a2fe0e066fJarod Wilson	lirc_irctl_init(ir);
3044a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	irctls[minor] = ir;
3054a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	d->minor = minor;
3064a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
3074a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	if (d->sample_rate) {
3084a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		ir->jiffies_to_wait = HZ / d->sample_rate;
3094a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	} else {
3104a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		/* it means - wait for external event in task queue */
3114a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		ir->jiffies_to_wait = 0;
3124a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	}
3134a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
3144a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	/* some safety check 8-) */
3154a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	d->name[sizeof(d->name)-1] = '\0';
3164a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
3174a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	bytes_in_key = BITS_TO_LONGS(d->code_length) +
3184a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			(d->code_length % 8 ? 1 : 0);
3194a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	buffer_size = d->buffer_size ? d->buffer_size : BUFLEN / bytes_in_key;
3204a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	chunk_size  = d->chunk_size  ? d->chunk_size  : bytes_in_key;
3214a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
3224a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	if (d->rbuf) {
3234a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		ir->buf = d->rbuf;
3244a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	} else {
3254a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		ir->buf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
3264a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		if (!ir->buf) {
3274a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			err = -ENOMEM;
3284a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			goto out_lock;
3294a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		}
3304a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		err = lirc_buffer_init(ir->buf, chunk_size, buffer_size);
3314a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		if (err) {
3324a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			kfree(ir->buf);
3334a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			goto out_lock;
3344a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		}
3354a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	}
3364a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	ir->chunk_size = ir->buf->chunk_size;
3374a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
3384a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	if (d->features == 0)
3394a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		d->features = LIRC_CAN_REC_LIRCCODE;
3404a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
3414a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	ir->d = *d;
3424a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
3434a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	device_create(lirc_class, ir->d.dev,
3444a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		      MKDEV(MAJOR(lirc_base_dev), ir->d.minor), NULL,
3454a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		      "lirc%u", ir->d.minor);
3464a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
3474a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	if (d->sample_rate) {
3484a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		/* try to fire up polling thread */
3494a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		ir->task = kthread_run(lirc_thread, (void *)ir, "lirc_dev");
3504a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		if (IS_ERR(ir->task)) {
3514a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			dev_err(d->dev, "lirc_dev: lirc_register_driver: "
3524a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson				"cannot run poll thread for minor = %d\n",
3534a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson				d->minor);
3544a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			err = -ECHILD;
3554a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			goto out_sysfs;
3564a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		}
3574a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	}
3584a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
3594a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	err = lirc_cdev_add(ir);
3604a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	if (err)
3614a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		goto out_sysfs;
3624a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
3634a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	ir->attached = 1;
3644a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	mutex_unlock(&lirc_dev_lock);
3654a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
3664a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	dev_info(ir->d.dev, "lirc_dev: driver %s registered at minor = %d\n",
3674a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		 ir->d.name, ir->d.minor);
3684a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	return minor;
3694a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
3704a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilsonout_sysfs:
3714a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	device_destroy(lirc_class, MKDEV(MAJOR(lirc_base_dev), ir->d.minor));
3724a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilsonout_lock:
3734a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	mutex_unlock(&lirc_dev_lock);
3744a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilsonout:
3754a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	return err;
3764a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson}
3774a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod WilsonEXPORT_SYMBOL(lirc_register_driver);
3784a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
3794a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilsonint lirc_unregister_driver(int minor)
3804a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson{
3814a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	struct irctl *ir;
382c1cbb7029e81894c056680d61c64741bd2ff246fJarod Wilson	struct cdev *cdev;
3834a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
3844a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	if (minor < 0 || minor >= MAX_IRCTL_DEVICES) {
3854fc215430f6764f3e30bdf86bc2a46ab2e0c4404Jarod Wilson		printk(KERN_ERR "lirc_dev: %s: minor (%d) must be between "
3868de111e27688798623b9e9062235bb0cac29f599Jarod Wilson		       "0 and %d!\n", __func__, minor, MAX_IRCTL_DEVICES - 1);
3874a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		return -EBADRQC;
3884a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	}
3894a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
3904a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	ir = irctls[minor];
391df1868e4ee605444bbb98505126bdfb3519749afJarod Wilson	if (!ir) {
3924fc215430f6764f3e30bdf86bc2a46ab2e0c4404Jarod Wilson		printk(KERN_ERR "lirc_dev: %s: failed to get irctl struct "
3934fc215430f6764f3e30bdf86bc2a46ab2e0c4404Jarod Wilson		       "for minor %d!\n", __func__, minor);
394df1868e4ee605444bbb98505126bdfb3519749afJarod Wilson		return -ENOENT;
395df1868e4ee605444bbb98505126bdfb3519749afJarod Wilson	}
3964a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
3978de111e27688798623b9e9062235bb0cac29f599Jarod Wilson	cdev = ir->cdev;
3984a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
3994a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	mutex_lock(&lirc_dev_lock);
4004a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
4014a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	if (ir->d.minor != minor) {
4024fc215430f6764f3e30bdf86bc2a46ab2e0c4404Jarod Wilson		printk(KERN_ERR "lirc_dev: %s: minor (%d) device not "
4034fc215430f6764f3e30bdf86bc2a46ab2e0c4404Jarod Wilson		       "registered!\n", __func__, minor);
4044a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		mutex_unlock(&lirc_dev_lock);
4054a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		return -ENOENT;
4064a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	}
4074a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
4084a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	/* end up polling thread */
4094a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	if (ir->task)
4104a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		kthread_stop(ir->task);
4114a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
4124a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	dev_dbg(ir->d.dev, "lirc_dev: driver %s unregistered from minor = %d\n",
4134a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		ir->d.name, ir->d.minor);
4144a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
4154a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	ir->attached = 0;
4164a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	if (ir->open) {
4174a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		dev_dbg(ir->d.dev, LOGHEAD "releasing opened driver\n",
4184a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			ir->d.name, ir->d.minor);
4194a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		wake_up_interruptible(&ir->buf->wait_poll);
4204a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		mutex_lock(&ir->irctl_lock);
4214a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		ir->d.set_use_dec(ir->d.data);
422c1cbb7029e81894c056680d61c64741bd2ff246fJarod Wilson		module_put(cdev->owner);
4234a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		mutex_unlock(&ir->irctl_lock);
4244a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	} else {
425578fcb8e5f3046493932105c404792a2fe0e066fJarod Wilson		lirc_irctl_cleanup(ir);
426c1cbb7029e81894c056680d61c64741bd2ff246fJarod Wilson		cdev_del(cdev);
4278de111e27688798623b9e9062235bb0cac29f599Jarod Wilson		kfree(cdev);
4284a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		kfree(ir);
4294a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		irctls[minor] = NULL;
4304a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	}
4314a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
4324a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	mutex_unlock(&lirc_dev_lock);
4334a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
4344a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	return 0;
4354a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson}
4364a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod WilsonEXPORT_SYMBOL(lirc_unregister_driver);
4374a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
4384a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilsonint lirc_dev_fop_open(struct inode *inode, struct file *file)
4394a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson{
4404a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	struct irctl *ir;
441c1cbb7029e81894c056680d61c64741bd2ff246fJarod Wilson	struct cdev *cdev;
4424a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	int retval = 0;
4434a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
4444a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	if (iminor(inode) >= MAX_IRCTL_DEVICES) {
4454a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		printk(KERN_WARNING "lirc_dev [%d]: open result = -ENODEV\n",
4464a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		       iminor(inode));
4474a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		return -ENODEV;
4484a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	}
4494a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
4504a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	if (mutex_lock_interruptible(&lirc_dev_lock))
4514a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		return -ERESTARTSYS;
4524a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
4534a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	ir = irctls[iminor(inode)];
4544a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	if (!ir) {
4554a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		retval = -ENODEV;
4564a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		goto error;
4574a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	}
4584a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
4594a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	dev_dbg(ir->d.dev, LOGHEAD "open called\n", ir->d.name, ir->d.minor);
4604a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
4614a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	if (ir->d.minor == NOPLUG) {
4624a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		retval = -ENODEV;
4634a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		goto error;
4644a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	}
4654a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
4664a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	if (ir->open) {
4674a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		retval = -EBUSY;
4684a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		goto error;
4694a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	}
4704a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
471ca7a722db1c90dfe0dba165ecef01d6ac8cfee0dSrinivas Kandagatla	if (ir->d.rdev) {
472ca7a722db1c90dfe0dba165ecef01d6ac8cfee0dSrinivas Kandagatla		retval = rc_open(ir->d.rdev);
473ca7a722db1c90dfe0dba165ecef01d6ac8cfee0dSrinivas Kandagatla		if (retval)
474ca7a722db1c90dfe0dba165ecef01d6ac8cfee0dSrinivas Kandagatla			goto error;
475ca7a722db1c90dfe0dba165ecef01d6ac8cfee0dSrinivas Kandagatla	}
476ca7a722db1c90dfe0dba165ecef01d6ac8cfee0dSrinivas Kandagatla
4778de111e27688798623b9e9062235bb0cac29f599Jarod Wilson	cdev = ir->cdev;
478c1cbb7029e81894c056680d61c64741bd2ff246fJarod Wilson	if (try_module_get(cdev->owner)) {
479c1cbb7029e81894c056680d61c64741bd2ff246fJarod Wilson		ir->open++;
4804a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		retval = ir->d.set_use_inc(ir->d.data);
4814a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
4824a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		if (retval) {
483c1cbb7029e81894c056680d61c64741bd2ff246fJarod Wilson			module_put(cdev->owner);
484c1cbb7029e81894c056680d61c64741bd2ff246fJarod Wilson			ir->open--;
4854a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		} else {
4864a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			lirc_buffer_clear(ir->buf);
4874a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		}
4884a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		if (ir->task)
4894a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			wake_up_process(ir->task);
4904a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	}
4914a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
4924a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilsonerror:
4934a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	if (ir)
4944a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		dev_dbg(ir->d.dev, LOGHEAD "open result = %d\n",
4954a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			ir->d.name, ir->d.minor, retval);
4964a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
4974a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	mutex_unlock(&lirc_dev_lock);
4984a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
499d9d2e9d5c9eead1f73f92f5fc03424dab90b6c95Arnd Bergmann	nonseekable_open(inode, file);
500d9d2e9d5c9eead1f73f92f5fc03424dab90b6c95Arnd Bergmann
5014a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	return retval;
5024a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson}
5034a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod WilsonEXPORT_SYMBOL(lirc_dev_fop_open);
5044a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
5054a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilsonint lirc_dev_fop_close(struct inode *inode, struct file *file)
5064a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson{
5074a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	struct irctl *ir = irctls[iminor(inode)];
5088de111e27688798623b9e9062235bb0cac29f599Jarod Wilson	struct cdev *cdev;
5094a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
510715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson	if (!ir) {
511715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson		printk(KERN_ERR "%s: called with invalid irctl\n", __func__);
512715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson		return -EINVAL;
513715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson	}
5144a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
5158de111e27688798623b9e9062235bb0cac29f599Jarod Wilson	cdev = ir->cdev;
5168de111e27688798623b9e9062235bb0cac29f599Jarod Wilson
5174a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	dev_dbg(ir->d.dev, LOGHEAD "close called\n", ir->d.name, ir->d.minor);
5184a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
5194a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	WARN_ON(mutex_lock_killable(&lirc_dev_lock));
5204a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
521ca7a722db1c90dfe0dba165ecef01d6ac8cfee0dSrinivas Kandagatla	if (ir->d.rdev)
522ca7a722db1c90dfe0dba165ecef01d6ac8cfee0dSrinivas Kandagatla		rc_close(ir->d.rdev);
523ca7a722db1c90dfe0dba165ecef01d6ac8cfee0dSrinivas Kandagatla
524715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson	ir->open--;
5254a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	if (ir->attached) {
5264a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		ir->d.set_use_dec(ir->d.data);
527c1cbb7029e81894c056680d61c64741bd2ff246fJarod Wilson		module_put(cdev->owner);
5284a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	} else {
529578fcb8e5f3046493932105c404792a2fe0e066fJarod Wilson		lirc_irctl_cleanup(ir);
530c1cbb7029e81894c056680d61c64741bd2ff246fJarod Wilson		cdev_del(cdev);
5314a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		irctls[ir->d.minor] = NULL;
5328de111e27688798623b9e9062235bb0cac29f599Jarod Wilson		kfree(cdev);
5334a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		kfree(ir);
5344a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	}
5354a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
5364a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	mutex_unlock(&lirc_dev_lock);
5374a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
5384a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	return 0;
5394a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson}
5404a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod WilsonEXPORT_SYMBOL(lirc_dev_fop_close);
5414a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
5424a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilsonunsigned int lirc_dev_fop_poll(struct file *file, poll_table *wait)
5434a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson{
544496ad9aa8ef448058e36ca7a787c61f2e63f0f54Al Viro	struct irctl *ir = irctls[iminor(file_inode(file))];
5454a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	unsigned int ret;
5464a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
547715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson	if (!ir) {
548715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson		printk(KERN_ERR "%s: called with invalid irctl\n", __func__);
549715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson		return POLLERR;
550715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson	}
551715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson
5524a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	dev_dbg(ir->d.dev, LOGHEAD "poll called\n", ir->d.name, ir->d.minor);
5534a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
5545c769a68beaee924e1dc90bf06e1b087b1d46237Dan Carpenter	if (!ir->attached)
5554a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		return POLLERR;
5564a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
5574a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	poll_wait(file, &ir->buf->wait_poll, wait);
5584a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
5594a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	if (ir->buf)
5604a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		if (lirc_buffer_empty(ir->buf))
5614a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			ret = 0;
5624a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		else
5634a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			ret = POLLIN | POLLRDNORM;
5644a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	else
5654a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		ret = POLLERR;
5664a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
5674a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	dev_dbg(ir->d.dev, LOGHEAD "poll result = %d\n",
5684a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		ir->d.name, ir->d.minor, ret);
5694a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
5704a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	return ret;
5714a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson}
5724a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod WilsonEXPORT_SYMBOL(lirc_dev_fop_poll);
5734a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
574044e5878c2158d701e6f47a9604910589a384ee2Arnd Bergmannlong lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
5754a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson{
576be1f985ffa49467f604318182616678b3e5184fdJarod Wilson	__u32 mode;
5774a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	int result = 0;
578496ad9aa8ef448058e36ca7a787c61f2e63f0f54Al Viro	struct irctl *ir = irctls[iminor(file_inode(file))];
5794a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
5808be292cc035ebc3422f08e84682626dd8ed8334bJarod Wilson	if (!ir) {
5818be292cc035ebc3422f08e84682626dd8ed8334bJarod Wilson		printk(KERN_ERR "lirc_dev: %s: no irctl found!\n", __func__);
5828be292cc035ebc3422f08e84682626dd8ed8334bJarod Wilson		return -ENODEV;
5838be292cc035ebc3422f08e84682626dd8ed8334bJarod Wilson	}
5844a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
5854a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	dev_dbg(ir->d.dev, LOGHEAD "ioctl called (0x%x)\n",
5864a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		ir->d.name, ir->d.minor, cmd);
5874a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
5884a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	if (ir->d.minor == NOPLUG || !ir->attached) {
5894a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		dev_dbg(ir->d.dev, LOGHEAD "ioctl result = -ENODEV\n",
5904a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			ir->d.name, ir->d.minor);
5914a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		return -ENODEV;
5924a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	}
5934a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
5944a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	mutex_lock(&ir->irctl_lock);
5954a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
5964a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	switch (cmd) {
5974a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	case LIRC_GET_FEATURES:
59860519af3fd0e75a97036075fc657f1ebe87e0f0dHans Verkuil		result = put_user(ir->d.features, (__u32 __user *)arg);
5994a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		break;
6004a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	case LIRC_GET_REC_MODE:
6014a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		if (!(ir->d.features & LIRC_CAN_REC_MASK)) {
6024a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			result = -ENOSYS;
6034a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			break;
6044a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		}
6054a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
6064a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		result = put_user(LIRC_REC2MODE
6074a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson				  (ir->d.features & LIRC_CAN_REC_MASK),
60860519af3fd0e75a97036075fc657f1ebe87e0f0dHans Verkuil				  (__u32 __user *)arg);
6094a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		break;
6104a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	case LIRC_SET_REC_MODE:
6114a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		if (!(ir->d.features & LIRC_CAN_REC_MASK)) {
6124a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			result = -ENOSYS;
6134a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			break;
6144a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		}
6154a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
61660519af3fd0e75a97036075fc657f1ebe87e0f0dHans Verkuil		result = get_user(mode, (__u32 __user *)arg);
6174a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		if (!result && !(LIRC_MODE2REC(mode) & ir->d.features))
6184a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			result = -EINVAL;
6194a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		/*
6204a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		 * FIXME: We should actually set the mode somehow but
6214a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		 * for now, lirc_serial doesn't support mode changing either
6224a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		 */
6234a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		break;
6244a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	case LIRC_GET_LENGTH:
62560519af3fd0e75a97036075fc657f1ebe87e0f0dHans Verkuil		result = put_user(ir->d.code_length, (__u32 __user *)arg);
6264a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		break;
6274a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	case LIRC_GET_MIN_TIMEOUT:
6284a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		if (!(ir->d.features & LIRC_CAN_SET_REC_TIMEOUT) ||
6294a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		    ir->d.min_timeout == 0) {
6304a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			result = -ENOSYS;
6314a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			break;
6324a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		}
6334a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
63460519af3fd0e75a97036075fc657f1ebe87e0f0dHans Verkuil		result = put_user(ir->d.min_timeout, (__u32 __user *)arg);
6354a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		break;
6364a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	case LIRC_GET_MAX_TIMEOUT:
6374a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		if (!(ir->d.features & LIRC_CAN_SET_REC_TIMEOUT) ||
6384a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		    ir->d.max_timeout == 0) {
6394a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			result = -ENOSYS;
6404a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			break;
6414a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		}
6424a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
64360519af3fd0e75a97036075fc657f1ebe87e0f0dHans Verkuil		result = put_user(ir->d.max_timeout, (__u32 __user *)arg);
6444a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		break;
6454a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	default:
6464a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		result = -EINVAL;
6474a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	}
6484a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
6494a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	dev_dbg(ir->d.dev, LOGHEAD "ioctl result = %d\n",
6504a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		ir->d.name, ir->d.minor, result);
6514a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
6524a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	mutex_unlock(&ir->irctl_lock);
6534a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
6544a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	return result;
6554a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson}
6564a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod WilsonEXPORT_SYMBOL(lirc_dev_fop_ioctl);
6574a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
6584a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilsonssize_t lirc_dev_fop_read(struct file *file,
6590e835087dfe7db19f1f072046f5e116d4ec6662bDan Carpenter			  char __user *buffer,
6604a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			  size_t length,
6614a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			  loff_t *ppos)
6624a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson{
663496ad9aa8ef448058e36ca7a787c61f2e63f0f54Al Viro	struct irctl *ir = irctls[iminor(file_inode(file))];
664715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson	unsigned char *buf;
6654a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	int ret = 0, written = 0;
6664a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	DECLARE_WAITQUEUE(wait, current);
6674a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
668715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson	if (!ir) {
669715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson		printk(KERN_ERR "%s: called with invalid irctl\n", __func__);
670715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson		return -ENODEV;
671715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson	}
672715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson
6734a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	dev_dbg(ir->d.dev, LOGHEAD "read called\n", ir->d.name, ir->d.minor);
6744a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
675715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson	buf = kzalloc(ir->chunk_size, GFP_KERNEL);
676715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson	if (!buf)
677715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson		return -ENOMEM;
678715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson
679250f7a5f62a08985af5cf7728ae7ba9edbfdc0a9Dan Carpenter	if (mutex_lock_interruptible(&ir->irctl_lock)) {
680250f7a5f62a08985af5cf7728ae7ba9edbfdc0a9Dan Carpenter		ret = -ERESTARTSYS;
681250f7a5f62a08985af5cf7728ae7ba9edbfdc0a9Dan Carpenter		goto out_unlocked;
682250f7a5f62a08985af5cf7728ae7ba9edbfdc0a9Dan Carpenter	}
6834a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	if (!ir->attached) {
684250f7a5f62a08985af5cf7728ae7ba9edbfdc0a9Dan Carpenter		ret = -ENODEV;
685250f7a5f62a08985af5cf7728ae7ba9edbfdc0a9Dan Carpenter		goto out_locked;
6864a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	}
6874a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
6884a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	if (length % ir->chunk_size) {
689250f7a5f62a08985af5cf7728ae7ba9edbfdc0a9Dan Carpenter		ret = -EINVAL;
690250f7a5f62a08985af5cf7728ae7ba9edbfdc0a9Dan Carpenter		goto out_locked;
6914a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	}
6924a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
6934a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	/*
6944a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	 * we add ourselves to the task queue before buffer check
6954a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	 * to avoid losing scan code (in case when queue is awaken somewhere
6964a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	 * between while condition checking and scheduling)
6974a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	 */
6984a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	add_wait_queue(&ir->buf->wait_poll, &wait);
6994a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	set_current_state(TASK_INTERRUPTIBLE);
7004a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
7014a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	/*
7024a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	 * while we didn't provide 'length' bytes, device is opened in blocking
7034a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	 * mode and 'copy_to_user' is happy, wait for data.
7044a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	 */
7054a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	while (written < length && ret == 0) {
7064a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		if (lirc_buffer_empty(ir->buf)) {
7074a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			/* According to the read(2) man page, 'written' can be
7084a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			 * returned as less than 'length', instead of blocking
7094a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			 * again, returning -EWOULDBLOCK, or returning
7104a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			 * -ERESTARTSYS */
7114a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			if (written)
7124a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson				break;
7134a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			if (file->f_flags & O_NONBLOCK) {
7144a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson				ret = -EWOULDBLOCK;
7154a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson				break;
7164a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			}
7174a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			if (signal_pending(current)) {
7184a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson				ret = -ERESTARTSYS;
7194a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson				break;
7204a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			}
7214a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
7224a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			mutex_unlock(&ir->irctl_lock);
7234a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			schedule();
7244a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			set_current_state(TASK_INTERRUPTIBLE);
7254a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
7264a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			if (mutex_lock_interruptible(&ir->irctl_lock)) {
7274a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson				ret = -ERESTARTSYS;
72869c271f33b949a7b1cbe6f7f39ce3db9e80997a2Jarod Wilson				remove_wait_queue(&ir->buf->wait_poll, &wait);
72969c271f33b949a7b1cbe6f7f39ce3db9e80997a2Jarod Wilson				set_current_state(TASK_RUNNING);
73069c271f33b949a7b1cbe6f7f39ce3db9e80997a2Jarod Wilson				goto out_unlocked;
7314a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			}
7324a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
7334a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			if (!ir->attached) {
7344a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson				ret = -ENODEV;
7354a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson				break;
7364a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			}
7374a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		} else {
7384a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			lirc_buffer_read(ir->buf, buf);
73960519af3fd0e75a97036075fc657f1ebe87e0f0dHans Verkuil			ret = copy_to_user((void __user *)buffer+written, buf,
7404a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson					   ir->buf->chunk_size);
741250f7a5f62a08985af5cf7728ae7ba9edbfdc0a9Dan Carpenter			if (!ret)
742250f7a5f62a08985af5cf7728ae7ba9edbfdc0a9Dan Carpenter				written += ir->buf->chunk_size;
743250f7a5f62a08985af5cf7728ae7ba9edbfdc0a9Dan Carpenter			else
744250f7a5f62a08985af5cf7728ae7ba9edbfdc0a9Dan Carpenter				ret = -EFAULT;
7454a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		}
7464a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	}
7474a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
7484a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	remove_wait_queue(&ir->buf->wait_poll, &wait);
7494a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	set_current_state(TASK_RUNNING);
750250f7a5f62a08985af5cf7728ae7ba9edbfdc0a9Dan Carpenter
751250f7a5f62a08985af5cf7728ae7ba9edbfdc0a9Dan Carpenterout_locked:
7524a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	mutex_unlock(&ir->irctl_lock);
7534a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
75469c271f33b949a7b1cbe6f7f39ce3db9e80997a2Jarod Wilsonout_unlocked:
755715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson	kfree(buf);
7564a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	dev_dbg(ir->d.dev, LOGHEAD "read result = %s (%d)\n",
757250f7a5f62a08985af5cf7728ae7ba9edbfdc0a9Dan Carpenter		ir->d.name, ir->d.minor, ret ? "<fail>" : "<ok>", ret);
7584a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
7594a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	return ret ? ret : written;
7604a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson}
7614a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod WilsonEXPORT_SYMBOL(lirc_dev_fop_read);
7624a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
7634a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilsonvoid *lirc_get_pdata(struct file *file)
7644a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson{
7650990a97a1fc649bf9a7e6057c326a835d1520847Al Viro	return irctls[iminor(file_inode(file))]->d.data;
7664a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson}
7674a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod WilsonEXPORT_SYMBOL(lirc_get_pdata);
7684a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
7694a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
7700e835087dfe7db19f1f072046f5e116d4ec6662bDan Carpenterssize_t lirc_dev_fop_write(struct file *file, const char __user *buffer,
7714a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson			   size_t length, loff_t *ppos)
7724a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson{
773496ad9aa8ef448058e36ca7a787c61f2e63f0f54Al Viro	struct irctl *ir = irctls[iminor(file_inode(file))];
7744a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
775715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson	if (!ir) {
776715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson		printk(KERN_ERR "%s: called with invalid irctl\n", __func__);
777715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson		return -ENODEV;
778715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson	}
779715d29a74450696696dc064f85ba4ff0eaadb1d2Jarod Wilson
7804a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	dev_dbg(ir->d.dev, LOGHEAD "write called\n", ir->d.name, ir->d.minor);
7814a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
7824a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	if (!ir->attached)
7834a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		return -ENODEV;
7844a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
7854a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	return -EINVAL;
7864a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson}
7874a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod WilsonEXPORT_SYMBOL(lirc_dev_fop_write);
7884a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
7894a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
7904a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilsonstatic int __init lirc_dev_init(void)
7914a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson{
7924a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	int retval;
7934a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
7944a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	lirc_class = class_create(THIS_MODULE, "lirc");
7954a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	if (IS_ERR(lirc_class)) {
7964a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		retval = PTR_ERR(lirc_class);
7974a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		printk(KERN_ERR "lirc_dev: class_create failed\n");
7984a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		goto error;
7994a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	}
8004a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
8014a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	retval = alloc_chrdev_region(&lirc_base_dev, 0, MAX_IRCTL_DEVICES,
8024a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson				     IRCTL_DEV_NAME);
8034a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	if (retval) {
8044a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		class_destroy(lirc_class);
8054a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		printk(KERN_ERR "lirc_dev: alloc_chrdev_region failed\n");
8064a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson		goto error;
8074a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	}
8084a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
8094a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
8104a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	printk(KERN_INFO "lirc_dev: IR Remote Control driver registered, "
8114a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	       "major %d \n", MAJOR(lirc_base_dev));
8124a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
8134a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilsonerror:
8144a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	return retval;
8154a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson}
8164a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
8174a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
8184a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
8194a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilsonstatic void __exit lirc_dev_exit(void)
8204a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson{
8214a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	class_destroy(lirc_class);
8224a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	unregister_chrdev_region(lirc_base_dev, MAX_IRCTL_DEVICES);
8234a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson	printk(KERN_INFO "lirc_dev: module unloaded\n");
8244a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson}
8254a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
8264a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilsonmodule_init(lirc_dev_init);
8274a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilsonmodule_exit(lirc_dev_exit);
8284a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
8294a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod WilsonMODULE_DESCRIPTION("LIRC base driver module");
8304a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod WilsonMODULE_AUTHOR("Artur Lipowski");
8314a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod WilsonMODULE_LICENSE("GPL");
8324a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilson
8334a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod Wilsonmodule_param(debug, bool, S_IRUGO | S_IWUSR);
8344a62a5ab59742331a4e17ccaa894968d40ed9b16Jarod WilsonMODULE_PARM_DESC(debug, "Enable debugging messages");
835