132cf86f6SMauro Carvalho Chehab /* 232cf86f6SMauro Carvalho Chehab * LIRC base driver 332cf86f6SMauro Carvalho Chehab * 432cf86f6SMauro Carvalho Chehab * by Artur Lipowski <alipowski@interia.pl> 532cf86f6SMauro Carvalho Chehab * 632cf86f6SMauro Carvalho Chehab * This program is free software; you can redistribute it and/or modify 732cf86f6SMauro Carvalho Chehab * it under the terms of the GNU General Public License as published by 832cf86f6SMauro Carvalho Chehab * the Free Software Foundation; either version 2 of the License, or 932cf86f6SMauro Carvalho Chehab * (at your option) any later version. 1032cf86f6SMauro Carvalho Chehab * 1132cf86f6SMauro Carvalho Chehab * This program is distributed in the hope that it will be useful, 1232cf86f6SMauro Carvalho Chehab * but WITHOUT ANY WARRANTY; without even the implied warranty of 1332cf86f6SMauro Carvalho Chehab * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1432cf86f6SMauro Carvalho Chehab * GNU General Public License for more details. 1532cf86f6SMauro Carvalho Chehab * 1632cf86f6SMauro Carvalho Chehab */ 1732cf86f6SMauro Carvalho Chehab 183fac0314SAndi Shyti #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 193fac0314SAndi Shyti 2032cf86f6SMauro Carvalho Chehab #include <linux/module.h> 2132cf86f6SMauro Carvalho Chehab #include <linux/kernel.h> 22174cd4b1SIngo Molnar #include <linux/sched/signal.h> 2332cf86f6SMauro Carvalho Chehab #include <linux/errno.h> 2432cf86f6SMauro Carvalho Chehab #include <linux/ioctl.h> 2532cf86f6SMauro Carvalho Chehab #include <linux/fs.h> 2632cf86f6SMauro Carvalho Chehab #include <linux/poll.h> 2732cf86f6SMauro Carvalho Chehab #include <linux/completion.h> 2832cf86f6SMauro Carvalho Chehab #include <linux/mutex.h> 2932cf86f6SMauro Carvalho Chehab #include <linux/wait.h> 3032cf86f6SMauro Carvalho Chehab #include <linux/unistd.h> 3132cf86f6SMauro Carvalho Chehab #include <linux/kthread.h> 3232cf86f6SMauro Carvalho Chehab #include <linux/bitops.h> 3332cf86f6SMauro Carvalho Chehab #include <linux/device.h> 3432cf86f6SMauro Carvalho Chehab #include <linux/cdev.h> 3532cf86f6SMauro Carvalho Chehab 36ca7a722dSSrinivas Kandagatla #include <media/rc-core.h> 3732cf86f6SMauro Carvalho Chehab #include <media/lirc.h> 3832cf86f6SMauro Carvalho Chehab #include <media/lirc_dev.h> 3932cf86f6SMauro Carvalho Chehab 4090ab5ee9SRusty Russell static bool debug; 4132cf86f6SMauro Carvalho Chehab 4232cf86f6SMauro Carvalho Chehab #define IRCTL_DEV_NAME "BaseRemoteCtl" 4332cf86f6SMauro Carvalho Chehab #define NOPLUG -1 4432cf86f6SMauro Carvalho Chehab #define LOGHEAD "lirc_dev (%s[%d]): " 4532cf86f6SMauro Carvalho Chehab 4632cf86f6SMauro Carvalho Chehab static dev_t lirc_base_dev; 4732cf86f6SMauro Carvalho Chehab 4832cf86f6SMauro Carvalho Chehab struct irctl { 4932cf86f6SMauro Carvalho Chehab struct lirc_driver d; 5032cf86f6SMauro Carvalho Chehab int attached; 5132cf86f6SMauro Carvalho Chehab int open; 5232cf86f6SMauro Carvalho Chehab 5332cf86f6SMauro Carvalho Chehab struct mutex irctl_lock; 5432cf86f6SMauro Carvalho Chehab struct lirc_buffer *buf; 5532cf86f6SMauro Carvalho Chehab unsigned int chunk_size; 5632cf86f6SMauro Carvalho Chehab 578de111e2SJarod Wilson struct cdev *cdev; 588de111e2SJarod Wilson 5932cf86f6SMauro Carvalho Chehab struct task_struct *task; 6032cf86f6SMauro Carvalho Chehab long jiffies_to_wait; 6132cf86f6SMauro Carvalho Chehab }; 6232cf86f6SMauro Carvalho Chehab 6332cf86f6SMauro Carvalho Chehab static DEFINE_MUTEX(lirc_dev_lock); 6432cf86f6SMauro Carvalho Chehab 6532cf86f6SMauro Carvalho Chehab static struct irctl *irctls[MAX_IRCTL_DEVICES]; 6632cf86f6SMauro Carvalho Chehab 6732cf86f6SMauro Carvalho Chehab /* Only used for sysfs but defined to void otherwise */ 6832cf86f6SMauro Carvalho Chehab static struct class *lirc_class; 6932cf86f6SMauro Carvalho Chehab 7032cf86f6SMauro Carvalho Chehab /* helper function 7132cf86f6SMauro Carvalho Chehab * initializes the irctl structure 7232cf86f6SMauro Carvalho Chehab */ 7332cf86f6SMauro Carvalho Chehab static void lirc_irctl_init(struct irctl *ir) 7432cf86f6SMauro Carvalho Chehab { 7532cf86f6SMauro Carvalho Chehab mutex_init(&ir->irctl_lock); 7632cf86f6SMauro Carvalho Chehab ir->d.minor = NOPLUG; 7732cf86f6SMauro Carvalho Chehab } 7832cf86f6SMauro Carvalho Chehab 7932cf86f6SMauro Carvalho Chehab static void lirc_irctl_cleanup(struct irctl *ir) 8032cf86f6SMauro Carvalho Chehab { 8132cf86f6SMauro Carvalho Chehab device_destroy(lirc_class, MKDEV(MAJOR(lirc_base_dev), ir->d.minor)); 8232cf86f6SMauro Carvalho Chehab 8332cf86f6SMauro Carvalho Chehab if (ir->buf != ir->d.rbuf) { 8432cf86f6SMauro Carvalho Chehab lirc_buffer_free(ir->buf); 8532cf86f6SMauro Carvalho Chehab kfree(ir->buf); 8632cf86f6SMauro Carvalho Chehab } 8732cf86f6SMauro Carvalho Chehab ir->buf = NULL; 8832cf86f6SMauro Carvalho Chehab } 8932cf86f6SMauro Carvalho Chehab 9032cf86f6SMauro Carvalho Chehab /* helper function 9132cf86f6SMauro Carvalho Chehab * reads key codes from driver and puts them into buffer 9232cf86f6SMauro Carvalho Chehab * returns 0 on success 9332cf86f6SMauro Carvalho Chehab */ 9432cf86f6SMauro Carvalho Chehab static int lirc_add_to_buf(struct irctl *ir) 9532cf86f6SMauro Carvalho Chehab { 9619e56539SAndi Shyti int res; 977fe579d2SAndi Shyti int got_data = -1; 9832cf86f6SMauro Carvalho Chehab 9919e56539SAndi Shyti if (!ir->d.add_to_buf) 10019e56539SAndi Shyti return 0; 10119e56539SAndi Shyti 10232cf86f6SMauro Carvalho Chehab /* 10332cf86f6SMauro Carvalho Chehab * service the device as long as it is returning 10432cf86f6SMauro Carvalho Chehab * data and we have space 10532cf86f6SMauro Carvalho Chehab */ 1067fe579d2SAndi Shyti do { 10732cf86f6SMauro Carvalho Chehab got_data++; 1087fe579d2SAndi Shyti res = ir->d.add_to_buf(ir->d.data, ir->buf); 1097fe579d2SAndi Shyti } while (!res); 11032cf86f6SMauro Carvalho Chehab 11132cf86f6SMauro Carvalho Chehab if (res == -ENODEV) 11232cf86f6SMauro Carvalho Chehab kthread_stop(ir->task); 11332cf86f6SMauro Carvalho Chehab 11432cf86f6SMauro Carvalho Chehab return got_data ? 0 : res; 11532cf86f6SMauro Carvalho Chehab } 11632cf86f6SMauro Carvalho Chehab 11732cf86f6SMauro Carvalho Chehab /* main function of the polling thread 11832cf86f6SMauro Carvalho Chehab */ 11932cf86f6SMauro Carvalho Chehab static int lirc_thread(void *irctl) 12032cf86f6SMauro Carvalho Chehab { 12132cf86f6SMauro Carvalho Chehab struct irctl *ir = irctl; 12232cf86f6SMauro Carvalho Chehab 12332cf86f6SMauro Carvalho Chehab do { 12432cf86f6SMauro Carvalho Chehab if (ir->open) { 12532cf86f6SMauro Carvalho Chehab if (ir->jiffies_to_wait) { 12632cf86f6SMauro Carvalho Chehab set_current_state(TASK_INTERRUPTIBLE); 12732cf86f6SMauro Carvalho Chehab schedule_timeout(ir->jiffies_to_wait); 12832cf86f6SMauro Carvalho Chehab } 12932cf86f6SMauro Carvalho Chehab if (kthread_should_stop()) 13032cf86f6SMauro Carvalho Chehab break; 13132cf86f6SMauro Carvalho Chehab if (!lirc_add_to_buf(ir)) 13232cf86f6SMauro Carvalho Chehab wake_up_interruptible(&ir->buf->wait_poll); 13332cf86f6SMauro Carvalho Chehab } else { 13432cf86f6SMauro Carvalho Chehab set_current_state(TASK_INTERRUPTIBLE); 13532cf86f6SMauro Carvalho Chehab schedule(); 13632cf86f6SMauro Carvalho Chehab } 13732cf86f6SMauro Carvalho Chehab } while (!kthread_should_stop()); 13832cf86f6SMauro Carvalho Chehab 13932cf86f6SMauro Carvalho Chehab return 0; 14032cf86f6SMauro Carvalho Chehab } 14132cf86f6SMauro Carvalho Chehab 14232cf86f6SMauro Carvalho Chehab 14375ef9de1SAl Viro static const struct file_operations lirc_dev_fops = { 14432cf86f6SMauro Carvalho Chehab .owner = THIS_MODULE, 14532cf86f6SMauro Carvalho Chehab .read = lirc_dev_fop_read, 14632cf86f6SMauro Carvalho Chehab .write = lirc_dev_fop_write, 14732cf86f6SMauro Carvalho Chehab .poll = lirc_dev_fop_poll, 14832cf86f6SMauro Carvalho Chehab .unlocked_ioctl = lirc_dev_fop_ioctl, 14932cf86f6SMauro Carvalho Chehab .open = lirc_dev_fop_open, 15032cf86f6SMauro Carvalho Chehab .release = lirc_dev_fop_close, 15132cf86f6SMauro Carvalho Chehab .llseek = noop_llseek, 15232cf86f6SMauro Carvalho Chehab }; 15332cf86f6SMauro Carvalho Chehab 15432cf86f6SMauro Carvalho Chehab static int lirc_cdev_add(struct irctl *ir) 15532cf86f6SMauro Carvalho Chehab { 15632cf86f6SMauro Carvalho Chehab struct lirc_driver *d = &ir->d; 1578de111e2SJarod Wilson struct cdev *cdev; 158b40769eeSSean Young int retval; 1598de111e2SJarod Wilson 160afbb1101SSean Young cdev = cdev_alloc(); 1618de111e2SJarod Wilson if (!cdev) 162b40769eeSSean Young return -ENOMEM; 16332cf86f6SMauro Carvalho Chehab 16432cf86f6SMauro Carvalho Chehab if (d->fops) { 165afbb1101SSean Young cdev->ops = d->fops; 16632cf86f6SMauro Carvalho Chehab cdev->owner = d->owner; 16732cf86f6SMauro Carvalho Chehab } else { 168afbb1101SSean Young cdev->ops = &lirc_dev_fops; 16932cf86f6SMauro Carvalho Chehab cdev->owner = THIS_MODULE; 17032cf86f6SMauro Carvalho Chehab } 171b395cbacSVasiliy Kulikov retval = kobject_set_name(&cdev->kobj, "lirc%d", d->minor); 172b395cbacSVasiliy Kulikov if (retval) 1738de111e2SJarod Wilson goto err_out; 17432cf86f6SMauro Carvalho Chehab 17532cf86f6SMauro Carvalho Chehab retval = cdev_add(cdev, MKDEV(MAJOR(lirc_base_dev), d->minor), 1); 176b40769eeSSean Young if (retval) 1778de111e2SJarod Wilson goto err_out; 17832cf86f6SMauro Carvalho Chehab 1798de111e2SJarod Wilson ir->cdev = cdev; 1808de111e2SJarod Wilson 1818de111e2SJarod Wilson return 0; 1828de111e2SJarod Wilson 1838de111e2SJarod Wilson err_out: 184afbb1101SSean Young cdev_del(cdev); 18532cf86f6SMauro Carvalho Chehab return retval; 18632cf86f6SMauro Carvalho Chehab } 18732cf86f6SMauro Carvalho Chehab 1886fa99e1aSAndi Shyti static int lirc_allocate_buffer(struct irctl *ir) 1896fa99e1aSAndi Shyti { 19070143984SAndi Shyti int err = 0; 1916fa99e1aSAndi Shyti int bytes_in_key; 1926fa99e1aSAndi Shyti unsigned int chunk_size; 1936fa99e1aSAndi Shyti unsigned int buffer_size; 1946fa99e1aSAndi Shyti struct lirc_driver *d = &ir->d; 1956fa99e1aSAndi Shyti 19670143984SAndi Shyti mutex_lock(&lirc_dev_lock); 19770143984SAndi Shyti 1986fa99e1aSAndi Shyti bytes_in_key = BITS_TO_LONGS(d->code_length) + 1996fa99e1aSAndi Shyti (d->code_length % 8 ? 1 : 0); 2006fa99e1aSAndi Shyti buffer_size = d->buffer_size ? d->buffer_size : BUFLEN / bytes_in_key; 2016fa99e1aSAndi Shyti chunk_size = d->chunk_size ? d->chunk_size : bytes_in_key; 2026fa99e1aSAndi Shyti 2036fa99e1aSAndi Shyti if (d->rbuf) { 2046fa99e1aSAndi Shyti ir->buf = d->rbuf; 2056fa99e1aSAndi Shyti } else { 2066fa99e1aSAndi Shyti ir->buf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL); 20770143984SAndi Shyti if (!ir->buf) { 20870143984SAndi Shyti err = -ENOMEM; 20970143984SAndi Shyti goto out; 21070143984SAndi Shyti } 2116fa99e1aSAndi Shyti 2126fa99e1aSAndi Shyti err = lirc_buffer_init(ir->buf, chunk_size, buffer_size); 2136fa99e1aSAndi Shyti if (err) { 2146fa99e1aSAndi Shyti kfree(ir->buf); 21570143984SAndi Shyti goto out; 2166fa99e1aSAndi Shyti } 2176fa99e1aSAndi Shyti } 2186fa99e1aSAndi Shyti ir->chunk_size = ir->buf->chunk_size; 2196fa99e1aSAndi Shyti 22070143984SAndi Shyti out: 22170143984SAndi Shyti mutex_unlock(&lirc_dev_lock); 22270143984SAndi Shyti 22370143984SAndi Shyti return err; 2246fa99e1aSAndi Shyti } 2256fa99e1aSAndi Shyti 22670143984SAndi Shyti static int lirc_allocate_driver(struct lirc_driver *d) 22732cf86f6SMauro Carvalho Chehab { 22832cf86f6SMauro Carvalho Chehab struct irctl *ir; 22932cf86f6SMauro Carvalho Chehab int minor; 23032cf86f6SMauro Carvalho Chehab int err; 23132cf86f6SMauro Carvalho Chehab 23232cf86f6SMauro Carvalho Chehab if (!d) { 2333fac0314SAndi Shyti pr_err("driver pointer must be not NULL!\n"); 23454fcecafSAndi Shyti return -EBADRQC; 23532cf86f6SMauro Carvalho Chehab } 23632cf86f6SMauro Carvalho Chehab 23732cf86f6SMauro Carvalho Chehab if (!d->dev) { 2383fac0314SAndi Shyti pr_err("dev pointer not filled in!\n"); 23954fcecafSAndi Shyti return -EINVAL; 24032cf86f6SMauro Carvalho Chehab } 24132cf86f6SMauro Carvalho Chehab 2429675ee5aSAndi Shyti if (d->minor >= MAX_IRCTL_DEVICES) { 2433fac0314SAndi Shyti dev_err(d->dev, "minor must be between 0 and %d!\n", 2443fac0314SAndi Shyti MAX_IRCTL_DEVICES - 1); 24554fcecafSAndi Shyti return -EBADRQC; 24632cf86f6SMauro Carvalho Chehab } 24732cf86f6SMauro Carvalho Chehab 2489675ee5aSAndi Shyti if (d->code_length < 1 || d->code_length > (BUFLEN * 8)) { 2493fac0314SAndi Shyti dev_err(d->dev, "code length must be less than %d bits\n", 2503fac0314SAndi Shyti BUFLEN * 8); 25154fcecafSAndi Shyti return -EBADRQC; 25232cf86f6SMauro Carvalho Chehab } 25332cf86f6SMauro Carvalho Chehab 25432cf86f6SMauro Carvalho Chehab if (d->sample_rate) { 25532cf86f6SMauro Carvalho Chehab if (2 > d->sample_rate || HZ < d->sample_rate) { 2563fac0314SAndi Shyti dev_err(d->dev, "invalid %d sample rate\n", 2573fac0314SAndi Shyti d->sample_rate); 25854fcecafSAndi Shyti return -EBADRQC; 25932cf86f6SMauro Carvalho Chehab } 26032cf86f6SMauro Carvalho Chehab if (!d->add_to_buf) { 2613fac0314SAndi Shyti dev_err(d->dev, "add_to_buf not set\n"); 26254fcecafSAndi Shyti return -EBADRQC; 26332cf86f6SMauro Carvalho Chehab } 26414db9fc2SAndi Shyti } else if (!d->rbuf && !(d->fops && d->fops->read && 26514db9fc2SAndi Shyti d->fops->poll && d->fops->unlocked_ioctl)) { 2663fac0314SAndi Shyti dev_err(d->dev, "undefined read, poll, ioctl\n"); 26754fcecafSAndi Shyti return -EBADRQC; 26832cf86f6SMauro Carvalho Chehab } 26932cf86f6SMauro Carvalho Chehab 27032cf86f6SMauro Carvalho Chehab mutex_lock(&lirc_dev_lock); 27132cf86f6SMauro Carvalho Chehab 27232cf86f6SMauro Carvalho Chehab minor = d->minor; 27332cf86f6SMauro Carvalho Chehab 27432cf86f6SMauro Carvalho Chehab if (minor < 0) { 27532cf86f6SMauro Carvalho Chehab /* find first free slot for driver */ 27632cf86f6SMauro Carvalho Chehab for (minor = 0; minor < MAX_IRCTL_DEVICES; minor++) 27732cf86f6SMauro Carvalho Chehab if (!irctls[minor]) 27832cf86f6SMauro Carvalho Chehab break; 2799675ee5aSAndi Shyti if (minor == MAX_IRCTL_DEVICES) { 2803fac0314SAndi Shyti dev_err(d->dev, "no free slots for drivers!\n"); 28132cf86f6SMauro Carvalho Chehab err = -ENOMEM; 28232cf86f6SMauro Carvalho Chehab goto out_lock; 28332cf86f6SMauro Carvalho Chehab } 28432cf86f6SMauro Carvalho Chehab } else if (irctls[minor]) { 2853fac0314SAndi Shyti dev_err(d->dev, "minor (%d) just registered!\n", minor); 28632cf86f6SMauro Carvalho Chehab err = -EBUSY; 28732cf86f6SMauro Carvalho Chehab goto out_lock; 28832cf86f6SMauro Carvalho Chehab } 28932cf86f6SMauro Carvalho Chehab 29032cf86f6SMauro Carvalho Chehab ir = kzalloc(sizeof(struct irctl), GFP_KERNEL); 29132cf86f6SMauro Carvalho Chehab if (!ir) { 29232cf86f6SMauro Carvalho Chehab err = -ENOMEM; 29332cf86f6SMauro Carvalho Chehab goto out_lock; 29432cf86f6SMauro Carvalho Chehab } 29532cf86f6SMauro Carvalho Chehab lirc_irctl_init(ir); 29632cf86f6SMauro Carvalho Chehab irctls[minor] = ir; 29732cf86f6SMauro Carvalho Chehab d->minor = minor; 29832cf86f6SMauro Carvalho Chehab 29932cf86f6SMauro Carvalho Chehab /* some safety check 8-) */ 30032cf86f6SMauro Carvalho Chehab d->name[sizeof(d->name)-1] = '\0'; 30132cf86f6SMauro Carvalho Chehab 30232cf86f6SMauro Carvalho Chehab if (d->features == 0) 30332cf86f6SMauro Carvalho Chehab d->features = LIRC_CAN_REC_LIRCCODE; 30432cf86f6SMauro Carvalho Chehab 30532cf86f6SMauro Carvalho Chehab ir->d = *d; 30632cf86f6SMauro Carvalho Chehab 30732cf86f6SMauro Carvalho Chehab device_create(lirc_class, ir->d.dev, 30832cf86f6SMauro Carvalho Chehab MKDEV(MAJOR(lirc_base_dev), ir->d.minor), NULL, 30932cf86f6SMauro Carvalho Chehab "lirc%u", ir->d.minor); 31032cf86f6SMauro Carvalho Chehab 31132cf86f6SMauro Carvalho Chehab if (d->sample_rate) { 3126ab86d2aSAndi Shyti ir->jiffies_to_wait = HZ / d->sample_rate; 3136ab86d2aSAndi Shyti 31432cf86f6SMauro Carvalho Chehab /* try to fire up polling thread */ 31532cf86f6SMauro Carvalho Chehab ir->task = kthread_run(lirc_thread, (void *)ir, "lirc_dev"); 31632cf86f6SMauro Carvalho Chehab if (IS_ERR(ir->task)) { 3173fac0314SAndi Shyti dev_err(d->dev, "cannot run thread for minor = %d\n", 31832cf86f6SMauro Carvalho Chehab d->minor); 31932cf86f6SMauro Carvalho Chehab err = -ECHILD; 32032cf86f6SMauro Carvalho Chehab goto out_sysfs; 32132cf86f6SMauro Carvalho Chehab } 3226ab86d2aSAndi Shyti } else { 3236ab86d2aSAndi Shyti /* it means - wait for external event in task queue */ 3246ab86d2aSAndi Shyti ir->jiffies_to_wait = 0; 32532cf86f6SMauro Carvalho Chehab } 32632cf86f6SMauro Carvalho Chehab 32732cf86f6SMauro Carvalho Chehab err = lirc_cdev_add(ir); 32832cf86f6SMauro Carvalho Chehab if (err) 32932cf86f6SMauro Carvalho Chehab goto out_sysfs; 33032cf86f6SMauro Carvalho Chehab 33132cf86f6SMauro Carvalho Chehab ir->attached = 1; 33232cf86f6SMauro Carvalho Chehab mutex_unlock(&lirc_dev_lock); 33332cf86f6SMauro Carvalho Chehab 33432cf86f6SMauro Carvalho Chehab dev_info(ir->d.dev, "lirc_dev: driver %s registered at minor = %d\n", 33532cf86f6SMauro Carvalho Chehab ir->d.name, ir->d.minor); 33632cf86f6SMauro Carvalho Chehab return minor; 33732cf86f6SMauro Carvalho Chehab 33832cf86f6SMauro Carvalho Chehab out_sysfs: 33932cf86f6SMauro Carvalho Chehab device_destroy(lirc_class, MKDEV(MAJOR(lirc_base_dev), ir->d.minor)); 34032cf86f6SMauro Carvalho Chehab out_lock: 34132cf86f6SMauro Carvalho Chehab mutex_unlock(&lirc_dev_lock); 34254fcecafSAndi Shyti 34332cf86f6SMauro Carvalho Chehab return err; 34432cf86f6SMauro Carvalho Chehab } 34570143984SAndi Shyti 34670143984SAndi Shyti int lirc_register_driver(struct lirc_driver *d) 34770143984SAndi Shyti { 34870143984SAndi Shyti int minor, err = 0; 34970143984SAndi Shyti 35070143984SAndi Shyti minor = lirc_allocate_driver(d); 35170143984SAndi Shyti if (minor < 0) 35270143984SAndi Shyti return minor; 35370143984SAndi Shyti 35470143984SAndi Shyti if (LIRC_CAN_REC(d->features)) { 35570143984SAndi Shyti err = lirc_allocate_buffer(irctls[minor]); 35670143984SAndi Shyti if (err) 35770143984SAndi Shyti lirc_unregister_driver(minor); 35870143984SAndi Shyti } 35970143984SAndi Shyti 36070143984SAndi Shyti return err ? err : minor; 36170143984SAndi Shyti } 36232cf86f6SMauro Carvalho Chehab EXPORT_SYMBOL(lirc_register_driver); 36332cf86f6SMauro Carvalho Chehab 36432cf86f6SMauro Carvalho Chehab int lirc_unregister_driver(int minor) 36532cf86f6SMauro Carvalho Chehab { 36632cf86f6SMauro Carvalho Chehab struct irctl *ir; 36732cf86f6SMauro Carvalho Chehab struct cdev *cdev; 36832cf86f6SMauro Carvalho Chehab 36932cf86f6SMauro Carvalho Chehab if (minor < 0 || minor >= MAX_IRCTL_DEVICES) { 3703fac0314SAndi Shyti pr_err("minor (%d) must be between 0 and %d!\n", 3713fac0314SAndi Shyti minor, MAX_IRCTL_DEVICES - 1); 37232cf86f6SMauro Carvalho Chehab return -EBADRQC; 37332cf86f6SMauro Carvalho Chehab } 37432cf86f6SMauro Carvalho Chehab 37532cf86f6SMauro Carvalho Chehab ir = irctls[minor]; 37632cf86f6SMauro Carvalho Chehab if (!ir) { 3773fac0314SAndi Shyti pr_err("failed to get irctl\n"); 37832cf86f6SMauro Carvalho Chehab return -ENOENT; 37932cf86f6SMauro Carvalho Chehab } 38032cf86f6SMauro Carvalho Chehab 3818de111e2SJarod Wilson cdev = ir->cdev; 38232cf86f6SMauro Carvalho Chehab 38332cf86f6SMauro Carvalho Chehab mutex_lock(&lirc_dev_lock); 38432cf86f6SMauro Carvalho Chehab 38532cf86f6SMauro Carvalho Chehab if (ir->d.minor != minor) { 3863fac0314SAndi Shyti dev_err(ir->d.dev, "lirc_dev: minor %d device not registered\n", 3873fac0314SAndi Shyti minor); 38832cf86f6SMauro Carvalho Chehab mutex_unlock(&lirc_dev_lock); 38932cf86f6SMauro Carvalho Chehab return -ENOENT; 39032cf86f6SMauro Carvalho Chehab } 39132cf86f6SMauro Carvalho Chehab 39232cf86f6SMauro Carvalho Chehab /* end up polling thread */ 39332cf86f6SMauro Carvalho Chehab if (ir->task) 39432cf86f6SMauro Carvalho Chehab kthread_stop(ir->task); 39532cf86f6SMauro Carvalho Chehab 39632cf86f6SMauro Carvalho Chehab dev_dbg(ir->d.dev, "lirc_dev: driver %s unregistered from minor = %d\n", 39732cf86f6SMauro Carvalho Chehab ir->d.name, ir->d.minor); 39832cf86f6SMauro Carvalho Chehab 39932cf86f6SMauro Carvalho Chehab ir->attached = 0; 40032cf86f6SMauro Carvalho Chehab if (ir->open) { 40132cf86f6SMauro Carvalho Chehab dev_dbg(ir->d.dev, LOGHEAD "releasing opened driver\n", 40232cf86f6SMauro Carvalho Chehab ir->d.name, ir->d.minor); 40332cf86f6SMauro Carvalho Chehab wake_up_interruptible(&ir->buf->wait_poll); 40432cf86f6SMauro Carvalho Chehab mutex_lock(&ir->irctl_lock); 4058e5fa4c6SAndi Shyti 4068e5fa4c6SAndi Shyti if (ir->d.set_use_dec) 40732cf86f6SMauro Carvalho Chehab ir->d.set_use_dec(ir->d.data); 4088e5fa4c6SAndi Shyti 40932cf86f6SMauro Carvalho Chehab module_put(cdev->owner); 41032cf86f6SMauro Carvalho Chehab mutex_unlock(&ir->irctl_lock); 41132cf86f6SMauro Carvalho Chehab } else { 41232cf86f6SMauro Carvalho Chehab lirc_irctl_cleanup(ir); 41332cf86f6SMauro Carvalho Chehab cdev_del(cdev); 41432cf86f6SMauro Carvalho Chehab kfree(ir); 41532cf86f6SMauro Carvalho Chehab irctls[minor] = NULL; 41632cf86f6SMauro Carvalho Chehab } 41732cf86f6SMauro Carvalho Chehab 41832cf86f6SMauro Carvalho Chehab mutex_unlock(&lirc_dev_lock); 41932cf86f6SMauro Carvalho Chehab 42032cf86f6SMauro Carvalho Chehab return 0; 42132cf86f6SMauro Carvalho Chehab } 42232cf86f6SMauro Carvalho Chehab EXPORT_SYMBOL(lirc_unregister_driver); 42332cf86f6SMauro Carvalho Chehab 42432cf86f6SMauro Carvalho Chehab int lirc_dev_fop_open(struct inode *inode, struct file *file) 42532cf86f6SMauro Carvalho Chehab { 42632cf86f6SMauro Carvalho Chehab struct irctl *ir; 42732cf86f6SMauro Carvalho Chehab struct cdev *cdev; 42832cf86f6SMauro Carvalho Chehab int retval = 0; 42932cf86f6SMauro Carvalho Chehab 43032cf86f6SMauro Carvalho Chehab if (iminor(inode) >= MAX_IRCTL_DEVICES) { 4313fac0314SAndi Shyti pr_err("open result for %d is -ENODEV\n", iminor(inode)); 43232cf86f6SMauro Carvalho Chehab return -ENODEV; 43332cf86f6SMauro Carvalho Chehab } 43432cf86f6SMauro Carvalho Chehab 43532cf86f6SMauro Carvalho Chehab if (mutex_lock_interruptible(&lirc_dev_lock)) 43632cf86f6SMauro Carvalho Chehab return -ERESTARTSYS; 43732cf86f6SMauro Carvalho Chehab 43832cf86f6SMauro Carvalho Chehab ir = irctls[iminor(inode)]; 43932cf86f6SMauro Carvalho Chehab if (!ir) { 44032cf86f6SMauro Carvalho Chehab retval = -ENODEV; 44132cf86f6SMauro Carvalho Chehab goto error; 44232cf86f6SMauro Carvalho Chehab } 44332cf86f6SMauro Carvalho Chehab 44432cf86f6SMauro Carvalho Chehab dev_dbg(ir->d.dev, LOGHEAD "open called\n", ir->d.name, ir->d.minor); 44532cf86f6SMauro Carvalho Chehab 44632cf86f6SMauro Carvalho Chehab if (ir->d.minor == NOPLUG) { 44732cf86f6SMauro Carvalho Chehab retval = -ENODEV; 44832cf86f6SMauro Carvalho Chehab goto error; 44932cf86f6SMauro Carvalho Chehab } 45032cf86f6SMauro Carvalho Chehab 45132cf86f6SMauro Carvalho Chehab if (ir->open) { 45232cf86f6SMauro Carvalho Chehab retval = -EBUSY; 45332cf86f6SMauro Carvalho Chehab goto error; 45432cf86f6SMauro Carvalho Chehab } 45532cf86f6SMauro Carvalho Chehab 456ca7a722dSSrinivas Kandagatla if (ir->d.rdev) { 457ca7a722dSSrinivas Kandagatla retval = rc_open(ir->d.rdev); 458ca7a722dSSrinivas Kandagatla if (retval) 459ca7a722dSSrinivas Kandagatla goto error; 460ca7a722dSSrinivas Kandagatla } 461ca7a722dSSrinivas Kandagatla 4628de111e2SJarod Wilson cdev = ir->cdev; 46332cf86f6SMauro Carvalho Chehab if (try_module_get(cdev->owner)) { 46432cf86f6SMauro Carvalho Chehab ir->open++; 4658e5fa4c6SAndi Shyti if (ir->d.set_use_inc) 46632cf86f6SMauro Carvalho Chehab retval = ir->d.set_use_inc(ir->d.data); 46732cf86f6SMauro Carvalho Chehab 46832cf86f6SMauro Carvalho Chehab if (retval) { 46932cf86f6SMauro Carvalho Chehab module_put(cdev->owner); 47032cf86f6SMauro Carvalho Chehab ir->open--; 4717cebf2eeSSean Young } else if (ir->buf) { 47232cf86f6SMauro Carvalho Chehab lirc_buffer_clear(ir->buf); 47332cf86f6SMauro Carvalho Chehab } 47432cf86f6SMauro Carvalho Chehab if (ir->task) 47532cf86f6SMauro Carvalho Chehab wake_up_process(ir->task); 47632cf86f6SMauro Carvalho Chehab } 47732cf86f6SMauro Carvalho Chehab 47832cf86f6SMauro Carvalho Chehab error: 47932cf86f6SMauro Carvalho Chehab mutex_unlock(&lirc_dev_lock); 48032cf86f6SMauro Carvalho Chehab 48132cf86f6SMauro Carvalho Chehab nonseekable_open(inode, file); 48232cf86f6SMauro Carvalho Chehab 48332cf86f6SMauro Carvalho Chehab return retval; 48432cf86f6SMauro Carvalho Chehab } 48532cf86f6SMauro Carvalho Chehab EXPORT_SYMBOL(lirc_dev_fop_open); 48632cf86f6SMauro Carvalho Chehab 48732cf86f6SMauro Carvalho Chehab int lirc_dev_fop_close(struct inode *inode, struct file *file) 48832cf86f6SMauro Carvalho Chehab { 48932cf86f6SMauro Carvalho Chehab struct irctl *ir = irctls[iminor(inode)]; 4908de111e2SJarod Wilson struct cdev *cdev; 491b64e10f3SMauro Carvalho Chehab int ret; 49232cf86f6SMauro Carvalho Chehab 49332cf86f6SMauro Carvalho Chehab if (!ir) { 4943fac0314SAndi Shyti pr_err("called with invalid irctl\n"); 49532cf86f6SMauro Carvalho Chehab return -EINVAL; 49632cf86f6SMauro Carvalho Chehab } 49732cf86f6SMauro Carvalho Chehab 4988de111e2SJarod Wilson cdev = ir->cdev; 4998de111e2SJarod Wilson 500b64e10f3SMauro Carvalho Chehab ret = mutex_lock_killable(&lirc_dev_lock); 501b64e10f3SMauro Carvalho Chehab WARN_ON(ret); 50232cf86f6SMauro Carvalho Chehab 503ca7a722dSSrinivas Kandagatla rc_close(ir->d.rdev); 504ca7a722dSSrinivas Kandagatla 50532cf86f6SMauro Carvalho Chehab ir->open--; 50632cf86f6SMauro Carvalho Chehab if (ir->attached) { 5078e5fa4c6SAndi Shyti if (ir->d.set_use_dec) 50832cf86f6SMauro Carvalho Chehab ir->d.set_use_dec(ir->d.data); 50932cf86f6SMauro Carvalho Chehab module_put(cdev->owner); 51032cf86f6SMauro Carvalho Chehab } else { 51132cf86f6SMauro Carvalho Chehab lirc_irctl_cleanup(ir); 51232cf86f6SMauro Carvalho Chehab cdev_del(cdev); 51332cf86f6SMauro Carvalho Chehab irctls[ir->d.minor] = NULL; 51432cf86f6SMauro Carvalho Chehab kfree(ir); 51532cf86f6SMauro Carvalho Chehab } 51632cf86f6SMauro Carvalho Chehab 517b64e10f3SMauro Carvalho Chehab if (!ret) 51832cf86f6SMauro Carvalho Chehab mutex_unlock(&lirc_dev_lock); 51932cf86f6SMauro Carvalho Chehab 52032cf86f6SMauro Carvalho Chehab return 0; 52132cf86f6SMauro Carvalho Chehab } 52232cf86f6SMauro Carvalho Chehab EXPORT_SYMBOL(lirc_dev_fop_close); 52332cf86f6SMauro Carvalho Chehab 52432cf86f6SMauro Carvalho Chehab unsigned int lirc_dev_fop_poll(struct file *file, poll_table *wait) 52532cf86f6SMauro Carvalho Chehab { 526496ad9aaSAl Viro struct irctl *ir = irctls[iminor(file_inode(file))]; 52732cf86f6SMauro Carvalho Chehab unsigned int ret; 52832cf86f6SMauro Carvalho Chehab 52932cf86f6SMauro Carvalho Chehab if (!ir) { 5303fac0314SAndi Shyti pr_err("called with invalid irctl\n"); 53132cf86f6SMauro Carvalho Chehab return POLLERR; 53232cf86f6SMauro Carvalho Chehab } 53332cf86f6SMauro Carvalho Chehab 53432cf86f6SMauro Carvalho Chehab if (!ir->attached) 53532cf86f6SMauro Carvalho Chehab return POLLERR; 53632cf86f6SMauro Carvalho Chehab 5373656cdddSAndy Shevchenko if (ir->buf) { 53832cf86f6SMauro Carvalho Chehab poll_wait(file, &ir->buf->wait_poll, wait); 53932cf86f6SMauro Carvalho Chehab 54032cf86f6SMauro Carvalho Chehab if (lirc_buffer_empty(ir->buf)) 54132cf86f6SMauro Carvalho Chehab ret = 0; 54232cf86f6SMauro Carvalho Chehab else 54332cf86f6SMauro Carvalho Chehab ret = POLLIN | POLLRDNORM; 5443656cdddSAndy Shevchenko } else 54532cf86f6SMauro Carvalho Chehab ret = POLLERR; 54632cf86f6SMauro Carvalho Chehab 54732cf86f6SMauro Carvalho Chehab dev_dbg(ir->d.dev, LOGHEAD "poll result = %d\n", 54832cf86f6SMauro Carvalho Chehab ir->d.name, ir->d.minor, ret); 54932cf86f6SMauro Carvalho Chehab 55032cf86f6SMauro Carvalho Chehab return ret; 55132cf86f6SMauro Carvalho Chehab } 55232cf86f6SMauro Carvalho Chehab EXPORT_SYMBOL(lirc_dev_fop_poll); 55332cf86f6SMauro Carvalho Chehab 55432cf86f6SMauro Carvalho Chehab long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 55532cf86f6SMauro Carvalho Chehab { 55632cf86f6SMauro Carvalho Chehab __u32 mode; 55732cf86f6SMauro Carvalho Chehab int result = 0; 558496ad9aaSAl Viro struct irctl *ir = irctls[iminor(file_inode(file))]; 55932cf86f6SMauro Carvalho Chehab 56032cf86f6SMauro Carvalho Chehab if (!ir) { 5613fac0314SAndi Shyti pr_err("no irctl found!\n"); 56232cf86f6SMauro Carvalho Chehab return -ENODEV; 56332cf86f6SMauro Carvalho Chehab } 56432cf86f6SMauro Carvalho Chehab 56532cf86f6SMauro Carvalho Chehab dev_dbg(ir->d.dev, LOGHEAD "ioctl called (0x%x)\n", 56632cf86f6SMauro Carvalho Chehab ir->d.name, ir->d.minor, cmd); 56732cf86f6SMauro Carvalho Chehab 56832cf86f6SMauro Carvalho Chehab if (ir->d.minor == NOPLUG || !ir->attached) { 5693fac0314SAndi Shyti dev_err(ir->d.dev, LOGHEAD "ioctl result = -ENODEV\n", 57032cf86f6SMauro Carvalho Chehab ir->d.name, ir->d.minor); 57132cf86f6SMauro Carvalho Chehab return -ENODEV; 57232cf86f6SMauro Carvalho Chehab } 57332cf86f6SMauro Carvalho Chehab 57432cf86f6SMauro Carvalho Chehab mutex_lock(&ir->irctl_lock); 57532cf86f6SMauro Carvalho Chehab 57632cf86f6SMauro Carvalho Chehab switch (cmd) { 57732cf86f6SMauro Carvalho Chehab case LIRC_GET_FEATURES: 57860519af3SHans Verkuil result = put_user(ir->d.features, (__u32 __user *)arg); 57932cf86f6SMauro Carvalho Chehab break; 58032cf86f6SMauro Carvalho Chehab case LIRC_GET_REC_MODE: 581bd291208SSean Young if (!LIRC_CAN_REC(ir->d.features)) { 582b4088094SAndi Shyti result = -ENOTTY; 58332cf86f6SMauro Carvalho Chehab break; 58432cf86f6SMauro Carvalho Chehab } 58532cf86f6SMauro Carvalho Chehab 58632cf86f6SMauro Carvalho Chehab result = put_user(LIRC_REC2MODE 58732cf86f6SMauro Carvalho Chehab (ir->d.features & LIRC_CAN_REC_MASK), 58860519af3SHans Verkuil (__u32 __user *)arg); 58932cf86f6SMauro Carvalho Chehab break; 59032cf86f6SMauro Carvalho Chehab case LIRC_SET_REC_MODE: 591bd291208SSean Young if (!LIRC_CAN_REC(ir->d.features)) { 592b4088094SAndi Shyti result = -ENOTTY; 59332cf86f6SMauro Carvalho Chehab break; 59432cf86f6SMauro Carvalho Chehab } 59532cf86f6SMauro Carvalho Chehab 59660519af3SHans Verkuil result = get_user(mode, (__u32 __user *)arg); 59732cf86f6SMauro Carvalho Chehab if (!result && !(LIRC_MODE2REC(mode) & ir->d.features)) 59832cf86f6SMauro Carvalho Chehab result = -EINVAL; 59932cf86f6SMauro Carvalho Chehab /* 60032cf86f6SMauro Carvalho Chehab * FIXME: We should actually set the mode somehow but 60132cf86f6SMauro Carvalho Chehab * for now, lirc_serial doesn't support mode changing either 60232cf86f6SMauro Carvalho Chehab */ 60332cf86f6SMauro Carvalho Chehab break; 60432cf86f6SMauro Carvalho Chehab case LIRC_GET_LENGTH: 60560519af3SHans Verkuil result = put_user(ir->d.code_length, (__u32 __user *)arg); 60632cf86f6SMauro Carvalho Chehab break; 60732cf86f6SMauro Carvalho Chehab case LIRC_GET_MIN_TIMEOUT: 60832cf86f6SMauro Carvalho Chehab if (!(ir->d.features & LIRC_CAN_SET_REC_TIMEOUT) || 60932cf86f6SMauro Carvalho Chehab ir->d.min_timeout == 0) { 610b4088094SAndi Shyti result = -ENOTTY; 61132cf86f6SMauro Carvalho Chehab break; 61232cf86f6SMauro Carvalho Chehab } 61332cf86f6SMauro Carvalho Chehab 61460519af3SHans Verkuil result = put_user(ir->d.min_timeout, (__u32 __user *)arg); 61532cf86f6SMauro Carvalho Chehab break; 61632cf86f6SMauro Carvalho Chehab case LIRC_GET_MAX_TIMEOUT: 61732cf86f6SMauro Carvalho Chehab if (!(ir->d.features & LIRC_CAN_SET_REC_TIMEOUT) || 61832cf86f6SMauro Carvalho Chehab ir->d.max_timeout == 0) { 619b4088094SAndi Shyti result = -ENOTTY; 62032cf86f6SMauro Carvalho Chehab break; 62132cf86f6SMauro Carvalho Chehab } 62232cf86f6SMauro Carvalho Chehab 62360519af3SHans Verkuil result = put_user(ir->d.max_timeout, (__u32 __user *)arg); 62432cf86f6SMauro Carvalho Chehab break; 62532cf86f6SMauro Carvalho Chehab default: 6265c862758SSean Young result = -ENOTTY; 62732cf86f6SMauro Carvalho Chehab } 62832cf86f6SMauro Carvalho Chehab 62932cf86f6SMauro Carvalho Chehab mutex_unlock(&ir->irctl_lock); 63032cf86f6SMauro Carvalho Chehab 63132cf86f6SMauro Carvalho Chehab return result; 63232cf86f6SMauro Carvalho Chehab } 63332cf86f6SMauro Carvalho Chehab EXPORT_SYMBOL(lirc_dev_fop_ioctl); 63432cf86f6SMauro Carvalho Chehab 63532cf86f6SMauro Carvalho Chehab ssize_t lirc_dev_fop_read(struct file *file, 6360e835087SDan Carpenter char __user *buffer, 63732cf86f6SMauro Carvalho Chehab size_t length, 63832cf86f6SMauro Carvalho Chehab loff_t *ppos) 63932cf86f6SMauro Carvalho Chehab { 640496ad9aaSAl Viro struct irctl *ir = irctls[iminor(file_inode(file))]; 64132cf86f6SMauro Carvalho Chehab unsigned char *buf; 64232cf86f6SMauro Carvalho Chehab int ret = 0, written = 0; 64332cf86f6SMauro Carvalho Chehab DECLARE_WAITQUEUE(wait, current); 64432cf86f6SMauro Carvalho Chehab 64532cf86f6SMauro Carvalho Chehab if (!ir) { 6463fac0314SAndi Shyti pr_err("called with invalid irctl\n"); 64732cf86f6SMauro Carvalho Chehab return -ENODEV; 64832cf86f6SMauro Carvalho Chehab } 64932cf86f6SMauro Carvalho Chehab 65032002f72SSean Young if (!LIRC_CAN_REC(ir->d.features)) 65132002f72SSean Young return -EINVAL; 65232002f72SSean Young 65332cf86f6SMauro Carvalho Chehab dev_dbg(ir->d.dev, LOGHEAD "read called\n", ir->d.name, ir->d.minor); 65432cf86f6SMauro Carvalho Chehab 65532cf86f6SMauro Carvalho Chehab buf = kzalloc(ir->chunk_size, GFP_KERNEL); 65632cf86f6SMauro Carvalho Chehab if (!buf) 65732cf86f6SMauro Carvalho Chehab return -ENOMEM; 65832cf86f6SMauro Carvalho Chehab 65932cf86f6SMauro Carvalho Chehab if (mutex_lock_interruptible(&ir->irctl_lock)) { 66032cf86f6SMauro Carvalho Chehab ret = -ERESTARTSYS; 66132cf86f6SMauro Carvalho Chehab goto out_unlocked; 66232cf86f6SMauro Carvalho Chehab } 66332cf86f6SMauro Carvalho Chehab if (!ir->attached) { 66432cf86f6SMauro Carvalho Chehab ret = -ENODEV; 66532cf86f6SMauro Carvalho Chehab goto out_locked; 66632cf86f6SMauro Carvalho Chehab } 66732cf86f6SMauro Carvalho Chehab 66832cf86f6SMauro Carvalho Chehab if (length % ir->chunk_size) { 66932cf86f6SMauro Carvalho Chehab ret = -EINVAL; 67032cf86f6SMauro Carvalho Chehab goto out_locked; 67132cf86f6SMauro Carvalho Chehab } 67232cf86f6SMauro Carvalho Chehab 67332cf86f6SMauro Carvalho Chehab /* 67432cf86f6SMauro Carvalho Chehab * we add ourselves to the task queue before buffer check 67532cf86f6SMauro Carvalho Chehab * to avoid losing scan code (in case when queue is awaken somewhere 67632cf86f6SMauro Carvalho Chehab * between while condition checking and scheduling) 67732cf86f6SMauro Carvalho Chehab */ 67832cf86f6SMauro Carvalho Chehab add_wait_queue(&ir->buf->wait_poll, &wait); 67932cf86f6SMauro Carvalho Chehab 68032cf86f6SMauro Carvalho Chehab /* 68132cf86f6SMauro Carvalho Chehab * while we didn't provide 'length' bytes, device is opened in blocking 68232cf86f6SMauro Carvalho Chehab * mode and 'copy_to_user' is happy, wait for data. 68332cf86f6SMauro Carvalho Chehab */ 68432cf86f6SMauro Carvalho Chehab while (written < length && ret == 0) { 68532cf86f6SMauro Carvalho Chehab if (lirc_buffer_empty(ir->buf)) { 68632cf86f6SMauro Carvalho Chehab /* According to the read(2) man page, 'written' can be 68732cf86f6SMauro Carvalho Chehab * returned as less than 'length', instead of blocking 68832cf86f6SMauro Carvalho Chehab * again, returning -EWOULDBLOCK, or returning 68962e92682SAndi Shyti * -ERESTARTSYS 69062e92682SAndi Shyti */ 69132cf86f6SMauro Carvalho Chehab if (written) 69232cf86f6SMauro Carvalho Chehab break; 69332cf86f6SMauro Carvalho Chehab if (file->f_flags & O_NONBLOCK) { 69432cf86f6SMauro Carvalho Chehab ret = -EWOULDBLOCK; 69532cf86f6SMauro Carvalho Chehab break; 69632cf86f6SMauro Carvalho Chehab } 69732cf86f6SMauro Carvalho Chehab if (signal_pending(current)) { 69832cf86f6SMauro Carvalho Chehab ret = -ERESTARTSYS; 69932cf86f6SMauro Carvalho Chehab break; 70032cf86f6SMauro Carvalho Chehab } 70132cf86f6SMauro Carvalho Chehab 70232cf86f6SMauro Carvalho Chehab mutex_unlock(&ir->irctl_lock); 70332cf86f6SMauro Carvalho Chehab set_current_state(TASK_INTERRUPTIBLE); 70412accdcbSSean Young schedule(); 70512accdcbSSean Young set_current_state(TASK_RUNNING); 70632cf86f6SMauro Carvalho Chehab 70732cf86f6SMauro Carvalho Chehab if (mutex_lock_interruptible(&ir->irctl_lock)) { 70832cf86f6SMauro Carvalho Chehab ret = -ERESTARTSYS; 70932cf86f6SMauro Carvalho Chehab remove_wait_queue(&ir->buf->wait_poll, &wait); 71032cf86f6SMauro Carvalho Chehab goto out_unlocked; 71132cf86f6SMauro Carvalho Chehab } 71232cf86f6SMauro Carvalho Chehab 71332cf86f6SMauro Carvalho Chehab if (!ir->attached) { 71432cf86f6SMauro Carvalho Chehab ret = -ENODEV; 715c77d17c0SSean Young goto out_locked; 71632cf86f6SMauro Carvalho Chehab } 71732cf86f6SMauro Carvalho Chehab } else { 71832cf86f6SMauro Carvalho Chehab lirc_buffer_read(ir->buf, buf); 71960519af3SHans Verkuil ret = copy_to_user((void __user *)buffer+written, buf, 72032cf86f6SMauro Carvalho Chehab ir->buf->chunk_size); 72132cf86f6SMauro Carvalho Chehab if (!ret) 72232cf86f6SMauro Carvalho Chehab written += ir->buf->chunk_size; 72332cf86f6SMauro Carvalho Chehab else 72432cf86f6SMauro Carvalho Chehab ret = -EFAULT; 72532cf86f6SMauro Carvalho Chehab } 72632cf86f6SMauro Carvalho Chehab } 72732cf86f6SMauro Carvalho Chehab 72832cf86f6SMauro Carvalho Chehab remove_wait_queue(&ir->buf->wait_poll, &wait); 72932cf86f6SMauro Carvalho Chehab 73032cf86f6SMauro Carvalho Chehab out_locked: 73132cf86f6SMauro Carvalho Chehab mutex_unlock(&ir->irctl_lock); 73232cf86f6SMauro Carvalho Chehab 73332cf86f6SMauro Carvalho Chehab out_unlocked: 73432cf86f6SMauro Carvalho Chehab kfree(buf); 73532cf86f6SMauro Carvalho Chehab 73632cf86f6SMauro Carvalho Chehab return ret ? ret : written; 73732cf86f6SMauro Carvalho Chehab } 73832cf86f6SMauro Carvalho Chehab EXPORT_SYMBOL(lirc_dev_fop_read); 73932cf86f6SMauro Carvalho Chehab 74032cf86f6SMauro Carvalho Chehab void *lirc_get_pdata(struct file *file) 74132cf86f6SMauro Carvalho Chehab { 7420990a97aSAl Viro return irctls[iminor(file_inode(file))]->d.data; 74332cf86f6SMauro Carvalho Chehab } 74432cf86f6SMauro Carvalho Chehab EXPORT_SYMBOL(lirc_get_pdata); 74532cf86f6SMauro Carvalho Chehab 74632cf86f6SMauro Carvalho Chehab 7470e835087SDan Carpenter ssize_t lirc_dev_fop_write(struct file *file, const char __user *buffer, 74832cf86f6SMauro Carvalho Chehab size_t length, loff_t *ppos) 74932cf86f6SMauro Carvalho Chehab { 750496ad9aaSAl Viro struct irctl *ir = irctls[iminor(file_inode(file))]; 75132cf86f6SMauro Carvalho Chehab 75232cf86f6SMauro Carvalho Chehab if (!ir) { 7533fac0314SAndi Shyti pr_err("called with invalid irctl\n"); 75432cf86f6SMauro Carvalho Chehab return -ENODEV; 75532cf86f6SMauro Carvalho Chehab } 75632cf86f6SMauro Carvalho Chehab 75732cf86f6SMauro Carvalho Chehab if (!ir->attached) 75832cf86f6SMauro Carvalho Chehab return -ENODEV; 75932cf86f6SMauro Carvalho Chehab 76032cf86f6SMauro Carvalho Chehab return -EINVAL; 76132cf86f6SMauro Carvalho Chehab } 76232cf86f6SMauro Carvalho Chehab EXPORT_SYMBOL(lirc_dev_fop_write); 76332cf86f6SMauro Carvalho Chehab 76432cf86f6SMauro Carvalho Chehab 76532cf86f6SMauro Carvalho Chehab static int __init lirc_dev_init(void) 76632cf86f6SMauro Carvalho Chehab { 76732cf86f6SMauro Carvalho Chehab int retval; 76832cf86f6SMauro Carvalho Chehab 76932cf86f6SMauro Carvalho Chehab lirc_class = class_create(THIS_MODULE, "lirc"); 77032cf86f6SMauro Carvalho Chehab if (IS_ERR(lirc_class)) { 7713fac0314SAndi Shyti pr_err("class_create failed\n"); 77254fcecafSAndi Shyti return PTR_ERR(lirc_class); 77332cf86f6SMauro Carvalho Chehab } 77432cf86f6SMauro Carvalho Chehab 77532cf86f6SMauro Carvalho Chehab retval = alloc_chrdev_region(&lirc_base_dev, 0, MAX_IRCTL_DEVICES, 77632cf86f6SMauro Carvalho Chehab IRCTL_DEV_NAME); 77732cf86f6SMauro Carvalho Chehab if (retval) { 77832cf86f6SMauro Carvalho Chehab class_destroy(lirc_class); 7793fac0314SAndi Shyti pr_err("alloc_chrdev_region failed\n"); 78054fcecafSAndi Shyti return retval; 78132cf86f6SMauro Carvalho Chehab } 78232cf86f6SMauro Carvalho Chehab 78332cf86f6SMauro Carvalho Chehab 7843fac0314SAndi Shyti pr_info("IR Remote Control driver registered, major %d\n", 7853fac0314SAndi Shyti MAJOR(lirc_base_dev)); 78632cf86f6SMauro Carvalho Chehab 78754fcecafSAndi Shyti return 0; 78832cf86f6SMauro Carvalho Chehab } 78932cf86f6SMauro Carvalho Chehab 79032cf86f6SMauro Carvalho Chehab 79132cf86f6SMauro Carvalho Chehab 79232cf86f6SMauro Carvalho Chehab static void __exit lirc_dev_exit(void) 79332cf86f6SMauro Carvalho Chehab { 79432cf86f6SMauro Carvalho Chehab class_destroy(lirc_class); 79532cf86f6SMauro Carvalho Chehab unregister_chrdev_region(lirc_base_dev, MAX_IRCTL_DEVICES); 7963fac0314SAndi Shyti pr_info("module unloaded\n"); 79732cf86f6SMauro Carvalho Chehab } 79832cf86f6SMauro Carvalho Chehab 79932cf86f6SMauro Carvalho Chehab module_init(lirc_dev_init); 80032cf86f6SMauro Carvalho Chehab module_exit(lirc_dev_exit); 80132cf86f6SMauro Carvalho Chehab 80232cf86f6SMauro Carvalho Chehab MODULE_DESCRIPTION("LIRC base driver module"); 80332cf86f6SMauro Carvalho Chehab MODULE_AUTHOR("Artur Lipowski"); 80432cf86f6SMauro Carvalho Chehab MODULE_LICENSE("GPL"); 80532cf86f6SMauro Carvalho Chehab 80632cf86f6SMauro Carvalho Chehab module_param(debug, bool, S_IRUGO | S_IWUSR); 80732cf86f6SMauro Carvalho Chehab MODULE_PARM_DESC(debug, "Enable debugging messages"); 808