834 lines · 18.1 KB · C
RawDownload
/* * LIRC base driver * * by Artur Lipowski <alipowski@interia.pl> * *  This program is free software; you can redistribute it and/or modify *  it under the terms of the GNU General Public License as published by *  the Free Software Foundation; either version 2 of the License, or *  (at your option) any later version. * *  This program is distributed in the hope that it will be useful, *  but WITHOUT ANY WARRANTY; without even the implied warranty of *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the *  GNU General Public License for more details. * *  You should have received a copy of the GNU General Public License *  along with this program; if not, write to the Free Software *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA * */ #include <linux/module.h>#include <linux/kernel.h>#include <linux/sched.h>#include <linux/errno.h>#include <linux/ioctl.h>#include <linux/fs.h>#include <linux/poll.h>#include <linux/completion.h>#include <linux/mutex.h>#include <linux/wait.h>#include <linux/unistd.h>#include <linux/kthread.h>#include <linux/bitops.h>#include <linux/device.h>#include <linux/cdev.h> #include <media/rc-core.h>#include <media/lirc.h>#include <media/lirc_dev.h> static bool debug; #define IRCTL_DEV_NAME	"BaseRemoteCtl"#define NOPLUG		-1#define LOGHEAD		"lirc_dev (%s[%d]): " static dev_t lirc_base_dev; struct irctl {	struct lirc_driver d;	int attached;	int open; 	struct mutex irctl_lock;	struct lirc_buffer *buf;	unsigned int chunk_size; 	struct cdev *cdev; 	struct task_struct *task;	long jiffies_to_wait;}; static DEFINE_MUTEX(lirc_dev_lock); static struct irctl *irctls[MAX_IRCTL_DEVICES]; /* Only used for sysfs but defined to void otherwise */static struct class *lirc_class; /*  helper function *  initializes the irctl structure */static void lirc_irctl_init(struct irctl *ir){	mutex_init(&ir->irctl_lock);	ir->d.minor = NOPLUG;} static void lirc_irctl_cleanup(struct irctl *ir){	dev_dbg(ir->d.dev, LOGHEAD "cleaning up\n", ir->d.name, ir->d.minor); 	device_destroy(lirc_class, MKDEV(MAJOR(lirc_base_dev), ir->d.minor)); 	if (ir->buf != ir->d.rbuf) {		lirc_buffer_free(ir->buf);		kfree(ir->buf);	}	ir->buf = NULL;} /*  helper function *  reads key codes from driver and puts them into buffer *  returns 0 on success */static int lirc_add_to_buf(struct irctl *ir){	if (ir->d.add_to_buf) {		int res = -ENODATA;		int got_data = 0; 		/*		 * service the device as long as it is returning		 * data and we have space		 */get_data:		res = ir->d.add_to_buf(ir->d.data, ir->buf);		if (res == 0) {			got_data++;			goto get_data;		} 		if (res == -ENODEV)			kthread_stop(ir->task); 		return got_data ? 0 : res;	} 	return 0;} /* main function of the polling thread */static int lirc_thread(void *irctl){	struct irctl *ir = irctl; 	dev_dbg(ir->d.dev, LOGHEAD "poll thread started\n",		ir->d.name, ir->d.minor); 	do {		if (ir->open) {			if (ir->jiffies_to_wait) {				set_current_state(TASK_INTERRUPTIBLE);				schedule_timeout(ir->jiffies_to_wait);			}			if (kthread_should_stop())				break;			if (!lirc_add_to_buf(ir))				wake_up_interruptible(&ir->buf->wait_poll);		} else {			set_current_state(TASK_INTERRUPTIBLE);			schedule();		}	} while (!kthread_should_stop()); 	dev_dbg(ir->d.dev, LOGHEAD "poll thread ended\n",		ir->d.name, ir->d.minor); 	return 0;}  static const struct file_operations lirc_dev_fops = {	.owner		= THIS_MODULE,	.read		= lirc_dev_fop_read,	.write		= lirc_dev_fop_write,	.poll		= lirc_dev_fop_poll,	.unlocked_ioctl	= lirc_dev_fop_ioctl,#ifdef CONFIG_COMPAT	.compat_ioctl	= lirc_dev_fop_ioctl,#endif	.open		= lirc_dev_fop_open,	.release	= lirc_dev_fop_close,	.llseek		= noop_llseek,}; static int lirc_cdev_add(struct irctl *ir){	int retval = -ENOMEM;	struct lirc_driver *d = &ir->d;	struct cdev *cdev; 	cdev = kzalloc(sizeof(*cdev), GFP_KERNEL);	if (!cdev)		goto err_out; 	if (d->fops) {		cdev_init(cdev, d->fops);		cdev->owner = d->owner;	} else {		cdev_init(cdev, &lirc_dev_fops);		cdev->owner = THIS_MODULE;	}	retval = kobject_set_name(&cdev->kobj, "lirc%d", d->minor);	if (retval)		goto err_out; 	retval = cdev_add(cdev, MKDEV(MAJOR(lirc_base_dev), d->minor), 1);	if (retval) {		kobject_put(&cdev->kobj);		goto err_out;	} 	ir->cdev = cdev; 	return 0; err_out:	kfree(cdev);	return retval;} int lirc_register_driver(struct lirc_driver *d){	struct irctl *ir;	int minor;	int bytes_in_key;	unsigned int chunk_size;	unsigned int buffer_size;	int err; 	if (!d) {		printk(KERN_ERR "lirc_dev: lirc_register_driver: "		       "driver pointer must be not NULL!\n");		err = -EBADRQC;		goto out;	} 	if (!d->dev) {		printk(KERN_ERR "%s: dev pointer not filled in!\n", __func__);		err = -EINVAL;		goto out;	} 	if (MAX_IRCTL_DEVICES <= d->minor) {		dev_err(d->dev, "lirc_dev: lirc_register_driver: "			"\"minor\" must be between 0 and %d (%d)!\n",			MAX_IRCTL_DEVICES - 1, d->minor);		err = -EBADRQC;		goto out;	} 	if (1 > d->code_length || (BUFLEN * 8) < d->code_length) {		dev_err(d->dev, "lirc_dev: lirc_register_driver: "			"code length in bits for minor (%d) "			"must be less than %d!\n",			d->minor, BUFLEN * 8);		err = -EBADRQC;		goto out;	} 	dev_dbg(d->dev, "lirc_dev: lirc_register_driver: sample_rate: %d\n",		d->sample_rate);	if (d->sample_rate) {		if (2 > d->sample_rate || HZ < d->sample_rate) {			dev_err(d->dev, "lirc_dev: lirc_register_driver: "				"sample_rate must be between 2 and %d!\n", HZ);			err = -EBADRQC;			goto out;		}		if (!d->add_to_buf) {			dev_err(d->dev, "lirc_dev: lirc_register_driver: "				"add_to_buf cannot be NULL when "				"sample_rate is set\n");			err = -EBADRQC;			goto out;		}	} else if (!(d->fops && d->fops->read) && !d->rbuf) {		dev_err(d->dev, "lirc_dev: lirc_register_driver: "			"fops->read and rbuf cannot all be NULL!\n");		err = -EBADRQC;		goto out;	} else if (!d->rbuf) {		if (!(d->fops && d->fops->read && d->fops->poll &&		      d->fops->unlocked_ioctl)) {			dev_err(d->dev, "lirc_dev: lirc_register_driver: "				"neither read, poll nor unlocked_ioctl can be NULL!\n");			err = -EBADRQC;			goto out;		}	} 	mutex_lock(&lirc_dev_lock); 	minor = d->minor; 	if (minor < 0) {		/* find first free slot for driver */		for (minor = 0; minor < MAX_IRCTL_DEVICES; minor++)			if (!irctls[minor])				break;		if (MAX_IRCTL_DEVICES == minor) {			dev_err(d->dev, "lirc_dev: lirc_register_driver: "				"no free slots for drivers!\n");			err = -ENOMEM;			goto out_lock;		}	} else if (irctls[minor]) {		dev_err(d->dev, "lirc_dev: lirc_register_driver: "			"minor (%d) just registered!\n", minor);		err = -EBUSY;		goto out_lock;	} 	ir = kzalloc(sizeof(struct irctl), GFP_KERNEL);	if (!ir) {		err = -ENOMEM;		goto out_lock;	}	lirc_irctl_init(ir);	irctls[minor] = ir;	d->minor = minor; 	if (d->sample_rate) {		ir->jiffies_to_wait = HZ / d->sample_rate;	} else {		/* it means - wait for external event in task queue */		ir->jiffies_to_wait = 0;	} 	/* some safety check 8-) */	d->name[sizeof(d->name)-1] = '\0'; 	bytes_in_key = BITS_TO_LONGS(d->code_length) +			(d->code_length % 8 ? 1 : 0);	buffer_size = d->buffer_size ? d->buffer_size : BUFLEN / bytes_in_key;	chunk_size  = d->chunk_size  ? d->chunk_size  : bytes_in_key; 	if (d->rbuf) {		ir->buf = d->rbuf;	} else {		ir->buf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);		if (!ir->buf) {			err = -ENOMEM;			goto out_lock;		}		err = lirc_buffer_init(ir->buf, chunk_size, buffer_size);		if (err) {			kfree(ir->buf);			goto out_lock;		}	}	ir->chunk_size = ir->buf->chunk_size; 	if (d->features == 0)		d->features = LIRC_CAN_REC_LIRCCODE; 	ir->d = *d; 	device_create(lirc_class, ir->d.dev,		      MKDEV(MAJOR(lirc_base_dev), ir->d.minor), NULL,		      "lirc%u", ir->d.minor); 	if (d->sample_rate) {		/* try to fire up polling thread */		ir->task = kthread_run(lirc_thread, (void *)ir, "lirc_dev");		if (IS_ERR(ir->task)) {			dev_err(d->dev, "lirc_dev: lirc_register_driver: "				"cannot run poll thread for minor = %d\n",				d->minor);			err = -ECHILD;			goto out_sysfs;		}	} 	err = lirc_cdev_add(ir);	if (err)		goto out_sysfs; 	ir->attached = 1;	mutex_unlock(&lirc_dev_lock); 	dev_info(ir->d.dev, "lirc_dev: driver %s registered at minor = %d\n",		 ir->d.name, ir->d.minor);	return minor; out_sysfs:	device_destroy(lirc_class, MKDEV(MAJOR(lirc_base_dev), ir->d.minor));out_lock:	mutex_unlock(&lirc_dev_lock);out:	return err;}EXPORT_SYMBOL(lirc_register_driver); int lirc_unregister_driver(int minor){	struct irctl *ir;	struct cdev *cdev; 	if (minor < 0 || minor >= MAX_IRCTL_DEVICES) {		printk(KERN_ERR "lirc_dev: %s: minor (%d) must be between "		       "0 and %d!\n", __func__, minor, MAX_IRCTL_DEVICES - 1);		return -EBADRQC;	} 	ir = irctls[minor];	if (!ir) {		printk(KERN_ERR "lirc_dev: %s: failed to get irctl struct "		       "for minor %d!\n", __func__, minor);		return -ENOENT;	} 	cdev = ir->cdev; 	mutex_lock(&lirc_dev_lock); 	if (ir->d.minor != minor) {		printk(KERN_ERR "lirc_dev: %s: minor (%d) device not "		       "registered!\n", __func__, minor);		mutex_unlock(&lirc_dev_lock);		return -ENOENT;	} 	/* end up polling thread */	if (ir->task)		kthread_stop(ir->task); 	dev_dbg(ir->d.dev, "lirc_dev: driver %s unregistered from minor = %d\n",		ir->d.name, ir->d.minor); 	ir->attached = 0;	if (ir->open) {		dev_dbg(ir->d.dev, LOGHEAD "releasing opened driver\n",			ir->d.name, ir->d.minor);		wake_up_interruptible(&ir->buf->wait_poll);		mutex_lock(&ir->irctl_lock);		ir->d.set_use_dec(ir->d.data);		module_put(cdev->owner);		mutex_unlock(&ir->irctl_lock);	} else {		lirc_irctl_cleanup(ir);		cdev_del(cdev);		kfree(cdev);		kfree(ir);		irctls[minor] = NULL;	} 	mutex_unlock(&lirc_dev_lock); 	return 0;}EXPORT_SYMBOL(lirc_unregister_driver); int lirc_dev_fop_open(struct inode *inode, struct file *file){	struct irctl *ir;	struct cdev *cdev;	int retval = 0; 	if (iminor(inode) >= MAX_IRCTL_DEVICES) {		printk(KERN_WARNING "lirc_dev [%d]: open result = -ENODEV\n",		       iminor(inode));		return -ENODEV;	} 	if (mutex_lock_interruptible(&lirc_dev_lock))		return -ERESTARTSYS; 	ir = irctls[iminor(inode)];	if (!ir) {		retval = -ENODEV;		goto error;	} 	dev_dbg(ir->d.dev, LOGHEAD "open called\n", ir->d.name, ir->d.minor); 	if (ir->d.minor == NOPLUG) {		retval = -ENODEV;		goto error;	} 	if (ir->open) {		retval = -EBUSY;		goto error;	} 	if (ir->d.rdev) {		retval = rc_open(ir->d.rdev);		if (retval)			goto error;	} 	cdev = ir->cdev;	if (try_module_get(cdev->owner)) {		ir->open++;		retval = ir->d.set_use_inc(ir->d.data); 		if (retval) {			module_put(cdev->owner);			ir->open--;		} else {			lirc_buffer_clear(ir->buf);		}		if (ir->task)			wake_up_process(ir->task);	} error:	if (ir)		dev_dbg(ir->d.dev, LOGHEAD "open result = %d\n",			ir->d.name, ir->d.minor, retval); 	mutex_unlock(&lirc_dev_lock); 	nonseekable_open(inode, file); 

Lines 1–500 of 834.