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 NOPLUG -1 3332cf86f6SMauro Carvalho Chehab #define LOGHEAD "lirc_dev (%s[%d]): " 3432cf86f6SMauro Carvalho Chehab 3532cf86f6SMauro Carvalho Chehab static dev_t lirc_base_dev; 3632cf86f6SMauro Carvalho Chehab 3732cf86f6SMauro Carvalho Chehab struct irctl { 3832cf86f6SMauro Carvalho Chehab struct lirc_driver d; 3932cf86f6SMauro Carvalho Chehab int attached; 4032cf86f6SMauro Carvalho Chehab int open; 4132cf86f6SMauro Carvalho Chehab 4232cf86f6SMauro Carvalho Chehab struct mutex irctl_lock; 4332cf86f6SMauro Carvalho Chehab struct lirc_buffer *buf; 440f7c4063SDavid Härdeman bool buf_internal; 4532cf86f6SMauro Carvalho Chehab unsigned int chunk_size; 4632cf86f6SMauro Carvalho Chehab 4774c839b2SSean Young struct device dev; 4874c839b2SSean Young struct cdev cdev; 4932cf86f6SMauro Carvalho Chehab }; 5032cf86f6SMauro Carvalho Chehab 5132cf86f6SMauro Carvalho Chehab static DEFINE_MUTEX(lirc_dev_lock); 5232cf86f6SMauro Carvalho Chehab 5332cf86f6SMauro Carvalho Chehab static struct irctl *irctls[MAX_IRCTL_DEVICES]; 5432cf86f6SMauro Carvalho Chehab 5532cf86f6SMauro Carvalho Chehab /* Only used for sysfs but defined to void otherwise */ 5632cf86f6SMauro Carvalho Chehab static struct class *lirc_class; 5732cf86f6SMauro Carvalho Chehab 5874c839b2SSean Young static void lirc_release(struct device *ld) 5932cf86f6SMauro Carvalho Chehab { 6074c839b2SSean Young struct irctl *ir = container_of(ld, struct irctl, dev); 6174c839b2SSean Young 620f7c4063SDavid Härdeman if (ir->buf_internal) { 6332cf86f6SMauro Carvalho Chehab lirc_buffer_free(ir->buf); 6432cf86f6SMauro Carvalho Chehab kfree(ir->buf); 6532cf86f6SMauro Carvalho Chehab } 6674c839b2SSean Young 6774c839b2SSean Young mutex_lock(&lirc_dev_lock); 6874c839b2SSean Young irctls[ir->d.minor] = NULL; 6974c839b2SSean Young mutex_unlock(&lirc_dev_lock); 7074c839b2SSean Young kfree(ir); 7132cf86f6SMauro Carvalho Chehab } 7232cf86f6SMauro Carvalho Chehab 736fa99e1aSAndi Shyti static int lirc_allocate_buffer(struct irctl *ir) 746fa99e1aSAndi Shyti { 7570143984SAndi Shyti int err = 0; 766fa99e1aSAndi Shyti int bytes_in_key; 776fa99e1aSAndi Shyti unsigned int chunk_size; 786fa99e1aSAndi Shyti unsigned int buffer_size; 796fa99e1aSAndi Shyti struct lirc_driver *d = &ir->d; 806fa99e1aSAndi Shyti 816fa99e1aSAndi Shyti bytes_in_key = BITS_TO_LONGS(d->code_length) + 826fa99e1aSAndi Shyti (d->code_length % 8 ? 1 : 0); 836fa99e1aSAndi Shyti buffer_size = d->buffer_size ? d->buffer_size : BUFLEN / bytes_in_key; 846fa99e1aSAndi Shyti chunk_size = d->chunk_size ? d->chunk_size : bytes_in_key; 856fa99e1aSAndi Shyti 866fa99e1aSAndi Shyti if (d->rbuf) { 876fa99e1aSAndi Shyti ir->buf = d->rbuf; 880f7c4063SDavid Härdeman ir->buf_internal = false; 896fa99e1aSAndi Shyti } else { 906fa99e1aSAndi Shyti ir->buf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL); 9170143984SAndi Shyti if (!ir->buf) { 9270143984SAndi Shyti err = -ENOMEM; 9370143984SAndi Shyti goto out; 9470143984SAndi Shyti } 956fa99e1aSAndi Shyti 966fa99e1aSAndi Shyti err = lirc_buffer_init(ir->buf, chunk_size, buffer_size); 976fa99e1aSAndi Shyti if (err) { 986fa99e1aSAndi Shyti kfree(ir->buf); 990f7c4063SDavid Härdeman ir->buf = NULL; 10070143984SAndi Shyti goto out; 1016fa99e1aSAndi Shyti } 1020f7c4063SDavid Härdeman 1030f7c4063SDavid Härdeman ir->buf_internal = true; 10456481f00SDavid Härdeman d->rbuf = ir->buf; 1056fa99e1aSAndi Shyti } 1066fa99e1aSAndi Shyti ir->chunk_size = ir->buf->chunk_size; 1076fa99e1aSAndi Shyti 10870143984SAndi Shyti out: 10970143984SAndi Shyti return err; 1106fa99e1aSAndi Shyti } 1116fa99e1aSAndi Shyti 11256481f00SDavid Härdeman int lirc_register_driver(struct lirc_driver *d) 11332cf86f6SMauro Carvalho Chehab { 11432cf86f6SMauro Carvalho Chehab struct irctl *ir; 11532cf86f6SMauro Carvalho Chehab int minor; 11632cf86f6SMauro Carvalho Chehab int err; 11732cf86f6SMauro Carvalho Chehab 11832cf86f6SMauro Carvalho Chehab if (!d) { 1193fac0314SAndi Shyti pr_err("driver pointer must be not NULL!\n"); 12054fcecafSAndi Shyti return -EBADRQC; 12132cf86f6SMauro Carvalho Chehab } 12232cf86f6SMauro Carvalho Chehab 12332cf86f6SMauro Carvalho Chehab if (!d->dev) { 1243fac0314SAndi Shyti pr_err("dev pointer not filled in!\n"); 12554fcecafSAndi Shyti return -EINVAL; 12632cf86f6SMauro Carvalho Chehab } 12732cf86f6SMauro Carvalho Chehab 128712551f0SDavid Härdeman if (!d->fops) { 129712551f0SDavid Härdeman pr_err("fops pointer not filled in!\n"); 130712551f0SDavid Härdeman return -EINVAL; 131712551f0SDavid Härdeman } 132712551f0SDavid Härdeman 1339675ee5aSAndi Shyti if (d->minor >= MAX_IRCTL_DEVICES) { 1343fac0314SAndi Shyti dev_err(d->dev, "minor must be between 0 and %d!\n", 1353fac0314SAndi Shyti MAX_IRCTL_DEVICES - 1); 13654fcecafSAndi Shyti return -EBADRQC; 13732cf86f6SMauro Carvalho Chehab } 13832cf86f6SMauro Carvalho Chehab 1399675ee5aSAndi Shyti if (d->code_length < 1 || d->code_length > (BUFLEN * 8)) { 1403fac0314SAndi Shyti dev_err(d->dev, "code length must be less than %d bits\n", 1413fac0314SAndi Shyti BUFLEN * 8); 14254fcecafSAndi Shyti return -EBADRQC; 14332cf86f6SMauro Carvalho Chehab } 14432cf86f6SMauro Carvalho Chehab 145c3104e1bSDavid Härdeman if (!d->rbuf && !(d->fops && d->fops->read && 14614db9fc2SAndi Shyti d->fops->poll && d->fops->unlocked_ioctl)) { 1473fac0314SAndi Shyti dev_err(d->dev, "undefined read, poll, ioctl\n"); 14854fcecafSAndi Shyti return -EBADRQC; 14932cf86f6SMauro Carvalho Chehab } 15032cf86f6SMauro Carvalho Chehab 15132cf86f6SMauro Carvalho Chehab mutex_lock(&lirc_dev_lock); 15232cf86f6SMauro Carvalho Chehab 15332cf86f6SMauro Carvalho Chehab minor = d->minor; 15432cf86f6SMauro Carvalho Chehab 15532cf86f6SMauro Carvalho Chehab if (minor < 0) { 15632cf86f6SMauro Carvalho Chehab /* find first free slot for driver */ 15732cf86f6SMauro Carvalho Chehab for (minor = 0; minor < MAX_IRCTL_DEVICES; minor++) 15832cf86f6SMauro Carvalho Chehab if (!irctls[minor]) 15932cf86f6SMauro Carvalho Chehab break; 1609675ee5aSAndi Shyti if (minor == MAX_IRCTL_DEVICES) { 1613fac0314SAndi Shyti dev_err(d->dev, "no free slots for drivers!\n"); 16232cf86f6SMauro Carvalho Chehab err = -ENOMEM; 16332cf86f6SMauro Carvalho Chehab goto out_lock; 16432cf86f6SMauro Carvalho Chehab } 16532cf86f6SMauro Carvalho Chehab } else if (irctls[minor]) { 1663fac0314SAndi Shyti dev_err(d->dev, "minor (%d) just registered!\n", minor); 16732cf86f6SMauro Carvalho Chehab err = -EBUSY; 16832cf86f6SMauro Carvalho Chehab goto out_lock; 16932cf86f6SMauro Carvalho Chehab } 17032cf86f6SMauro Carvalho Chehab 17132cf86f6SMauro Carvalho Chehab ir = kzalloc(sizeof(struct irctl), GFP_KERNEL); 17232cf86f6SMauro Carvalho Chehab if (!ir) { 17332cf86f6SMauro Carvalho Chehab err = -ENOMEM; 17432cf86f6SMauro Carvalho Chehab goto out_lock; 17532cf86f6SMauro Carvalho Chehab } 176712551f0SDavid Härdeman 177712551f0SDavid Härdeman mutex_init(&ir->irctl_lock); 17832cf86f6SMauro Carvalho Chehab irctls[minor] = ir; 17932cf86f6SMauro Carvalho Chehab d->minor = minor; 18032cf86f6SMauro Carvalho Chehab 18132cf86f6SMauro Carvalho Chehab /* some safety check 8-) */ 18232cf86f6SMauro Carvalho Chehab d->name[sizeof(d->name)-1] = '\0'; 18332cf86f6SMauro Carvalho Chehab 18432cf86f6SMauro Carvalho Chehab if (d->features == 0) 18532cf86f6SMauro Carvalho Chehab d->features = LIRC_CAN_REC_LIRCCODE; 18632cf86f6SMauro Carvalho Chehab 18732cf86f6SMauro Carvalho Chehab ir->d = *d; 18832cf86f6SMauro Carvalho Chehab 18956481f00SDavid Härdeman if (LIRC_CAN_REC(d->features)) { 19056481f00SDavid Härdeman err = lirc_allocate_buffer(irctls[minor]); 19156481f00SDavid Härdeman if (err) { 19256481f00SDavid Härdeman kfree(ir); 19356481f00SDavid Härdeman goto out_lock; 19456481f00SDavid Härdeman } 19556481f00SDavid Härdeman d->rbuf = ir->buf; 19656481f00SDavid Härdeman } 19756481f00SDavid Härdeman 19825823226SDavid Härdeman device_initialize(&ir->dev); 19974c839b2SSean Young ir->dev.devt = MKDEV(MAJOR(lirc_base_dev), ir->d.minor); 20074c839b2SSean Young ir->dev.class = lirc_class; 20174c839b2SSean Young ir->dev.parent = d->dev; 20274c839b2SSean Young ir->dev.release = lirc_release; 20374c839b2SSean Young dev_set_name(&ir->dev, "lirc%d", ir->d.minor); 20432cf86f6SMauro Carvalho Chehab 205712551f0SDavid Härdeman cdev_init(&ir->cdev, d->fops); 206712551f0SDavid Härdeman ir->cdev.owner = ir->d.owner; 207712551f0SDavid Härdeman ir->cdev.kobj.parent = &ir->dev.kobj; 208712551f0SDavid Härdeman 209712551f0SDavid Härdeman err = cdev_add(&ir->cdev, ir->dev.devt, 1); 21032cf86f6SMauro Carvalho Chehab if (err) 21125823226SDavid Härdeman goto out_free_dev; 21232cf86f6SMauro Carvalho Chehab 21332cf86f6SMauro Carvalho Chehab ir->attached = 1; 21474c839b2SSean Young 21574c839b2SSean Young err = device_add(&ir->dev); 21674c839b2SSean Young if (err) 21774c839b2SSean Young goto out_cdev; 21874c839b2SSean Young 21932cf86f6SMauro Carvalho Chehab mutex_unlock(&lirc_dev_lock); 22032cf86f6SMauro Carvalho Chehab 22132cf86f6SMauro Carvalho Chehab dev_info(ir->d.dev, "lirc_dev: driver %s registered at minor = %d\n", 22232cf86f6SMauro Carvalho Chehab ir->d.name, ir->d.minor); 22356481f00SDavid Härdeman 22432cf86f6SMauro Carvalho Chehab return minor; 22556481f00SDavid Härdeman 22674c839b2SSean Young out_cdev: 22774c839b2SSean Young cdev_del(&ir->cdev); 22825823226SDavid Härdeman out_free_dev: 22974c839b2SSean Young put_device(&ir->dev); 23032cf86f6SMauro Carvalho Chehab out_lock: 23132cf86f6SMauro Carvalho Chehab mutex_unlock(&lirc_dev_lock); 23254fcecafSAndi Shyti 23332cf86f6SMauro Carvalho Chehab return err; 23432cf86f6SMauro Carvalho Chehab } 23532cf86f6SMauro Carvalho Chehab EXPORT_SYMBOL(lirc_register_driver); 23632cf86f6SMauro Carvalho Chehab 23732cf86f6SMauro Carvalho Chehab int lirc_unregister_driver(int minor) 23832cf86f6SMauro Carvalho Chehab { 23932cf86f6SMauro Carvalho Chehab struct irctl *ir; 24032cf86f6SMauro Carvalho Chehab 24132cf86f6SMauro Carvalho Chehab if (minor < 0 || minor >= MAX_IRCTL_DEVICES) { 2423fac0314SAndi Shyti pr_err("minor (%d) must be between 0 and %d!\n", 2433fac0314SAndi Shyti minor, MAX_IRCTL_DEVICES - 1); 24432cf86f6SMauro Carvalho Chehab return -EBADRQC; 24532cf86f6SMauro Carvalho Chehab } 24632cf86f6SMauro Carvalho Chehab 24732cf86f6SMauro Carvalho Chehab ir = irctls[minor]; 24832cf86f6SMauro Carvalho Chehab if (!ir) { 2493fac0314SAndi Shyti pr_err("failed to get irctl\n"); 25032cf86f6SMauro Carvalho Chehab return -ENOENT; 25132cf86f6SMauro Carvalho Chehab } 25232cf86f6SMauro Carvalho Chehab 25332cf86f6SMauro Carvalho Chehab mutex_lock(&lirc_dev_lock); 25432cf86f6SMauro Carvalho Chehab 25532cf86f6SMauro Carvalho Chehab if (ir->d.minor != minor) { 2563fac0314SAndi Shyti dev_err(ir->d.dev, "lirc_dev: minor %d device not registered\n", 2573fac0314SAndi Shyti minor); 25832cf86f6SMauro Carvalho Chehab mutex_unlock(&lirc_dev_lock); 25932cf86f6SMauro Carvalho Chehab return -ENOENT; 26032cf86f6SMauro Carvalho Chehab } 26132cf86f6SMauro Carvalho Chehab 26232cf86f6SMauro Carvalho Chehab dev_dbg(ir->d.dev, "lirc_dev: driver %s unregistered from minor = %d\n", 26332cf86f6SMauro Carvalho Chehab ir->d.name, ir->d.minor); 26432cf86f6SMauro Carvalho Chehab 26532cf86f6SMauro Carvalho Chehab ir->attached = 0; 26632cf86f6SMauro Carvalho Chehab if (ir->open) { 26732cf86f6SMauro Carvalho Chehab dev_dbg(ir->d.dev, LOGHEAD "releasing opened driver\n", 26832cf86f6SMauro Carvalho Chehab ir->d.name, ir->d.minor); 26932cf86f6SMauro Carvalho Chehab wake_up_interruptible(&ir->buf->wait_poll); 27074c839b2SSean Young } 27174c839b2SSean Young 27232cf86f6SMauro Carvalho Chehab mutex_unlock(&lirc_dev_lock); 27332cf86f6SMauro Carvalho Chehab 27474c839b2SSean Young device_del(&ir->dev); 27574c839b2SSean Young cdev_del(&ir->cdev); 27674c839b2SSean Young put_device(&ir->dev); 27774c839b2SSean Young 27832cf86f6SMauro Carvalho Chehab return 0; 27932cf86f6SMauro Carvalho Chehab } 28032cf86f6SMauro Carvalho Chehab EXPORT_SYMBOL(lirc_unregister_driver); 28132cf86f6SMauro Carvalho Chehab 28232cf86f6SMauro Carvalho Chehab int lirc_dev_fop_open(struct inode *inode, struct file *file) 28332cf86f6SMauro Carvalho Chehab { 28432cf86f6SMauro Carvalho Chehab struct irctl *ir; 28532cf86f6SMauro Carvalho Chehab int retval = 0; 28632cf86f6SMauro Carvalho Chehab 28732cf86f6SMauro Carvalho Chehab if (iminor(inode) >= MAX_IRCTL_DEVICES) { 2883fac0314SAndi Shyti pr_err("open result for %d is -ENODEV\n", iminor(inode)); 28932cf86f6SMauro Carvalho Chehab return -ENODEV; 29032cf86f6SMauro Carvalho Chehab } 29132cf86f6SMauro Carvalho Chehab 29232cf86f6SMauro Carvalho Chehab if (mutex_lock_interruptible(&lirc_dev_lock)) 29332cf86f6SMauro Carvalho Chehab return -ERESTARTSYS; 29432cf86f6SMauro Carvalho Chehab 29532cf86f6SMauro Carvalho Chehab ir = irctls[iminor(inode)]; 296db5b15b7SSean Young mutex_unlock(&lirc_dev_lock); 297db5b15b7SSean Young 29832cf86f6SMauro Carvalho Chehab if (!ir) { 29932cf86f6SMauro Carvalho Chehab retval = -ENODEV; 30032cf86f6SMauro Carvalho Chehab goto error; 30132cf86f6SMauro Carvalho Chehab } 30232cf86f6SMauro Carvalho Chehab 30332cf86f6SMauro Carvalho Chehab dev_dbg(ir->d.dev, LOGHEAD "open called\n", ir->d.name, ir->d.minor); 30432cf86f6SMauro Carvalho Chehab 30532cf86f6SMauro Carvalho Chehab if (ir->d.minor == NOPLUG) { 30632cf86f6SMauro Carvalho Chehab retval = -ENODEV; 30732cf86f6SMauro Carvalho Chehab goto error; 30832cf86f6SMauro Carvalho Chehab } 30932cf86f6SMauro Carvalho Chehab 31032cf86f6SMauro Carvalho Chehab if (ir->open) { 31132cf86f6SMauro Carvalho Chehab retval = -EBUSY; 31232cf86f6SMauro Carvalho Chehab goto error; 31332cf86f6SMauro Carvalho Chehab } 31432cf86f6SMauro Carvalho Chehab 315ca7a722dSSrinivas Kandagatla if (ir->d.rdev) { 316ca7a722dSSrinivas Kandagatla retval = rc_open(ir->d.rdev); 317ca7a722dSSrinivas Kandagatla if (retval) 318ca7a722dSSrinivas Kandagatla goto error; 319ca7a722dSSrinivas Kandagatla } 320ca7a722dSSrinivas Kandagatla 32174c839b2SSean Young if (ir->buf) 32232cf86f6SMauro Carvalho Chehab lirc_buffer_clear(ir->buf); 3232c5a1f44SDavid Härdeman 3242c5a1f44SDavid Härdeman ir->open++; 32532cf86f6SMauro Carvalho Chehab 32632cf86f6SMauro Carvalho Chehab error: 32732cf86f6SMauro Carvalho Chehab nonseekable_open(inode, file); 32832cf86f6SMauro Carvalho Chehab 32932cf86f6SMauro Carvalho Chehab return retval; 33032cf86f6SMauro Carvalho Chehab } 33132cf86f6SMauro Carvalho Chehab EXPORT_SYMBOL(lirc_dev_fop_open); 33232cf86f6SMauro Carvalho Chehab 33332cf86f6SMauro Carvalho Chehab int lirc_dev_fop_close(struct inode *inode, struct file *file) 33432cf86f6SMauro Carvalho Chehab { 33532cf86f6SMauro Carvalho Chehab struct irctl *ir = irctls[iminor(inode)]; 336b64e10f3SMauro Carvalho Chehab int ret; 33732cf86f6SMauro Carvalho Chehab 33832cf86f6SMauro Carvalho Chehab if (!ir) { 3393fac0314SAndi Shyti pr_err("called with invalid irctl\n"); 34032cf86f6SMauro Carvalho Chehab return -EINVAL; 34132cf86f6SMauro Carvalho Chehab } 34232cf86f6SMauro Carvalho Chehab 343b64e10f3SMauro Carvalho Chehab ret = mutex_lock_killable(&lirc_dev_lock); 344b64e10f3SMauro Carvalho Chehab WARN_ON(ret); 34532cf86f6SMauro Carvalho Chehab 346ca7a722dSSrinivas Kandagatla rc_close(ir->d.rdev); 347ca7a722dSSrinivas Kandagatla 34832cf86f6SMauro Carvalho Chehab ir->open--; 349b64e10f3SMauro Carvalho Chehab if (!ret) 35032cf86f6SMauro Carvalho Chehab mutex_unlock(&lirc_dev_lock); 35132cf86f6SMauro Carvalho Chehab 35232cf86f6SMauro Carvalho Chehab return 0; 35332cf86f6SMauro Carvalho Chehab } 35432cf86f6SMauro Carvalho Chehab EXPORT_SYMBOL(lirc_dev_fop_close); 35532cf86f6SMauro Carvalho Chehab 35632cf86f6SMauro Carvalho Chehab unsigned int lirc_dev_fop_poll(struct file *file, poll_table *wait) 35732cf86f6SMauro Carvalho Chehab { 358496ad9aaSAl Viro struct irctl *ir = irctls[iminor(file_inode(file))]; 35932cf86f6SMauro Carvalho Chehab unsigned int ret; 36032cf86f6SMauro Carvalho Chehab 36132cf86f6SMauro Carvalho Chehab if (!ir) { 3623fac0314SAndi Shyti pr_err("called with invalid irctl\n"); 36332cf86f6SMauro Carvalho Chehab return POLLERR; 36432cf86f6SMauro Carvalho Chehab } 36532cf86f6SMauro Carvalho Chehab 36632cf86f6SMauro Carvalho Chehab if (!ir->attached) 36729debf3dSDavid Härdeman return POLLHUP | POLLERR; 36832cf86f6SMauro Carvalho Chehab 3693656cdddSAndy Shevchenko if (ir->buf) { 37032cf86f6SMauro Carvalho Chehab poll_wait(file, &ir->buf->wait_poll, wait); 37132cf86f6SMauro Carvalho Chehab 37232cf86f6SMauro Carvalho Chehab if (lirc_buffer_empty(ir->buf)) 37332cf86f6SMauro Carvalho Chehab ret = 0; 37432cf86f6SMauro Carvalho Chehab else 37532cf86f6SMauro Carvalho Chehab ret = POLLIN | POLLRDNORM; 3763656cdddSAndy Shevchenko } else 37732cf86f6SMauro Carvalho Chehab ret = POLLERR; 37832cf86f6SMauro Carvalho Chehab 37932cf86f6SMauro Carvalho Chehab dev_dbg(ir->d.dev, LOGHEAD "poll result = %d\n", 38032cf86f6SMauro Carvalho Chehab ir->d.name, ir->d.minor, ret); 38132cf86f6SMauro Carvalho Chehab 38232cf86f6SMauro Carvalho Chehab return ret; 38332cf86f6SMauro Carvalho Chehab } 38432cf86f6SMauro Carvalho Chehab EXPORT_SYMBOL(lirc_dev_fop_poll); 38532cf86f6SMauro Carvalho Chehab 38632cf86f6SMauro Carvalho Chehab long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 38732cf86f6SMauro Carvalho Chehab { 38832cf86f6SMauro Carvalho Chehab __u32 mode; 38932cf86f6SMauro Carvalho Chehab int result = 0; 390496ad9aaSAl Viro struct irctl *ir = irctls[iminor(file_inode(file))]; 39132cf86f6SMauro Carvalho Chehab 39232cf86f6SMauro Carvalho Chehab if (!ir) { 3933fac0314SAndi Shyti pr_err("no irctl found!\n"); 39432cf86f6SMauro Carvalho Chehab return -ENODEV; 39532cf86f6SMauro Carvalho Chehab } 39632cf86f6SMauro Carvalho Chehab 39732cf86f6SMauro Carvalho Chehab dev_dbg(ir->d.dev, LOGHEAD "ioctl called (0x%x)\n", 39832cf86f6SMauro Carvalho Chehab ir->d.name, ir->d.minor, cmd); 39932cf86f6SMauro Carvalho Chehab 40032cf86f6SMauro Carvalho Chehab if (ir->d.minor == NOPLUG || !ir->attached) { 4013fac0314SAndi Shyti dev_err(ir->d.dev, LOGHEAD "ioctl result = -ENODEV\n", 40232cf86f6SMauro Carvalho Chehab ir->d.name, ir->d.minor); 40332cf86f6SMauro Carvalho Chehab return -ENODEV; 40432cf86f6SMauro Carvalho Chehab } 40532cf86f6SMauro Carvalho Chehab 40632cf86f6SMauro Carvalho Chehab mutex_lock(&ir->irctl_lock); 40732cf86f6SMauro Carvalho Chehab 40832cf86f6SMauro Carvalho Chehab switch (cmd) { 40932cf86f6SMauro Carvalho Chehab case LIRC_GET_FEATURES: 41060519af3SHans Verkuil result = put_user(ir->d.features, (__u32 __user *)arg); 41132cf86f6SMauro Carvalho Chehab break; 41232cf86f6SMauro Carvalho Chehab case LIRC_GET_REC_MODE: 413bd291208SSean Young if (!LIRC_CAN_REC(ir->d.features)) { 414b4088094SAndi Shyti result = -ENOTTY; 41532cf86f6SMauro Carvalho Chehab break; 41632cf86f6SMauro Carvalho Chehab } 41732cf86f6SMauro Carvalho Chehab 41832cf86f6SMauro Carvalho Chehab result = put_user(LIRC_REC2MODE 41932cf86f6SMauro Carvalho Chehab (ir->d.features & LIRC_CAN_REC_MASK), 42060519af3SHans Verkuil (__u32 __user *)arg); 42132cf86f6SMauro Carvalho Chehab break; 42232cf86f6SMauro Carvalho Chehab case LIRC_SET_REC_MODE: 423bd291208SSean Young if (!LIRC_CAN_REC(ir->d.features)) { 424b4088094SAndi Shyti result = -ENOTTY; 42532cf86f6SMauro Carvalho Chehab break; 42632cf86f6SMauro Carvalho Chehab } 42732cf86f6SMauro Carvalho Chehab 42860519af3SHans Verkuil result = get_user(mode, (__u32 __user *)arg); 42932cf86f6SMauro Carvalho Chehab if (!result && !(LIRC_MODE2REC(mode) & ir->d.features)) 43032cf86f6SMauro Carvalho Chehab result = -EINVAL; 43132cf86f6SMauro Carvalho Chehab /* 43232cf86f6SMauro Carvalho Chehab * FIXME: We should actually set the mode somehow but 43332cf86f6SMauro Carvalho Chehab * for now, lirc_serial doesn't support mode changing either 43432cf86f6SMauro Carvalho Chehab */ 43532cf86f6SMauro Carvalho Chehab break; 43632cf86f6SMauro Carvalho Chehab case LIRC_GET_LENGTH: 43760519af3SHans Verkuil result = put_user(ir->d.code_length, (__u32 __user *)arg); 43832cf86f6SMauro Carvalho Chehab break; 43932cf86f6SMauro Carvalho Chehab case LIRC_GET_MIN_TIMEOUT: 44032cf86f6SMauro Carvalho Chehab if (!(ir->d.features & LIRC_CAN_SET_REC_TIMEOUT) || 44132cf86f6SMauro Carvalho Chehab ir->d.min_timeout == 0) { 442b4088094SAndi Shyti result = -ENOTTY; 44332cf86f6SMauro Carvalho Chehab break; 44432cf86f6SMauro Carvalho Chehab } 44532cf86f6SMauro Carvalho Chehab 44660519af3SHans Verkuil result = put_user(ir->d.min_timeout, (__u32 __user *)arg); 44732cf86f6SMauro Carvalho Chehab break; 44832cf86f6SMauro Carvalho Chehab case LIRC_GET_MAX_TIMEOUT: 44932cf86f6SMauro Carvalho Chehab if (!(ir->d.features & LIRC_CAN_SET_REC_TIMEOUT) || 45032cf86f6SMauro Carvalho Chehab ir->d.max_timeout == 0) { 451b4088094SAndi Shyti result = -ENOTTY; 45232cf86f6SMauro Carvalho Chehab break; 45332cf86f6SMauro Carvalho Chehab } 45432cf86f6SMauro Carvalho Chehab 45560519af3SHans Verkuil result = put_user(ir->d.max_timeout, (__u32 __user *)arg); 45632cf86f6SMauro Carvalho Chehab break; 45732cf86f6SMauro Carvalho Chehab default: 4585c862758SSean Young result = -ENOTTY; 45932cf86f6SMauro Carvalho Chehab } 46032cf86f6SMauro Carvalho Chehab 46132cf86f6SMauro Carvalho Chehab mutex_unlock(&ir->irctl_lock); 46232cf86f6SMauro Carvalho Chehab 46332cf86f6SMauro Carvalho Chehab return result; 46432cf86f6SMauro Carvalho Chehab } 46532cf86f6SMauro Carvalho Chehab EXPORT_SYMBOL(lirc_dev_fop_ioctl); 46632cf86f6SMauro Carvalho Chehab 46732cf86f6SMauro Carvalho Chehab ssize_t lirc_dev_fop_read(struct file *file, 4680e835087SDan Carpenter char __user *buffer, 46932cf86f6SMauro Carvalho Chehab size_t length, 47032cf86f6SMauro Carvalho Chehab loff_t *ppos) 47132cf86f6SMauro Carvalho Chehab { 472496ad9aaSAl Viro struct irctl *ir = irctls[iminor(file_inode(file))]; 47332cf86f6SMauro Carvalho Chehab unsigned char *buf; 47432cf86f6SMauro Carvalho Chehab int ret = 0, written = 0; 47532cf86f6SMauro Carvalho Chehab DECLARE_WAITQUEUE(wait, current); 47632cf86f6SMauro Carvalho Chehab 47732cf86f6SMauro Carvalho Chehab if (!ir) { 4783fac0314SAndi Shyti pr_err("called with invalid irctl\n"); 47932cf86f6SMauro Carvalho Chehab return -ENODEV; 48032cf86f6SMauro Carvalho Chehab } 48132cf86f6SMauro Carvalho Chehab 48232002f72SSean Young if (!LIRC_CAN_REC(ir->d.features)) 48332002f72SSean Young return -EINVAL; 48432002f72SSean Young 48532cf86f6SMauro Carvalho Chehab dev_dbg(ir->d.dev, LOGHEAD "read called\n", ir->d.name, ir->d.minor); 48632cf86f6SMauro Carvalho Chehab 48732cf86f6SMauro Carvalho Chehab buf = kzalloc(ir->chunk_size, GFP_KERNEL); 48832cf86f6SMauro Carvalho Chehab if (!buf) 48932cf86f6SMauro Carvalho Chehab return -ENOMEM; 49032cf86f6SMauro Carvalho Chehab 49132cf86f6SMauro Carvalho Chehab if (mutex_lock_interruptible(&ir->irctl_lock)) { 49232cf86f6SMauro Carvalho Chehab ret = -ERESTARTSYS; 49332cf86f6SMauro Carvalho Chehab goto out_unlocked; 49432cf86f6SMauro Carvalho Chehab } 49532cf86f6SMauro Carvalho Chehab if (!ir->attached) { 49632cf86f6SMauro Carvalho Chehab ret = -ENODEV; 49732cf86f6SMauro Carvalho Chehab goto out_locked; 49832cf86f6SMauro Carvalho Chehab } 49932cf86f6SMauro Carvalho Chehab 50032cf86f6SMauro Carvalho Chehab if (length % ir->chunk_size) { 50132cf86f6SMauro Carvalho Chehab ret = -EINVAL; 50232cf86f6SMauro Carvalho Chehab goto out_locked; 50332cf86f6SMauro Carvalho Chehab } 50432cf86f6SMauro Carvalho Chehab 50532cf86f6SMauro Carvalho Chehab /* 50632cf86f6SMauro Carvalho Chehab * we add ourselves to the task queue before buffer check 50732cf86f6SMauro Carvalho Chehab * to avoid losing scan code (in case when queue is awaken somewhere 50832cf86f6SMauro Carvalho Chehab * between while condition checking and scheduling) 50932cf86f6SMauro Carvalho Chehab */ 51032cf86f6SMauro Carvalho Chehab add_wait_queue(&ir->buf->wait_poll, &wait); 51132cf86f6SMauro Carvalho Chehab 51232cf86f6SMauro Carvalho Chehab /* 51332cf86f6SMauro Carvalho Chehab * while we didn't provide 'length' bytes, device is opened in blocking 51432cf86f6SMauro Carvalho Chehab * mode and 'copy_to_user' is happy, wait for data. 51532cf86f6SMauro Carvalho Chehab */ 51632cf86f6SMauro Carvalho Chehab while (written < length && ret == 0) { 51732cf86f6SMauro Carvalho Chehab if (lirc_buffer_empty(ir->buf)) { 51832cf86f6SMauro Carvalho Chehab /* According to the read(2) man page, 'written' can be 51932cf86f6SMauro Carvalho Chehab * returned as less than 'length', instead of blocking 52032cf86f6SMauro Carvalho Chehab * again, returning -EWOULDBLOCK, or returning 52162e92682SAndi Shyti * -ERESTARTSYS 52262e92682SAndi Shyti */ 52332cf86f6SMauro Carvalho Chehab if (written) 52432cf86f6SMauro Carvalho Chehab break; 52532cf86f6SMauro Carvalho Chehab if (file->f_flags & O_NONBLOCK) { 52632cf86f6SMauro Carvalho Chehab ret = -EWOULDBLOCK; 52732cf86f6SMauro Carvalho Chehab break; 52832cf86f6SMauro Carvalho Chehab } 52932cf86f6SMauro Carvalho Chehab if (signal_pending(current)) { 53032cf86f6SMauro Carvalho Chehab ret = -ERESTARTSYS; 53132cf86f6SMauro Carvalho Chehab break; 53232cf86f6SMauro Carvalho Chehab } 53332cf86f6SMauro Carvalho Chehab 53432cf86f6SMauro Carvalho Chehab mutex_unlock(&ir->irctl_lock); 53532cf86f6SMauro Carvalho Chehab set_current_state(TASK_INTERRUPTIBLE); 53612accdcbSSean Young schedule(); 53712accdcbSSean Young set_current_state(TASK_RUNNING); 53832cf86f6SMauro Carvalho Chehab 53932cf86f6SMauro Carvalho Chehab if (mutex_lock_interruptible(&ir->irctl_lock)) { 54032cf86f6SMauro Carvalho Chehab ret = -ERESTARTSYS; 54132cf86f6SMauro Carvalho Chehab remove_wait_queue(&ir->buf->wait_poll, &wait); 54232cf86f6SMauro Carvalho Chehab goto out_unlocked; 54332cf86f6SMauro Carvalho Chehab } 54432cf86f6SMauro Carvalho Chehab 54532cf86f6SMauro Carvalho Chehab if (!ir->attached) { 54632cf86f6SMauro Carvalho Chehab ret = -ENODEV; 547c77d17c0SSean Young goto out_locked; 54832cf86f6SMauro Carvalho Chehab } 54932cf86f6SMauro Carvalho Chehab } else { 55032cf86f6SMauro Carvalho Chehab lirc_buffer_read(ir->buf, buf); 55160519af3SHans Verkuil ret = copy_to_user((void __user *)buffer+written, buf, 55232cf86f6SMauro Carvalho Chehab ir->buf->chunk_size); 55332cf86f6SMauro Carvalho Chehab if (!ret) 55432cf86f6SMauro Carvalho Chehab written += ir->buf->chunk_size; 55532cf86f6SMauro Carvalho Chehab else 55632cf86f6SMauro Carvalho Chehab ret = -EFAULT; 55732cf86f6SMauro Carvalho Chehab } 55832cf86f6SMauro Carvalho Chehab } 55932cf86f6SMauro Carvalho Chehab 56032cf86f6SMauro Carvalho Chehab remove_wait_queue(&ir->buf->wait_poll, &wait); 56132cf86f6SMauro Carvalho Chehab 56232cf86f6SMauro Carvalho Chehab out_locked: 56332cf86f6SMauro Carvalho Chehab mutex_unlock(&ir->irctl_lock); 56432cf86f6SMauro Carvalho Chehab 56532cf86f6SMauro Carvalho Chehab out_unlocked: 56632cf86f6SMauro Carvalho Chehab kfree(buf); 56732cf86f6SMauro Carvalho Chehab 56832cf86f6SMauro Carvalho Chehab return ret ? ret : written; 56932cf86f6SMauro Carvalho Chehab } 57032cf86f6SMauro Carvalho Chehab EXPORT_SYMBOL(lirc_dev_fop_read); 57132cf86f6SMauro Carvalho Chehab 57232cf86f6SMauro Carvalho Chehab void *lirc_get_pdata(struct file *file) 57332cf86f6SMauro Carvalho Chehab { 5740990a97aSAl Viro return irctls[iminor(file_inode(file))]->d.data; 57532cf86f6SMauro Carvalho Chehab } 57632cf86f6SMauro Carvalho Chehab EXPORT_SYMBOL(lirc_get_pdata); 57732cf86f6SMauro Carvalho Chehab 57832cf86f6SMauro Carvalho Chehab 57932cf86f6SMauro Carvalho Chehab static int __init lirc_dev_init(void) 58032cf86f6SMauro Carvalho Chehab { 58132cf86f6SMauro Carvalho Chehab int retval; 58232cf86f6SMauro Carvalho Chehab 58332cf86f6SMauro Carvalho Chehab lirc_class = class_create(THIS_MODULE, "lirc"); 58432cf86f6SMauro Carvalho Chehab if (IS_ERR(lirc_class)) { 5853fac0314SAndi Shyti pr_err("class_create failed\n"); 58654fcecafSAndi Shyti return PTR_ERR(lirc_class); 58732cf86f6SMauro Carvalho Chehab } 58832cf86f6SMauro Carvalho Chehab 58932cf86f6SMauro Carvalho Chehab retval = alloc_chrdev_region(&lirc_base_dev, 0, MAX_IRCTL_DEVICES, 590463015ddSDavid Härdeman "BaseRemoteCtl"); 59132cf86f6SMauro Carvalho Chehab if (retval) { 59232cf86f6SMauro Carvalho Chehab class_destroy(lirc_class); 5933fac0314SAndi Shyti pr_err("alloc_chrdev_region failed\n"); 59454fcecafSAndi Shyti return retval; 59532cf86f6SMauro Carvalho Chehab } 59632cf86f6SMauro Carvalho Chehab 5973fac0314SAndi Shyti pr_info("IR Remote Control driver registered, major %d\n", 5983fac0314SAndi Shyti MAJOR(lirc_base_dev)); 59932cf86f6SMauro Carvalho Chehab 60054fcecafSAndi Shyti return 0; 60132cf86f6SMauro Carvalho Chehab } 60232cf86f6SMauro Carvalho Chehab 60332cf86f6SMauro Carvalho Chehab static void __exit lirc_dev_exit(void) 60432cf86f6SMauro Carvalho Chehab { 60532cf86f6SMauro Carvalho Chehab class_destroy(lirc_class); 60632cf86f6SMauro Carvalho Chehab unregister_chrdev_region(lirc_base_dev, MAX_IRCTL_DEVICES); 6073fac0314SAndi Shyti pr_info("module unloaded\n"); 60832cf86f6SMauro Carvalho Chehab } 60932cf86f6SMauro Carvalho Chehab 61032cf86f6SMauro Carvalho Chehab module_init(lirc_dev_init); 61132cf86f6SMauro Carvalho Chehab module_exit(lirc_dev_exit); 61232cf86f6SMauro Carvalho Chehab 61332cf86f6SMauro Carvalho Chehab MODULE_DESCRIPTION("LIRC base driver module"); 61432cf86f6SMauro Carvalho Chehab MODULE_AUTHOR("Artur Lipowski"); 61532cf86f6SMauro Carvalho Chehab MODULE_LICENSE("GPL"); 616