Since a lirc char device can only be opened once, there can only be one reader. By using a plain kfifo we don't need a spinlock and we can use kfifo_to_user. The code is much simplified. Unfortunately we cannot eliminate lirc_buffer from the tree yet, as there are still some staging lirc drivers which use it. Signed-off-by: Sean Young <sean@xxxxxxxx> --- drivers/media/rc/ir-lirc-codec.c | 136 ++++++++++++++++++++++++--------------- drivers/media/rc/lirc_dev.c | 5 +- drivers/media/rc/rc-core-priv.h | 33 ++++++---- include/media/rc-core.h | 2 + 4 files changed, 107 insertions(+), 69 deletions(-) diff --git a/drivers/media/rc/ir-lirc-codec.c b/drivers/media/rc/ir-lirc-codec.c index 78f354a..74f7863 100644 --- a/drivers/media/rc/ir-lirc-codec.c +++ b/drivers/media/rc/ir-lirc-codec.c @@ -19,23 +19,16 @@ #include <media/rc-core.h> #include "rc-core-priv.h" -#define LIRCBUF_SIZE 256 - /** * ir_lirc_raw_event() - Send raw IR data to lirc to be relayed to userspace * * @input_dev: the struct rc_dev descriptor of the device * @duration: the struct ir_raw_event descriptor of the pulse/space - * - * This function returns -EINVAL if the lirc interfaces aren't wired up. */ -int ir_lirc_raw_event(struct rc_dev *dev, struct ir_raw_event ev) +void ir_lirc_raw_event(struct rc_dev *dev, struct ir_raw_event ev) { - struct lirc_codec *lirc = &dev->raw->lirc; - int sample; - - if (!dev->raw->lirc.drv || !dev->raw->lirc.drv->rbuf) - return -EINVAL; + struct lirc_node *lirc = dev->lirc; + unsigned int sample; /* Packet start */ if (ev.reset) { @@ -54,26 +47,22 @@ int ir_lirc_raw_event(struct rc_dev *dev, struct ir_raw_event ev) /* Packet end */ } else if (ev.timeout) { - if (lirc->gap) - return 0; + return; lirc->gap_start = ktime_get(); lirc->gap = true; lirc->gap_duration = ev.duration; if (!lirc->send_timeout_reports) - return 0; + return; sample = LIRC_TIMEOUT(ev.duration / 1000); IR_dprintk(2, "timeout report (duration: %d)\n", sample); /* Normal sample */ } else { - if (lirc->gap) { - int gap_sample; - lirc->gap_duration += ktime_to_ns(ktime_sub(ktime_get(), lirc->gap_start)); @@ -82,9 +71,7 @@ int ir_lirc_raw_event(struct rc_dev *dev, struct ir_raw_event ev) lirc->gap_duration = min(lirc->gap_duration, (u64)LIRC_VALUE_MASK); - gap_sample = LIRC_SPACE(lirc->gap_duration); - lirc_buffer_write(dev->raw->lirc.drv->rbuf, - (unsigned char *) &gap_sample); + kfifo_put(&lirc->rawir, LIRC_SPACE(lirc->gap_duration)); lirc->gap = false; } @@ -94,17 +81,14 @@ int ir_lirc_raw_event(struct rc_dev *dev, struct ir_raw_event ev) TO_US(ev.duration), TO_STR(ev.pulse)); } - lirc_buffer_write(dev->raw->lirc.drv->rbuf, - (unsigned char *) &sample); - wake_up(&dev->raw->lirc.drv->rbuf->wait_poll); - - return 0; + kfifo_put(&lirc->rawir, sample); + wake_up_poll(&lirc->wait_poll, POLLIN | POLLRDNORM); } static ssize_t ir_lirc_transmit_ir(struct file *file, const char __user *buf, size_t n, loff_t *ppos) { - struct lirc_codec *lirc; + struct lirc_node *lirc; struct rc_dev *dev; unsigned int *txbuf; /* buffer with values to transmit */ ssize_t ret = -EINVAL; @@ -179,7 +163,7 @@ static ssize_t ir_lirc_transmit_ir(struct file *file, const char __user *buf, static long ir_lirc_ioctl(struct file *filep, unsigned int cmd, unsigned long arg) { - struct lirc_codec *lirc; + struct lirc_node *lirc; struct rc_dev *dev; u32 __user *argp = (u32 __user *)(arg); int ret = 0; @@ -248,7 +232,7 @@ static long ir_lirc_ioctl(struct file *filep, unsigned int cmd, return -EINVAL; return dev->s_rx_carrier_range(dev, - dev->raw->lirc.carrier_low, + dev->lirc->carrier_low, val); case LIRC_SET_REC_CARRIER_RANGE: @@ -258,7 +242,7 @@ static long ir_lirc_ioctl(struct file *filep, unsigned int cmd, if (val <= 0) return -EINVAL; - dev->raw->lirc.carrier_low = val; + dev->lirc->carrier_low = val; return 0; case LIRC_GET_REC_RESOLUTION: @@ -326,8 +310,64 @@ static long ir_lirc_ioctl(struct file *filep, unsigned int cmd, return ret; } +static unsigned int ir_lirc_poll(struct file *filep, + struct poll_table_struct *wait) +{ + struct lirc_node *lirc = lirc_get_pdata(filep); + unsigned int events = 0; + + poll_wait(filep, &lirc->wait_poll, wait); + + if (!lirc->drv.attached) + events = POLLHUP; + else if (!kfifo_is_empty(&lirc->rawir)) + events = POLLIN | POLLRDNORM; + + return events; +} + +static ssize_t ir_lirc_read(struct file *filep, char __user *buffer, + size_t length, loff_t *ppos) +{ + struct lirc_node *lirc = lirc_get_pdata(filep); + unsigned int copied; + int ret; + + if (length % sizeof(unsigned int)) + return -EINVAL; + + if (!lirc->drv.attached) + return -ENODEV; + + do { + if (kfifo_is_empty(&lirc->rawir)) { + if (filep->f_flags & O_NONBLOCK) + return -EAGAIN; + + ret = wait_event_interruptible(lirc->wait_poll, + !kfifo_is_empty(&lirc->rawir) || + !lirc->drv.attached); + if (ret) + return ret; + } + + if (!lirc->drv.attached) + return -ENODEV; + + ret = kfifo_to_user(&lirc->rawir, buffer, length, &copied); + if (ret) + return ret; + } while (copied == 0); + + return copied; +} + static int ir_lirc_open(void *data) { + struct lirc_node *lirc = data; + + kfifo_reset_out(&lirc->rawir); + return 0; } @@ -343,8 +383,8 @@ static const struct file_operations lirc_fops = { #ifdef CONFIG_COMPAT .compat_ioctl = ir_lirc_ioctl, #endif - .read = lirc_dev_fop_read, - .poll = lirc_dev_fop_poll, + .read = ir_lirc_read, + .poll = ir_lirc_poll, .open = lirc_dev_fop_open, .release = lirc_dev_fop_close, .llseek = no_llseek, @@ -353,22 +393,14 @@ static const struct file_operations lirc_fops = { int ir_lirc_register(struct rc_dev *dev) { struct lirc_driver *drv; - struct lirc_buffer *rbuf; + struct lirc_node *node; int rc = -ENOMEM; unsigned long features = 0; - drv = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL); - if (!drv) + node = kzalloc(sizeof(*node), GFP_KERNEL); + if (!node) return rc; - rbuf = kzalloc(sizeof(struct lirc_buffer), GFP_KERNEL); - if (!rbuf) - goto rbuf_alloc_failed; - - rc = lirc_buffer_init(rbuf, sizeof(int), LIRCBUF_SIZE); - if (rc) - goto rbuf_init_failed; - if (dev->driver_type != RC_DRIVER_IR_RAW_TX) { features |= LIRC_CAN_REC_MODE2; if (dev->rx_resolution) @@ -397,12 +429,12 @@ int ir_lirc_register(struct rc_dev *dev) if (dev->max_timeout) features |= LIRC_CAN_SET_REC_TIMEOUT; + drv = &node->drv; snprintf(drv->name, sizeof(drv->name), "ir-lirc-codec (%s)", dev->driver_name); drv->minor = -1; drv->features = features; - drv->data = &dev->raw->lirc; - drv->rbuf = rbuf; + drv->data = node; drv->set_use_inc = &ir_lirc_open; drv->set_use_dec = &ir_lirc_close; drv->code_length = sizeof(struct ir_raw_event) * 8; @@ -411,30 +443,28 @@ int ir_lirc_register(struct rc_dev *dev) drv->rdev = dev; drv->owner = THIS_MODULE; + INIT_KFIFO(node->rawir); + init_waitqueue_head(&node->wait_poll); + drv->minor = lirc_register_driver(drv); if (drv->minor < 0) { rc = -ENODEV; goto lirc_register_failed; } - dev->raw->lirc.drv = drv; - dev->raw->lirc.dev = dev; + node->dev = dev; + dev->lirc = node; return 0; lirc_register_failed: -rbuf_init_failed: - kfree(rbuf); -rbuf_alloc_failed: kfree(drv); - return rc; } void ir_lirc_unregister(struct rc_dev *dev) { - struct lirc_codec *lirc = &dev->raw->lirc; + struct lirc_node *lirc = dev->lirc; - lirc_unregister_driver(lirc->drv->minor); - lirc_buffer_free(lirc->drv->rbuf); - kfree(lirc->drv->rbuf); + wake_up_poll(&lirc->wait_poll, POLLHUP); + lirc_unregister_driver(lirc->drv.minor); } diff --git a/drivers/media/rc/lirc_dev.c b/drivers/media/rc/lirc_dev.c index 44650e4..7d705af 100644 --- a/drivers/media/rc/lirc_dev.c +++ b/drivers/media/rc/lirc_dev.c @@ -328,7 +328,7 @@ int lirc_register_driver(struct lirc_driver *d) if (minor < 0) return minor; - if (LIRC_CAN_REC(d->features)) { + if (!d->rdev) { err = lirc_allocate_buffer(irctls[minor]); if (err) lirc_unregister_driver(minor); @@ -374,7 +374,8 @@ int lirc_unregister_driver(int minor) if (d->open) { dev_dbg(d->dev.parent, LOGHEAD "releasing opened driver\n", d->name, d->minor); - wake_up_interruptible(&d->buf->wait_poll); + if (d->buf) + wake_up_interruptible(&d->buf->wait_poll); } mutex_lock(&d->irctl_lock); diff --git a/drivers/media/rc/rc-core-priv.h b/drivers/media/rc/rc-core-priv.h index da31738..9b561c3 100644 --- a/drivers/media/rc/rc-core-priv.h +++ b/drivers/media/rc/rc-core-priv.h @@ -19,7 +19,11 @@ /* Define the max number of pulse/space transitions to buffer */ #define MAX_IR_EVENT_SIZE 512 +/* Define the number of samples lirc can buffer or transmit */ +#define LIRCBUF_SIZE 256 + #include <linux/slab.h> +#include <media/lirc_dev.h> #include <media/rc-core.h> struct ir_raw_handler { @@ -35,6 +39,18 @@ struct ir_raw_handler { int (*raw_unregister)(struct rc_dev *dev); }; +struct lirc_node { + struct lirc_driver drv; + struct rc_dev *dev; + int carrier_low; + DECLARE_KFIFO(rawir, unsigned int, LIRCBUF_SIZE); + wait_queue_head_t wait_poll; + ktime_t gap_start; + u64 gap_duration; + bool gap; + bool send_timeout_reports; +}; + struct ir_raw_event_ctrl { struct list_head list; /* to keep track of raw clients */ struct task_struct *thread; @@ -103,17 +119,6 @@ struct ir_raw_event_ctrl { unsigned count; unsigned wanted_bits; } mce_kbd; - struct lirc_codec { - struct rc_dev *dev; - struct lirc_driver *drv; - int carrier_low; - - ktime_t gap_start; - u64 gap_duration; - bool gap; - bool send_timeout_reports; - - } lirc; struct xmp_dec { int state; unsigned count; @@ -273,12 +278,12 @@ void ir_raw_init(void); * lirc interface bridge */ #ifdef CONFIG_IR_LIRC_CODEC -int ir_lirc_raw_event(struct rc_dev *dev, struct ir_raw_event ev); +void ir_lirc_raw_event(struct rc_dev *dev, struct ir_raw_event ev); int ir_lirc_register(struct rc_dev *dev); void ir_lirc_unregister(struct rc_dev *dev); #else -static inline int ir_lirc_raw_event(struct rc_dev *dev, - struct ir_raw_event ev) { return 0; } +static inline void ir_lirc_raw_event(struct rc_dev *dev, + struct ir_raw_event ev) { } static inline int ir_lirc_register(struct rc_dev *dev) { return 0; } static inline void ir_lirc_unregister(struct rc_dev *dev) { } #endif diff --git a/include/media/rc-core.h b/include/media/rc-core.h index 73ddd721..45e8623 100644 --- a/include/media/rc-core.h +++ b/include/media/rc-core.h @@ -115,6 +115,7 @@ enum rc_filter_type { * @max_timeout: maximum timeout supported by device * @rx_resolution : resolution (in ns) of input sampler * @tx_resolution: resolution (in ns) of output sampler + * @lirc: lirc chardev node * @change_protocol: allow changing the protocol used on hardware decoders * @open: callback to allow drivers to enable polling/irq when IR input device * is opened. @@ -175,6 +176,7 @@ struct rc_dev { u32 max_timeout; u32 rx_resolution; u32 tx_resolution; + struct lirc_node *lirc; int (*change_protocol)(struct rc_dev *dev, u64 *rc_type); int (*open)(struct rc_dev *dev); void (*close)(struct rc_dev *dev); -- 2.9.3