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> 2746c8f477SDavid Härdeman #include <linux/idr.h> 2832cf86f6SMauro Carvalho Chehab 29*a60d64b1SSean Young #include "rc-core-priv.h" 3032cf86f6SMauro Carvalho Chehab #include <media/lirc.h> 3132cf86f6SMauro Carvalho Chehab #include <media/lirc_dev.h> 3232cf86f6SMauro Carvalho Chehab 3332cf86f6SMauro Carvalho Chehab #define LOGHEAD "lirc_dev (%s[%d]): " 3432cf86f6SMauro Carvalho Chehab 3532cf86f6SMauro Carvalho Chehab static dev_t lirc_base_dev; 3632cf86f6SMauro Carvalho Chehab 3746c8f477SDavid Härdeman /* Used to keep track of allocated lirc devices */ 3846c8f477SDavid Härdeman #define LIRC_MAX_DEVICES 256 3946c8f477SDavid Härdeman static DEFINE_IDA(lirc_ida); 4032cf86f6SMauro Carvalho Chehab 4132cf86f6SMauro Carvalho Chehab /* Only used for sysfs but defined to void otherwise */ 4232cf86f6SMauro Carvalho Chehab static struct class *lirc_class; 4332cf86f6SMauro Carvalho Chehab 44b15e3937SDavid Härdeman static void lirc_release_device(struct device *ld) 4532cf86f6SMauro Carvalho Chehab { 46b15e3937SDavid Härdeman struct lirc_dev *d = container_of(ld, struct lirc_dev, dev); 47a607f51eSSean Young 48b15e3937SDavid Härdeman put_device(d->dev.parent); 49b15e3937SDavid Härdeman 50b15e3937SDavid Härdeman if (d->buf_internal) { 51b15e3937SDavid Härdeman lirc_buffer_free(d->buf); 52b15e3937SDavid Härdeman kfree(d->buf); 53b15e3937SDavid Härdeman d->buf = NULL; 5432cf86f6SMauro Carvalho Chehab } 55b15e3937SDavid Härdeman kfree(d); 56b15e3937SDavid Härdeman module_put(THIS_MODULE); 573381b779SDavid Härdeman } 583381b779SDavid Härdeman 59b15e3937SDavid Härdeman static int lirc_allocate_buffer(struct lirc_dev *d) 603381b779SDavid Härdeman { 61b15e3937SDavid Härdeman int err; 6274c839b2SSean Young 63b15e3937SDavid Härdeman if (d->buf) { 64b15e3937SDavid Härdeman d->buf_internal = false; 65b15e3937SDavid Härdeman return 0; 6632cf86f6SMauro Carvalho Chehab } 6732cf86f6SMauro Carvalho Chehab 68b15e3937SDavid Härdeman d->buf = kmalloc(sizeof(*d->buf), GFP_KERNEL); 69b15e3937SDavid Härdeman if (!d->buf) 70b15e3937SDavid Härdeman return -ENOMEM; 716fa99e1aSAndi Shyti 72b15e3937SDavid Härdeman err = lirc_buffer_init(d->buf, d->chunk_size, d->buffer_size); 736fa99e1aSAndi Shyti if (err) { 74b15e3937SDavid Härdeman kfree(d->buf); 75b15e3937SDavid Härdeman d->buf = NULL; 7670143984SAndi Shyti return err; 776fa99e1aSAndi Shyti } 786fa99e1aSAndi Shyti 79b15e3937SDavid Härdeman d->buf_internal = true; 80b15e3937SDavid Härdeman return 0; 81b15e3937SDavid Härdeman } 82b15e3937SDavid Härdeman 836ecccc37SDavid Härdeman struct lirc_dev * 846ecccc37SDavid Härdeman lirc_allocate_device(void) 856ecccc37SDavid Härdeman { 86b15e3937SDavid Härdeman struct lirc_dev *d; 87b15e3937SDavid Härdeman 88b15e3937SDavid Härdeman d = kzalloc(sizeof(*d), GFP_KERNEL); 89b15e3937SDavid Härdeman if (d) { 90b15e3937SDavid Härdeman mutex_init(&d->mutex); 91b15e3937SDavid Härdeman device_initialize(&d->dev); 92b15e3937SDavid Härdeman d->dev.class = lirc_class; 93b15e3937SDavid Härdeman d->dev.release = lirc_release_device; 94b15e3937SDavid Härdeman __module_get(THIS_MODULE); 95b15e3937SDavid Härdeman } 96b15e3937SDavid Härdeman 97b15e3937SDavid Härdeman return d; 986ecccc37SDavid Härdeman } 996ecccc37SDavid Härdeman EXPORT_SYMBOL(lirc_allocate_device); 1006ecccc37SDavid Härdeman 1016ecccc37SDavid Härdeman void lirc_free_device(struct lirc_dev *d) 1026ecccc37SDavid Härdeman { 103b15e3937SDavid Härdeman if (!d) 104b15e3937SDavid Härdeman return; 105b15e3937SDavid Härdeman 106b15e3937SDavid Härdeman put_device(&d->dev); 1076ecccc37SDavid Härdeman } 1086ecccc37SDavid Härdeman EXPORT_SYMBOL(lirc_free_device); 1096ecccc37SDavid Härdeman 1105ddc9c09SDavid Härdeman int lirc_register_device(struct lirc_dev *d) 11132cf86f6SMauro Carvalho Chehab { 11246c8f477SDavid Härdeman 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 120b15e3937SDavid Härdeman if (!d->dev.parent) { 121b15e3937SDavid Härdeman pr_err("dev parent 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 130b15e3937SDavid Härdeman if (!d->buf && 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 135b15e3937SDavid Härdeman if (!d->buf && 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 140b15e3937SDavid Härdeman if (!d->buf && !(d->fops && d->fops->read && 14114db9fc2SAndi Shyti d->fops->poll && d->fops->unlocked_ioctl)) { 142b15e3937SDavid Härdeman dev_err(&d->dev, "undefined read, poll, ioctl\n"); 14354fcecafSAndi Shyti return -EBADRQC; 14432cf86f6SMauro Carvalho Chehab } 14532cf86f6SMauro Carvalho Chehab 1463381b779SDavid Härdeman /* some safety check 8-) */ 1473381b779SDavid Härdeman d->name[sizeof(d->name) - 1] = '\0'; 1483381b779SDavid Härdeman 1493381b779SDavid Härdeman if (LIRC_CAN_REC(d->features)) { 150b15e3937SDavid Härdeman err = lirc_allocate_buffer(d); 151b15e3937SDavid Härdeman if (err) 1523381b779SDavid Härdeman return err; 1533381b779SDavid Härdeman } 1543381b779SDavid Härdeman 15546c8f477SDavid Härdeman minor = ida_simple_get(&lirc_ida, 0, LIRC_MAX_DEVICES, GFP_KERNEL); 156b15e3937SDavid Härdeman if (minor < 0) 15746c8f477SDavid Härdeman return minor; 15832cf86f6SMauro Carvalho Chehab 15932cf86f6SMauro Carvalho Chehab d->minor = minor; 160b15e3937SDavid Härdeman d->dev.devt = MKDEV(MAJOR(lirc_base_dev), d->minor); 161b15e3937SDavid Härdeman dev_set_name(&d->dev, "lirc%d", d->minor); 16232cf86f6SMauro Carvalho Chehab 163b15e3937SDavid Härdeman cdev_init(&d->cdev, d->fops); 164b15e3937SDavid Härdeman d->cdev.owner = d->owner; 165b15e3937SDavid Härdeman d->attached = true; 16632cf86f6SMauro Carvalho Chehab 167b15e3937SDavid Härdeman err = cdev_device_add(&d->cdev, &d->dev); 1683381b779SDavid Härdeman if (err) { 16946c8f477SDavid Härdeman ida_simple_remove(&lirc_ida, minor); 1703381b779SDavid Härdeman return err; 1713381b779SDavid Härdeman } 17232cf86f6SMauro Carvalho Chehab 173b15e3937SDavid Härdeman get_device(d->dev.parent); 174a607f51eSSean Young 175b15e3937SDavid Härdeman dev_info(&d->dev, "lirc_dev: driver %s registered at minor = %d\n", 176b15e3937SDavid Härdeman d->name, d->minor); 17756481f00SDavid Härdeman 178c3c6dd75SDavid Härdeman return 0; 17932cf86f6SMauro Carvalho Chehab } 1805ddc9c09SDavid Härdeman EXPORT_SYMBOL(lirc_register_device); 18132cf86f6SMauro Carvalho Chehab 1825ddc9c09SDavid Härdeman void lirc_unregister_device(struct lirc_dev *d) 18332cf86f6SMauro Carvalho Chehab { 184b15e3937SDavid Härdeman if (!d) 185c3c6dd75SDavid Härdeman return; 18632cf86f6SMauro Carvalho Chehab 187b15e3937SDavid Härdeman dev_dbg(&d->dev, "lirc_dev: driver %s unregistered from minor = %d\n", 188c3c6dd75SDavid Härdeman d->name, d->minor); 18932cf86f6SMauro Carvalho Chehab 190b15e3937SDavid Härdeman mutex_lock(&d->mutex); 1913381b779SDavid Härdeman 192b15e3937SDavid Härdeman d->attached = false; 193b15e3937SDavid Härdeman if (d->open) { 194b15e3937SDavid Härdeman dev_dbg(&d->dev, LOGHEAD "releasing opened driver\n", 195c3c6dd75SDavid Härdeman d->name, d->minor); 196b15e3937SDavid Härdeman wake_up_interruptible(&d->buf->wait_poll); 19774c839b2SSean Young } 19874c839b2SSean Young 199b15e3937SDavid Härdeman mutex_unlock(&d->mutex); 20032cf86f6SMauro Carvalho Chehab 201b15e3937SDavid Härdeman cdev_device_del(&d->cdev, &d->dev); 20246c8f477SDavid Härdeman ida_simple_remove(&lirc_ida, d->minor); 203b15e3937SDavid Härdeman put_device(&d->dev); 20432cf86f6SMauro Carvalho Chehab } 2055ddc9c09SDavid Härdeman EXPORT_SYMBOL(lirc_unregister_device); 20632cf86f6SMauro Carvalho Chehab 20732cf86f6SMauro Carvalho Chehab int lirc_dev_fop_open(struct inode *inode, struct file *file) 20832cf86f6SMauro Carvalho Chehab { 209b15e3937SDavid Härdeman struct lirc_dev *d = container_of(inode->i_cdev, struct lirc_dev, cdev); 210de226ec8SDavid Härdeman int retval; 21132cf86f6SMauro Carvalho Chehab 212b15e3937SDavid Härdeman dev_dbg(&d->dev, LOGHEAD "open called\n", d->name, d->minor); 21332cf86f6SMauro Carvalho Chehab 214b15e3937SDavid Härdeman retval = mutex_lock_interruptible(&d->mutex); 2153381b779SDavid Härdeman if (retval) 2163381b779SDavid Härdeman return retval; 2173381b779SDavid Härdeman 218b15e3937SDavid Härdeman if (!d->attached) { 2193381b779SDavid Härdeman retval = -ENODEV; 2203381b779SDavid Härdeman goto out; 2213381b779SDavid Härdeman } 2223381b779SDavid Härdeman 223b15e3937SDavid Härdeman if (d->open) { 2243381b779SDavid Härdeman retval = -EBUSY; 2253381b779SDavid Härdeman goto out; 2263381b779SDavid Härdeman } 22732cf86f6SMauro Carvalho Chehab 228b15e3937SDavid Härdeman if (d->rdev) { 229b15e3937SDavid Härdeman retval = rc_open(d->rdev); 230ca7a722dSSrinivas Kandagatla if (retval) 2313381b779SDavid Härdeman goto out; 232ca7a722dSSrinivas Kandagatla } 233ca7a722dSSrinivas Kandagatla 234b15e3937SDavid Härdeman if (d->buf) 235b15e3937SDavid Härdeman lirc_buffer_clear(d->buf); 2362c5a1f44SDavid Härdeman 237b15e3937SDavid Härdeman d->open++; 23832cf86f6SMauro Carvalho Chehab 239*a60d64b1SSean Young file->private_data = d->rdev; 24032cf86f6SMauro Carvalho Chehab nonseekable_open(inode, file); 241b15e3937SDavid Härdeman mutex_unlock(&d->mutex); 24232cf86f6SMauro Carvalho Chehab 243de226ec8SDavid Härdeman return 0; 2443381b779SDavid Härdeman 2453381b779SDavid Härdeman out: 246b15e3937SDavid Härdeman mutex_unlock(&d->mutex); 2473381b779SDavid Härdeman return retval; 24832cf86f6SMauro Carvalho Chehab } 24932cf86f6SMauro Carvalho Chehab EXPORT_SYMBOL(lirc_dev_fop_open); 25032cf86f6SMauro Carvalho Chehab 25132cf86f6SMauro Carvalho Chehab int lirc_dev_fop_close(struct inode *inode, struct file *file) 25232cf86f6SMauro Carvalho Chehab { 253*a60d64b1SSean Young struct rc_dev *rcdev = file->private_data; 254*a60d64b1SSean Young struct lirc_dev *d = rcdev->lirc_dev; 25532cf86f6SMauro Carvalho Chehab 256b15e3937SDavid Härdeman mutex_lock(&d->mutex); 25732cf86f6SMauro Carvalho Chehab 258*a60d64b1SSean Young rc_close(rcdev); 259b15e3937SDavid Härdeman d->open--; 2603381b779SDavid Härdeman 261b15e3937SDavid Härdeman mutex_unlock(&d->mutex); 26232cf86f6SMauro Carvalho Chehab 26332cf86f6SMauro Carvalho Chehab return 0; 26432cf86f6SMauro Carvalho Chehab } 26532cf86f6SMauro Carvalho Chehab EXPORT_SYMBOL(lirc_dev_fop_close); 26632cf86f6SMauro Carvalho Chehab 26732cf86f6SMauro Carvalho Chehab unsigned int lirc_dev_fop_poll(struct file *file, poll_table *wait) 26832cf86f6SMauro Carvalho Chehab { 269*a60d64b1SSean Young struct rc_dev *rcdev = file->private_data; 270*a60d64b1SSean Young struct lirc_dev *d = rcdev->lirc_dev; 27132cf86f6SMauro Carvalho Chehab unsigned int ret; 27232cf86f6SMauro Carvalho Chehab 273b15e3937SDavid Härdeman if (!d->attached) 27429debf3dSDavid Härdeman return POLLHUP | POLLERR; 27532cf86f6SMauro Carvalho Chehab 276b15e3937SDavid Härdeman if (d->buf) { 277b15e3937SDavid Härdeman poll_wait(file, &d->buf->wait_poll, wait); 27832cf86f6SMauro Carvalho Chehab 279b15e3937SDavid Härdeman if (lirc_buffer_empty(d->buf)) 28032cf86f6SMauro Carvalho Chehab ret = 0; 28132cf86f6SMauro Carvalho Chehab else 28232cf86f6SMauro Carvalho Chehab ret = POLLIN | POLLRDNORM; 283b15e3937SDavid Härdeman } else { 28432cf86f6SMauro Carvalho Chehab ret = POLLERR; 285b15e3937SDavid Härdeman } 28632cf86f6SMauro Carvalho Chehab 287b15e3937SDavid Härdeman dev_dbg(&d->dev, LOGHEAD "poll result = %d\n", d->name, d->minor, ret); 28832cf86f6SMauro Carvalho Chehab 28932cf86f6SMauro Carvalho Chehab return ret; 29032cf86f6SMauro Carvalho Chehab } 29132cf86f6SMauro Carvalho Chehab EXPORT_SYMBOL(lirc_dev_fop_poll); 29232cf86f6SMauro Carvalho Chehab 29332cf86f6SMauro Carvalho Chehab long lirc_dev_fop_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 29432cf86f6SMauro Carvalho Chehab { 295*a60d64b1SSean Young struct rc_dev *rcdev = file->private_data; 296*a60d64b1SSean Young struct lirc_dev *d = rcdev->lirc_dev; 29732cf86f6SMauro Carvalho Chehab __u32 mode; 2983381b779SDavid Härdeman int result; 29932cf86f6SMauro Carvalho Chehab 300b15e3937SDavid Härdeman dev_dbg(&d->dev, LOGHEAD "ioctl called (0x%x)\n", 301b15e3937SDavid Härdeman d->name, d->minor, cmd); 30232cf86f6SMauro Carvalho Chehab 303b15e3937SDavid Härdeman result = mutex_lock_interruptible(&d->mutex); 3043381b779SDavid Härdeman if (result) 3053381b779SDavid Härdeman return result; 30632cf86f6SMauro Carvalho Chehab 307b15e3937SDavid Härdeman if (!d->attached) { 3083381b779SDavid Härdeman result = -ENODEV; 3093381b779SDavid Härdeman goto out; 3103381b779SDavid Härdeman } 31132cf86f6SMauro Carvalho Chehab 31232cf86f6SMauro Carvalho Chehab switch (cmd) { 31332cf86f6SMauro Carvalho Chehab case LIRC_GET_FEATURES: 314b15e3937SDavid Härdeman result = put_user(d->features, (__u32 __user *)arg); 31532cf86f6SMauro Carvalho Chehab break; 31632cf86f6SMauro Carvalho Chehab case LIRC_GET_REC_MODE: 317b15e3937SDavid Härdeman if (!LIRC_CAN_REC(d->features)) { 318b4088094SAndi Shyti result = -ENOTTY; 31932cf86f6SMauro Carvalho Chehab break; 32032cf86f6SMauro Carvalho Chehab } 32132cf86f6SMauro Carvalho Chehab 32232cf86f6SMauro Carvalho Chehab result = put_user(LIRC_REC2MODE 323b15e3937SDavid Härdeman (d->features & LIRC_CAN_REC_MASK), 32460519af3SHans Verkuil (__u32 __user *)arg); 32532cf86f6SMauro Carvalho Chehab break; 32632cf86f6SMauro Carvalho Chehab case LIRC_SET_REC_MODE: 327b15e3937SDavid Härdeman if (!LIRC_CAN_REC(d->features)) { 328b4088094SAndi Shyti result = -ENOTTY; 32932cf86f6SMauro Carvalho Chehab break; 33032cf86f6SMauro Carvalho Chehab } 33132cf86f6SMauro Carvalho Chehab 33260519af3SHans Verkuil result = get_user(mode, (__u32 __user *)arg); 333b15e3937SDavid Härdeman if (!result && !(LIRC_MODE2REC(mode) & d->features)) 33432cf86f6SMauro Carvalho Chehab result = -EINVAL; 33532cf86f6SMauro Carvalho Chehab /* 33632cf86f6SMauro Carvalho Chehab * FIXME: We should actually set the mode somehow but 33732cf86f6SMauro Carvalho Chehab * for now, lirc_serial doesn't support mode changing either 33832cf86f6SMauro Carvalho Chehab */ 33932cf86f6SMauro Carvalho Chehab break; 34032cf86f6SMauro Carvalho Chehab default: 3415c862758SSean Young result = -ENOTTY; 34232cf86f6SMauro Carvalho Chehab } 34332cf86f6SMauro Carvalho Chehab 3443381b779SDavid Härdeman out: 345b15e3937SDavid Härdeman mutex_unlock(&d->mutex); 34632cf86f6SMauro Carvalho Chehab return result; 34732cf86f6SMauro Carvalho Chehab } 34832cf86f6SMauro Carvalho Chehab EXPORT_SYMBOL(lirc_dev_fop_ioctl); 34932cf86f6SMauro Carvalho Chehab 35032cf86f6SMauro Carvalho Chehab ssize_t lirc_dev_fop_read(struct file *file, 3510e835087SDan Carpenter char __user *buffer, 35232cf86f6SMauro Carvalho Chehab size_t length, 35332cf86f6SMauro Carvalho Chehab loff_t *ppos) 35432cf86f6SMauro Carvalho Chehab { 355*a60d64b1SSean Young struct rc_dev *rcdev = file->private_data; 356*a60d64b1SSean Young struct lirc_dev *d = rcdev->lirc_dev; 35732cf86f6SMauro Carvalho Chehab unsigned char *buf; 3583381b779SDavid Härdeman int ret, written = 0; 35932cf86f6SMauro Carvalho Chehab DECLARE_WAITQUEUE(wait, current); 36032cf86f6SMauro Carvalho Chehab 361b15e3937SDavid Härdeman buf = kzalloc(d->buf->chunk_size, GFP_KERNEL); 36232cf86f6SMauro Carvalho Chehab if (!buf) 36332cf86f6SMauro Carvalho Chehab return -ENOMEM; 36432cf86f6SMauro Carvalho Chehab 365b15e3937SDavid Härdeman dev_dbg(&d->dev, LOGHEAD "read called\n", d->name, d->minor); 366b15e3937SDavid Härdeman 367b15e3937SDavid Härdeman ret = mutex_lock_interruptible(&d->mutex); 3683381b779SDavid Härdeman if (ret) { 3693381b779SDavid Härdeman kfree(buf); 3703381b779SDavid Härdeman return ret; 37132cf86f6SMauro Carvalho Chehab } 3723381b779SDavid Härdeman 373b15e3937SDavid Härdeman if (!d->attached) { 37432cf86f6SMauro Carvalho Chehab ret = -ENODEV; 37532cf86f6SMauro Carvalho Chehab goto out_locked; 37632cf86f6SMauro Carvalho Chehab } 37732cf86f6SMauro Carvalho Chehab 378b15e3937SDavid Härdeman if (!LIRC_CAN_REC(d->features)) { 3793381b779SDavid Härdeman ret = -EINVAL; 3803381b779SDavid Härdeman goto out_locked; 3813381b779SDavid Härdeman } 3823381b779SDavid Härdeman 383b15e3937SDavid Härdeman if (length % d->buf->chunk_size) { 38432cf86f6SMauro Carvalho Chehab ret = -EINVAL; 38532cf86f6SMauro Carvalho Chehab goto out_locked; 38632cf86f6SMauro Carvalho Chehab } 38732cf86f6SMauro Carvalho Chehab 38832cf86f6SMauro Carvalho Chehab /* 38932cf86f6SMauro Carvalho Chehab * we add ourselves to the task queue before buffer check 39032cf86f6SMauro Carvalho Chehab * to avoid losing scan code (in case when queue is awaken somewhere 39132cf86f6SMauro Carvalho Chehab * between while condition checking and scheduling) 39232cf86f6SMauro Carvalho Chehab */ 393b15e3937SDavid Härdeman add_wait_queue(&d->buf->wait_poll, &wait); 39432cf86f6SMauro Carvalho Chehab 39532cf86f6SMauro Carvalho Chehab /* 39632cf86f6SMauro Carvalho Chehab * while we didn't provide 'length' bytes, device is opened in blocking 39732cf86f6SMauro Carvalho Chehab * mode and 'copy_to_user' is happy, wait for data. 39832cf86f6SMauro Carvalho Chehab */ 39932cf86f6SMauro Carvalho Chehab while (written < length && ret == 0) { 400b15e3937SDavid Härdeman if (lirc_buffer_empty(d->buf)) { 40132cf86f6SMauro Carvalho Chehab /* According to the read(2) man page, 'written' can be 40232cf86f6SMauro Carvalho Chehab * returned as less than 'length', instead of blocking 40332cf86f6SMauro Carvalho Chehab * again, returning -EWOULDBLOCK, or returning 40462e92682SAndi Shyti * -ERESTARTSYS 40562e92682SAndi Shyti */ 40632cf86f6SMauro Carvalho Chehab if (written) 40732cf86f6SMauro Carvalho Chehab break; 40832cf86f6SMauro Carvalho Chehab if (file->f_flags & O_NONBLOCK) { 40932cf86f6SMauro Carvalho Chehab ret = -EWOULDBLOCK; 41032cf86f6SMauro Carvalho Chehab break; 41132cf86f6SMauro Carvalho Chehab } 41232cf86f6SMauro Carvalho Chehab if (signal_pending(current)) { 41332cf86f6SMauro Carvalho Chehab ret = -ERESTARTSYS; 41432cf86f6SMauro Carvalho Chehab break; 41532cf86f6SMauro Carvalho Chehab } 41632cf86f6SMauro Carvalho Chehab 417b15e3937SDavid Härdeman mutex_unlock(&d->mutex); 41832cf86f6SMauro Carvalho Chehab set_current_state(TASK_INTERRUPTIBLE); 41912accdcbSSean Young schedule(); 42012accdcbSSean Young set_current_state(TASK_RUNNING); 42132cf86f6SMauro Carvalho Chehab 422b15e3937SDavid Härdeman ret = mutex_lock_interruptible(&d->mutex); 4233381b779SDavid Härdeman if (ret) { 424b15e3937SDavid Härdeman remove_wait_queue(&d->buf->wait_poll, &wait); 42532cf86f6SMauro Carvalho Chehab goto out_unlocked; 42632cf86f6SMauro Carvalho Chehab } 42732cf86f6SMauro Carvalho Chehab 428b15e3937SDavid Härdeman if (!d->attached) { 42932cf86f6SMauro Carvalho Chehab ret = -ENODEV; 430c77d17c0SSean Young goto out_locked; 43132cf86f6SMauro Carvalho Chehab } 43232cf86f6SMauro Carvalho Chehab } else { 433b15e3937SDavid Härdeman lirc_buffer_read(d->buf, buf); 43460519af3SHans Verkuil ret = copy_to_user((void __user *)buffer+written, buf, 435b15e3937SDavid Härdeman d->buf->chunk_size); 43632cf86f6SMauro Carvalho Chehab if (!ret) 437b15e3937SDavid Härdeman written += d->buf->chunk_size; 43832cf86f6SMauro Carvalho Chehab else 43932cf86f6SMauro Carvalho Chehab ret = -EFAULT; 44032cf86f6SMauro Carvalho Chehab } 44132cf86f6SMauro Carvalho Chehab } 44232cf86f6SMauro Carvalho Chehab 443b15e3937SDavid Härdeman remove_wait_queue(&d->buf->wait_poll, &wait); 44432cf86f6SMauro Carvalho Chehab 44532cf86f6SMauro Carvalho Chehab out_locked: 446b15e3937SDavid Härdeman mutex_unlock(&d->mutex); 44732cf86f6SMauro Carvalho Chehab 44832cf86f6SMauro Carvalho Chehab out_unlocked: 44932cf86f6SMauro Carvalho Chehab kfree(buf); 45032cf86f6SMauro Carvalho Chehab 45132cf86f6SMauro Carvalho Chehab return ret ? ret : written; 45232cf86f6SMauro Carvalho Chehab } 45332cf86f6SMauro Carvalho Chehab EXPORT_SYMBOL(lirc_dev_fop_read); 45432cf86f6SMauro Carvalho Chehab 455*a60d64b1SSean Young int __init lirc_dev_init(void) 45632cf86f6SMauro Carvalho Chehab { 45732cf86f6SMauro Carvalho Chehab int retval; 45832cf86f6SMauro Carvalho Chehab 45932cf86f6SMauro Carvalho Chehab lirc_class = class_create(THIS_MODULE, "lirc"); 46032cf86f6SMauro Carvalho Chehab if (IS_ERR(lirc_class)) { 4613fac0314SAndi Shyti pr_err("class_create failed\n"); 46254fcecafSAndi Shyti return PTR_ERR(lirc_class); 46332cf86f6SMauro Carvalho Chehab } 46432cf86f6SMauro Carvalho Chehab 46546c8f477SDavid Härdeman retval = alloc_chrdev_region(&lirc_base_dev, 0, LIRC_MAX_DEVICES, 466463015ddSDavid Härdeman "BaseRemoteCtl"); 46732cf86f6SMauro Carvalho Chehab if (retval) { 46832cf86f6SMauro Carvalho Chehab class_destroy(lirc_class); 4693fac0314SAndi Shyti pr_err("alloc_chrdev_region failed\n"); 47054fcecafSAndi Shyti return retval; 47132cf86f6SMauro Carvalho Chehab } 47232cf86f6SMauro Carvalho Chehab 4733fac0314SAndi Shyti pr_info("IR Remote Control driver registered, major %d\n", 4743fac0314SAndi Shyti MAJOR(lirc_base_dev)); 47532cf86f6SMauro Carvalho Chehab 47654fcecafSAndi Shyti return 0; 47732cf86f6SMauro Carvalho Chehab } 47832cf86f6SMauro Carvalho Chehab 479*a60d64b1SSean Young void __exit lirc_dev_exit(void) 48032cf86f6SMauro Carvalho Chehab { 48132cf86f6SMauro Carvalho Chehab class_destroy(lirc_class); 48246c8f477SDavid Härdeman unregister_chrdev_region(lirc_base_dev, LIRC_MAX_DEVICES); 48332cf86f6SMauro Carvalho Chehab } 484