diff options
author | Sean Young <sean@mess.org> | 2017-11-02 17:21:13 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@s-opensource.com> | 2017-12-14 10:35:25 -0500 |
commit | 7e45d660e4d487aa2cbeb003bd4338433feba30a (patch) | |
tree | 9a3b2bfd8602c6a8bd160fedcacdff3a4eb9cdd4 | |
parent | aefb5e3434db4a98f30fee460b0d8885aad0f456 (diff) | |
download | linux-7e45d660e4d487aa2cbeb003bd4338433feba30a.tar.bz2 |
media: lirc: allow lirc device to be opened more than once
This makes it possible for lircd to read from a lirc chardev, and not
keep it busy.
Note that this changes the default for timeout reports to on. lircd
already enables timeout reports when it opens a lirc device, leaving
them on until the next reboot.
Signed-off-by: Sean Young <sean@mess.org>
Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
-rw-r--r-- | drivers/media/rc/lirc_dev.c | 247 | ||||
-rw-r--r-- | include/media/rc-core.h | 50 |
2 files changed, 165 insertions, 132 deletions
diff --git a/drivers/media/rc/lirc_dev.c b/drivers/media/rc/lirc_dev.c index 8a0e9f74329a..7b9246fb9652 100644 --- a/drivers/media/rc/lirc_dev.c +++ b/drivers/media/rc/lirc_dev.c @@ -28,7 +28,6 @@ #include "rc-core-priv.h" #include <uapi/linux/lirc.h> -#define LOGHEAD "lirc_dev (%s[%d]): " #define LIRCBUF_SIZE 256 static dev_t lirc_base_dev; @@ -47,6 +46,8 @@ static struct class *lirc_class; */ void ir_lirc_raw_event(struct rc_dev *dev, struct ir_raw_event ev) { + unsigned long flags; + struct lirc_fh *fh; int sample; /* Packet start */ @@ -75,9 +76,6 @@ void ir_lirc_raw_event(struct rc_dev *dev, struct ir_raw_event ev) dev->gap = true; dev->gap_duration = ev.duration; - if (!dev->send_timeout_reports) - return; - sample = LIRC_TIMEOUT(ev.duration / 1000); IR_dprintk(2, "timeout report (duration: %d)\n", sample); @@ -92,7 +90,11 @@ void ir_lirc_raw_event(struct rc_dev *dev, struct ir_raw_event ev) dev->gap_duration = min_t(u64, dev->gap_duration, LIRC_VALUE_MASK); - kfifo_put(&dev->rawir, LIRC_SPACE(dev->gap_duration)); + spin_lock_irqsave(&dev->lirc_fh_lock, flags); + list_for_each_entry(fh, &dev->lirc_fh, list) + kfifo_put(&fh->rawir, + LIRC_SPACE(dev->gap_duration)); + spin_unlock_irqrestore(&dev->lirc_fh_lock, flags); dev->gap = false; } @@ -102,22 +104,35 @@ void ir_lirc_raw_event(struct rc_dev *dev, struct ir_raw_event ev) TO_US(ev.duration), TO_STR(ev.pulse)); } - kfifo_put(&dev->rawir, sample); - wake_up_poll(&dev->wait_poll, POLLIN | POLLRDNORM); + spin_lock_irqsave(&dev->lirc_fh_lock, flags); + list_for_each_entry(fh, &dev->lirc_fh, list) { + if (LIRC_IS_TIMEOUT(sample) && !fh->send_timeout_reports) + continue; + if (kfifo_put(&fh->rawir, sample)) + wake_up_poll(&fh->wait_poll, POLLIN | POLLRDNORM); + } + spin_unlock_irqrestore(&dev->lirc_fh_lock, flags); } /** * ir_lirc_scancode_event() - Send scancode data to lirc to be relayed to - * userspace + * userspace. This can be called in atomic context. * @dev: the struct rc_dev descriptor of the device * @lsc: the struct lirc_scancode describing the decoded scancode */ void ir_lirc_scancode_event(struct rc_dev *dev, struct lirc_scancode *lsc) { + unsigned long flags; + struct lirc_fh *fh; + lsc->timestamp = ktime_get_ns(); - if (kfifo_put(&dev->scancodes, *lsc)) - wake_up_poll(&dev->wait_poll, POLLIN | POLLRDNORM); + spin_lock_irqsave(&dev->lirc_fh_lock, flags); + list_for_each_entry(fh, &dev->lirc_fh, list) { + if (kfifo_put(&fh->scancodes, *lsc)) + wake_up_poll(&fh->wait_poll, POLLIN | POLLRDNORM); + } + spin_unlock_irqrestore(&dev->lirc_fh_lock, flags); } EXPORT_SYMBOL_GPL(ir_lirc_scancode_event); @@ -125,55 +140,88 @@ static int ir_lirc_open(struct inode *inode, struct file *file) { struct rc_dev *dev = container_of(inode->i_cdev, struct rc_dev, lirc_cdev); + struct lirc_fh *fh = kzalloc(sizeof(*fh), GFP_KERNEL); + unsigned long flags; int retval; - retval = rc_open(dev); - if (retval) - return retval; + if (!fh) + return -ENOMEM; - retval = mutex_lock_interruptible(&dev->lock); - if (retval) - goto out_rc; + get_device(&dev->dev); if (!dev->registered) { retval = -ENODEV; - goto out_unlock; + goto out_fh; } - if (dev->lirc_open) { - retval = -EBUSY; - goto out_unlock; + if (dev->driver_type == RC_DRIVER_IR_RAW) { + if (kfifo_alloc(&fh->rawir, MAX_IR_EVENT_SIZE, GFP_KERNEL)) { + retval = -ENOMEM; + goto out_fh; + } } - if (dev->driver_type == RC_DRIVER_IR_RAW) - kfifo_reset_out(&dev->rawir); - if (dev->driver_type != RC_DRIVER_IR_RAW_TX) - kfifo_reset_out(&dev->scancodes); + if (dev->driver_type != RC_DRIVER_IR_RAW_TX) { + if (kfifo_alloc(&fh->scancodes, 32, GFP_KERNEL)) { + retval = -ENOMEM; + goto out_rawir; + } + } + + fh->send_mode = LIRC_MODE_PULSE; + fh->rc = dev; + fh->send_timeout_reports = true; + + if (dev->driver_type == RC_DRIVER_SCANCODE) + fh->rec_mode = LIRC_MODE_SCANCODE; + else + fh->rec_mode = LIRC_MODE_MODE2; + + retval = rc_open(dev); + if (retval) + goto out_kfifo; + + init_waitqueue_head(&fh->wait_poll); - dev->lirc_open++; - file->private_data = dev; + file->private_data = fh; + spin_lock_irqsave(&dev->lirc_fh_lock, flags); + list_add(&fh->list, &dev->lirc_fh); + spin_unlock_irqrestore(&dev->lirc_fh_lock, flags); nonseekable_open(inode, file); - mutex_unlock(&dev->lock); return 0; +out_kfifo: + if (dev->driver_type != RC_DRIVER_IR_RAW_TX) + kfifo_free(&fh->scancodes); +out_rawir: + if (dev->driver_type == RC_DRIVER_IR_RAW) + kfifo_free(&fh->rawir); +out_fh: + kfree(fh); + put_device(&dev->dev); -out_unlock: - mutex_unlock(&dev->lock); -out_rc: - rc_close(dev); return retval; } static int ir_lirc_close(struct inode *inode, struct file *file) { - struct rc_dev *dev = file->private_data; + struct lirc_fh *fh = file->private_data; + struct rc_dev *dev = fh->rc; + unsigned long flags; - mutex_lock(&dev->lock); - dev->lirc_open--; - mutex_unlock(&dev->lock); + spin_lock_irqsave(&dev->lirc_fh_lock, flags); + list_del(&fh->list); + spin_unlock_irqrestore(&dev->lirc_fh_lock, flags); + + if (dev->driver_type == RC_DRIVER_IR_RAW) + kfifo_free(&fh->rawir); + if (dev->driver_type != RC_DRIVER_IR_RAW_TX) + kfifo_free(&fh->scancodes); + kfree(fh); rc_close(dev); + put_device(&dev->dev); return 0; } @@ -181,7 +229,8 @@ static int ir_lirc_close(struct inode *inode, struct file *file) static ssize_t ir_lirc_transmit_ir(struct file *file, const char __user *buf, size_t n, loff_t *ppos) { - struct rc_dev *dev = file->private_data; + struct lirc_fh *fh = file->private_data; + struct rc_dev *dev = fh->rc; unsigned int *txbuf = NULL; struct ir_raw_event *raw = NULL; ssize_t ret = -EINVAL; @@ -201,7 +250,7 @@ static ssize_t ir_lirc_transmit_ir(struct file *file, const char __user *buf, goto out; } - if (dev->send_mode == LIRC_MODE_SCANCODE) { + if (fh->send_mode == LIRC_MODE_SCANCODE) { struct lirc_scancode scan; if (n != sizeof(scan)) @@ -276,7 +325,7 @@ static ssize_t ir_lirc_transmit_ir(struct file *file, const char __user *buf, if (ret < 0) goto out; - if (dev->send_mode == LIRC_MODE_SCANCODE) { + if (fh->send_mode == LIRC_MODE_SCANCODE) { ret = n; } else { for (duration = i = 0; i < ret; i++) @@ -303,10 +352,11 @@ out: return ret; } -static long ir_lirc_ioctl(struct file *filep, unsigned int cmd, +static long ir_lirc_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - struct rc_dev *dev = filep->private_data; + struct lirc_fh *fh = file->private_data; + struct rc_dev *dev = fh->rc; u32 __user *argp = (u32 __user *)(arg); int ret = 0; __u32 val = 0, tmp; @@ -361,7 +411,7 @@ static long ir_lirc_ioctl(struct file *filep, unsigned int cmd, if (dev->driver_type == RC_DRIVER_IR_RAW_TX) return -ENOTTY; - val = dev->rec_mode; + val = fh->rec_mode; break; case LIRC_SET_REC_MODE: @@ -379,14 +429,14 @@ static long ir_lirc_ioctl(struct file *filep, unsigned int cmd, break; } - dev->rec_mode = val; + fh->rec_mode = val; return 0; case LIRC_GET_SEND_MODE: if (!dev->tx_ir) return -ENOTTY; - val = dev->send_mode; + val = fh->send_mode; break; case LIRC_SET_SEND_MODE: @@ -396,7 +446,7 @@ static long ir_lirc_ioctl(struct file *filep, unsigned int cmd, if (!(val == LIRC_MODE_PULSE || val == LIRC_MODE_SCANCODE)) return -EINVAL; - dev->send_mode = val; + fh->send_mode = val; return 0; /* TX settings */ @@ -430,7 +480,7 @@ static long ir_lirc_ioctl(struct file *filep, unsigned int cmd, return -EINVAL; return dev->s_rx_carrier_range(dev, - dev->carrier_low, + fh->carrier_low, val); case LIRC_SET_REC_CARRIER_RANGE: @@ -440,7 +490,7 @@ static long ir_lirc_ioctl(struct file *filep, unsigned int cmd, if (val <= 0) return -EINVAL; - dev->carrier_low = val; + fh->carrier_low = val; return 0; case LIRC_GET_REC_RESOLUTION: @@ -498,7 +548,7 @@ static long ir_lirc_ioctl(struct file *filep, unsigned int cmd, if (!dev->timeout) return -ENOTTY; - dev->send_timeout_reports = !!val; + fh->send_timeout_reports = !!val; break; default: @@ -514,20 +564,21 @@ static long ir_lirc_ioctl(struct file *filep, unsigned int cmd, static unsigned int ir_lirc_poll(struct file *file, struct poll_table_struct *wait) { - struct rc_dev *rcdev = file->private_data; + struct lirc_fh *fh = file->private_data; + struct rc_dev *rcdev = fh->rc; unsigned int events = 0; - poll_wait(file, &rcdev->wait_poll, wait); + poll_wait(file, &fh->wait_poll, wait); if (!rcdev->registered) { events = POLLHUP | POLLERR; } else if (rcdev->driver_type != RC_DRIVER_IR_RAW_TX) { - if (rcdev->rec_mode == LIRC_MODE_SCANCODE && - !kfifo_is_empty(&rcdev->scancodes)) + if (fh->rec_mode == LIRC_MODE_SCANCODE && + !kfifo_is_empty(&fh->scancodes)) events = POLLIN | POLLRDNORM; - if (rcdev->rec_mode == LIRC_MODE_MODE2 && - !kfifo_is_empty(&rcdev->rawir)) + if (fh->rec_mode == LIRC_MODE_MODE2 && + !kfifo_is_empty(&fh->rawir)) events = POLLIN | POLLRDNORM; } @@ -537,7 +588,8 @@ static unsigned int ir_lirc_poll(struct file *file, static ssize_t ir_lirc_read_mode2(struct file *file, char __user *buffer, size_t length) { - struct rc_dev *rcdev = file->private_data; + struct lirc_fh *fh = file->private_data; + struct rc_dev *rcdev = fh->rc; unsigned int copied; int ret; @@ -545,12 +597,12 @@ static ssize_t ir_lirc_read_mode2(struct file *file, char __user *buffer, return -EINVAL; do { - if (kfifo_is_empty(&rcdev->rawir)) { + if (kfifo_is_empty(&fh->rawir)) { if (file->f_flags & O_NONBLOCK) return -EAGAIN; - ret = wait_event_interruptible(rcdev->wait_poll, - !kfifo_is_empty(&rcdev->rawir) || + ret = wait_event_interruptible(fh->wait_poll, + !kfifo_is_empty(&fh->rawir) || !rcdev->registered); if (ret) return ret; @@ -562,7 +614,7 @@ static ssize_t ir_lirc_read_mode2(struct file *file, char __user *buffer, ret = mutex_lock_interruptible(&rcdev->lock); if (ret) return ret; - ret = kfifo_to_user(&rcdev->rawir, buffer, length, &copied); + ret = kfifo_to_user(&fh->rawir, buffer, length, &copied); mutex_unlock(&rcdev->lock); if (ret) return ret; @@ -574,7 +626,8 @@ static ssize_t ir_lirc_read_mode2(struct file *file, char __user *buffer, static ssize_t ir_lirc_read_scancode(struct file *file, char __user *buffer, size_t length) { - struct rc_dev *rcdev = file->private_data; + struct lirc_fh *fh = file->private_data; + struct rc_dev *rcdev = fh->rc; unsigned int copied; int ret; @@ -583,12 +636,12 @@ static ssize_t ir_lirc_read_scancode(struct file *file, char __user *buffer, return -EINVAL; do { - if (kfifo_is_empty(&rcdev->scancodes)) { + if (kfifo_is_empty(&fh->scancodes)) { if (file->f_flags & O_NONBLOCK) return -EAGAIN; - ret = wait_event_interruptible(rcdev->wait_poll, - !kfifo_is_empty(&rcdev->scancodes) || + ret = wait_event_interruptible(fh->wait_poll, + !kfifo_is_empty(&fh->scancodes) || !rcdev->registered); if (ret) return ret; @@ -600,7 +653,7 @@ static ssize_t ir_lirc_read_scancode(struct file *file, char __user *buffer, ret = mutex_lock_interruptible(&rcdev->lock); if (ret) return ret; - ret = kfifo_to_user(&rcdev->scancodes, buffer, length, &copied); + ret = kfifo_to_user(&fh->scancodes, buffer, length, &copied); mutex_unlock(&rcdev->lock); if (ret) return ret; @@ -612,7 +665,8 @@ static ssize_t ir_lirc_read_scancode(struct file *file, char __user *buffer, static ssize_t ir_lirc_read(struct file *file, char __user *buffer, size_t length, loff_t *ppos) { - struct rc_dev *rcdev = file->private_data; + struct lirc_fh *fh = file->private_data; + struct rc_dev *rcdev = fh->rc; if (rcdev->driver_type == RC_DRIVER_IR_RAW_TX) return -EINVAL; @@ -620,7 +674,7 @@ static ssize_t ir_lirc_read(struct file *file, char __user *buffer, if (!rcdev->registered) return -ENODEV; - if (rcdev->rec_mode == LIRC_MODE_MODE2) + if (fh->rec_mode == LIRC_MODE_MODE2) return ir_lirc_read_mode2(file, buffer, length); else /* LIRC_MODE_SCANCODE */ return ir_lirc_read_scancode(file, buffer, length); @@ -644,11 +698,6 @@ static void lirc_release_device(struct device *ld) { struct rc_dev *rcdev = container_of(ld, struct rc_dev, lirc_dev); - if (rcdev->driver_type == RC_DRIVER_IR_RAW) - kfifo_free(&rcdev->rawir); - if (rcdev->driver_type != RC_DRIVER_IR_RAW_TX) - kfifo_free(&rcdev->scancodes); - put_device(&rcdev->dev); } @@ -656,40 +705,20 @@ int ir_lirc_register(struct rc_dev *dev) { int err, minor; - device_initialize(&dev->lirc_dev); - dev->lirc_dev.class = lirc_class; - dev->lirc_dev.release = lirc_release_device; - dev->send_mode = LIRC_MODE_PULSE; - - if (dev->driver_type == RC_DRIVER_SCANCODE) - dev->rec_mode = LIRC_MODE_SCANCODE; - else - dev->rec_mode = LIRC_MODE_MODE2; - - if (dev->driver_type == RC_DRIVER_IR_RAW) { - if (kfifo_alloc(&dev->rawir, MAX_IR_EVENT_SIZE, GFP_KERNEL)) - return -ENOMEM; - } - - if (dev->driver_type != RC_DRIVER_IR_RAW_TX) { - if (kfifo_alloc(&dev->scancodes, 32, GFP_KERNEL)) { - kfifo_free(&dev->rawir); - return -ENOMEM; - } - } - - init_waitqueue_head(&dev->wait_poll); - minor = ida_simple_get(&lirc_ida, 0, RC_DEV_MAX, GFP_KERNEL); - if (minor < 0) { - err = minor; - goto out_kfifo; - } + if (minor < 0) + return minor; + device_initialize(&dev->lirc_dev); + dev->lirc_dev.class = lirc_class; dev->lirc_dev.parent = &dev->dev; + dev->lirc_dev.release = lirc_release_device; dev->lirc_dev.devt = MKDEV(MAJOR(lirc_base_dev), minor); dev_set_name(&dev->lirc_dev, "lirc%d", minor); + INIT_LIST_HEAD(&dev->lirc_fh); + spin_lock_init(&dev->lirc_fh_lock); + cdev_init(&dev->lirc_cdev, &lirc_fops); err = cdev_device_add(&dev->lirc_cdev, &dev->lirc_dev); @@ -705,32 +734,24 @@ int ir_lirc_register(struct rc_dev *dev) out_ida: ida_simple_remove(&lirc_ida, minor); -out_kfifo: - if (dev->driver_type == RC_DRIVER_IR_RAW) - kfifo_free(&dev->rawir); - if (dev->driver_type != RC_DRIVER_IR_RAW_TX) - kfifo_free(&dev->scancodes); return err; } void ir_lirc_unregister(struct rc_dev *dev) { + unsigned long flags; + struct lirc_fh *fh; + dev_dbg(&dev->dev, "lirc_dev: driver %s unregistered from minor = %d\n", dev->driver_name, MINOR(dev->lirc_dev.devt)); - mutex_lock(&dev->lock); - - if (dev->lirc_open) { - dev_dbg(&dev->dev, LOGHEAD "releasing opened driver\n", - dev->driver_name, MINOR(dev->lirc_dev.devt)); - wake_up_poll(&dev->wait_poll, POLLHUP); - } - - mutex_unlock(&dev->lock); + spin_lock_irqsave(&dev->lirc_fh_lock, flags); + list_for_each_entry(fh, &dev->lirc_fh, list) + wake_up_poll(&fh->wait_poll, POLLHUP | POLLERR); + spin_unlock_irqrestore(&dev->lirc_fh_lock, flags); cdev_device_del(&dev->lirc_cdev, &dev->lirc_dev); ida_simple_remove(&lirc_ida, MINOR(dev->lirc_dev.devt)); - put_device(&dev->lirc_dev); } int __init lirc_dev_init(void) diff --git a/include/media/rc-core.h b/include/media/rc-core.h index fbf1648d2ec9..3a47a25a6593 100644 --- a/include/media/rc-core.h +++ b/include/media/rc-core.h @@ -69,6 +69,33 @@ enum rc_filter_type { }; /** + * struct lirc_fh - represents an open lirc file + * @list: list of open file handles + * @rc: rcdev for this lirc chardev + * @carrier_low: when setting the carrier range, first the low end must be + * set with an ioctl and then the high end with another ioctl + * @send_timeout_reports: report timeouts in lirc raw IR. + * @rawir: queue for incoming raw IR + * @scancodes: queue for incoming decoded scancodes + * @wait_poll: poll struct for lirc device + * @send_mode: lirc mode for sending, either LIRC_MODE_SCANCODE or + * LIRC_MODE_PULSE + * @rec_mode: lirc mode for receiving, either LIRC_MODE_SCANCODE or + * LIRC_MODE_MODE2 + */ +struct lirc_fh { + struct list_head list; + struct rc_dev *rc; + int carrier_low; + bool send_timeout_reports; + DECLARE_KFIFO_PTR(rawir, unsigned int); + DECLARE_KFIFO_PTR(scancodes, struct lirc_scancode); + wait_queue_head_t wait_poll; + u8 send_mode; + u8 rec_mode; +}; + +/** * struct rc_dev - represents a remote control device * @dev: driver model's view of this device * @managed_alloc: devm_rc_allocate_device was used to create rc_dev @@ -118,20 +145,11 @@ enum rc_filter_type { * @tx_resolution: resolution (in ns) of output sampler * @lirc_dev: lirc device * @lirc_cdev: lirc char cdev - * @lirc_open: count of the number of times the device has been opened - * @carrier_low: when setting the carrier range, first the low end must be - * set with an ioctl and then the high end with another ioctl * @gap_start: time when gap starts * @gap_duration: duration of initial gap * @gap: true if we're in a gap - * @send_timeout_reports: report timeouts in lirc raw IR. - * @rawir: queue for incoming raw IR - * @scancodes: queue for incoming decoded scancodes - * @wait_poll: poll struct for lirc device - * @send_mode: lirc mode for sending, either LIRC_MODE_SCANCODE or - * LIRC_MODE_PULSE - * @rec_mode: lirc mode for receiving, either LIRC_MODE_SCANCODE or - * LIRC_MODE_MODE2 + * @lirc_fh_lock: protects lirc_fh list + * @lirc_fh: list of open files * @registered: set to true by rc_register_device(), false by * rc_unregister_device * @change_protocol: allow changing the protocol used on hardware decoders @@ -196,17 +214,11 @@ struct rc_dev { #ifdef CONFIG_LIRC struct device lirc_dev; struct cdev lirc_cdev; - int lirc_open; - int carrier_low; ktime_t gap_start; u64 gap_duration; bool gap; - bool send_timeout_reports; - DECLARE_KFIFO_PTR(rawir, unsigned int); - DECLARE_KFIFO_PTR(scancodes, struct lirc_scancode); - wait_queue_head_t wait_poll; - u8 send_mode; - u8 rec_mode; + spinlock_t lirc_fh_lock; + struct list_head lirc_fh; #endif bool registered; int (*change_protocol)(struct rc_dev *dev, u64 *rc_proto); |