615 lines · 12.6 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. * */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/module.h>#include <linux/sched/signal.h>#include <linux/ioctl.h>#include <linux/poll.h>#include <linux/mutex.h>#include <linux/device.h>#include <linux/cdev.h> #include <media/rc-core.h>#include <media/lirc.h>#include <media/lirc_dev.h> #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;	bool buf_internal;	unsigned int chunk_size; 	struct device dev;	struct cdev cdev;}; 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; static void lirc_release(struct device *ld){	struct irctl *ir = container_of(ld, struct irctl, dev); 	if (ir->buf_internal) {		lirc_buffer_free(ir->buf);		kfree(ir->buf);	} 	mutex_lock(&lirc_dev_lock);	irctls[ir->d.minor] = NULL;	mutex_unlock(&lirc_dev_lock);	kfree(ir);} static int lirc_allocate_buffer(struct irctl *ir){	int err = 0;	int bytes_in_key;	unsigned int chunk_size;	unsigned int buffer_size;	struct lirc_driver *d = &ir->d; 	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;		ir->buf_internal = false;	} else {		ir->buf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);		if (!ir->buf) {			err = -ENOMEM;			goto out;		} 		err = lirc_buffer_init(ir->buf, chunk_size, buffer_size);		if (err) {			kfree(ir->buf);			ir->buf = NULL;			goto out;		} 		ir->buf_internal = true;		d->rbuf = ir->buf;	}	ir->chunk_size = ir->buf->chunk_size; out:	return err;} int lirc_register_driver(struct lirc_driver *d){	struct irctl *ir;	int minor;	int err; 	if (!d) {		pr_err("driver pointer must be not NULL!\n");		return -EBADRQC;	} 	if (!d->dev) {		pr_err("dev pointer not filled in!\n");		return -EINVAL;	} 	if (!d->fops) {		pr_err("fops pointer not filled in!\n");		return -EINVAL;	} 	if (d->minor >= MAX_IRCTL_DEVICES) {		dev_err(d->dev, "minor must be between 0 and %d!\n",						MAX_IRCTL_DEVICES - 1);		return -EBADRQC;	} 	if (d->code_length < 1 || d->code_length > (BUFLEN * 8)) {		dev_err(d->dev, "code length must be less than %d bits\n",								BUFLEN * 8);		return -EBADRQC;	} 	if (!d->rbuf && !(d->fops && d->fops->read &&			  d->fops->poll && d->fops->unlocked_ioctl)) {		dev_err(d->dev, "undefined read, poll, ioctl\n");		return -EBADRQC;	} 	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 (minor == MAX_IRCTL_DEVICES) {			dev_err(d->dev, "no free slots for drivers!\n");			err = -ENOMEM;			goto out_lock;		}	} else if (irctls[minor]) {		dev_err(d->dev, "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;	} 	mutex_init(&ir->irctl_lock);	irctls[minor] = ir;	d->minor = minor; 	/* some safety check 8-) */	d->name[sizeof(d->name)-1] = '\0'; 	if (d->features == 0)		d->features = LIRC_CAN_REC_LIRCCODE; 	ir->d = *d; 	if (LIRC_CAN_REC(d->features)) {		err = lirc_allocate_buffer(irctls[minor]);		if (err) {			kfree(ir);			goto out_lock;		}		d->rbuf = ir->buf;	} 	device_initialize(&ir->dev);	ir->dev.devt = MKDEV(MAJOR(lirc_base_dev), ir->d.minor);	ir->dev.class = lirc_class;	ir->dev.parent = d->dev;	ir->dev.release = lirc_release;	dev_set_name(&ir->dev, "lirc%d", ir->d.minor); 	cdev_init(&ir->cdev, d->fops);	ir->cdev.owner = ir->d.owner;	ir->cdev.kobj.parent = &ir->dev.kobj; 	err = cdev_add(&ir->cdev, ir->dev.devt, 1);	if (err)		goto out_free_dev; 	ir->attached = 1; 	err = device_add(&ir->dev);	if (err)		goto out_cdev; 	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_cdev:	cdev_del(&ir->cdev);out_free_dev:	put_device(&ir->dev);out_lock:	mutex_unlock(&lirc_dev_lock); 	return err;}EXPORT_SYMBOL(lirc_register_driver); int lirc_unregister_driver(int minor){	struct irctl *ir; 	if (minor < 0 || minor >= MAX_IRCTL_DEVICES) {		pr_err("minor (%d) must be between 0 and %d!\n",					minor, MAX_IRCTL_DEVICES - 1);		return -EBADRQC;	} 	ir = irctls[minor];	if (!ir) {		pr_err("failed to get irctl\n");		return -ENOENT;	} 	mutex_lock(&lirc_dev_lock); 	if (ir->d.minor != minor) {		dev_err(ir->d.dev, "lirc_dev: minor %d device not registered\n",									minor);		mutex_unlock(&lirc_dev_lock);		return -ENOENT;	} 	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_unlock(&lirc_dev_lock); 	device_del(&ir->dev);	cdev_del(&ir->cdev);	put_device(&ir->dev); 	return 0;}EXPORT_SYMBOL(lirc_unregister_driver); int lirc_dev_fop_open(struct inode *inode, struct file *file){	struct irctl *ir;	int retval = 0; 	if (iminor(inode) >= MAX_IRCTL_DEVICES) {		pr_err("open result for %d is -ENODEV\n", iminor(inode));		return -ENODEV;	} 	if (mutex_lock_interruptible(&lirc_dev_lock))		return -ERESTARTSYS; 	ir = irctls[iminor(inode)];	mutex_unlock(&lirc_dev_lock); 	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;	} 	if (ir->buf)		lirc_buffer_clear(ir->buf); 	ir->open++; error:	nonseekable_open(inode, file); 	return retval;}EXPORT_SYMBOL(lirc_dev_fop_open); int lirc_dev_fop_close(struct inode *inode, struct file *file){	struct irctl *ir = irctls[iminor(inode)];	int ret; 	if (!ir) {		pr_err("called with invalid irctl\n");		return -EINVAL;	} 	ret = mutex_lock_killable(&lirc_dev_lock);	WARN_ON(ret); 	rc_close(ir->d.rdev); 	ir->open--;	if (!ret)		mutex_unlock(&lirc_dev_lock); 	return 0;}EXPORT_SYMBOL(lirc_dev_fop_close); unsigned int lirc_dev_fop_poll(struct file *file, poll_table *wait){	struct irctl *ir = irctls[iminor(file_inode(file))];	unsigned int ret; 	if (!ir) {		pr_err("called with invalid irctl\n");		return POLLERR;	} 	if (!ir->attached)		return POLLHUP | POLLERR; 	if (ir->buf) {		poll_wait(file, &ir->buf->wait_poll, wait); 		if (lirc_buffer_empty(ir->buf))			ret = 0;		else			ret = POLLIN | POLLRDNORM;	} else		ret = POLLERR; 	dev_dbg(ir->d.dev, LOGHEAD "poll result = %d\n",		ir->d.name, ir->d.minor, ret); 	return ret;}EXPORT_SYMBOL(lirc_dev_fop_poll); long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg){	__u32 mode;	int result = 0;	struct irctl *ir = irctls[iminor(file_inode(file))]; 	if (!ir) {		pr_err("no irctl found!\n");		return -ENODEV;	} 	dev_dbg(ir->d.dev, LOGHEAD "ioctl called (0x%x)\n",		ir->d.name, ir->d.minor, cmd); 	if (ir->d.minor == NOPLUG || !ir->attached) {		dev_err(ir->d.dev, LOGHEAD "ioctl result = -ENODEV\n",			ir->d.name, ir->d.minor);		return -ENODEV;	} 	mutex_lock(&ir->irctl_lock); 	switch (cmd) {	case LIRC_GET_FEATURES:		result = put_user(ir->d.features, (__u32 __user *)arg);		break;	case LIRC_GET_REC_MODE:		if (!LIRC_CAN_REC(ir->d.features)) {			result = -ENOTTY;			break;		} 		result = put_user(LIRC_REC2MODE				  (ir->d.features & LIRC_CAN_REC_MASK),				  (__u32 __user *)arg);		break;	case LIRC_SET_REC_MODE:		if (!LIRC_CAN_REC(ir->d.features)) {			result = -ENOTTY;			break;		} 		result = get_user(mode, (__u32 __user *)arg);		if (!result && !(LIRC_MODE2REC(mode) & ir->d.features))			result = -EINVAL;		/*		 * FIXME: We should actually set the mode somehow but		 * for now, lirc_serial doesn't support mode changing either		 */		break;	case LIRC_GET_LENGTH:		result = put_user(ir->d.code_length, (__u32 __user *)arg);		break;	case LIRC_GET_MIN_TIMEOUT:		if (!(ir->d.features & LIRC_CAN_SET_REC_TIMEOUT) ||		    ir->d.min_timeout == 0) {			result = -ENOTTY;			break;		} 		result = put_user(ir->d.min_timeout, (__u32 __user *)arg);		break;	case LIRC_GET_MAX_TIMEOUT:		if (!(ir->d.features & LIRC_CAN_SET_REC_TIMEOUT) ||		    ir->d.max_timeout == 0) {			result = -ENOTTY;			break;		} 		result = put_user(ir->d.max_timeout, (__u32 __user *)arg);		break;	default:		result = -ENOTTY;	} 	mutex_unlock(&ir->irctl_lock); 	return result;}EXPORT_SYMBOL(lirc_dev_fop_ioctl); ssize_t lirc_dev_fop_read(struct file *file,			  char __user *buffer,			  size_t length,			  loff_t *ppos){	struct irctl *ir = irctls[iminor(file_inode(file))];	unsigned char *buf;	int ret = 0, written = 0;	DECLARE_WAITQUEUE(wait, current); 	if (!ir) {		pr_err("called with invalid irctl\n");		return -ENODEV;	} 	if (!LIRC_CAN_REC(ir->d.features))		return -EINVAL; 	dev_dbg(ir->d.dev, LOGHEAD "read called\n", ir->d.name, ir->d.minor); 	buf = kzalloc(ir->chunk_size, GFP_KERNEL);	if (!buf)		return -ENOMEM; 	if (mutex_lock_interruptible(&ir->irctl_lock)) {		ret = -ERESTARTSYS;		goto out_unlocked;	}	if (!ir->attached) {		ret = -ENODEV;		goto out_locked;	} 	if (length % ir->chunk_size) {

Lines 1–500 of 615.