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> 21174cd4b1SIngo Molnar #include <linux/sched/signal.h> 2232cf86f6SMauro Carvalho Chehab #include <linux/ioctl.h> 2332cf86f6SMauro Carvalho Chehab #include <linux/poll.h> 2432cf86f6SMauro Carvalho Chehab #include <linux/mutex.h> 2532cf86f6SMauro Carvalho Chehab #include <linux/device.h> 2632cf86f6SMauro Carvalho Chehab #include <linux/cdev.h> 2732cf86f6SMauro Carvalho Chehab 28ca7a722dSSrinivas Kandagatla #include <media/rc-core.h> 2932cf86f6SMauro Carvalho Chehab #include <media/lirc.h> 3032cf86f6SMauro Carvalho Chehab #include <media/lirc_dev.h> 3132cf86f6SMauro Carvalho Chehab 3232cf86f6SMauro Carvalho Chehab #define LOGHEAD "lirc_dev (%s[%d]): " 3332cf86f6SMauro Carvalho Chehab 3432cf86f6SMauro Carvalho Chehab static dev_t lirc_base_dev; 3532cf86f6SMauro Carvalho Chehab 3632cf86f6SMauro Carvalho Chehab struct irctl { 3732cf86f6SMauro Carvalho Chehab struct lirc_driver d; 383bce5572SDavid Härdeman bool attached; 3932cf86f6SMauro Carvalho Chehab int open; 4032cf86f6SMauro Carvalho Chehab 413381b779SDavid Härdeman struct mutex mutex; /* protect from simultaneous accesses */ 4232cf86f6SMauro Carvalho Chehab struct lirc_buffer *buf; 430f7c4063SDavid Härdeman bool buf_internal; 4432cf86f6SMauro Carvalho Chehab 4574c839b2SSean Young struct device dev; 4674c839b2SSean Young struct cdev cdev; 4732cf86f6SMauro Carvalho Chehab }; 4832cf86f6SMauro Carvalho Chehab 493381b779SDavid Härdeman /* This mutex protects the irctls array */ 5032cf86f6SMauro Carvalho Chehab static DEFINE_MUTEX(lirc_dev_lock); 5132cf86f6SMauro Carvalho Chehab 5232cf86f6SMauro Carvalho Chehab static struct irctl *irctls[MAX_IRCTL_DEVICES]; 5332cf86f6SMauro Carvalho Chehab 5432cf86f6SMauro Carvalho Chehab /* Only used for sysfs but defined to void otherwise */ 5532cf86f6SMauro Carvalho Chehab static struct class *lirc_class; 5632cf86f6SMauro Carvalho Chehab 573381b779SDavid Härdeman static void lirc_free_buffer(struct irctl *ir) 5832cf86f6SMauro Carvalho Chehab { 59a607f51eSSean Young put_device(ir->dev.parent); 60a607f51eSSean Young 610f7c4063SDavid Härdeman if (ir->buf_internal) { 6232cf86f6SMauro Carvalho Chehab lirc_buffer_free(ir->buf); 6332cf86f6SMauro Carvalho Chehab kfree(ir->buf); 643381b779SDavid Härdeman ir->buf = NULL; 6532cf86f6SMauro Carvalho Chehab } 663381b779SDavid Härdeman } 673381b779SDavid Härdeman 683381b779SDavid Härdeman static void lirc_release(struct device *ld) 693381b779SDavid Härdeman { 703381b779SDavid Härdeman struct irctl *ir = container_of(ld, struct irctl, dev); 7174c839b2SSean Young 7274c839b2SSean Young mutex_lock(&lirc_dev_lock); 7374c839b2SSean Young irctls[ir->d.minor] = NULL; 7474c839b2SSean Young mutex_unlock(&lirc_dev_lock); 753381b779SDavid Härdeman lirc_free_buffer(ir); 7674c839b2SSean Young kfree(ir); 7732cf86f6SMauro Carvalho Chehab } 7832cf86f6SMauro Carvalho Chehab 796fa99e1aSAndi Shyti static int lirc_allocate_buffer(struct irctl *ir) 806fa99e1aSAndi Shyti { 8170143984SAndi Shyti int err = 0; 826fa99e1aSAndi Shyti struct lirc_driver *d = &ir->d; 836fa99e1aSAndi Shyti 846fa99e1aSAndi Shyti if (d->rbuf) { 856fa99e1aSAndi Shyti ir->buf = d->rbuf; 860f7c4063SDavid Härdeman ir->buf_internal = false; 876fa99e1aSAndi Shyti } else { 886fa99e1aSAndi Shyti ir->buf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL); 8970143984SAndi Shyti if (!ir->buf) { 9070143984SAndi Shyti err = -ENOMEM; 9170143984SAndi Shyti goto out; 9270143984SAndi Shyti } 936fa99e1aSAndi Shyti 94b145ef94SDavid Härdeman err = lirc_buffer_init(ir->buf, d->chunk_size, d->buffer_size); 956fa99e1aSAndi Shyti if (err) { 966fa99e1aSAndi Shyti kfree(ir->buf); 970f7c4063SDavid Härdeman ir->buf = NULL; 9870143984SAndi Shyti goto out; 996fa99e1aSAndi Shyti } 1000f7c4063SDavid Härdeman 1010f7c4063SDavid Härdeman ir->buf_internal = true; 10256481f00SDavid Härdeman d->rbuf = ir->buf; 1036fa99e1aSAndi Shyti } 1046fa99e1aSAndi Shyti 10570143984SAndi Shyti out: 10670143984SAndi Shyti return err; 1076fa99e1aSAndi Shyti } 1086fa99e1aSAndi Shyti 10956481f00SDavid Härdeman int lirc_register_driver(struct lirc_driver *d) 11032cf86f6SMauro Carvalho Chehab { 11132cf86f6SMauro Carvalho Chehab struct irctl *ir; 112c3c6dd75SDavid Härdeman unsigned int minor; 11332cf86f6SMauro Carvalho Chehab int err; 11432cf86f6SMauro Carvalho Chehab 11532cf86f6SMauro Carvalho Chehab if (!d) { 1163fac0314SAndi Shyti pr_err("driver pointer must be not NULL!\n"); 11754fcecafSAndi Shyti return -EBADRQC; 11832cf86f6SMauro Carvalho Chehab } 11932cf86f6SMauro Carvalho Chehab 12032cf86f6SMauro Carvalho Chehab if (!d->dev) { 1213fac0314SAndi Shyti pr_err("dev pointer not filled in!\n"); 12254fcecafSAndi Shyti return -EINVAL; 12332cf86f6SMauro Carvalho Chehab } 12432cf86f6SMauro Carvalho Chehab 125712551f0SDavid Härdeman if (!d->fops) { 126712551f0SDavid Härdeman pr_err("fops pointer not filled in!\n"); 127712551f0SDavid Härdeman return -EINVAL; 128712551f0SDavid Härdeman } 129712551f0SDavid Härdeman 130b145ef94SDavid Härdeman if (!d->rbuf && d->chunk_size < 1) { 131b145ef94SDavid Härdeman pr_err("chunk_size must be set!\n"); 132b145ef94SDavid Härdeman return -EINVAL; 133b145ef94SDavid Härdeman } 134b145ef94SDavid Härdeman 135b145ef94SDavid Härdeman if (!d->rbuf && d->buffer_size < 1) { 136b145ef94SDavid Härdeman pr_err("buffer_size must be set!\n"); 137b145ef94SDavid Härdeman return -EINVAL; 138b145ef94SDavid Härdeman } 139b145ef94SDavid Härdeman 1409675ee5aSAndi Shyti if (d->code_length < 1 || d->code_length > (BUFLEN * 8)) { 1413fac0314SAndi Shyti dev_err(d->dev, "code length must be less than %d bits\n", 1423fac0314SAndi Shyti BUFLEN * 8); 14354fcecafSAndi Shyti return -EBADRQC; 14432cf86f6SMauro Carvalho Chehab } 14532cf86f6SMauro Carvalho Chehab 146c3104e1bSDavid Härdeman if (!d->rbuf && !(d->fops && d->fops->read && 14714db9fc2SAndi Shyti d->fops->poll && d->fops->unlocked_ioctl)) { 1483fac0314SAndi Shyti dev_err(d->dev, "undefined read, poll, ioctl\n"); 14954fcecafSAndi Shyti return -EBADRQC; 15032cf86f6SMauro Carvalho Chehab } 15132cf86f6SMauro Carvalho Chehab 1523381b779SDavid Härdeman /* some safety check 8-) */ 1533381b779SDavid Härdeman d->name[sizeof(d->name) - 1] = '\0'; 1543381b779SDavid Härdeman 1553381b779SDavid Härdeman if (d->features == 0) 1563381b779SDavid Härdeman d->features = LIRC_CAN_REC_LIRCCODE; 1573381b779SDavid Härdeman 1583381b779SDavid Härdeman ir = kzalloc(sizeof(*ir), GFP_KERNEL); 1593381b779SDavid Härdeman if (!ir) 1603381b779SDavid Härdeman return -ENOMEM; 1613381b779SDavid Härdeman 1623381b779SDavid Härdeman mutex_init(&ir->mutex); 1633381b779SDavid Härdeman ir->d = *d; 1643381b779SDavid Härdeman 1653381b779SDavid Härdeman if (LIRC_CAN_REC(d->features)) { 1663381b779SDavid Härdeman err = lirc_allocate_buffer(ir); 1673381b779SDavid Härdeman if (err) { 1683381b779SDavid Härdeman kfree(ir); 1693381b779SDavid Härdeman return err; 1703381b779SDavid Härdeman } 1713381b779SDavid Härdeman d->rbuf = ir->buf; 1723381b779SDavid Härdeman } 1733381b779SDavid Härdeman 17432cf86f6SMauro Carvalho Chehab mutex_lock(&lirc_dev_lock); 17532cf86f6SMauro Carvalho Chehab 17632cf86f6SMauro Carvalho Chehab /* find first free slot for driver */ 17732cf86f6SMauro Carvalho Chehab for (minor = 0; minor < MAX_IRCTL_DEVICES; minor++) 17832cf86f6SMauro Carvalho Chehab if (!irctls[minor]) 17932cf86f6SMauro Carvalho Chehab break; 180c3c6dd75SDavid Härdeman 1819675ee5aSAndi Shyti if (minor == MAX_IRCTL_DEVICES) { 1823fac0314SAndi Shyti dev_err(d->dev, "no free slots for drivers!\n"); 1833381b779SDavid Härdeman mutex_unlock(&lirc_dev_lock); 1843381b779SDavid Härdeman lirc_free_buffer(ir); 1853381b779SDavid Härdeman kfree(ir); 1863381b779SDavid Härdeman return -ENOMEM; 18732cf86f6SMauro Carvalho Chehab } 18832cf86f6SMauro Carvalho Chehab 18932cf86f6SMauro Carvalho Chehab irctls[minor] = ir; 190c3c6dd75SDavid Härdeman d->irctl = ir; 19132cf86f6SMauro Carvalho Chehab d->minor = minor; 1923381b779SDavid Härdeman ir->d.minor = minor; 19332cf86f6SMauro Carvalho Chehab 1943381b779SDavid Härdeman mutex_unlock(&lirc_dev_lock); 19556481f00SDavid Härdeman 19625823226SDavid Härdeman device_initialize(&ir->dev); 19774c839b2SSean Young ir->dev.devt = MKDEV(MAJOR(lirc_base_dev), ir->d.minor); 19874c839b2SSean Young ir->dev.class = lirc_class; 19974c839b2SSean Young ir->dev.parent = d->dev; 20074c839b2SSean Young ir->dev.release = lirc_release; 20174c839b2SSean Young dev_set_name(&ir->dev, "lirc%d", ir->d.minor); 20232cf86f6SMauro Carvalho Chehab 203712551f0SDavid Härdeman cdev_init(&ir->cdev, d->fops); 204712551f0SDavid Härdeman ir->cdev.owner = ir->d.owner; 2053bce5572SDavid Härdeman ir->attached = true; 20674c839b2SSean Young 2070510d810SDavid Härdeman err = cdev_device_add(&ir->cdev, &ir->dev); 2083381b779SDavid Härdeman if (err) { 2093381b779SDavid Härdeman put_device(&ir->dev); 2103381b779SDavid Härdeman return err; 2113381b779SDavid Härdeman } 21232cf86f6SMauro Carvalho Chehab 213a607f51eSSean Young get_device(ir->dev.parent); 214a607f51eSSean Young 21532cf86f6SMauro Carvalho Chehab dev_info(ir->d.dev, "lirc_dev: driver %s registered at minor = %d\n", 21632cf86f6SMauro Carvalho Chehab ir->d.name, ir->d.minor); 21756481f00SDavid Härdeman 218c3c6dd75SDavid Härdeman return 0; 21932cf86f6SMauro Carvalho Chehab } 22032cf86f6SMauro Carvalho Chehab EXPORT_SYMBOL(lirc_register_driver); 22132cf86f6SMauro Carvalho Chehab 222c3c6dd75SDavid Härdeman void lirc_unregister_driver(struct lirc_driver *d) 22332cf86f6SMauro Carvalho Chehab { 22432cf86f6SMauro Carvalho Chehab struct irctl *ir; 22532cf86f6SMauro Carvalho Chehab 226c3c6dd75SDavid Härdeman if (!d || !d->irctl) 227c3c6dd75SDavid Härdeman return; 22832cf86f6SMauro Carvalho Chehab 229c3c6dd75SDavid Härdeman ir = d->irctl; 23032cf86f6SMauro Carvalho Chehab 23132cf86f6SMauro Carvalho Chehab dev_dbg(ir->d.dev, "lirc_dev: driver %s unregistered from minor = %d\n", 232c3c6dd75SDavid Härdeman d->name, d->minor); 23332cf86f6SMauro Carvalho Chehab 2343381b779SDavid Härdeman cdev_device_del(&ir->cdev, &ir->dev); 2353381b779SDavid Härdeman 2363381b779SDavid Härdeman mutex_lock(&ir->mutex); 2373381b779SDavid Härdeman 2383bce5572SDavid Härdeman ir->attached = false; 23932cf86f6SMauro Carvalho Chehab if (ir->open) { 24032cf86f6SMauro Carvalho Chehab dev_dbg(ir->d.dev, LOGHEAD "releasing opened driver\n", 241c3c6dd75SDavid Härdeman d->name, d->minor); 24232cf86f6SMauro Carvalho Chehab wake_up_interruptible(&ir->buf->wait_poll); 24374c839b2SSean Young } 24474c839b2SSean Young 2453381b779SDavid Härdeman mutex_unlock(&ir->mutex); 24632cf86f6SMauro Carvalho Chehab 24774c839b2SSean Young put_device(&ir->dev); 24832cf86f6SMauro Carvalho Chehab } 24932cf86f6SMauro Carvalho Chehab EXPORT_SYMBOL(lirc_unregister_driver); 25032cf86f6SMauro Carvalho Chehab 25132cf86f6SMauro Carvalho Chehab int lirc_dev_fop_open(struct inode *inode, struct file *file) 25232cf86f6SMauro Carvalho Chehab { 253615cd3feSDavid Härdeman struct irctl *ir = container_of(inode->i_cdev, struct irctl, cdev); 254de226ec8SDavid Härdeman int retval; 25532cf86f6SMauro Carvalho Chehab 25632cf86f6SMauro Carvalho Chehab dev_dbg(ir->d.dev, LOGHEAD "open called\n", ir->d.name, ir->d.minor); 25732cf86f6SMauro Carvalho Chehab 2583381b779SDavid Härdeman retval = mutex_lock_interruptible(&ir->mutex); 2593381b779SDavid Härdeman if (retval) 2603381b779SDavid Härdeman return retval; 2613381b779SDavid Härdeman 2623381b779SDavid Härdeman if (!ir->attached) { 2633381b779SDavid Härdeman retval = -ENODEV; 2643381b779SDavid Härdeman goto out; 2653381b779SDavid Härdeman } 2663381b779SDavid Härdeman 2673381b779SDavid Härdeman if (ir->open) { 2683381b779SDavid Härdeman retval = -EBUSY; 2693381b779SDavid Härdeman goto out; 2703381b779SDavid Härdeman } 27132cf86f6SMauro Carvalho Chehab 272ca7a722dSSrinivas Kandagatla if (ir->d.rdev) { 273ca7a722dSSrinivas Kandagatla retval = rc_open(ir->d.rdev); 274ca7a722dSSrinivas Kandagatla if (retval) 2753381b779SDavid Härdeman goto out; 276ca7a722dSSrinivas Kandagatla } 277ca7a722dSSrinivas Kandagatla 27874c839b2SSean Young if (ir->buf) 27932cf86f6SMauro Carvalho Chehab lirc_buffer_clear(ir->buf); 2802c5a1f44SDavid Härdeman 2812c5a1f44SDavid Härdeman ir->open++; 28232cf86f6SMauro Carvalho Chehab 283615cd3feSDavid Härdeman lirc_init_pdata(inode, file); 28432cf86f6SMauro Carvalho Chehab nonseekable_open(inode, file); 2853381b779SDavid Härdeman mutex_unlock(&ir->mutex); 28632cf86f6SMauro Carvalho Chehab 287de226ec8SDavid Härdeman return 0; 2883381b779SDavid Härdeman 2893381b779SDavid Härdeman out: 2903381b779SDavid Härdeman mutex_unlock(&ir->mutex); 2913381b779SDavid Härdeman return retval; 29232cf86f6SMauro Carvalho Chehab } 29332cf86f6SMauro Carvalho Chehab EXPORT_SYMBOL(lirc_dev_fop_open); 29432cf86f6SMauro Carvalho Chehab 29532cf86f6SMauro Carvalho Chehab int lirc_dev_fop_close(struct inode *inode, struct file *file) 29632cf86f6SMauro Carvalho Chehab { 297615cd3feSDavid Härdeman struct irctl *ir = file->private_data; 29832cf86f6SMauro Carvalho Chehab 2993381b779SDavid Härdeman mutex_lock(&ir->mutex); 30032cf86f6SMauro Carvalho Chehab 301ca7a722dSSrinivas Kandagatla rc_close(ir->d.rdev); 30232cf86f6SMauro Carvalho Chehab ir->open--; 3033381b779SDavid Härdeman 3043381b779SDavid Härdeman mutex_unlock(&ir->mutex); 30532cf86f6SMauro Carvalho Chehab 30632cf86f6SMauro Carvalho Chehab return 0; 30732cf86f6SMauro Carvalho Chehab } 30832cf86f6SMauro Carvalho Chehab EXPORT_SYMBOL(lirc_dev_fop_close); 30932cf86f6SMauro Carvalho Chehab 31032cf86f6SMauro Carvalho Chehab unsigned int lirc_dev_fop_poll(struct file *file, poll_table *wait) 31132cf86f6SMauro Carvalho Chehab { 312615cd3feSDavid Härdeman struct irctl *ir = file->private_data; 31332cf86f6SMauro Carvalho Chehab unsigned int ret; 31432cf86f6SMauro Carvalho Chehab 31532cf86f6SMauro Carvalho Chehab if (!ir->attached) 31629debf3dSDavid Härdeman return POLLHUP | POLLERR; 31732cf86f6SMauro Carvalho Chehab 3183656cdddSAndy Shevchenko if (ir->buf) { 31932cf86f6SMauro Carvalho Chehab poll_wait(file, &ir->buf->wait_poll, wait); 32032cf86f6SMauro Carvalho Chehab 32132cf86f6SMauro Carvalho Chehab if (lirc_buffer_empty(ir->buf)) 32232cf86f6SMauro Carvalho Chehab ret = 0; 32332cf86f6SMauro Carvalho Chehab else 32432cf86f6SMauro Carvalho Chehab ret = POLLIN | POLLRDNORM; 3253656cdddSAndy Shevchenko } else 32632cf86f6SMauro Carvalho Chehab ret = POLLERR; 32732cf86f6SMauro Carvalho Chehab 32832cf86f6SMauro Carvalho Chehab dev_dbg(ir->d.dev, LOGHEAD "poll result = %d\n", 32932cf86f6SMauro Carvalho Chehab ir->d.name, ir->d.minor, ret); 33032cf86f6SMauro Carvalho Chehab 33132cf86f6SMauro Carvalho Chehab return ret; 33232cf86f6SMauro Carvalho Chehab } 33332cf86f6SMauro Carvalho Chehab EXPORT_SYMBOL(lirc_dev_fop_poll); 33432cf86f6SMauro Carvalho Chehab 33532cf86f6SMauro Carvalho Chehab long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 33632cf86f6SMauro Carvalho Chehab { 337615cd3feSDavid Härdeman struct irctl *ir = file->private_data; 33832cf86f6SMauro Carvalho Chehab __u32 mode; 3393381b779SDavid Härdeman int result; 34032cf86f6SMauro Carvalho Chehab 34132cf86f6SMauro Carvalho Chehab dev_dbg(ir->d.dev, LOGHEAD "ioctl called (0x%x)\n", 34232cf86f6SMauro Carvalho Chehab ir->d.name, ir->d.minor, cmd); 34332cf86f6SMauro Carvalho Chehab 3443381b779SDavid Härdeman result = mutex_lock_interruptible(&ir->mutex); 3453381b779SDavid Härdeman if (result) 3463381b779SDavid Härdeman return result; 34732cf86f6SMauro Carvalho Chehab 3483381b779SDavid Härdeman if (!ir->attached) { 3493381b779SDavid Härdeman result = -ENODEV; 3503381b779SDavid Härdeman goto out; 3513381b779SDavid Härdeman } 35232cf86f6SMauro Carvalho Chehab 35332cf86f6SMauro Carvalho Chehab switch (cmd) { 35432cf86f6SMauro Carvalho Chehab case LIRC_GET_FEATURES: 35560519af3SHans Verkuil result = put_user(ir->d.features, (__u32 __user *)arg); 35632cf86f6SMauro Carvalho Chehab break; 35732cf86f6SMauro Carvalho Chehab case LIRC_GET_REC_MODE: 358bd291208SSean Young if (!LIRC_CAN_REC(ir->d.features)) { 359b4088094SAndi Shyti result = -ENOTTY; 36032cf86f6SMauro Carvalho Chehab break; 36132cf86f6SMauro Carvalho Chehab } 36232cf86f6SMauro Carvalho Chehab 36332cf86f6SMauro Carvalho Chehab result = put_user(LIRC_REC2MODE 36432cf86f6SMauro Carvalho Chehab (ir->d.features & LIRC_CAN_REC_MASK), 36560519af3SHans Verkuil (__u32 __user *)arg); 36632cf86f6SMauro Carvalho Chehab break; 36732cf86f6SMauro Carvalho Chehab case LIRC_SET_REC_MODE: 368bd291208SSean Young if (!LIRC_CAN_REC(ir->d.features)) { 369b4088094SAndi Shyti result = -ENOTTY; 37032cf86f6SMauro Carvalho Chehab break; 37132cf86f6SMauro Carvalho Chehab } 37232cf86f6SMauro Carvalho Chehab 37360519af3SHans Verkuil result = get_user(mode, (__u32 __user *)arg); 37432cf86f6SMauro Carvalho Chehab if (!result && !(LIRC_MODE2REC(mode) & ir->d.features)) 37532cf86f6SMauro Carvalho Chehab result = -EINVAL; 37632cf86f6SMauro Carvalho Chehab /* 37732cf86f6SMauro Carvalho Chehab * FIXME: We should actually set the mode somehow but 37832cf86f6SMauro Carvalho Chehab * for now, lirc_serial doesn't support mode changing either 37932cf86f6SMauro Carvalho Chehab */ 38032cf86f6SMauro Carvalho Chehab break; 38132cf86f6SMauro Carvalho Chehab case LIRC_GET_LENGTH: 38260519af3SHans Verkuil result = put_user(ir->d.code_length, (__u32 __user *)arg); 38332cf86f6SMauro Carvalho Chehab break; 38432cf86f6SMauro Carvalho Chehab case LIRC_GET_MIN_TIMEOUT: 38532cf86f6SMauro Carvalho Chehab if (!(ir->d.features & LIRC_CAN_SET_REC_TIMEOUT) || 38632cf86f6SMauro Carvalho Chehab ir->d.min_timeout == 0) { 387b4088094SAndi Shyti result = -ENOTTY; 38832cf86f6SMauro Carvalho Chehab break; 38932cf86f6SMauro Carvalho Chehab } 39032cf86f6SMauro Carvalho Chehab 39160519af3SHans Verkuil result = put_user(ir->d.min_timeout, (__u32 __user *)arg); 39232cf86f6SMauro Carvalho Chehab break; 39332cf86f6SMauro Carvalho Chehab case LIRC_GET_MAX_TIMEOUT: 39432cf86f6SMauro Carvalho Chehab if (!(ir->d.features & LIRC_CAN_SET_REC_TIMEOUT) || 39532cf86f6SMauro Carvalho Chehab ir->d.max_timeout == 0) { 396b4088094SAndi Shyti result = -ENOTTY; 39732cf86f6SMauro Carvalho Chehab break; 39832cf86f6SMauro Carvalho Chehab } 39932cf86f6SMauro Carvalho Chehab 40060519af3SHans Verkuil result = put_user(ir->d.max_timeout, (__u32 __user *)arg); 40132cf86f6SMauro Carvalho Chehab break; 40232cf86f6SMauro Carvalho Chehab default: 4035c862758SSean Young result = -ENOTTY; 40432cf86f6SMauro Carvalho Chehab } 40532cf86f6SMauro Carvalho Chehab 4063381b779SDavid Härdeman out: 4073381b779SDavid Härdeman mutex_unlock(&ir->mutex); 40832cf86f6SMauro Carvalho Chehab return result; 40932cf86f6SMauro Carvalho Chehab } 41032cf86f6SMauro Carvalho Chehab EXPORT_SYMBOL(lirc_dev_fop_ioctl); 41132cf86f6SMauro Carvalho Chehab 41232cf86f6SMauro Carvalho Chehab ssize_t lirc_dev_fop_read(struct file *file, 4130e835087SDan Carpenter char __user *buffer, 41432cf86f6SMauro Carvalho Chehab size_t length, 41532cf86f6SMauro Carvalho Chehab loff_t *ppos) 41632cf86f6SMauro Carvalho Chehab { 417615cd3feSDavid Härdeman struct irctl *ir = file->private_data; 41832cf86f6SMauro Carvalho Chehab unsigned char *buf; 4193381b779SDavid Härdeman int ret, written = 0; 42032cf86f6SMauro Carvalho Chehab DECLARE_WAITQUEUE(wait, current); 42132cf86f6SMauro Carvalho Chehab 42232cf86f6SMauro Carvalho Chehab dev_dbg(ir->d.dev, LOGHEAD "read called\n", ir->d.name, ir->d.minor); 42332cf86f6SMauro Carvalho Chehab 424b145ef94SDavid Härdeman buf = kzalloc(ir->buf->chunk_size, GFP_KERNEL); 42532cf86f6SMauro Carvalho Chehab if (!buf) 42632cf86f6SMauro Carvalho Chehab return -ENOMEM; 42732cf86f6SMauro Carvalho Chehab 4283381b779SDavid Härdeman ret = mutex_lock_interruptible(&ir->mutex); 4293381b779SDavid Härdeman if (ret) { 4303381b779SDavid Härdeman kfree(buf); 4313381b779SDavid Härdeman return ret; 43232cf86f6SMauro Carvalho Chehab } 4333381b779SDavid Härdeman 43432cf86f6SMauro Carvalho Chehab if (!ir->attached) { 43532cf86f6SMauro Carvalho Chehab ret = -ENODEV; 43632cf86f6SMauro Carvalho Chehab goto out_locked; 43732cf86f6SMauro Carvalho Chehab } 43832cf86f6SMauro Carvalho Chehab 4393381b779SDavid Härdeman if (!LIRC_CAN_REC(ir->d.features)) { 4403381b779SDavid Härdeman ret = -EINVAL; 4413381b779SDavid Härdeman goto out_locked; 4423381b779SDavid Härdeman } 4433381b779SDavid Härdeman 444b145ef94SDavid Härdeman if (length % ir->buf->chunk_size) { 44532cf86f6SMauro Carvalho Chehab ret = -EINVAL; 44632cf86f6SMauro Carvalho Chehab goto out_locked; 44732cf86f6SMauro Carvalho Chehab } 44832cf86f6SMauro Carvalho Chehab 44932cf86f6SMauro Carvalho Chehab /* 45032cf86f6SMauro Carvalho Chehab * we add ourselves to the task queue before buffer check 45132cf86f6SMauro Carvalho Chehab * to avoid losing scan code (in case when queue is awaken somewhere 45232cf86f6SMauro Carvalho Chehab * between while condition checking and scheduling) 45332cf86f6SMauro Carvalho Chehab */ 45432cf86f6SMauro Carvalho Chehab add_wait_queue(&ir->buf->wait_poll, &wait); 45532cf86f6SMauro Carvalho Chehab 45632cf86f6SMauro Carvalho Chehab /* 45732cf86f6SMauro Carvalho Chehab * while we didn't provide 'length' bytes, device is opened in blocking 45832cf86f6SMauro Carvalho Chehab * mode and 'copy_to_user' is happy, wait for data. 45932cf86f6SMauro Carvalho Chehab */ 46032cf86f6SMauro Carvalho Chehab while (written < length && ret == 0) { 46132cf86f6SMauro Carvalho Chehab if (lirc_buffer_empty(ir->buf)) { 46232cf86f6SMauro Carvalho Chehab /* According to the read(2) man page, 'written' can be 46332cf86f6SMauro Carvalho Chehab * returned as less than 'length', instead of blocking 46432cf86f6SMauro Carvalho Chehab * again, returning -EWOULDBLOCK, or returning 46562e92682SAndi Shyti * -ERESTARTSYS 46662e92682SAndi Shyti */ 46732cf86f6SMauro Carvalho Chehab if (written) 46832cf86f6SMauro Carvalho Chehab break; 46932cf86f6SMauro Carvalho Chehab if (file->f_flags & O_NONBLOCK) { 47032cf86f6SMauro Carvalho Chehab ret = -EWOULDBLOCK; 47132cf86f6SMauro Carvalho Chehab break; 47232cf86f6SMauro Carvalho Chehab } 47332cf86f6SMauro Carvalho Chehab if (signal_pending(current)) { 47432cf86f6SMauro Carvalho Chehab ret = -ERESTARTSYS; 47532cf86f6SMauro Carvalho Chehab break; 47632cf86f6SMauro Carvalho Chehab } 47732cf86f6SMauro Carvalho Chehab 4783381b779SDavid Härdeman mutex_unlock(&ir->mutex); 47932cf86f6SMauro Carvalho Chehab set_current_state(TASK_INTERRUPTIBLE); 48012accdcbSSean Young schedule(); 48112accdcbSSean Young set_current_state(TASK_RUNNING); 48232cf86f6SMauro Carvalho Chehab 4833381b779SDavid Härdeman ret = mutex_lock_interruptible(&ir->mutex); 4843381b779SDavid Härdeman if (ret) { 48532cf86f6SMauro Carvalho Chehab remove_wait_queue(&ir->buf->wait_poll, &wait); 48632cf86f6SMauro Carvalho Chehab goto out_unlocked; 48732cf86f6SMauro Carvalho Chehab } 48832cf86f6SMauro Carvalho Chehab 48932cf86f6SMauro Carvalho Chehab if (!ir->attached) { 49032cf86f6SMauro Carvalho Chehab ret = -ENODEV; 491c77d17c0SSean Young goto out_locked; 49232cf86f6SMauro Carvalho Chehab } 49332cf86f6SMauro Carvalho Chehab } else { 49432cf86f6SMauro Carvalho Chehab lirc_buffer_read(ir->buf, buf); 49560519af3SHans Verkuil ret = copy_to_user((void __user *)buffer+written, buf, 49632cf86f6SMauro Carvalho Chehab ir->buf->chunk_size); 49732cf86f6SMauro Carvalho Chehab if (!ret) 49832cf86f6SMauro Carvalho Chehab written += ir->buf->chunk_size; 49932cf86f6SMauro Carvalho Chehab else 50032cf86f6SMauro Carvalho Chehab ret = -EFAULT; 50132cf86f6SMauro Carvalho Chehab } 50232cf86f6SMauro Carvalho Chehab } 50332cf86f6SMauro Carvalho Chehab 50432cf86f6SMauro Carvalho Chehab remove_wait_queue(&ir->buf->wait_poll, &wait); 50532cf86f6SMauro Carvalho Chehab 50632cf86f6SMauro Carvalho Chehab out_locked: 5073381b779SDavid Härdeman mutex_unlock(&ir->mutex); 50832cf86f6SMauro Carvalho Chehab 50932cf86f6SMauro Carvalho Chehab out_unlocked: 51032cf86f6SMauro Carvalho Chehab kfree(buf); 51132cf86f6SMauro Carvalho Chehab 51232cf86f6SMauro Carvalho Chehab return ret ? ret : written; 51332cf86f6SMauro Carvalho Chehab } 51432cf86f6SMauro Carvalho Chehab EXPORT_SYMBOL(lirc_dev_fop_read); 51532cf86f6SMauro Carvalho Chehab 516615cd3feSDavid Härdeman void lirc_init_pdata(struct inode *inode, struct file *file) 517615cd3feSDavid Härdeman { 518615cd3feSDavid Härdeman struct irctl *ir = container_of(inode->i_cdev, struct irctl, cdev); 519615cd3feSDavid Härdeman 520615cd3feSDavid Härdeman file->private_data = ir; 521615cd3feSDavid Härdeman } 522615cd3feSDavid Härdeman EXPORT_SYMBOL(lirc_init_pdata); 523615cd3feSDavid Härdeman 52432cf86f6SMauro Carvalho Chehab void *lirc_get_pdata(struct file *file) 52532cf86f6SMauro Carvalho Chehab { 526615cd3feSDavid Härdeman struct irctl *ir = file->private_data; 527615cd3feSDavid Härdeman 528615cd3feSDavid Härdeman return ir->d.data; 52932cf86f6SMauro Carvalho Chehab } 53032cf86f6SMauro Carvalho Chehab EXPORT_SYMBOL(lirc_get_pdata); 53132cf86f6SMauro Carvalho Chehab 53232cf86f6SMauro Carvalho Chehab 53332cf86f6SMauro Carvalho Chehab static int __init lirc_dev_init(void) 53432cf86f6SMauro Carvalho Chehab { 53532cf86f6SMauro Carvalho Chehab int retval; 53632cf86f6SMauro Carvalho Chehab 53732cf86f6SMauro Carvalho Chehab lirc_class = class_create(THIS_MODULE, "lirc"); 53832cf86f6SMauro Carvalho Chehab if (IS_ERR(lirc_class)) { 5393fac0314SAndi Shyti pr_err("class_create failed\n"); 54054fcecafSAndi Shyti return PTR_ERR(lirc_class); 54132cf86f6SMauro Carvalho Chehab } 54232cf86f6SMauro Carvalho Chehab 54332cf86f6SMauro Carvalho Chehab retval = alloc_chrdev_region(&lirc_base_dev, 0, MAX_IRCTL_DEVICES, 544463015ddSDavid Härdeman "BaseRemoteCtl"); 54532cf86f6SMauro Carvalho Chehab if (retval) { 54632cf86f6SMauro Carvalho Chehab class_destroy(lirc_class); 5473fac0314SAndi Shyti pr_err("alloc_chrdev_region failed\n"); 54854fcecafSAndi Shyti return retval; 54932cf86f6SMauro Carvalho Chehab } 55032cf86f6SMauro Carvalho Chehab 5513fac0314SAndi Shyti pr_info("IR Remote Control driver registered, major %d\n", 5523fac0314SAndi Shyti MAJOR(lirc_base_dev)); 55332cf86f6SMauro Carvalho Chehab 55454fcecafSAndi Shyti return 0; 55532cf86f6SMauro Carvalho Chehab } 55632cf86f6SMauro Carvalho Chehab 55732cf86f6SMauro Carvalho Chehab static void __exit lirc_dev_exit(void) 55832cf86f6SMauro Carvalho Chehab { 55932cf86f6SMauro Carvalho Chehab class_destroy(lirc_class); 56032cf86f6SMauro Carvalho Chehab unregister_chrdev_region(lirc_base_dev, MAX_IRCTL_DEVICES); 5613fac0314SAndi Shyti pr_info("module unloaded\n"); 56232cf86f6SMauro Carvalho Chehab } 56332cf86f6SMauro Carvalho Chehab 56432cf86f6SMauro Carvalho Chehab module_init(lirc_dev_init); 56532cf86f6SMauro Carvalho Chehab module_exit(lirc_dev_exit); 56632cf86f6SMauro Carvalho Chehab 56732cf86f6SMauro Carvalho Chehab MODULE_DESCRIPTION("LIRC base driver module"); 56832cf86f6SMauro Carvalho Chehab MODULE_AUTHOR("Artur Lipowski"); 56932cf86f6SMauro Carvalho Chehab MODULE_LICENSE("GPL"); 570