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 * You should have received a copy of the GNU General Public License 1732cf86f6SMauro Carvalho Chehab * along with this program; if not, write to the Free Software 1832cf86f6SMauro Carvalho Chehab * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 1932cf86f6SMauro Carvalho Chehab * 2032cf86f6SMauro Carvalho Chehab */ 2132cf86f6SMauro Carvalho Chehab 2232cf86f6SMauro Carvalho Chehab #include <linux/module.h> 2332cf86f6SMauro Carvalho Chehab #include <linux/kernel.h> 2432cf86f6SMauro Carvalho Chehab #include <linux/sched.h> 2532cf86f6SMauro Carvalho Chehab #include <linux/errno.h> 2632cf86f6SMauro Carvalho Chehab #include <linux/ioctl.h> 2732cf86f6SMauro Carvalho Chehab #include <linux/fs.h> 2832cf86f6SMauro Carvalho Chehab #include <linux/poll.h> 2932cf86f6SMauro Carvalho Chehab #include <linux/completion.h> 3032cf86f6SMauro Carvalho Chehab #include <linux/mutex.h> 3132cf86f6SMauro Carvalho Chehab #include <linux/wait.h> 3232cf86f6SMauro Carvalho Chehab #include <linux/unistd.h> 3332cf86f6SMauro Carvalho Chehab #include <linux/kthread.h> 3432cf86f6SMauro Carvalho Chehab #include <linux/bitops.h> 3532cf86f6SMauro Carvalho Chehab #include <linux/device.h> 3632cf86f6SMauro Carvalho Chehab #include <linux/cdev.h> 3732cf86f6SMauro Carvalho Chehab 38ca7a722dSSrinivas Kandagatla #include <media/rc-core.h> 3932cf86f6SMauro Carvalho Chehab #include <media/lirc.h> 4032cf86f6SMauro Carvalho Chehab #include <media/lirc_dev.h> 4132cf86f6SMauro Carvalho Chehab 4290ab5ee9SRusty Russell static bool debug; 4332cf86f6SMauro Carvalho Chehab 4432cf86f6SMauro Carvalho Chehab #define IRCTL_DEV_NAME "BaseRemoteCtl" 4532cf86f6SMauro Carvalho Chehab #define NOPLUG -1 4632cf86f6SMauro Carvalho Chehab #define LOGHEAD "lirc_dev (%s[%d]): " 4732cf86f6SMauro Carvalho Chehab 4832cf86f6SMauro Carvalho Chehab static dev_t lirc_base_dev; 4932cf86f6SMauro Carvalho Chehab 5032cf86f6SMauro Carvalho Chehab struct irctl { 5132cf86f6SMauro Carvalho Chehab struct lirc_driver d; 5232cf86f6SMauro Carvalho Chehab int attached; 5332cf86f6SMauro Carvalho Chehab int open; 5432cf86f6SMauro Carvalho Chehab 5532cf86f6SMauro Carvalho Chehab struct mutex irctl_lock; 5632cf86f6SMauro Carvalho Chehab struct lirc_buffer *buf; 5732cf86f6SMauro Carvalho Chehab unsigned int chunk_size; 5832cf86f6SMauro Carvalho Chehab 598de111e2SJarod Wilson struct cdev *cdev; 608de111e2SJarod Wilson 6132cf86f6SMauro Carvalho Chehab struct task_struct *task; 6232cf86f6SMauro Carvalho Chehab long jiffies_to_wait; 6332cf86f6SMauro Carvalho Chehab }; 6432cf86f6SMauro Carvalho Chehab 6532cf86f6SMauro Carvalho Chehab static DEFINE_MUTEX(lirc_dev_lock); 6632cf86f6SMauro Carvalho Chehab 6732cf86f6SMauro Carvalho Chehab static struct irctl *irctls[MAX_IRCTL_DEVICES]; 6832cf86f6SMauro Carvalho Chehab 6932cf86f6SMauro Carvalho Chehab /* Only used for sysfs but defined to void otherwise */ 7032cf86f6SMauro Carvalho Chehab static struct class *lirc_class; 7132cf86f6SMauro Carvalho Chehab 7232cf86f6SMauro Carvalho Chehab /* helper function 7332cf86f6SMauro Carvalho Chehab * initializes the irctl structure 7432cf86f6SMauro Carvalho Chehab */ 7532cf86f6SMauro Carvalho Chehab static void lirc_irctl_init(struct irctl *ir) 7632cf86f6SMauro Carvalho Chehab { 7732cf86f6SMauro Carvalho Chehab mutex_init(&ir->irctl_lock); 7832cf86f6SMauro Carvalho Chehab ir->d.minor = NOPLUG; 7932cf86f6SMauro Carvalho Chehab } 8032cf86f6SMauro Carvalho Chehab 8132cf86f6SMauro Carvalho Chehab static void lirc_irctl_cleanup(struct irctl *ir) 8232cf86f6SMauro Carvalho Chehab { 8332cf86f6SMauro Carvalho Chehab dev_dbg(ir->d.dev, LOGHEAD "cleaning up\n", ir->d.name, ir->d.minor); 8432cf86f6SMauro Carvalho Chehab 8532cf86f6SMauro Carvalho Chehab device_destroy(lirc_class, MKDEV(MAJOR(lirc_base_dev), ir->d.minor)); 8632cf86f6SMauro Carvalho Chehab 8732cf86f6SMauro Carvalho Chehab if (ir->buf != ir->d.rbuf) { 8832cf86f6SMauro Carvalho Chehab lirc_buffer_free(ir->buf); 8932cf86f6SMauro Carvalho Chehab kfree(ir->buf); 9032cf86f6SMauro Carvalho Chehab } 9132cf86f6SMauro Carvalho Chehab ir->buf = NULL; 9232cf86f6SMauro Carvalho Chehab } 9332cf86f6SMauro Carvalho Chehab 9432cf86f6SMauro Carvalho Chehab /* helper function 9532cf86f6SMauro Carvalho Chehab * reads key codes from driver and puts them into buffer 9632cf86f6SMauro Carvalho Chehab * returns 0 on success 9732cf86f6SMauro Carvalho Chehab */ 9832cf86f6SMauro Carvalho Chehab static int lirc_add_to_buf(struct irctl *ir) 9932cf86f6SMauro Carvalho Chehab { 10032cf86f6SMauro Carvalho Chehab if (ir->d.add_to_buf) { 10132cf86f6SMauro Carvalho Chehab int res = -ENODATA; 10232cf86f6SMauro Carvalho Chehab int got_data = 0; 10332cf86f6SMauro Carvalho Chehab 10432cf86f6SMauro Carvalho Chehab /* 10532cf86f6SMauro Carvalho Chehab * service the device as long as it is returning 10632cf86f6SMauro Carvalho Chehab * data and we have space 10732cf86f6SMauro Carvalho Chehab */ 10832cf86f6SMauro Carvalho Chehab get_data: 10932cf86f6SMauro Carvalho Chehab res = ir->d.add_to_buf(ir->d.data, ir->buf); 11032cf86f6SMauro Carvalho Chehab if (res == 0) { 11132cf86f6SMauro Carvalho Chehab got_data++; 11232cf86f6SMauro Carvalho Chehab goto get_data; 11332cf86f6SMauro Carvalho Chehab } 11432cf86f6SMauro Carvalho Chehab 11532cf86f6SMauro Carvalho Chehab if (res == -ENODEV) 11632cf86f6SMauro Carvalho Chehab kthread_stop(ir->task); 11732cf86f6SMauro Carvalho Chehab 11832cf86f6SMauro Carvalho Chehab return got_data ? 0 : res; 11932cf86f6SMauro Carvalho Chehab } 12032cf86f6SMauro Carvalho Chehab 12132cf86f6SMauro Carvalho Chehab return 0; 12232cf86f6SMauro Carvalho Chehab } 12332cf86f6SMauro Carvalho Chehab 12432cf86f6SMauro Carvalho Chehab /* main function of the polling thread 12532cf86f6SMauro Carvalho Chehab */ 12632cf86f6SMauro Carvalho Chehab static int lirc_thread(void *irctl) 12732cf86f6SMauro Carvalho Chehab { 12832cf86f6SMauro Carvalho Chehab struct irctl *ir = irctl; 12932cf86f6SMauro Carvalho Chehab 13032cf86f6SMauro Carvalho Chehab dev_dbg(ir->d.dev, LOGHEAD "poll thread started\n", 13132cf86f6SMauro Carvalho Chehab ir->d.name, ir->d.minor); 13232cf86f6SMauro Carvalho Chehab 13332cf86f6SMauro Carvalho Chehab do { 13432cf86f6SMauro Carvalho Chehab if (ir->open) { 13532cf86f6SMauro Carvalho Chehab if (ir->jiffies_to_wait) { 13632cf86f6SMauro Carvalho Chehab set_current_state(TASK_INTERRUPTIBLE); 13732cf86f6SMauro Carvalho Chehab schedule_timeout(ir->jiffies_to_wait); 13832cf86f6SMauro Carvalho Chehab } 13932cf86f6SMauro Carvalho Chehab if (kthread_should_stop()) 14032cf86f6SMauro Carvalho Chehab break; 14132cf86f6SMauro Carvalho Chehab if (!lirc_add_to_buf(ir)) 14232cf86f6SMauro Carvalho Chehab wake_up_interruptible(&ir->buf->wait_poll); 14332cf86f6SMauro Carvalho Chehab } else { 14432cf86f6SMauro Carvalho Chehab set_current_state(TASK_INTERRUPTIBLE); 14532cf86f6SMauro Carvalho Chehab schedule(); 14632cf86f6SMauro Carvalho Chehab } 14732cf86f6SMauro Carvalho Chehab } while (!kthread_should_stop()); 14832cf86f6SMauro Carvalho Chehab 14932cf86f6SMauro Carvalho Chehab dev_dbg(ir->d.dev, LOGHEAD "poll thread ended\n", 15032cf86f6SMauro Carvalho Chehab ir->d.name, ir->d.minor); 15132cf86f6SMauro Carvalho Chehab 15232cf86f6SMauro Carvalho Chehab return 0; 15332cf86f6SMauro Carvalho Chehab } 15432cf86f6SMauro Carvalho Chehab 15532cf86f6SMauro Carvalho Chehab 15675ef9de1SAl Viro static const struct file_operations lirc_dev_fops = { 15732cf86f6SMauro Carvalho Chehab .owner = THIS_MODULE, 15832cf86f6SMauro Carvalho Chehab .read = lirc_dev_fop_read, 15932cf86f6SMauro Carvalho Chehab .write = lirc_dev_fop_write, 16032cf86f6SMauro Carvalho Chehab .poll = lirc_dev_fop_poll, 16132cf86f6SMauro Carvalho Chehab .unlocked_ioctl = lirc_dev_fop_ioctl, 16232cf86f6SMauro Carvalho Chehab #ifdef CONFIG_COMPAT 16332cf86f6SMauro Carvalho Chehab .compat_ioctl = lirc_dev_fop_ioctl, 16432cf86f6SMauro Carvalho Chehab #endif 16532cf86f6SMauro Carvalho Chehab .open = lirc_dev_fop_open, 16632cf86f6SMauro Carvalho Chehab .release = lirc_dev_fop_close, 16732cf86f6SMauro Carvalho Chehab .llseek = noop_llseek, 16832cf86f6SMauro Carvalho Chehab }; 16932cf86f6SMauro Carvalho Chehab 17032cf86f6SMauro Carvalho Chehab static int lirc_cdev_add(struct irctl *ir) 17132cf86f6SMauro Carvalho Chehab { 1728de111e2SJarod Wilson int retval = -ENOMEM; 17332cf86f6SMauro Carvalho Chehab struct lirc_driver *d = &ir->d; 1748de111e2SJarod Wilson struct cdev *cdev; 1758de111e2SJarod Wilson 1768de111e2SJarod Wilson cdev = kzalloc(sizeof(*cdev), GFP_KERNEL); 1778de111e2SJarod Wilson if (!cdev) 1788de111e2SJarod Wilson goto err_out; 17932cf86f6SMauro Carvalho Chehab 18032cf86f6SMauro Carvalho Chehab if (d->fops) { 18132cf86f6SMauro Carvalho Chehab cdev_init(cdev, d->fops); 18232cf86f6SMauro Carvalho Chehab cdev->owner = d->owner; 18332cf86f6SMauro Carvalho Chehab } else { 18432cf86f6SMauro Carvalho Chehab cdev_init(cdev, &lirc_dev_fops); 18532cf86f6SMauro Carvalho Chehab cdev->owner = THIS_MODULE; 18632cf86f6SMauro Carvalho Chehab } 187b395cbacSVasiliy Kulikov retval = kobject_set_name(&cdev->kobj, "lirc%d", d->minor); 188b395cbacSVasiliy Kulikov if (retval) 1898de111e2SJarod Wilson goto err_out; 19032cf86f6SMauro Carvalho Chehab 19132cf86f6SMauro Carvalho Chehab retval = cdev_add(cdev, MKDEV(MAJOR(lirc_base_dev), d->minor), 1); 1928de111e2SJarod Wilson if (retval) { 19332cf86f6SMauro Carvalho Chehab kobject_put(&cdev->kobj); 1948de111e2SJarod Wilson goto err_out; 1958de111e2SJarod Wilson } 19632cf86f6SMauro Carvalho Chehab 1978de111e2SJarod Wilson ir->cdev = cdev; 1988de111e2SJarod Wilson 1998de111e2SJarod Wilson return 0; 2008de111e2SJarod Wilson 2018de111e2SJarod Wilson err_out: 2028de111e2SJarod Wilson kfree(cdev); 20332cf86f6SMauro Carvalho Chehab return retval; 20432cf86f6SMauro Carvalho Chehab } 20532cf86f6SMauro Carvalho Chehab 20632cf86f6SMauro Carvalho Chehab int lirc_register_driver(struct lirc_driver *d) 20732cf86f6SMauro Carvalho Chehab { 20832cf86f6SMauro Carvalho Chehab struct irctl *ir; 20932cf86f6SMauro Carvalho Chehab int minor; 21032cf86f6SMauro Carvalho Chehab int bytes_in_key; 21132cf86f6SMauro Carvalho Chehab unsigned int chunk_size; 21232cf86f6SMauro Carvalho Chehab unsigned int buffer_size; 21332cf86f6SMauro Carvalho Chehab int err; 21432cf86f6SMauro Carvalho Chehab 21532cf86f6SMauro Carvalho Chehab if (!d) { 21632cf86f6SMauro Carvalho Chehab printk(KERN_ERR "lirc_dev: lirc_register_driver: " 21732cf86f6SMauro Carvalho Chehab "driver pointer must be not NULL!\n"); 21832cf86f6SMauro Carvalho Chehab err = -EBADRQC; 21932cf86f6SMauro Carvalho Chehab goto out; 22032cf86f6SMauro Carvalho Chehab } 22132cf86f6SMauro Carvalho Chehab 22232cf86f6SMauro Carvalho Chehab if (!d->dev) { 22332cf86f6SMauro Carvalho Chehab printk(KERN_ERR "%s: dev pointer not filled in!\n", __func__); 22432cf86f6SMauro Carvalho Chehab err = -EINVAL; 22532cf86f6SMauro Carvalho Chehab goto out; 22632cf86f6SMauro Carvalho Chehab } 22732cf86f6SMauro Carvalho Chehab 22832cf86f6SMauro Carvalho Chehab if (MAX_IRCTL_DEVICES <= d->minor) { 22932cf86f6SMauro Carvalho Chehab dev_err(d->dev, "lirc_dev: lirc_register_driver: " 23032cf86f6SMauro Carvalho Chehab "\"minor\" must be between 0 and %d (%d)!\n", 23132cf86f6SMauro Carvalho Chehab MAX_IRCTL_DEVICES - 1, d->minor); 23232cf86f6SMauro Carvalho Chehab err = -EBADRQC; 23332cf86f6SMauro Carvalho Chehab goto out; 23432cf86f6SMauro Carvalho Chehab } 23532cf86f6SMauro Carvalho Chehab 23632cf86f6SMauro Carvalho Chehab if (1 > d->code_length || (BUFLEN * 8) < d->code_length) { 23732cf86f6SMauro Carvalho Chehab dev_err(d->dev, "lirc_dev: lirc_register_driver: " 23832cf86f6SMauro Carvalho Chehab "code length in bits for minor (%d) " 23932cf86f6SMauro Carvalho Chehab "must be less than %d!\n", 24032cf86f6SMauro Carvalho Chehab d->minor, BUFLEN * 8); 24132cf86f6SMauro Carvalho Chehab err = -EBADRQC; 24232cf86f6SMauro Carvalho Chehab goto out; 24332cf86f6SMauro Carvalho Chehab } 24432cf86f6SMauro Carvalho Chehab 24532cf86f6SMauro Carvalho Chehab dev_dbg(d->dev, "lirc_dev: lirc_register_driver: sample_rate: %d\n", 24632cf86f6SMauro Carvalho Chehab d->sample_rate); 24732cf86f6SMauro Carvalho Chehab if (d->sample_rate) { 24832cf86f6SMauro Carvalho Chehab if (2 > d->sample_rate || HZ < d->sample_rate) { 24932cf86f6SMauro Carvalho Chehab dev_err(d->dev, "lirc_dev: lirc_register_driver: " 25032cf86f6SMauro Carvalho Chehab "sample_rate must be between 2 and %d!\n", HZ); 25132cf86f6SMauro Carvalho Chehab err = -EBADRQC; 25232cf86f6SMauro Carvalho Chehab goto out; 25332cf86f6SMauro Carvalho Chehab } 25432cf86f6SMauro Carvalho Chehab if (!d->add_to_buf) { 25532cf86f6SMauro Carvalho Chehab dev_err(d->dev, "lirc_dev: lirc_register_driver: " 25632cf86f6SMauro Carvalho Chehab "add_to_buf cannot be NULL when " 25732cf86f6SMauro Carvalho Chehab "sample_rate is set\n"); 25832cf86f6SMauro Carvalho Chehab err = -EBADRQC; 25932cf86f6SMauro Carvalho Chehab goto out; 26032cf86f6SMauro Carvalho Chehab } 26132cf86f6SMauro Carvalho Chehab } else if (!(d->fops && d->fops->read) && !d->rbuf) { 26232cf86f6SMauro Carvalho Chehab dev_err(d->dev, "lirc_dev: lirc_register_driver: " 26332cf86f6SMauro Carvalho Chehab "fops->read and rbuf cannot all be NULL!\n"); 26432cf86f6SMauro Carvalho Chehab err = -EBADRQC; 26532cf86f6SMauro Carvalho Chehab goto out; 26632cf86f6SMauro Carvalho Chehab } else if (!d->rbuf) { 26732cf86f6SMauro Carvalho Chehab if (!(d->fops && d->fops->read && d->fops->poll && 26832cf86f6SMauro Carvalho Chehab d->fops->unlocked_ioctl)) { 26932cf86f6SMauro Carvalho Chehab dev_err(d->dev, "lirc_dev: lirc_register_driver: " 27032cf86f6SMauro Carvalho Chehab "neither read, poll nor unlocked_ioctl can be NULL!\n"); 27132cf86f6SMauro Carvalho Chehab err = -EBADRQC; 27232cf86f6SMauro Carvalho Chehab goto out; 27332cf86f6SMauro Carvalho Chehab } 27432cf86f6SMauro Carvalho Chehab } 27532cf86f6SMauro Carvalho Chehab 27632cf86f6SMauro Carvalho Chehab mutex_lock(&lirc_dev_lock); 27732cf86f6SMauro Carvalho Chehab 27832cf86f6SMauro Carvalho Chehab minor = d->minor; 27932cf86f6SMauro Carvalho Chehab 28032cf86f6SMauro Carvalho Chehab if (minor < 0) { 28132cf86f6SMauro Carvalho Chehab /* find first free slot for driver */ 28232cf86f6SMauro Carvalho Chehab for (minor = 0; minor < MAX_IRCTL_DEVICES; minor++) 28332cf86f6SMauro Carvalho Chehab if (!irctls[minor]) 28432cf86f6SMauro Carvalho Chehab break; 28532cf86f6SMauro Carvalho Chehab if (MAX_IRCTL_DEVICES == minor) { 28632cf86f6SMauro Carvalho Chehab dev_err(d->dev, "lirc_dev: lirc_register_driver: " 28732cf86f6SMauro Carvalho Chehab "no free slots for drivers!\n"); 28832cf86f6SMauro Carvalho Chehab err = -ENOMEM; 28932cf86f6SMauro Carvalho Chehab goto out_lock; 29032cf86f6SMauro Carvalho Chehab } 29132cf86f6SMauro Carvalho Chehab } else if (irctls[minor]) { 29232cf86f6SMauro Carvalho Chehab dev_err(d->dev, "lirc_dev: lirc_register_driver: " 29332cf86f6SMauro Carvalho Chehab "minor (%d) just registered!\n", minor); 29432cf86f6SMauro Carvalho Chehab err = -EBUSY; 29532cf86f6SMauro Carvalho Chehab goto out_lock; 29632cf86f6SMauro Carvalho Chehab } 29732cf86f6SMauro Carvalho Chehab 29832cf86f6SMauro Carvalho Chehab ir = kzalloc(sizeof(struct irctl), GFP_KERNEL); 29932cf86f6SMauro Carvalho Chehab if (!ir) { 30032cf86f6SMauro Carvalho Chehab err = -ENOMEM; 30132cf86f6SMauro Carvalho Chehab goto out_lock; 30232cf86f6SMauro Carvalho Chehab } 30332cf86f6SMauro Carvalho Chehab lirc_irctl_init(ir); 30432cf86f6SMauro Carvalho Chehab irctls[minor] = ir; 30532cf86f6SMauro Carvalho Chehab d->minor = minor; 30632cf86f6SMauro Carvalho Chehab 30732cf86f6SMauro Carvalho Chehab if (d->sample_rate) { 30832cf86f6SMauro Carvalho Chehab ir->jiffies_to_wait = HZ / d->sample_rate; 30932cf86f6SMauro Carvalho Chehab } else { 31032cf86f6SMauro Carvalho Chehab /* it means - wait for external event in task queue */ 31132cf86f6SMauro Carvalho Chehab ir->jiffies_to_wait = 0; 31232cf86f6SMauro Carvalho Chehab } 31332cf86f6SMauro Carvalho Chehab 31432cf86f6SMauro Carvalho Chehab /* some safety check 8-) */ 31532cf86f6SMauro Carvalho Chehab d->name[sizeof(d->name)-1] = '\0'; 31632cf86f6SMauro Carvalho Chehab 31732cf86f6SMauro Carvalho Chehab bytes_in_key = BITS_TO_LONGS(d->code_length) + 31832cf86f6SMauro Carvalho Chehab (d->code_length % 8 ? 1 : 0); 31932cf86f6SMauro Carvalho Chehab buffer_size = d->buffer_size ? d->buffer_size : BUFLEN / bytes_in_key; 32032cf86f6SMauro Carvalho Chehab chunk_size = d->chunk_size ? d->chunk_size : bytes_in_key; 32132cf86f6SMauro Carvalho Chehab 32232cf86f6SMauro Carvalho Chehab if (d->rbuf) { 32332cf86f6SMauro Carvalho Chehab ir->buf = d->rbuf; 32432cf86f6SMauro Carvalho Chehab } else { 32532cf86f6SMauro Carvalho Chehab ir->buf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL); 32632cf86f6SMauro Carvalho Chehab if (!ir->buf) { 32732cf86f6SMauro Carvalho Chehab err = -ENOMEM; 32832cf86f6SMauro Carvalho Chehab goto out_lock; 32932cf86f6SMauro Carvalho Chehab } 33032cf86f6SMauro Carvalho Chehab err = lirc_buffer_init(ir->buf, chunk_size, buffer_size); 33132cf86f6SMauro Carvalho Chehab if (err) { 33232cf86f6SMauro Carvalho Chehab kfree(ir->buf); 33332cf86f6SMauro Carvalho Chehab goto out_lock; 33432cf86f6SMauro Carvalho Chehab } 33532cf86f6SMauro Carvalho Chehab } 33632cf86f6SMauro Carvalho Chehab ir->chunk_size = ir->buf->chunk_size; 33732cf86f6SMauro Carvalho Chehab 33832cf86f6SMauro Carvalho Chehab if (d->features == 0) 33932cf86f6SMauro Carvalho Chehab d->features = LIRC_CAN_REC_LIRCCODE; 34032cf86f6SMauro Carvalho Chehab 34132cf86f6SMauro Carvalho Chehab ir->d = *d; 34232cf86f6SMauro Carvalho Chehab 34332cf86f6SMauro Carvalho Chehab device_create(lirc_class, ir->d.dev, 34432cf86f6SMauro Carvalho Chehab MKDEV(MAJOR(lirc_base_dev), ir->d.minor), NULL, 34532cf86f6SMauro Carvalho Chehab "lirc%u", ir->d.minor); 34632cf86f6SMauro Carvalho Chehab 34732cf86f6SMauro Carvalho Chehab if (d->sample_rate) { 34832cf86f6SMauro Carvalho Chehab /* try to fire up polling thread */ 34932cf86f6SMauro Carvalho Chehab ir->task = kthread_run(lirc_thread, (void *)ir, "lirc_dev"); 35032cf86f6SMauro Carvalho Chehab if (IS_ERR(ir->task)) { 35132cf86f6SMauro Carvalho Chehab dev_err(d->dev, "lirc_dev: lirc_register_driver: " 35232cf86f6SMauro Carvalho Chehab "cannot run poll thread for minor = %d\n", 35332cf86f6SMauro Carvalho Chehab d->minor); 35432cf86f6SMauro Carvalho Chehab err = -ECHILD; 35532cf86f6SMauro Carvalho Chehab goto out_sysfs; 35632cf86f6SMauro Carvalho Chehab } 35732cf86f6SMauro Carvalho Chehab } 35832cf86f6SMauro Carvalho Chehab 35932cf86f6SMauro Carvalho Chehab err = lirc_cdev_add(ir); 36032cf86f6SMauro Carvalho Chehab if (err) 36132cf86f6SMauro Carvalho Chehab goto out_sysfs; 36232cf86f6SMauro Carvalho Chehab 36332cf86f6SMauro Carvalho Chehab ir->attached = 1; 36432cf86f6SMauro Carvalho Chehab mutex_unlock(&lirc_dev_lock); 36532cf86f6SMauro Carvalho Chehab 36632cf86f6SMauro Carvalho Chehab dev_info(ir->d.dev, "lirc_dev: driver %s registered at minor = %d\n", 36732cf86f6SMauro Carvalho Chehab ir->d.name, ir->d.minor); 36832cf86f6SMauro Carvalho Chehab return minor; 36932cf86f6SMauro Carvalho Chehab 37032cf86f6SMauro Carvalho Chehab out_sysfs: 37132cf86f6SMauro Carvalho Chehab device_destroy(lirc_class, MKDEV(MAJOR(lirc_base_dev), ir->d.minor)); 37232cf86f6SMauro Carvalho Chehab out_lock: 37332cf86f6SMauro Carvalho Chehab mutex_unlock(&lirc_dev_lock); 37432cf86f6SMauro Carvalho Chehab out: 37532cf86f6SMauro Carvalho Chehab return err; 37632cf86f6SMauro Carvalho Chehab } 37732cf86f6SMauro Carvalho Chehab EXPORT_SYMBOL(lirc_register_driver); 37832cf86f6SMauro Carvalho Chehab 37932cf86f6SMauro Carvalho Chehab int lirc_unregister_driver(int minor) 38032cf86f6SMauro Carvalho Chehab { 38132cf86f6SMauro Carvalho Chehab struct irctl *ir; 38232cf86f6SMauro Carvalho Chehab struct cdev *cdev; 38332cf86f6SMauro Carvalho Chehab 38432cf86f6SMauro Carvalho Chehab if (minor < 0 || minor >= MAX_IRCTL_DEVICES) { 38532cf86f6SMauro Carvalho Chehab printk(KERN_ERR "lirc_dev: %s: minor (%d) must be between " 38632cf86f6SMauro Carvalho Chehab "0 and %d!\n", __func__, minor, MAX_IRCTL_DEVICES - 1); 38732cf86f6SMauro Carvalho Chehab return -EBADRQC; 38832cf86f6SMauro Carvalho Chehab } 38932cf86f6SMauro Carvalho Chehab 39032cf86f6SMauro Carvalho Chehab ir = irctls[minor]; 39132cf86f6SMauro Carvalho Chehab if (!ir) { 39232cf86f6SMauro Carvalho Chehab printk(KERN_ERR "lirc_dev: %s: failed to get irctl struct " 39332cf86f6SMauro Carvalho Chehab "for minor %d!\n", __func__, minor); 39432cf86f6SMauro Carvalho Chehab return -ENOENT; 39532cf86f6SMauro Carvalho Chehab } 39632cf86f6SMauro Carvalho Chehab 3978de111e2SJarod Wilson cdev = ir->cdev; 39832cf86f6SMauro Carvalho Chehab 39932cf86f6SMauro Carvalho Chehab mutex_lock(&lirc_dev_lock); 40032cf86f6SMauro Carvalho Chehab 40132cf86f6SMauro Carvalho Chehab if (ir->d.minor != minor) { 40232cf86f6SMauro Carvalho Chehab printk(KERN_ERR "lirc_dev: %s: minor (%d) device not " 40332cf86f6SMauro Carvalho Chehab "registered!\n", __func__, minor); 40432cf86f6SMauro Carvalho Chehab mutex_unlock(&lirc_dev_lock); 40532cf86f6SMauro Carvalho Chehab return -ENOENT; 40632cf86f6SMauro Carvalho Chehab } 40732cf86f6SMauro Carvalho Chehab 40832cf86f6SMauro Carvalho Chehab /* end up polling thread */ 40932cf86f6SMauro Carvalho Chehab if (ir->task) 41032cf86f6SMauro Carvalho Chehab kthread_stop(ir->task); 41132cf86f6SMauro Carvalho Chehab 41232cf86f6SMauro Carvalho Chehab dev_dbg(ir->d.dev, "lirc_dev: driver %s unregistered from minor = %d\n", 41332cf86f6SMauro Carvalho Chehab ir->d.name, ir->d.minor); 41432cf86f6SMauro Carvalho Chehab 41532cf86f6SMauro Carvalho Chehab ir->attached = 0; 41632cf86f6SMauro Carvalho Chehab if (ir->open) { 41732cf86f6SMauro Carvalho Chehab dev_dbg(ir->d.dev, LOGHEAD "releasing opened driver\n", 41832cf86f6SMauro Carvalho Chehab ir->d.name, ir->d.minor); 41932cf86f6SMauro Carvalho Chehab wake_up_interruptible(&ir->buf->wait_poll); 42032cf86f6SMauro Carvalho Chehab mutex_lock(&ir->irctl_lock); 42132cf86f6SMauro Carvalho Chehab ir->d.set_use_dec(ir->d.data); 42232cf86f6SMauro Carvalho Chehab module_put(cdev->owner); 42332cf86f6SMauro Carvalho Chehab mutex_unlock(&ir->irctl_lock); 42432cf86f6SMauro Carvalho Chehab } else { 42532cf86f6SMauro Carvalho Chehab lirc_irctl_cleanup(ir); 42632cf86f6SMauro Carvalho Chehab cdev_del(cdev); 4278de111e2SJarod Wilson kfree(cdev); 42832cf86f6SMauro Carvalho Chehab kfree(ir); 42932cf86f6SMauro Carvalho Chehab irctls[minor] = NULL; 43032cf86f6SMauro Carvalho Chehab } 43132cf86f6SMauro Carvalho Chehab 43232cf86f6SMauro Carvalho Chehab mutex_unlock(&lirc_dev_lock); 43332cf86f6SMauro Carvalho Chehab 43432cf86f6SMauro Carvalho Chehab return 0; 43532cf86f6SMauro Carvalho Chehab } 43632cf86f6SMauro Carvalho Chehab EXPORT_SYMBOL(lirc_unregister_driver); 43732cf86f6SMauro Carvalho Chehab 43832cf86f6SMauro Carvalho Chehab int lirc_dev_fop_open(struct inode *inode, struct file *file) 43932cf86f6SMauro Carvalho Chehab { 44032cf86f6SMauro Carvalho Chehab struct irctl *ir; 44132cf86f6SMauro Carvalho Chehab struct cdev *cdev; 44232cf86f6SMauro Carvalho Chehab int retval = 0; 44332cf86f6SMauro Carvalho Chehab 44432cf86f6SMauro Carvalho Chehab if (iminor(inode) >= MAX_IRCTL_DEVICES) { 44532cf86f6SMauro Carvalho Chehab printk(KERN_WARNING "lirc_dev [%d]: open result = -ENODEV\n", 44632cf86f6SMauro Carvalho Chehab iminor(inode)); 44732cf86f6SMauro Carvalho Chehab return -ENODEV; 44832cf86f6SMauro Carvalho Chehab } 44932cf86f6SMauro Carvalho Chehab 45032cf86f6SMauro Carvalho Chehab if (mutex_lock_interruptible(&lirc_dev_lock)) 45132cf86f6SMauro Carvalho Chehab return -ERESTARTSYS; 45232cf86f6SMauro Carvalho Chehab 45332cf86f6SMauro Carvalho Chehab ir = irctls[iminor(inode)]; 45432cf86f6SMauro Carvalho Chehab if (!ir) { 45532cf86f6SMauro Carvalho Chehab retval = -ENODEV; 45632cf86f6SMauro Carvalho Chehab goto error; 45732cf86f6SMauro Carvalho Chehab } 45832cf86f6SMauro Carvalho Chehab 45932cf86f6SMauro Carvalho Chehab dev_dbg(ir->d.dev, LOGHEAD "open called\n", ir->d.name, ir->d.minor); 46032cf86f6SMauro Carvalho Chehab 46132cf86f6SMauro Carvalho Chehab if (ir->d.minor == NOPLUG) { 46232cf86f6SMauro Carvalho Chehab retval = -ENODEV; 46332cf86f6SMauro Carvalho Chehab goto error; 46432cf86f6SMauro Carvalho Chehab } 46532cf86f6SMauro Carvalho Chehab 46632cf86f6SMauro Carvalho Chehab if (ir->open) { 46732cf86f6SMauro Carvalho Chehab retval = -EBUSY; 46832cf86f6SMauro Carvalho Chehab goto error; 46932cf86f6SMauro Carvalho Chehab } 47032cf86f6SMauro Carvalho Chehab 471ca7a722dSSrinivas Kandagatla if (ir->d.rdev) { 472ca7a722dSSrinivas Kandagatla retval = rc_open(ir->d.rdev); 473ca7a722dSSrinivas Kandagatla if (retval) 474ca7a722dSSrinivas Kandagatla goto error; 475ca7a722dSSrinivas Kandagatla } 476ca7a722dSSrinivas Kandagatla 4778de111e2SJarod Wilson cdev = ir->cdev; 47832cf86f6SMauro Carvalho Chehab if (try_module_get(cdev->owner)) { 47932cf86f6SMauro Carvalho Chehab ir->open++; 48032cf86f6SMauro Carvalho Chehab retval = ir->d.set_use_inc(ir->d.data); 48132cf86f6SMauro Carvalho Chehab 48232cf86f6SMauro Carvalho Chehab if (retval) { 48332cf86f6SMauro Carvalho Chehab module_put(cdev->owner); 48432cf86f6SMauro Carvalho Chehab ir->open--; 48532cf86f6SMauro Carvalho Chehab } else { 48632cf86f6SMauro Carvalho Chehab lirc_buffer_clear(ir->buf); 48732cf86f6SMauro Carvalho Chehab } 48832cf86f6SMauro Carvalho Chehab if (ir->task) 48932cf86f6SMauro Carvalho Chehab wake_up_process(ir->task); 49032cf86f6SMauro Carvalho Chehab } 49132cf86f6SMauro Carvalho Chehab 49232cf86f6SMauro Carvalho Chehab error: 49332cf86f6SMauro Carvalho Chehab if (ir) 49432cf86f6SMauro Carvalho Chehab dev_dbg(ir->d.dev, LOGHEAD "open result = %d\n", 49532cf86f6SMauro Carvalho Chehab ir->d.name, ir->d.minor, retval); 49632cf86f6SMauro Carvalho Chehab 49732cf86f6SMauro Carvalho Chehab mutex_unlock(&lirc_dev_lock); 49832cf86f6SMauro Carvalho Chehab 49932cf86f6SMauro Carvalho Chehab nonseekable_open(inode, file); 50032cf86f6SMauro Carvalho Chehab 50132cf86f6SMauro Carvalho Chehab return retval; 50232cf86f6SMauro Carvalho Chehab } 50332cf86f6SMauro Carvalho Chehab EXPORT_SYMBOL(lirc_dev_fop_open); 50432cf86f6SMauro Carvalho Chehab 50532cf86f6SMauro Carvalho Chehab int lirc_dev_fop_close(struct inode *inode, struct file *file) 50632cf86f6SMauro Carvalho Chehab { 50732cf86f6SMauro Carvalho Chehab struct irctl *ir = irctls[iminor(inode)]; 5088de111e2SJarod Wilson struct cdev *cdev; 509b64e10f3SMauro Carvalho Chehab int ret; 51032cf86f6SMauro Carvalho Chehab 51132cf86f6SMauro Carvalho Chehab if (!ir) { 51232cf86f6SMauro Carvalho Chehab printk(KERN_ERR "%s: called with invalid irctl\n", __func__); 51332cf86f6SMauro Carvalho Chehab return -EINVAL; 51432cf86f6SMauro Carvalho Chehab } 51532cf86f6SMauro Carvalho Chehab 5168de111e2SJarod Wilson cdev = ir->cdev; 5178de111e2SJarod Wilson 51832cf86f6SMauro Carvalho Chehab dev_dbg(ir->d.dev, LOGHEAD "close called\n", ir->d.name, ir->d.minor); 51932cf86f6SMauro Carvalho Chehab 520b64e10f3SMauro Carvalho Chehab ret = mutex_lock_killable(&lirc_dev_lock); 521b64e10f3SMauro Carvalho Chehab WARN_ON(ret); 52232cf86f6SMauro Carvalho Chehab 523ca7a722dSSrinivas Kandagatla rc_close(ir->d.rdev); 524ca7a722dSSrinivas Kandagatla 52532cf86f6SMauro Carvalho Chehab ir->open--; 52632cf86f6SMauro Carvalho Chehab if (ir->attached) { 52732cf86f6SMauro Carvalho Chehab ir->d.set_use_dec(ir->d.data); 52832cf86f6SMauro Carvalho Chehab module_put(cdev->owner); 52932cf86f6SMauro Carvalho Chehab } else { 53032cf86f6SMauro Carvalho Chehab lirc_irctl_cleanup(ir); 53132cf86f6SMauro Carvalho Chehab cdev_del(cdev); 53232cf86f6SMauro Carvalho Chehab irctls[ir->d.minor] = NULL; 5338de111e2SJarod Wilson kfree(cdev); 53432cf86f6SMauro Carvalho Chehab kfree(ir); 53532cf86f6SMauro Carvalho Chehab } 53632cf86f6SMauro Carvalho Chehab 537b64e10f3SMauro Carvalho Chehab if (!ret) 53832cf86f6SMauro Carvalho Chehab mutex_unlock(&lirc_dev_lock); 53932cf86f6SMauro Carvalho Chehab 54032cf86f6SMauro Carvalho Chehab return 0; 54132cf86f6SMauro Carvalho Chehab } 54232cf86f6SMauro Carvalho Chehab EXPORT_SYMBOL(lirc_dev_fop_close); 54332cf86f6SMauro Carvalho Chehab 54432cf86f6SMauro Carvalho Chehab unsigned int lirc_dev_fop_poll(struct file *file, poll_table *wait) 54532cf86f6SMauro Carvalho Chehab { 546496ad9aaSAl Viro struct irctl *ir = irctls[iminor(file_inode(file))]; 54732cf86f6SMauro Carvalho Chehab unsigned int ret; 54832cf86f6SMauro Carvalho Chehab 54932cf86f6SMauro Carvalho Chehab if (!ir) { 55032cf86f6SMauro Carvalho Chehab printk(KERN_ERR "%s: called with invalid irctl\n", __func__); 55132cf86f6SMauro Carvalho Chehab return POLLERR; 55232cf86f6SMauro Carvalho Chehab } 55332cf86f6SMauro Carvalho Chehab 55432cf86f6SMauro Carvalho Chehab dev_dbg(ir->d.dev, LOGHEAD "poll called\n", ir->d.name, ir->d.minor); 55532cf86f6SMauro Carvalho Chehab 55632cf86f6SMauro Carvalho Chehab if (!ir->attached) 55732cf86f6SMauro Carvalho Chehab return POLLERR; 55832cf86f6SMauro Carvalho Chehab 5593656cdddSAndy Shevchenko if (ir->buf) { 56032cf86f6SMauro Carvalho Chehab poll_wait(file, &ir->buf->wait_poll, wait); 56132cf86f6SMauro Carvalho Chehab 56232cf86f6SMauro Carvalho Chehab if (lirc_buffer_empty(ir->buf)) 56332cf86f6SMauro Carvalho Chehab ret = 0; 56432cf86f6SMauro Carvalho Chehab else 56532cf86f6SMauro Carvalho Chehab ret = POLLIN | POLLRDNORM; 5663656cdddSAndy Shevchenko } else 56732cf86f6SMauro Carvalho Chehab ret = POLLERR; 56832cf86f6SMauro Carvalho Chehab 56932cf86f6SMauro Carvalho Chehab dev_dbg(ir->d.dev, LOGHEAD "poll result = %d\n", 57032cf86f6SMauro Carvalho Chehab ir->d.name, ir->d.minor, ret); 57132cf86f6SMauro Carvalho Chehab 57232cf86f6SMauro Carvalho Chehab return ret; 57332cf86f6SMauro Carvalho Chehab } 57432cf86f6SMauro Carvalho Chehab EXPORT_SYMBOL(lirc_dev_fop_poll); 57532cf86f6SMauro Carvalho Chehab 57632cf86f6SMauro Carvalho Chehab long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 57732cf86f6SMauro Carvalho Chehab { 57832cf86f6SMauro Carvalho Chehab __u32 mode; 57932cf86f6SMauro Carvalho Chehab int result = 0; 580496ad9aaSAl Viro struct irctl *ir = irctls[iminor(file_inode(file))]; 58132cf86f6SMauro Carvalho Chehab 58232cf86f6SMauro Carvalho Chehab if (!ir) { 58332cf86f6SMauro Carvalho Chehab printk(KERN_ERR "lirc_dev: %s: no irctl found!\n", __func__); 58432cf86f6SMauro Carvalho Chehab return -ENODEV; 58532cf86f6SMauro Carvalho Chehab } 58632cf86f6SMauro Carvalho Chehab 58732cf86f6SMauro Carvalho Chehab dev_dbg(ir->d.dev, LOGHEAD "ioctl called (0x%x)\n", 58832cf86f6SMauro Carvalho Chehab ir->d.name, ir->d.minor, cmd); 58932cf86f6SMauro Carvalho Chehab 59032cf86f6SMauro Carvalho Chehab if (ir->d.minor == NOPLUG || !ir->attached) { 59132cf86f6SMauro Carvalho Chehab dev_dbg(ir->d.dev, LOGHEAD "ioctl result = -ENODEV\n", 59232cf86f6SMauro Carvalho Chehab ir->d.name, ir->d.minor); 59332cf86f6SMauro Carvalho Chehab return -ENODEV; 59432cf86f6SMauro Carvalho Chehab } 59532cf86f6SMauro Carvalho Chehab 59632cf86f6SMauro Carvalho Chehab mutex_lock(&ir->irctl_lock); 59732cf86f6SMauro Carvalho Chehab 59832cf86f6SMauro Carvalho Chehab switch (cmd) { 59932cf86f6SMauro Carvalho Chehab case LIRC_GET_FEATURES: 60060519af3SHans Verkuil result = put_user(ir->d.features, (__u32 __user *)arg); 60132cf86f6SMauro Carvalho Chehab break; 60232cf86f6SMauro Carvalho Chehab case LIRC_GET_REC_MODE: 60332cf86f6SMauro Carvalho Chehab if (!(ir->d.features & LIRC_CAN_REC_MASK)) { 60432cf86f6SMauro Carvalho Chehab result = -ENOSYS; 60532cf86f6SMauro Carvalho Chehab break; 60632cf86f6SMauro Carvalho Chehab } 60732cf86f6SMauro Carvalho Chehab 60832cf86f6SMauro Carvalho Chehab result = put_user(LIRC_REC2MODE 60932cf86f6SMauro Carvalho Chehab (ir->d.features & LIRC_CAN_REC_MASK), 61060519af3SHans Verkuil (__u32 __user *)arg); 61132cf86f6SMauro Carvalho Chehab break; 61232cf86f6SMauro Carvalho Chehab case LIRC_SET_REC_MODE: 61332cf86f6SMauro Carvalho Chehab if (!(ir->d.features & LIRC_CAN_REC_MASK)) { 61432cf86f6SMauro Carvalho Chehab result = -ENOSYS; 61532cf86f6SMauro Carvalho Chehab break; 61632cf86f6SMauro Carvalho Chehab } 61732cf86f6SMauro Carvalho Chehab 61860519af3SHans Verkuil result = get_user(mode, (__u32 __user *)arg); 61932cf86f6SMauro Carvalho Chehab if (!result && !(LIRC_MODE2REC(mode) & ir->d.features)) 62032cf86f6SMauro Carvalho Chehab result = -EINVAL; 62132cf86f6SMauro Carvalho Chehab /* 62232cf86f6SMauro Carvalho Chehab * FIXME: We should actually set the mode somehow but 62332cf86f6SMauro Carvalho Chehab * for now, lirc_serial doesn't support mode changing either 62432cf86f6SMauro Carvalho Chehab */ 62532cf86f6SMauro Carvalho Chehab break; 62632cf86f6SMauro Carvalho Chehab case LIRC_GET_LENGTH: 62760519af3SHans Verkuil result = put_user(ir->d.code_length, (__u32 __user *)arg); 62832cf86f6SMauro Carvalho Chehab break; 62932cf86f6SMauro Carvalho Chehab case LIRC_GET_MIN_TIMEOUT: 63032cf86f6SMauro Carvalho Chehab if (!(ir->d.features & LIRC_CAN_SET_REC_TIMEOUT) || 63132cf86f6SMauro Carvalho Chehab ir->d.min_timeout == 0) { 63232cf86f6SMauro Carvalho Chehab result = -ENOSYS; 63332cf86f6SMauro Carvalho Chehab break; 63432cf86f6SMauro Carvalho Chehab } 63532cf86f6SMauro Carvalho Chehab 63660519af3SHans Verkuil result = put_user(ir->d.min_timeout, (__u32 __user *)arg); 63732cf86f6SMauro Carvalho Chehab break; 63832cf86f6SMauro Carvalho Chehab case LIRC_GET_MAX_TIMEOUT: 63932cf86f6SMauro Carvalho Chehab if (!(ir->d.features & LIRC_CAN_SET_REC_TIMEOUT) || 64032cf86f6SMauro Carvalho Chehab ir->d.max_timeout == 0) { 64132cf86f6SMauro Carvalho Chehab result = -ENOSYS; 64232cf86f6SMauro Carvalho Chehab break; 64332cf86f6SMauro Carvalho Chehab } 64432cf86f6SMauro Carvalho Chehab 64560519af3SHans Verkuil result = put_user(ir->d.max_timeout, (__u32 __user *)arg); 64632cf86f6SMauro Carvalho Chehab break; 64732cf86f6SMauro Carvalho Chehab default: 64832cf86f6SMauro Carvalho Chehab result = -EINVAL; 64932cf86f6SMauro Carvalho Chehab } 65032cf86f6SMauro Carvalho Chehab 65132cf86f6SMauro Carvalho Chehab dev_dbg(ir->d.dev, LOGHEAD "ioctl result = %d\n", 65232cf86f6SMauro Carvalho Chehab ir->d.name, ir->d.minor, result); 65332cf86f6SMauro Carvalho Chehab 65432cf86f6SMauro Carvalho Chehab mutex_unlock(&ir->irctl_lock); 65532cf86f6SMauro Carvalho Chehab 65632cf86f6SMauro Carvalho Chehab return result; 65732cf86f6SMauro Carvalho Chehab } 65832cf86f6SMauro Carvalho Chehab EXPORT_SYMBOL(lirc_dev_fop_ioctl); 65932cf86f6SMauro Carvalho Chehab 66032cf86f6SMauro Carvalho Chehab ssize_t lirc_dev_fop_read(struct file *file, 6610e835087SDan Carpenter char __user *buffer, 66232cf86f6SMauro Carvalho Chehab size_t length, 66332cf86f6SMauro Carvalho Chehab loff_t *ppos) 66432cf86f6SMauro Carvalho Chehab { 665496ad9aaSAl Viro struct irctl *ir = irctls[iminor(file_inode(file))]; 66632cf86f6SMauro Carvalho Chehab unsigned char *buf; 66732cf86f6SMauro Carvalho Chehab int ret = 0, written = 0; 66832cf86f6SMauro Carvalho Chehab DECLARE_WAITQUEUE(wait, current); 66932cf86f6SMauro Carvalho Chehab 67032cf86f6SMauro Carvalho Chehab if (!ir) { 67132cf86f6SMauro Carvalho Chehab printk(KERN_ERR "%s: called with invalid irctl\n", __func__); 67232cf86f6SMauro Carvalho Chehab return -ENODEV; 67332cf86f6SMauro Carvalho Chehab } 67432cf86f6SMauro Carvalho Chehab 67532cf86f6SMauro Carvalho Chehab dev_dbg(ir->d.dev, LOGHEAD "read called\n", ir->d.name, ir->d.minor); 67632cf86f6SMauro Carvalho Chehab 67732cf86f6SMauro Carvalho Chehab buf = kzalloc(ir->chunk_size, GFP_KERNEL); 67832cf86f6SMauro Carvalho Chehab if (!buf) 67932cf86f6SMauro Carvalho Chehab return -ENOMEM; 68032cf86f6SMauro Carvalho Chehab 68132cf86f6SMauro Carvalho Chehab if (mutex_lock_interruptible(&ir->irctl_lock)) { 68232cf86f6SMauro Carvalho Chehab ret = -ERESTARTSYS; 68332cf86f6SMauro Carvalho Chehab goto out_unlocked; 68432cf86f6SMauro Carvalho Chehab } 68532cf86f6SMauro Carvalho Chehab if (!ir->attached) { 68632cf86f6SMauro Carvalho Chehab ret = -ENODEV; 68732cf86f6SMauro Carvalho Chehab goto out_locked; 68832cf86f6SMauro Carvalho Chehab } 68932cf86f6SMauro Carvalho Chehab 69032cf86f6SMauro Carvalho Chehab if (length % ir->chunk_size) { 69132cf86f6SMauro Carvalho Chehab ret = -EINVAL; 69232cf86f6SMauro Carvalho Chehab goto out_locked; 69332cf86f6SMauro Carvalho Chehab } 69432cf86f6SMauro Carvalho Chehab 69532cf86f6SMauro Carvalho Chehab /* 69632cf86f6SMauro Carvalho Chehab * we add ourselves to the task queue before buffer check 69732cf86f6SMauro Carvalho Chehab * to avoid losing scan code (in case when queue is awaken somewhere 69832cf86f6SMauro Carvalho Chehab * between while condition checking and scheduling) 69932cf86f6SMauro Carvalho Chehab */ 70032cf86f6SMauro Carvalho Chehab add_wait_queue(&ir->buf->wait_poll, &wait); 70132cf86f6SMauro Carvalho Chehab set_current_state(TASK_INTERRUPTIBLE); 70232cf86f6SMauro Carvalho Chehab 70332cf86f6SMauro Carvalho Chehab /* 70432cf86f6SMauro Carvalho Chehab * while we didn't provide 'length' bytes, device is opened in blocking 70532cf86f6SMauro Carvalho Chehab * mode and 'copy_to_user' is happy, wait for data. 70632cf86f6SMauro Carvalho Chehab */ 70732cf86f6SMauro Carvalho Chehab while (written < length && ret == 0) { 70832cf86f6SMauro Carvalho Chehab if (lirc_buffer_empty(ir->buf)) { 70932cf86f6SMauro Carvalho Chehab /* According to the read(2) man page, 'written' can be 71032cf86f6SMauro Carvalho Chehab * returned as less than 'length', instead of blocking 71132cf86f6SMauro Carvalho Chehab * again, returning -EWOULDBLOCK, or returning 71232cf86f6SMauro Carvalho Chehab * -ERESTARTSYS */ 71332cf86f6SMauro Carvalho Chehab if (written) 71432cf86f6SMauro Carvalho Chehab break; 71532cf86f6SMauro Carvalho Chehab if (file->f_flags & O_NONBLOCK) { 71632cf86f6SMauro Carvalho Chehab ret = -EWOULDBLOCK; 71732cf86f6SMauro Carvalho Chehab break; 71832cf86f6SMauro Carvalho Chehab } 71932cf86f6SMauro Carvalho Chehab if (signal_pending(current)) { 72032cf86f6SMauro Carvalho Chehab ret = -ERESTARTSYS; 72132cf86f6SMauro Carvalho Chehab break; 72232cf86f6SMauro Carvalho Chehab } 72332cf86f6SMauro Carvalho Chehab 72432cf86f6SMauro Carvalho Chehab mutex_unlock(&ir->irctl_lock); 72532cf86f6SMauro Carvalho Chehab schedule(); 72632cf86f6SMauro Carvalho Chehab set_current_state(TASK_INTERRUPTIBLE); 72732cf86f6SMauro Carvalho Chehab 72832cf86f6SMauro Carvalho Chehab if (mutex_lock_interruptible(&ir->irctl_lock)) { 72932cf86f6SMauro Carvalho Chehab ret = -ERESTARTSYS; 73032cf86f6SMauro Carvalho Chehab remove_wait_queue(&ir->buf->wait_poll, &wait); 73132cf86f6SMauro Carvalho Chehab set_current_state(TASK_RUNNING); 73232cf86f6SMauro Carvalho Chehab goto out_unlocked; 73332cf86f6SMauro Carvalho Chehab } 73432cf86f6SMauro Carvalho Chehab 73532cf86f6SMauro Carvalho Chehab if (!ir->attached) { 73632cf86f6SMauro Carvalho Chehab ret = -ENODEV; 73732cf86f6SMauro Carvalho Chehab break; 73832cf86f6SMauro Carvalho Chehab } 73932cf86f6SMauro Carvalho Chehab } else { 74032cf86f6SMauro Carvalho Chehab lirc_buffer_read(ir->buf, buf); 74160519af3SHans Verkuil ret = copy_to_user((void __user *)buffer+written, buf, 74232cf86f6SMauro Carvalho Chehab ir->buf->chunk_size); 74332cf86f6SMauro Carvalho Chehab if (!ret) 74432cf86f6SMauro Carvalho Chehab written += ir->buf->chunk_size; 74532cf86f6SMauro Carvalho Chehab else 74632cf86f6SMauro Carvalho Chehab ret = -EFAULT; 74732cf86f6SMauro Carvalho Chehab } 74832cf86f6SMauro Carvalho Chehab } 74932cf86f6SMauro Carvalho Chehab 75032cf86f6SMauro Carvalho Chehab remove_wait_queue(&ir->buf->wait_poll, &wait); 75132cf86f6SMauro Carvalho Chehab set_current_state(TASK_RUNNING); 75232cf86f6SMauro Carvalho Chehab 75332cf86f6SMauro Carvalho Chehab out_locked: 75432cf86f6SMauro Carvalho Chehab mutex_unlock(&ir->irctl_lock); 75532cf86f6SMauro Carvalho Chehab 75632cf86f6SMauro Carvalho Chehab out_unlocked: 75732cf86f6SMauro Carvalho Chehab kfree(buf); 75832cf86f6SMauro Carvalho Chehab dev_dbg(ir->d.dev, LOGHEAD "read result = %s (%d)\n", 75932cf86f6SMauro Carvalho Chehab ir->d.name, ir->d.minor, ret ? "<fail>" : "<ok>", ret); 76032cf86f6SMauro Carvalho Chehab 76132cf86f6SMauro Carvalho Chehab return ret ? ret : written; 76232cf86f6SMauro Carvalho Chehab } 76332cf86f6SMauro Carvalho Chehab EXPORT_SYMBOL(lirc_dev_fop_read); 76432cf86f6SMauro Carvalho Chehab 76532cf86f6SMauro Carvalho Chehab void *lirc_get_pdata(struct file *file) 76632cf86f6SMauro Carvalho Chehab { 7670990a97aSAl Viro return irctls[iminor(file_inode(file))]->d.data; 76832cf86f6SMauro Carvalho Chehab } 76932cf86f6SMauro Carvalho Chehab EXPORT_SYMBOL(lirc_get_pdata); 77032cf86f6SMauro Carvalho Chehab 77132cf86f6SMauro Carvalho Chehab 7720e835087SDan Carpenter ssize_t lirc_dev_fop_write(struct file *file, const char __user *buffer, 77332cf86f6SMauro Carvalho Chehab size_t length, loff_t *ppos) 77432cf86f6SMauro Carvalho Chehab { 775496ad9aaSAl Viro struct irctl *ir = irctls[iminor(file_inode(file))]; 77632cf86f6SMauro Carvalho Chehab 77732cf86f6SMauro Carvalho Chehab if (!ir) { 77832cf86f6SMauro Carvalho Chehab printk(KERN_ERR "%s: called with invalid irctl\n", __func__); 77932cf86f6SMauro Carvalho Chehab return -ENODEV; 78032cf86f6SMauro Carvalho Chehab } 78132cf86f6SMauro Carvalho Chehab 78232cf86f6SMauro Carvalho Chehab dev_dbg(ir->d.dev, LOGHEAD "write called\n", ir->d.name, ir->d.minor); 78332cf86f6SMauro Carvalho Chehab 78432cf86f6SMauro Carvalho Chehab if (!ir->attached) 78532cf86f6SMauro Carvalho Chehab return -ENODEV; 78632cf86f6SMauro Carvalho Chehab 78732cf86f6SMauro Carvalho Chehab return -EINVAL; 78832cf86f6SMauro Carvalho Chehab } 78932cf86f6SMauro Carvalho Chehab EXPORT_SYMBOL(lirc_dev_fop_write); 79032cf86f6SMauro Carvalho Chehab 79132cf86f6SMauro Carvalho Chehab 79232cf86f6SMauro Carvalho Chehab static int __init lirc_dev_init(void) 79332cf86f6SMauro Carvalho Chehab { 79432cf86f6SMauro Carvalho Chehab int retval; 79532cf86f6SMauro Carvalho Chehab 79632cf86f6SMauro Carvalho Chehab lirc_class = class_create(THIS_MODULE, "lirc"); 79732cf86f6SMauro Carvalho Chehab if (IS_ERR(lirc_class)) { 79832cf86f6SMauro Carvalho Chehab retval = PTR_ERR(lirc_class); 79932cf86f6SMauro Carvalho Chehab printk(KERN_ERR "lirc_dev: class_create failed\n"); 80032cf86f6SMauro Carvalho Chehab goto error; 80132cf86f6SMauro Carvalho Chehab } 80232cf86f6SMauro Carvalho Chehab 80332cf86f6SMauro Carvalho Chehab retval = alloc_chrdev_region(&lirc_base_dev, 0, MAX_IRCTL_DEVICES, 80432cf86f6SMauro Carvalho Chehab IRCTL_DEV_NAME); 80532cf86f6SMauro Carvalho Chehab if (retval) { 80632cf86f6SMauro Carvalho Chehab class_destroy(lirc_class); 80732cf86f6SMauro Carvalho Chehab printk(KERN_ERR "lirc_dev: alloc_chrdev_region failed\n"); 80832cf86f6SMauro Carvalho Chehab goto error; 80932cf86f6SMauro Carvalho Chehab } 81032cf86f6SMauro Carvalho Chehab 81132cf86f6SMauro Carvalho Chehab 81232cf86f6SMauro Carvalho Chehab printk(KERN_INFO "lirc_dev: IR Remote Control driver registered, " 81332cf86f6SMauro Carvalho Chehab "major %d \n", MAJOR(lirc_base_dev)); 81432cf86f6SMauro Carvalho Chehab 81532cf86f6SMauro Carvalho Chehab error: 81632cf86f6SMauro Carvalho Chehab return retval; 81732cf86f6SMauro Carvalho Chehab } 81832cf86f6SMauro Carvalho Chehab 81932cf86f6SMauro Carvalho Chehab 82032cf86f6SMauro Carvalho Chehab 82132cf86f6SMauro Carvalho Chehab static void __exit lirc_dev_exit(void) 82232cf86f6SMauro Carvalho Chehab { 82332cf86f6SMauro Carvalho Chehab class_destroy(lirc_class); 82432cf86f6SMauro Carvalho Chehab unregister_chrdev_region(lirc_base_dev, MAX_IRCTL_DEVICES); 82532cf86f6SMauro Carvalho Chehab printk(KERN_INFO "lirc_dev: module unloaded\n"); 82632cf86f6SMauro Carvalho Chehab } 82732cf86f6SMauro Carvalho Chehab 82832cf86f6SMauro Carvalho Chehab module_init(lirc_dev_init); 82932cf86f6SMauro Carvalho Chehab module_exit(lirc_dev_exit); 83032cf86f6SMauro Carvalho Chehab 83132cf86f6SMauro Carvalho Chehab MODULE_DESCRIPTION("LIRC base driver module"); 83232cf86f6SMauro Carvalho Chehab MODULE_AUTHOR("Artur Lipowski"); 83332cf86f6SMauro Carvalho Chehab MODULE_LICENSE("GPL"); 83432cf86f6SMauro Carvalho Chehab 83532cf86f6SMauro Carvalho Chehab module_param(debug, bool, S_IRUGO | S_IWUSR); 83632cf86f6SMauro Carvalho Chehab MODULE_PARM_DESC(debug, "Enable debugging messages"); 837