All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/5] lirc improvements
@ 2017-11-06 10:40 Sean Young
  2017-11-06 10:40 ` [PATCH 1/5] media: rc: move ir-lirc-codec.c contents into lirc_dev.c Sean Young
                   ` (4 more replies)
  0 siblings, 5 replies; 6+ messages in thread
From: Sean Young @ 2017-11-06 10:40 UTC (permalink / raw)
  To: linux-media

This patch series depend on the lirc scancode and lirc kapi removal
series.

This implements correct locking for lirc and allows the chardev to be
opened more than once.

Sean Young (5):
  media: rc: move ir-lirc-codec.c contents into lirc_dev.c
  media: rc: include <uapi/linux/lirc.h> rather than <media/lirc.h>
  media: lirc: allow lirc device to opened more than once
  media: lirc: improve locking
  media: rc: iguanair: remove unnecessary locking

 drivers/media/rc/Makefile        |   2 +-
 drivers/media/rc/iguanair.c      |  28 --
 drivers/media/rc/ir-lirc-codec.c | 637 --------------------------------
 drivers/media/rc/lirc_dev.c      | 771 ++++++++++++++++++++++++++++++++++++---
 drivers/media/rc/rc-core-priv.h  |   2 -
 include/media/lirc.h             |   1 -
 include/media/rc-core.h          |  56 +--
 7 files changed, 760 insertions(+), 737 deletions(-)
 delete mode 100644 drivers/media/rc/ir-lirc-codec.c
 delete mode 100644 include/media/lirc.h

-- 
2.13.6

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 1/5] media: rc: move ir-lirc-codec.c contents into lirc_dev.c
  2017-11-06 10:40 [PATCH 0/5] lirc improvements Sean Young
@ 2017-11-06 10:40 ` Sean Young
  2017-11-06 10:40 ` [PATCH 2/5] media: rc: include <uapi/linux/lirc.h> rather than <media/lirc.h> Sean Young
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Sean Young @ 2017-11-06 10:40 UTC (permalink / raw)
  To: linux-media

Since removing the lirc kapi, ir-lirc-codec.c only contains lirc fops
so the file name is no longer correct. By moving its content into
lirc_dev.c the ugly extern struct lirc_fops is not longer needed,
and everything lirc related is in one file.

Signed-off-by: Sean Young <sean@mess.org>
---
 drivers/media/rc/Makefile        |   2 +-
 drivers/media/rc/ir-lirc-codec.c | 637 ---------------------------------------
 drivers/media/rc/lirc_dev.c      | 618 +++++++++++++++++++++++++++++++++++++
 drivers/media/rc/rc-core-priv.h  |   2 -
 4 files changed, 619 insertions(+), 640 deletions(-)
 delete mode 100644 drivers/media/rc/ir-lirc-codec.c

diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile
index a1ef86767aef..9d487988b78d 100644
--- a/drivers/media/rc/Makefile
+++ b/drivers/media/rc/Makefile
@@ -3,7 +3,7 @@ obj-y += keymaps/
 
 obj-$(CONFIG_RC_CORE) += rc-core.o
 rc-core-y := rc-main.o rc-ir-raw.o
-rc-core-$(CONFIG_LIRC) += lirc_dev.o ir-lirc-codec.o
+rc-core-$(CONFIG_LIRC) += lirc_dev.o
 obj-$(CONFIG_IR_NEC_DECODER) += ir-nec-decoder.o
 obj-$(CONFIG_IR_RC5_DECODER) += ir-rc5-decoder.o
 obj-$(CONFIG_IR_RC6_DECODER) += ir-rc6-decoder.o
diff --git a/drivers/media/rc/ir-lirc-codec.c b/drivers/media/rc/ir-lirc-codec.c
deleted file mode 100644
index 78934479bcff..000000000000
--- a/drivers/media/rc/ir-lirc-codec.c
+++ /dev/null
@@ -1,637 +0,0 @@
-/* ir-lirc-codec.c - rc-core to classic lirc interface bridge
- *
- * Copyright (C) 2010 by Jarod Wilson <jarod@redhat.com>
- *
- * This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation version 2 of the License.
- *
- *  This program is distributed in the hope that it will be useful,
- *  but WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *  GNU General Public License for more details.
- */
-
-#include <linux/poll.h>
-#include <linux/sched.h>
-#include <linux/wait.h>
-#include <media/lirc.h>
-#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
- *
- * @dev:	the struct rc_dev descriptor of the device
- * @ev:		the struct ir_raw_event descriptor of the pulse/space
- */
-void ir_lirc_raw_event(struct rc_dev *dev, struct ir_raw_event ev)
-{
-	int sample;
-
-	/* Packet start */
-	if (ev.reset) {
-		/* Userspace expects a long space event before the start of
-		 * the signal to use as a sync.  This may be done with repeat
-		 * packets and normal samples.  But if a reset has been sent
-		 * then we assume that a long time has passed, so we send a
-		 * space with the maximum time value. */
-		sample = LIRC_SPACE(LIRC_VALUE_MASK);
-		IR_dprintk(2, "delivering reset sync space to lirc_dev\n");
-
-	/* Carrier reports */
-	} else if (ev.carrier_report) {
-		sample = LIRC_FREQUENCY(ev.carrier);
-		IR_dprintk(2, "carrier report (freq: %d)\n", sample);
-
-	/* Packet end */
-	} else if (ev.timeout) {
-
-		if (dev->gap)
-			return;
-
-		dev->gap_start = ktime_get();
-		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);
-
-	/* Normal sample */
-	} else {
-
-		if (dev->gap) {
-			dev->gap_duration += ktime_to_ns(ktime_sub(ktime_get(),
-							 dev->gap_start));
-
-			/* Convert to ms and cap by LIRC_VALUE_MASK */
-			do_div(dev->gap_duration, 1000);
-			dev->gap_duration = min_t(u64, dev->gap_duration,
-						  LIRC_VALUE_MASK);
-
-			kfifo_put(&dev->rawir, LIRC_SPACE(dev->gap_duration));
-			dev->gap = false;
-		}
-
-		sample = ev.pulse ? LIRC_PULSE(ev.duration / 1000) :
-					LIRC_SPACE(ev.duration / 1000);
-		IR_dprintk(2, "delivering %uus %s to lirc_dev\n",
-			   TO_US(ev.duration), TO_STR(ev.pulse));
-	}
-
-	kfifo_put(&dev->rawir, sample);
-	wake_up_poll(&dev->wait_poll, POLLIN | POLLRDNORM);
-}
-
-/**
- * ir_lirc_scancode_event() - Send scancode data to lirc to be relayed to
- *		userspace
- * @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)
-{
-	lsc->timestamp = ktime_get_ns();
-
-	if (kfifo_put(&dev->scancodes, *lsc))
-		wake_up_poll(&dev->wait_poll, POLLIN | POLLRDNORM);
-}
-EXPORT_SYMBOL_GPL(ir_lirc_scancode_event);
-
-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);
-	int retval;
-
-	retval = rc_open(dev);
-	if (retval)
-		return retval;
-
-	retval = mutex_lock_interruptible(&dev->lock);
-	if (retval)
-		goto out_rc;
-
-	if (!dev->registered) {
-		retval = -ENODEV;
-		goto out_unlock;
-	}
-
-	if (dev->lirc_open) {
-		retval = -EBUSY;
-		goto out_unlock;
-	}
-
-	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);
-
-	dev->lirc_open++;
-	file->private_data = dev;
-
-	nonseekable_open(inode, file);
-	mutex_unlock(&dev->lock);
-
-	return 0;
-
-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;
-
-	mutex_lock(&dev->lock);
-	dev->lirc_open--;
-	mutex_unlock(&dev->lock);
-
-	rc_close(dev);
-
-	return 0;
-}
-
-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;
-	unsigned int *txbuf = NULL;
-	struct ir_raw_event *raw = NULL;
-	ssize_t ret = -EINVAL;
-	size_t count;
-	ktime_t start;
-	s64 towait;
-	unsigned int duration = 0; /* signal duration in us */
-	int i;
-
-	if (!dev->registered)
-		return -ENODEV;
-
-	start = ktime_get();
-
-	if (!dev->tx_ir) {
-		ret = -EINVAL;
-		goto out;
-	}
-
-	if (dev->send_mode == LIRC_MODE_SCANCODE) {
-		struct lirc_scancode scan;
-
-		if (n != sizeof(scan))
-			return -EINVAL;
-
-		if (copy_from_user(&scan, buf, sizeof(scan)))
-			return -EFAULT;
-
-		if (scan.flags || scan.keycode || scan.timestamp)
-			return -EINVAL;
-
-		/*
-		 * The scancode field in lirc_scancode is 64-bit simply
-		 * to future-proof it, since there are IR protocols encode
-		 * use more than 32 bits. For now only 32-bit protocols
-		 * are supported.
-		 */
-		if (scan.scancode > U32_MAX ||
-		    !rc_validate_scancode(scan.rc_proto, scan.scancode))
-			return -EINVAL;
-
-		raw = kmalloc_array(LIRCBUF_SIZE, sizeof(*raw), GFP_KERNEL);
-		if (!raw)
-			return -ENOMEM;
-
-		ret = ir_raw_encode_scancode(scan.rc_proto, scan.scancode,
-					     raw, LIRCBUF_SIZE);
-		if (ret < 0)
-			goto out;
-
-		count = ret;
-
-		txbuf = kmalloc_array(count, sizeof(unsigned int), GFP_KERNEL);
-		if (!txbuf) {
-			ret = -ENOMEM;
-			goto out;
-		}
-
-		for (i = 0; i < count; i++)
-			/* Convert from NS to US */
-			txbuf[i] = DIV_ROUND_UP(raw[i].duration, 1000);
-
-		if (dev->s_tx_carrier) {
-			int carrier = ir_raw_encode_carrier(scan.rc_proto);
-
-			if (carrier > 0)
-				dev->s_tx_carrier(dev, carrier);
-		}
-	} else {
-		if (n < sizeof(unsigned int) || n % sizeof(unsigned int))
-			return -EINVAL;
-
-		count = n / sizeof(unsigned int);
-		if (count > LIRCBUF_SIZE || count % 2 == 0)
-			return -EINVAL;
-
-		txbuf = memdup_user(buf, n);
-		if (IS_ERR(txbuf))
-			return PTR_ERR(txbuf);
-	}
-
-	for (i = 0; i < count; i++) {
-		if (txbuf[i] > IR_MAX_DURATION / 1000 - duration || !txbuf[i]) {
-			ret = -EINVAL;
-			goto out;
-		}
-
-		duration += txbuf[i];
-	}
-
-	ret = dev->tx_ir(dev, txbuf, count);
-	if (ret < 0)
-		goto out;
-
-	if (dev->send_mode == LIRC_MODE_SCANCODE) {
-		ret = n;
-	} else {
-		for (duration = i = 0; i < ret; i++)
-			duration += txbuf[i];
-
-		ret *= sizeof(unsigned int);
-
-		/*
-		 * The lircd gap calculation expects the write function to
-		 * wait for the actual IR signal to be transmitted before
-		 * returning.
-		 */
-		towait = ktime_us_delta(ktime_add_us(start, duration),
-					ktime_get());
-		if (towait > 0) {
-			set_current_state(TASK_INTERRUPTIBLE);
-			schedule_timeout(usecs_to_jiffies(towait));
-		}
-	}
-
-out:
-	kfree(txbuf);
-	kfree(raw);
-	return ret;
-}
-
-static long ir_lirc_ioctl(struct file *filep, unsigned int cmd,
-			unsigned long arg)
-{
-	struct rc_dev *dev = filep->private_data;
-	u32 __user *argp = (u32 __user *)(arg);
-	int ret = 0;
-	__u32 val = 0, tmp;
-
-	if (_IOC_DIR(cmd) & _IOC_WRITE) {
-		ret = get_user(val, argp);
-		if (ret)
-			return ret;
-	}
-
-	if (!dev->registered)
-		return -ENODEV;
-
-	switch (cmd) {
-	case LIRC_GET_FEATURES:
-		if (dev->driver_type == RC_DRIVER_SCANCODE)
-			val |= LIRC_CAN_REC_SCANCODE;
-
-		if (dev->driver_type == RC_DRIVER_IR_RAW) {
-			val |= LIRC_CAN_REC_MODE2 | LIRC_CAN_REC_SCANCODE;
-			if (dev->rx_resolution)
-				val |= LIRC_CAN_GET_REC_RESOLUTION;
-		}
-
-		if (dev->tx_ir) {
-			val |= LIRC_CAN_SEND_PULSE | LIRC_CAN_SEND_SCANCODE;
-			if (dev->s_tx_mask)
-				val |= LIRC_CAN_SET_TRANSMITTER_MASK;
-			if (dev->s_tx_carrier)
-				val |= LIRC_CAN_SET_SEND_CARRIER;
-			if (dev->s_tx_duty_cycle)
-				val |= LIRC_CAN_SET_SEND_DUTY_CYCLE;
-		}
-
-		if (dev->s_rx_carrier_range)
-			val |= LIRC_CAN_SET_REC_CARRIER |
-				LIRC_CAN_SET_REC_CARRIER_RANGE;
-
-		if (dev->s_learning_mode)
-			val |= LIRC_CAN_USE_WIDEBAND_RECEIVER;
-
-		if (dev->s_carrier_report)
-			val |= LIRC_CAN_MEASURE_CARRIER;
-
-		if (dev->max_timeout)
-			val |= LIRC_CAN_SET_REC_TIMEOUT;
-
-		break;
-
-	/* mode support */
-	case LIRC_GET_REC_MODE:
-		if (dev->driver_type == RC_DRIVER_IR_RAW_TX)
-			return -ENOTTY;
-
-		val = dev->rec_mode;
-		break;
-
-	case LIRC_SET_REC_MODE:
-		switch (dev->driver_type) {
-		case RC_DRIVER_IR_RAW_TX:
-			return -ENOTTY;
-		case RC_DRIVER_SCANCODE:
-			if (val != LIRC_MODE_SCANCODE)
-				return -EINVAL;
-			break;
-		case RC_DRIVER_IR_RAW:
-			if (!(val == LIRC_MODE_MODE2 ||
-			      val == LIRC_MODE_SCANCODE))
-				return -EINVAL;
-			break;
-		}
-
-		dev->rec_mode = val;
-		dev->poll_mode = val;
-		return 0;
-
-	case LIRC_SET_POLL_MODES:
-		switch (dev->driver_type) {
-		case RC_DRIVER_IR_RAW_TX:
-			return -ENOTTY;
-		case RC_DRIVER_SCANCODE:
-			if (val != LIRC_MODE_SCANCODE)
-				return -EINVAL;
-			break;
-		case RC_DRIVER_IR_RAW:
-			if (val & ~(LIRC_MODE_MODE2 | LIRC_MODE_SCANCODE))
-				return -EINVAL;
-			break;
-		}
-
-		dev->poll_mode = val;
-		return 0;
-
-	case LIRC_GET_SEND_MODE:
-		if (!dev->tx_ir)
-			return -ENOTTY;
-
-		val = dev->send_mode;
-		break;
-
-	case LIRC_SET_SEND_MODE:
-		if (!dev->tx_ir)
-			return -ENOTTY;
-
-		if (!(val == LIRC_MODE_PULSE || val == LIRC_MODE_SCANCODE))
-			return -EINVAL;
-
-		dev->send_mode = val;
-		return 0;
-
-	/* TX settings */
-	case LIRC_SET_TRANSMITTER_MASK:
-		if (!dev->s_tx_mask)
-			return -ENOTTY;
-
-		return dev->s_tx_mask(dev, val);
-
-	case LIRC_SET_SEND_CARRIER:
-		if (!dev->s_tx_carrier)
-			return -ENOTTY;
-
-		return dev->s_tx_carrier(dev, val);
-
-	case LIRC_SET_SEND_DUTY_CYCLE:
-		if (!dev->s_tx_duty_cycle)
-			return -ENOTTY;
-
-		if (val <= 0 || val >= 100)
-			return -EINVAL;
-
-		return dev->s_tx_duty_cycle(dev, val);
-
-	/* RX settings */
-	case LIRC_SET_REC_CARRIER:
-		if (!dev->s_rx_carrier_range)
-			return -ENOTTY;
-
-		if (val <= 0)
-			return -EINVAL;
-
-		return dev->s_rx_carrier_range(dev,
-					       dev->carrier_low,
-					       val);
-
-	case LIRC_SET_REC_CARRIER_RANGE:
-		if (!dev->s_rx_carrier_range)
-			return -ENOTTY;
-
-		if (val <= 0)
-			return -EINVAL;
-
-		dev->carrier_low = val;
-		return 0;
-
-	case LIRC_GET_REC_RESOLUTION:
-		if (!dev->rx_resolution)
-			return -ENOTTY;
-
-		val = dev->rx_resolution / 1000;
-		break;
-
-	case LIRC_SET_WIDEBAND_RECEIVER:
-		if (!dev->s_learning_mode)
-			return -ENOTTY;
-
-		return dev->s_learning_mode(dev, !!val);
-
-	case LIRC_SET_MEASURE_CARRIER_MODE:
-		if (!dev->s_carrier_report)
-			return -ENOTTY;
-
-		return dev->s_carrier_report(dev, !!val);
-
-	/* Generic timeout support */
-	case LIRC_GET_MIN_TIMEOUT:
-		if (!dev->max_timeout)
-			return -ENOTTY;
-		val = DIV_ROUND_UP(dev->min_timeout, 1000);
-		break;
-
-	case LIRC_GET_MAX_TIMEOUT:
-		if (!dev->max_timeout)
-			return -ENOTTY;
-		val = dev->max_timeout / 1000;
-		break;
-
-	case LIRC_SET_REC_TIMEOUT:
-		if (!dev->max_timeout)
-			return -ENOTTY;
-
-		/* Check for multiply overflow */
-		if (val > U32_MAX / 1000)
-			return -EINVAL;
-
-		tmp = val * 1000;
-
-		if (tmp < dev->min_timeout || tmp > dev->max_timeout)
-			return -EINVAL;
-
-		if (dev->s_timeout)
-			ret = dev->s_timeout(dev, tmp);
-		if (!ret)
-			dev->timeout = tmp;
-		break;
-
-	case LIRC_SET_REC_TIMEOUT_REPORTS:
-		if (!dev->timeout)
-			return -ENOTTY;
-
-		dev->send_timeout_reports = !!val;
-		break;
-
-	default:
-		return -ENOTTY;
-	}
-
-	if (_IOC_DIR(cmd) & _IOC_READ)
-		ret = put_user(val, argp);
-
-	return ret;
-}
-
-static unsigned int ir_lirc_poll(struct file *file,
-				 struct poll_table_struct *wait)
-{
-	struct rc_dev *rcdev = file->private_data;
-	unsigned int events = 0;
-
-	poll_wait(file, &rcdev->wait_poll, wait);
-
-	if (!rcdev->registered) {
-		events = POLLHUP | POLLERR;
-	} else if (rcdev->driver_type != RC_DRIVER_IR_RAW_TX) {
-		if ((rcdev->poll_mode & LIRC_MODE_SCANCODE) &&
-		    !kfifo_is_empty(&rcdev->scancodes))
-			events |= POLLIN | POLLRDNORM;
-
-		if ((rcdev->poll_mode & LIRC_MODE_MODE2) &&
-		    !kfifo_is_empty(&rcdev->rawir))
-			events |= POLLIN | POLLRDNORM;
-	}
-
-	return events;
-}
-
-static ssize_t ir_lirc_read_mode2(struct file *file, char __user *buffer,
-				  size_t length)
-{
-	struct rc_dev *rcdev = file->private_data;
-	unsigned int copied;
-	int ret;
-
-	if (length < sizeof(unsigned int) || length % sizeof(unsigned int))
-		return -EINVAL;
-
-	do {
-		if (kfifo_is_empty(&rcdev->rawir)) {
-			if (file->f_flags & O_NONBLOCK)
-				return -EAGAIN;
-
-			ret = wait_event_interruptible(rcdev->wait_poll,
-					!kfifo_is_empty(&rcdev->rawir) ||
-					!rcdev->registered);
-			if (ret)
-				return ret;
-		}
-
-		if (!rcdev->registered)
-			return -ENODEV;
-
-		mutex_lock(&rcdev->lock);
-		ret = kfifo_to_user(&rcdev->rawir, buffer, length, &copied);
-		mutex_unlock(&rcdev->lock);
-		if (ret)
-			return ret;
-	} while (copied == 0);
-
-	return copied;
-}
-
-static ssize_t ir_lirc_read_scancode(struct file *file, char __user *buffer,
-				     size_t length)
-{
-	struct rc_dev *rcdev = file->private_data;
-	unsigned int copied;
-	int ret;
-
-	if (length < sizeof(struct lirc_scancode) ||
-	    length % sizeof(struct lirc_scancode))
-		return -EINVAL;
-
-	do {
-		if (kfifo_is_empty(&rcdev->scancodes)) {
-			if (file->f_flags & O_NONBLOCK)
-				return -EAGAIN;
-
-			ret = wait_event_interruptible(rcdev->wait_poll,
-					!kfifo_is_empty(&rcdev->scancodes) ||
-					!rcdev->registered);
-			if (ret)
-				return ret;
-		}
-
-		if (!rcdev->registered)
-			return -ENODEV;
-
-		mutex_lock(&rcdev->lock);
-		ret = kfifo_to_user(&rcdev->scancodes, buffer, length, &copied);
-		mutex_unlock(&rcdev->lock);
-		if (ret)
-			return ret;
-	} while (copied == 0);
-
-	return copied;
-}
-
-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;
-
-	if (rcdev->driver_type == RC_DRIVER_IR_RAW_TX)
-		return -EINVAL;
-
-	if (!rcdev->registered)
-		return -ENODEV;
-
-	if (rcdev->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);
-}
-
-const struct file_operations lirc_fops = {
-	.owner		= THIS_MODULE,
-	.write		= ir_lirc_transmit_ir,
-	.unlocked_ioctl	= ir_lirc_ioctl,
-#ifdef CONFIG_COMPAT
-	.compat_ioctl	= ir_lirc_ioctl,
-#endif
-	.read		= ir_lirc_read,
-	.poll		= ir_lirc_poll,
-	.open		= ir_lirc_open,
-	.release	= ir_lirc_close,
-	.llseek		= no_llseek,
-};
diff --git a/drivers/media/rc/lirc_dev.c b/drivers/media/rc/lirc_dev.c
index 04a2d521c441..5c0e6a3ea3d4 100644
--- a/drivers/media/rc/lirc_dev.c
+++ b/drivers/media/rc/lirc_dev.c
@@ -22,11 +22,14 @@
 #include <linux/device.h>
 #include <linux/idr.h>
 #include <linux/poll.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
 
 #include "rc-core-priv.h"
 #include <media/lirc.h>
 
 #define LOGHEAD		"lirc_dev (%s[%d]): "
+#define LIRCBUF_SIZE	256
 
 static dev_t lirc_base_dev;
 
@@ -36,6 +39,621 @@ static DEFINE_IDA(lirc_ida);
 /* Only used for sysfs but defined to void otherwise */
 static struct class *lirc_class;
 
+/**
+ * ir_lirc_raw_event() - Send raw IR data to lirc to be relayed to userspace
+ *
+ * @dev:	the struct rc_dev descriptor of the device
+ * @ev:		the struct ir_raw_event descriptor of the pulse/space
+ */
+void ir_lirc_raw_event(struct rc_dev *dev, struct ir_raw_event ev)
+{
+	int sample;
+
+	/* Packet start */
+	if (ev.reset) {
+		/*
+		 * Userspace expects a long space event before the start of
+		 * the signal to use as a sync.  This may be done with repeat
+		 * packets and normal samples.  But if a reset has been sent
+		 * then we assume that a long time has passed, so we send a
+		 * space with the maximum time value.
+		 */
+		sample = LIRC_SPACE(LIRC_VALUE_MASK);
+		IR_dprintk(2, "delivering reset sync space to lirc_dev\n");
+
+	/* Carrier reports */
+	} else if (ev.carrier_report) {
+		sample = LIRC_FREQUENCY(ev.carrier);
+		IR_dprintk(2, "carrier report (freq: %d)\n", sample);
+
+	/* Packet end */
+	} else if (ev.timeout) {
+		if (dev->gap)
+			return;
+
+		dev->gap_start = ktime_get();
+		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);
+
+	/* Normal sample */
+	} else {
+		if (dev->gap) {
+			dev->gap_duration += ktime_to_ns(ktime_sub(ktime_get(),
+							 dev->gap_start));
+
+			/* Convert to ms and cap by LIRC_VALUE_MASK */
+			do_div(dev->gap_duration, 1000);
+			dev->gap_duration = min_t(u64, dev->gap_duration,
+						  LIRC_VALUE_MASK);
+
+			kfifo_put(&dev->rawir, LIRC_SPACE(dev->gap_duration));
+			dev->gap = false;
+		}
+
+		sample = ev.pulse ? LIRC_PULSE(ev.duration / 1000) :
+					LIRC_SPACE(ev.duration / 1000);
+		IR_dprintk(2, "delivering %uus %s to lirc_dev\n",
+			   TO_US(ev.duration), TO_STR(ev.pulse));
+	}
+
+	kfifo_put(&dev->rawir, sample);
+	wake_up_poll(&dev->wait_poll, POLLIN | POLLRDNORM);
+}
+
+/**
+ * ir_lirc_scancode_event() - Send scancode data to lirc to be relayed to
+ *		userspace
+ * @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)
+{
+	lsc->timestamp = ktime_get_ns();
+
+	if (kfifo_put(&dev->scancodes, *lsc))
+		wake_up_poll(&dev->wait_poll, POLLIN | POLLRDNORM);
+}
+EXPORT_SYMBOL_GPL(ir_lirc_scancode_event);
+
+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);
+	int retval;
+
+	retval = rc_open(dev);
+	if (retval)
+		return retval;
+
+	retval = mutex_lock_interruptible(&dev->lock);
+	if (retval)
+		goto out_rc;
+
+	if (!dev->registered) {
+		retval = -ENODEV;
+		goto out_unlock;
+	}
+
+	if (dev->lirc_open) {
+		retval = -EBUSY;
+		goto out_unlock;
+	}
+
+	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);
+
+	dev->lirc_open++;
+	file->private_data = dev;
+
+	nonseekable_open(inode, file);
+	mutex_unlock(&dev->lock);
+
+	return 0;
+
+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;
+
+	mutex_lock(&dev->lock);
+	dev->lirc_open--;
+	mutex_unlock(&dev->lock);
+
+	rc_close(dev);
+
+	return 0;
+}
+
+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;
+	unsigned int *txbuf = NULL;
+	struct ir_raw_event *raw = NULL;
+	ssize_t ret = -EINVAL;
+	size_t count;
+	ktime_t start;
+	s64 towait;
+	unsigned int duration = 0; /* signal duration in us */
+	int i;
+
+	if (!dev->registered)
+		return -ENODEV;
+
+	start = ktime_get();
+
+	if (!dev->tx_ir) {
+		ret = -EINVAL;
+		goto out;
+	}
+
+	if (dev->send_mode == LIRC_MODE_SCANCODE) {
+		struct lirc_scancode scan;
+
+		if (n != sizeof(scan))
+			return -EINVAL;
+
+		if (copy_from_user(&scan, buf, sizeof(scan)))
+			return -EFAULT;
+
+		if (scan.flags || scan.keycode || scan.timestamp)
+			return -EINVAL;
+
+		/*
+		 * The scancode field in lirc_scancode is 64-bit simply
+		 * to future-proof it, since there are IR protocols encode
+		 * use more than 32 bits. For now only 32-bit protocols
+		 * are supported.
+		 */
+		if (scan.scancode > U32_MAX ||
+		    !rc_validate_scancode(scan.rc_proto, scan.scancode))
+			return -EINVAL;
+
+		raw = kmalloc_array(LIRCBUF_SIZE, sizeof(*raw), GFP_KERNEL);
+		if (!raw)
+			return -ENOMEM;
+
+		ret = ir_raw_encode_scancode(scan.rc_proto, scan.scancode,
+					     raw, LIRCBUF_SIZE);
+		if (ret < 0)
+			goto out;
+
+		count = ret;
+
+		txbuf = kmalloc_array(count, sizeof(unsigned int), GFP_KERNEL);
+		if (!txbuf) {
+			ret = -ENOMEM;
+			goto out;
+		}
+
+		for (i = 0; i < count; i++)
+			/* Convert from NS to US */
+			txbuf[i] = DIV_ROUND_UP(raw[i].duration, 1000);
+
+		if (dev->s_tx_carrier) {
+			int carrier = ir_raw_encode_carrier(scan.rc_proto);
+
+			if (carrier > 0)
+				dev->s_tx_carrier(dev, carrier);
+		}
+	} else {
+		if (n < sizeof(unsigned int) || n % sizeof(unsigned int))
+			return -EINVAL;
+
+		count = n / sizeof(unsigned int);
+		if (count > LIRCBUF_SIZE || count % 2 == 0)
+			return -EINVAL;
+
+		txbuf = memdup_user(buf, n);
+		if (IS_ERR(txbuf))
+			return PTR_ERR(txbuf);
+	}
+
+	for (i = 0; i < count; i++) {
+		if (txbuf[i] > IR_MAX_DURATION / 1000 - duration || !txbuf[i]) {
+			ret = -EINVAL;
+			goto out;
+		}
+
+		duration += txbuf[i];
+	}
+
+	ret = dev->tx_ir(dev, txbuf, count);
+	if (ret < 0)
+		goto out;
+
+	if (dev->send_mode == LIRC_MODE_SCANCODE) {
+		ret = n;
+	} else {
+		for (duration = i = 0; i < ret; i++)
+			duration += txbuf[i];
+
+		ret *= sizeof(unsigned int);
+
+		/*
+		 * The lircd gap calculation expects the write function to
+		 * wait for the actual IR signal to be transmitted before
+		 * returning.
+		 */
+		towait = ktime_us_delta(ktime_add_us(start, duration),
+					ktime_get());
+		if (towait > 0) {
+			set_current_state(TASK_INTERRUPTIBLE);
+			schedule_timeout(usecs_to_jiffies(towait));
+		}
+	}
+
+out:
+	kfree(txbuf);
+	kfree(raw);
+	return ret;
+}
+
+static long ir_lirc_ioctl(struct file *filep, unsigned int cmd,
+			  unsigned long arg)
+{
+	struct rc_dev *dev = filep->private_data;
+	u32 __user *argp = (u32 __user *)(arg);
+	int ret = 0;
+	__u32 val = 0, tmp;
+
+	if (_IOC_DIR(cmd) & _IOC_WRITE) {
+		ret = get_user(val, argp);
+		if (ret)
+			return ret;
+	}
+
+	if (!dev->registered)
+		return -ENODEV;
+
+	switch (cmd) {
+	case LIRC_GET_FEATURES:
+		if (dev->driver_type == RC_DRIVER_SCANCODE)
+			val |= LIRC_CAN_REC_SCANCODE;
+
+		if (dev->driver_type == RC_DRIVER_IR_RAW) {
+			val |= LIRC_CAN_REC_MODE2 | LIRC_CAN_REC_SCANCODE;
+			if (dev->rx_resolution)
+				val |= LIRC_CAN_GET_REC_RESOLUTION;
+		}
+
+		if (dev->tx_ir) {
+			val |= LIRC_CAN_SEND_PULSE | LIRC_CAN_SEND_SCANCODE;
+			if (dev->s_tx_mask)
+				val |= LIRC_CAN_SET_TRANSMITTER_MASK;
+			if (dev->s_tx_carrier)
+				val |= LIRC_CAN_SET_SEND_CARRIER;
+			if (dev->s_tx_duty_cycle)
+				val |= LIRC_CAN_SET_SEND_DUTY_CYCLE;
+		}
+
+		if (dev->s_rx_carrier_range)
+			val |= LIRC_CAN_SET_REC_CARRIER |
+				LIRC_CAN_SET_REC_CARRIER_RANGE;
+
+		if (dev->s_learning_mode)
+			val |= LIRC_CAN_USE_WIDEBAND_RECEIVER;
+
+		if (dev->s_carrier_report)
+			val |= LIRC_CAN_MEASURE_CARRIER;
+
+		if (dev->max_timeout)
+			val |= LIRC_CAN_SET_REC_TIMEOUT;
+
+		break;
+
+	/* mode support */
+	case LIRC_GET_REC_MODE:
+		if (dev->driver_type == RC_DRIVER_IR_RAW_TX)
+			return -ENOTTY;
+
+		val = dev->rec_mode;
+		break;
+
+	case LIRC_SET_REC_MODE:
+		switch (dev->driver_type) {
+		case RC_DRIVER_IR_RAW_TX:
+			return -ENOTTY;
+		case RC_DRIVER_SCANCODE:
+			if (val != LIRC_MODE_SCANCODE)
+				return -EINVAL;
+			break;
+		case RC_DRIVER_IR_RAW:
+			if (!(val == LIRC_MODE_MODE2 ||
+			      val == LIRC_MODE_SCANCODE))
+				return -EINVAL;
+			break;
+		}
+
+		dev->rec_mode = val;
+		dev->poll_mode = val;
+		return 0;
+
+	case LIRC_SET_POLL_MODES:
+		switch (dev->driver_type) {
+		case RC_DRIVER_IR_RAW_TX:
+			return -ENOTTY;
+		case RC_DRIVER_SCANCODE:
+			if (val != LIRC_MODE_SCANCODE)
+				return -EINVAL;
+			break;
+		case RC_DRIVER_IR_RAW:
+			if (val & ~(LIRC_MODE_MODE2 | LIRC_MODE_SCANCODE))
+				return -EINVAL;
+			break;
+		}
+
+		dev->poll_mode = val;
+		return 0;
+
+	case LIRC_GET_SEND_MODE:
+		if (!dev->tx_ir)
+			return -ENOTTY;
+
+		val = dev->send_mode;
+		break;
+
+	case LIRC_SET_SEND_MODE:
+		if (!dev->tx_ir)
+			return -ENOTTY;
+
+		if (!(val == LIRC_MODE_PULSE || val == LIRC_MODE_SCANCODE))
+			return -EINVAL;
+
+		dev->send_mode = val;
+		return 0;
+
+	/* TX settings */
+	case LIRC_SET_TRANSMITTER_MASK:
+		if (!dev->s_tx_mask)
+			return -ENOTTY;
+
+		return dev->s_tx_mask(dev, val);
+
+	case LIRC_SET_SEND_CARRIER:
+		if (!dev->s_tx_carrier)
+			return -ENOTTY;
+
+		return dev->s_tx_carrier(dev, val);
+
+	case LIRC_SET_SEND_DUTY_CYCLE:
+		if (!dev->s_tx_duty_cycle)
+			return -ENOTTY;
+
+		if (val <= 0 || val >= 100)
+			return -EINVAL;
+
+		return dev->s_tx_duty_cycle(dev, val);
+
+	/* RX settings */
+	case LIRC_SET_REC_CARRIER:
+		if (!dev->s_rx_carrier_range)
+			return -ENOTTY;
+
+		if (val <= 0)
+			return -EINVAL;
+
+		return dev->s_rx_carrier_range(dev,
+					       dev->carrier_low,
+					       val);
+
+	case LIRC_SET_REC_CARRIER_RANGE:
+		if (!dev->s_rx_carrier_range)
+			return -ENOTTY;
+
+		if (val <= 0)
+			return -EINVAL;
+
+		dev->carrier_low = val;
+		return 0;
+
+	case LIRC_GET_REC_RESOLUTION:
+		if (!dev->rx_resolution)
+			return -ENOTTY;
+
+		val = dev->rx_resolution / 1000;
+		break;
+
+	case LIRC_SET_WIDEBAND_RECEIVER:
+		if (!dev->s_learning_mode)
+			return -ENOTTY;
+
+		return dev->s_learning_mode(dev, !!val);
+
+	case LIRC_SET_MEASURE_CARRIER_MODE:
+		if (!dev->s_carrier_report)
+			return -ENOTTY;
+
+		return dev->s_carrier_report(dev, !!val);
+
+	/* Generic timeout support */
+	case LIRC_GET_MIN_TIMEOUT:
+		if (!dev->max_timeout)
+			return -ENOTTY;
+		val = DIV_ROUND_UP(dev->min_timeout, 1000);
+		break;
+
+	case LIRC_GET_MAX_TIMEOUT:
+		if (!dev->max_timeout)
+			return -ENOTTY;
+		val = dev->max_timeout / 1000;
+		break;
+
+	case LIRC_SET_REC_TIMEOUT:
+		if (!dev->max_timeout)
+			return -ENOTTY;
+
+		/* Check for multiply overflow */
+		if (val > U32_MAX / 1000)
+			return -EINVAL;
+
+		tmp = val * 1000;
+
+		if (tmp < dev->min_timeout || tmp > dev->max_timeout)
+			return -EINVAL;
+
+		if (dev->s_timeout)
+			ret = dev->s_timeout(dev, tmp);
+		if (!ret)
+			dev->timeout = tmp;
+		break;
+
+	case LIRC_SET_REC_TIMEOUT_REPORTS:
+		if (!dev->timeout)
+			return -ENOTTY;
+
+		dev->send_timeout_reports = !!val;
+		break;
+
+	default:
+		return -ENOTTY;
+	}
+
+	if (_IOC_DIR(cmd) & _IOC_READ)
+		ret = put_user(val, argp);
+
+	return ret;
+}
+
+static unsigned int ir_lirc_poll(struct file *file,
+				 struct poll_table_struct *wait)
+{
+	struct rc_dev *rcdev = file->private_data;
+	unsigned int events = 0;
+
+	poll_wait(file, &rcdev->wait_poll, wait);
+
+	if (!rcdev->registered) {
+		events = POLLHUP | POLLERR;
+	} else if (rcdev->driver_type != RC_DRIVER_IR_RAW_TX) {
+		if ((rcdev->poll_mode & LIRC_MODE_SCANCODE) &&
+		    !kfifo_is_empty(&rcdev->scancodes))
+			events |= POLLIN | POLLRDNORM;
+
+		if ((rcdev->poll_mode & LIRC_MODE_MODE2) &&
+		    !kfifo_is_empty(&rcdev->rawir))
+			events |= POLLIN | POLLRDNORM;
+	}
+
+	return events;
+}
+
+static ssize_t ir_lirc_read_mode2(struct file *file, char __user *buffer,
+				  size_t length)
+{
+	struct rc_dev *rcdev = file->private_data;
+	unsigned int copied;
+	int ret;
+
+	if (length < sizeof(unsigned int) || length % sizeof(unsigned int))
+		return -EINVAL;
+
+	do {
+		if (kfifo_is_empty(&rcdev->rawir)) {
+			if (file->f_flags & O_NONBLOCK)
+				return -EAGAIN;
+
+			ret = wait_event_interruptible(rcdev->wait_poll,
+					!kfifo_is_empty(&rcdev->rawir) ||
+					!rcdev->registered);
+			if (ret)
+				return ret;
+		}
+
+		if (!rcdev->registered)
+			return -ENODEV;
+
+		mutex_lock(&rcdev->lock);
+		ret = kfifo_to_user(&rcdev->rawir, buffer, length, &copied);
+		mutex_unlock(&rcdev->lock);
+		if (ret)
+			return ret;
+	} while (copied == 0);
+
+	return copied;
+}
+
+static ssize_t ir_lirc_read_scancode(struct file *file, char __user *buffer,
+				     size_t length)
+{
+	struct rc_dev *rcdev = file->private_data;
+	unsigned int copied;
+	int ret;
+
+	if (length < sizeof(struct lirc_scancode) ||
+	    length % sizeof(struct lirc_scancode))
+		return -EINVAL;
+
+	do {
+		if (kfifo_is_empty(&rcdev->scancodes)) {
+			if (file->f_flags & O_NONBLOCK)
+				return -EAGAIN;
+
+			ret = wait_event_interruptible(rcdev->wait_poll,
+					!kfifo_is_empty(&rcdev->scancodes) ||
+					!rcdev->registered);
+			if (ret)
+				return ret;
+		}
+
+		if (!rcdev->registered)
+			return -ENODEV;
+
+		mutex_lock(&rcdev->lock);
+		ret = kfifo_to_user(&rcdev->scancodes, buffer, length, &copied);
+		mutex_unlock(&rcdev->lock);
+		if (ret)
+			return ret;
+	} while (copied == 0);
+
+	return copied;
+}
+
+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;
+
+	if (rcdev->driver_type == RC_DRIVER_IR_RAW_TX)
+		return -EINVAL;
+
+	if (!rcdev->registered)
+		return -ENODEV;
+
+	if (rcdev->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);
+}
+
+static const struct file_operations lirc_fops = {
+	.owner		= THIS_MODULE,
+	.write		= ir_lirc_transmit_ir,
+	.unlocked_ioctl	= ir_lirc_ioctl,
+#ifdef CONFIG_COMPAT
+	.compat_ioctl	= ir_lirc_ioctl,
+#endif
+	.read		= ir_lirc_read,
+	.poll		= ir_lirc_poll,
+	.open		= ir_lirc_open,
+	.release	= ir_lirc_close,
+	.llseek		= no_llseek,
+};
+
 static void lirc_release_device(struct device *ld)
 {
 	struct rc_dev *rcdev = container_of(ld, struct rc_dev, lirc_dev);
diff --git a/drivers/media/rc/rc-core-priv.h b/drivers/media/rc/rc-core-priv.h
index 950de264666f..063a8c0457bd 100644
--- a/drivers/media/rc/rc-core-priv.h
+++ b/drivers/media/rc/rc-core-priv.h
@@ -289,8 +289,6 @@ void ir_lirc_raw_event(struct rc_dev *dev, struct ir_raw_event ev);
 void ir_lirc_scancode_event(struct rc_dev *dev, struct lirc_scancode *lsc);
 int ir_lirc_register(struct rc_dev *dev);
 void ir_lirc_unregister(struct rc_dev *dev);
-
-extern const struct file_operations lirc_fops;
 #else
 static inline int lirc_dev_init(void) { return 0; }
 static inline void lirc_dev_exit(void) {}
-- 
2.13.6

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 2/5] media: rc: include <uapi/linux/lirc.h> rather than <media/lirc.h>
  2017-11-06 10:40 [PATCH 0/5] lirc improvements Sean Young
  2017-11-06 10:40 ` [PATCH 1/5] media: rc: move ir-lirc-codec.c contents into lirc_dev.c Sean Young
@ 2017-11-06 10:40 ` Sean Young
  2017-11-06 10:40 ` [PATCH 3/5] media: lirc: allow lirc device to opened more than once Sean Young
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Sean Young @ 2017-11-06 10:40 UTC (permalink / raw)
  To: linux-media

This removes the need for include/media/lirc.h, which just includes
the uapi file.

Signed-off-by: Sean Young <sean@mess.org>
---
 drivers/media/rc/lirc_dev.c | 2 +-
 include/media/lirc.h        | 1 -
 2 files changed, 1 insertion(+), 2 deletions(-)
 delete mode 100644 include/media/lirc.h

diff --git a/drivers/media/rc/lirc_dev.c b/drivers/media/rc/lirc_dev.c
index 5c0e6a3ea3d4..24e0c56c9892 100644
--- a/drivers/media/rc/lirc_dev.c
+++ b/drivers/media/rc/lirc_dev.c
@@ -26,7 +26,7 @@
 #include <linux/wait.h>
 
 #include "rc-core-priv.h"
-#include <media/lirc.h>
+#include <uapi/linux/lirc.h>
 
 #define LOGHEAD		"lirc_dev (%s[%d]): "
 #define LIRCBUF_SIZE	256
diff --git a/include/media/lirc.h b/include/media/lirc.h
deleted file mode 100644
index 554988c860c1..000000000000
--- a/include/media/lirc.h
+++ /dev/null
@@ -1 +0,0 @@
-#include <uapi/linux/lirc.h>
-- 
2.13.6

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 3/5] media: lirc: allow lirc device to opened more than once
  2017-11-06 10:40 [PATCH 0/5] lirc improvements Sean Young
  2017-11-06 10:40 ` [PATCH 1/5] media: rc: move ir-lirc-codec.c contents into lirc_dev.c Sean Young
  2017-11-06 10:40 ` [PATCH 2/5] media: rc: include <uapi/linux/lirc.h> rather than <media/lirc.h> Sean Young
@ 2017-11-06 10:40 ` Sean Young
  2017-11-06 10:40 ` [PATCH 4/5] media: lirc: improve locking Sean Young
  2017-11-06 10:40 ` [PATCH 5/5] media: rc: iguanair: remove unnecessary locking Sean Young
  4 siblings, 0 replies; 6+ messages in thread
From: Sean Young @ 2017-11-06 10:40 UTC (permalink / raw)
  To: linux-media

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.

Signed-off-by: Sean Young <sean@mess.org>
---
 drivers/media/rc/lirc_dev.c | 280 ++++++++++++++++++++++++--------------------
 include/media/rc-core.h     |  56 +++++----
 2 files changed, 185 insertions(+), 151 deletions(-)

diff --git a/drivers/media/rc/lirc_dev.c b/drivers/media/rc/lirc_dev.c
index 24e0c56c9892..32beecf103cc 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,9 @@ static struct class *lirc_class;
  */
 void ir_lirc_raw_event(struct rc_dev *dev, struct ir_raw_event ev)
 {
+	unsigned long flags;
+	struct list_head *l;
+	struct lirc_fh *fh;
 	int sample;
 
 	/* Packet start */
@@ -75,9 +77,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 +91,13 @@ 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(l, &dev->lirc_fh) {
+				fh = list_entry(l, struct 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 +107,38 @@ 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(l, &dev->lirc_fh) {
+		fh = list_entry(l, struct 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 list_head *l;
+	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(l, &dev->lirc_fh) {
+		fh = list_entry(l, struct 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 +146,89 @@ 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;
-
-	retval = mutex_lock_interruptible(&dev->lock);
-	if (retval)
-		goto out_rc;
+	if (!fh)
+		return -ENOMEM;
 
 	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;
+		}
+	}
 
-	dev->lirc_open++;
-	file->private_data = dev;
+	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;
+		fh->poll_mode = LIRC_MODE_SCANCODE;
+	} else {
+		fh->rec_mode = LIRC_MODE_MODE2;
+		fh->poll_mode = LIRC_MODE_MODE2;
+	}
+
+	retval = rc_open(dev);
+	if (retval)
+		goto out_kfifo;
+
+	init_waitqueue_head(&fh->wait_poll);
+
+	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);
+
+	get_device(&dev->dev);
 
 	nonseekable_open(inode, file);
 	mutex_unlock(&dev->lock);
 
 	return 0;
-
-out_unlock:
-	mutex_unlock(&dev->lock);
-out_rc:
-	rc_close(dev);
+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);
 	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);
+	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);
 
+	put_device(&dev->dev);
 	rc_close(dev);
+	spin_lock_irqsave(&dev->lirc_fh_lock, flags);
+	list_del(&fh->list);
+	spin_unlock_irqrestore(&dev->lirc_fh_lock, flags);
+	kfree(fh);
 
 	return 0;
 }
@@ -181,7 +236,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 +257,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 +332,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 +359,11 @@ static ssize_t ir_lirc_transmit_ir(struct file *file, const char __user *buf,
 	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 +418,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,8 +436,8 @@ static long ir_lirc_ioctl(struct file *filep, unsigned int cmd,
 			break;
 		}
 
-		dev->rec_mode = val;
-		dev->poll_mode = val;
+		fh->rec_mode = val;
+		fh->poll_mode = val;
 		return 0;
 
 	case LIRC_SET_POLL_MODES:
@@ -397,14 +454,14 @@ static long ir_lirc_ioctl(struct file *filep, unsigned int cmd,
 			break;
 		}
 
-		dev->poll_mode = val;
+		fh->poll_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:
@@ -414,7 +471,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 */
@@ -448,7 +505,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:
@@ -458,7 +515,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:
@@ -516,7 +573,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:
@@ -532,20 +589,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->poll_mode & LIRC_MODE_SCANCODE) &&
-		    !kfifo_is_empty(&rcdev->scancodes))
+		if ((fh->poll_mode & LIRC_MODE_SCANCODE) &&
+		    !kfifo_is_empty(&fh->scancodes))
 			events |= POLLIN | POLLRDNORM;
 
-		if ((rcdev->poll_mode & LIRC_MODE_MODE2) &&
-		    !kfifo_is_empty(&rcdev->rawir))
+		if ((fh->poll_mode & LIRC_MODE_MODE2) &&
+		    !kfifo_is_empty(&fh->rawir))
 			events |= POLLIN | POLLRDNORM;
 	}
 
@@ -555,7 +613,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;
 
@@ -563,12 +622,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;
@@ -577,8 +636,10 @@ static ssize_t ir_lirc_read_mode2(struct file *file, char __user *buffer,
 		if (!rcdev->registered)
 			return -ENODEV;
 
-		mutex_lock(&rcdev->lock);
-		ret = kfifo_to_user(&rcdev->rawir, buffer, length, &copied);
+		ret = mutex_lock_interruptible(&rcdev->lock);
+		if (ret)
+			return ret;
+		ret = kfifo_to_user(&fh->rawir, buffer, length, &copied);
 		mutex_unlock(&rcdev->lock);
 		if (ret)
 			return ret;
@@ -590,7 +651,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;
 
@@ -599,12 +661,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;
@@ -613,8 +675,10 @@ static ssize_t ir_lirc_read_scancode(struct file *file, char __user *buffer,
 		if (!rcdev->registered)
 			return -ENODEV;
 
-		mutex_lock(&rcdev->lock);
-		ret = kfifo_to_user(&rcdev->scancodes, buffer, length, &copied);
+		ret = mutex_lock_interruptible(&rcdev->lock);
+		if (ret)
+			return ret;
+		ret = kfifo_to_user(&fh->scancodes, buffer, length, &copied);
 		mutex_unlock(&rcdev->lock);
 		if (ret)
 			return ret;
@@ -626,7 +690,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;
@@ -634,7 +699,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);
@@ -654,67 +719,29 @@ static const struct file_operations lirc_fops = {
 	.llseek		= no_llseek,
 };
 
-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);
-}
-
 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;
-		dev->poll_mode = LIRC_MODE_SCANCODE;
-	} else {
-		dev->rec_mode = LIRC_MODE_MODE2;
-		dev->poll_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.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);
 	if (err)
 		goto out_ida;
 
-	get_device(&dev->dev);
-
 	dev_info(&dev->dev, "lirc_dev: driver %s registered at minor = %d",
 		 dev->driver_name, minor);
 
@@ -722,32 +749,27 @@ 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 list_head *l;
+	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);
+	spin_lock_irqsave(&dev->lirc_fh_lock, flags);
+	list_for_each(l, &dev->lirc_fh) {
+		fh = list_entry(l, struct lirc_fh, list);
+		wake_up_poll(&fh->wait_poll, POLLHUP | POLLERR);
 	}
-
-	mutex_unlock(&dev->lock);
+	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 9a284365502d..b435c3bb2b6d 100644
--- a/include/media/rc-core.h
+++ b/include/media/rc-core.h
@@ -69,6 +69,36 @@ 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
+ * @poll_mode: lirc mode used for polling, can poll for both LIRC_MODE_SCANCODE
+ *	and 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;
+	u8				poll_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,22 +148,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
- * @poll_mode: lirc mode used for polling, can poll for both LIRC_MODE_SCANCODE
- *	and 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
@@ -198,18 +217,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;
-	u8				poll_mode;
+	spinlock_t			lirc_fh_lock;
+	struct list_head		lirc_fh;
 #endif
 	bool				registered;
 	int				(*change_protocol)(struct rc_dev *dev, u64 *rc_proto);
-- 
2.13.6

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 4/5] media: lirc: improve locking
  2017-11-06 10:40 [PATCH 0/5] lirc improvements Sean Young
                   ` (2 preceding siblings ...)
  2017-11-06 10:40 ` [PATCH 3/5] media: lirc: allow lirc device to opened more than once Sean Young
@ 2017-11-06 10:40 ` Sean Young
  2017-11-06 10:40 ` [PATCH 5/5] media: rc: iguanair: remove unnecessary locking Sean Young
  4 siblings, 0 replies; 6+ messages in thread
From: Sean Young @ 2017-11-06 10:40 UTC (permalink / raw)
  To: linux-media

Once rc_unregister_device() has been called, no driver function
should be called.

Signed-off-by: Sean Young <sean@mess.org>
---
 drivers/media/rc/lirc_dev.c | 255 +++++++++++++++++++++++++-------------------
 1 file changed, 147 insertions(+), 108 deletions(-)

diff --git a/drivers/media/rc/lirc_dev.c b/drivers/media/rc/lirc_dev.c
index 32beecf103cc..6b0053d4f041 100644
--- a/drivers/media/rc/lirc_dev.c
+++ b/drivers/media/rc/lirc_dev.c
@@ -240,15 +240,21 @@ static ssize_t ir_lirc_transmit_ir(struct file *file, const char __user *buf,
 	struct rc_dev *dev = fh->rc;
 	unsigned int *txbuf = NULL;
 	struct ir_raw_event *raw = NULL;
-	ssize_t ret = -EINVAL;
+	ssize_t ret;
 	size_t count;
 	ktime_t start;
 	s64 towait;
 	unsigned int duration = 0; /* signal duration in us */
 	int i;
 
-	if (!dev->registered)
-		return -ENODEV;
+	ret = mutex_lock_interruptible(&dev->lock);
+	if (ret)
+		return ret;
+
+	if (!dev->registered) {
+		ret = -ENODEV;
+		goto out;
+	}
 
 	start = ktime_get();
 
@@ -260,14 +266,20 @@ static ssize_t ir_lirc_transmit_ir(struct file *file, const char __user *buf,
 	if (fh->send_mode == LIRC_MODE_SCANCODE) {
 		struct lirc_scancode scan;
 
-		if (n != sizeof(scan))
-			return -EINVAL;
+		if (n != sizeof(scan)) {
+			ret = -EINVAL;
+			goto out;
+		}
 
-		if (copy_from_user(&scan, buf, sizeof(scan)))
-			return -EFAULT;
+		if (copy_from_user(&scan, buf, sizeof(scan))) {
+			ret = -EFAULT;
+			goto out;
+		}
 
-		if (scan.flags || scan.keycode || scan.timestamp)
-			return -EINVAL;
+		if (scan.flags || scan.keycode || scan.timestamp) {
+			ret = -EINVAL;
+			goto out;
+		}
 
 		/*
 		 * The scancode field in lirc_scancode is 64-bit simply
@@ -276,12 +288,16 @@ static ssize_t ir_lirc_transmit_ir(struct file *file, const char __user *buf,
 		 * are supported.
 		 */
 		if (scan.scancode > U32_MAX ||
-		    !rc_validate_scancode(scan.rc_proto, scan.scancode))
-			return -EINVAL;
+		    !rc_validate_scancode(scan.rc_proto, scan.scancode)) {
+			ret = -EINVAL;
+			goto out;
+		}
 
 		raw = kmalloc_array(LIRCBUF_SIZE, sizeof(*raw), GFP_KERNEL);
-		if (!raw)
-			return -ENOMEM;
+		if (!raw) {
+			ret = -ENOMEM;
+			goto out;
+		}
 
 		ret = ir_raw_encode_scancode(scan.rc_proto, scan.scancode,
 					     raw, LIRCBUF_SIZE);
@@ -307,16 +323,22 @@ static ssize_t ir_lirc_transmit_ir(struct file *file, const char __user *buf,
 				dev->s_tx_carrier(dev, carrier);
 		}
 	} else {
-		if (n < sizeof(unsigned int) || n % sizeof(unsigned int))
-			return -EINVAL;
+		if (n < sizeof(unsigned int) || n % sizeof(unsigned int)) {
+			ret = -EINVAL;
+			goto out;
+		}
 
 		count = n / sizeof(unsigned int);
-		if (count > LIRCBUF_SIZE || count % 2 == 0)
-			return -EINVAL;
+		if (count > LIRCBUF_SIZE || count % 2 == 0) {
+			ret = -EINVAL;
+			goto out;
+		}
 
 		txbuf = memdup_user(buf, n);
-		if (IS_ERR(txbuf))
-			return PTR_ERR(txbuf);
+		if (IS_ERR(txbuf)) {
+			ret = PTR_ERR(txbuf);
+			goto out;
+		}
 	}
 
 	for (i = 0; i < count; i++) {
@@ -354,6 +376,7 @@ static ssize_t ir_lirc_transmit_ir(struct file *file, const char __user *buf,
 	}
 
 out:
+	mutex_unlock(&dev->lock);
 	kfree(txbuf);
 	kfree(raw);
 	return ret;
@@ -365,8 +388,8 @@ static long ir_lirc_ioctl(struct file *file, unsigned int cmd,
 	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;
+	u32 val = 0;
+	int ret;
 
 	if (_IOC_DIR(cmd) & _IOC_WRITE) {
 		ret = get_user(val, argp);
@@ -374,8 +397,14 @@ static long ir_lirc_ioctl(struct file *file, unsigned int cmd,
 			return ret;
 	}
 
-	if (!dev->registered)
-		return -ENODEV;
+	ret = mutex_lock_interruptible(&dev->lock);
+	if (ret)
+		return ret;
+
+	if (!dev->registered) {
+		ret = -ENODEV;
+		goto out;
+	}
 
 	switch (cmd) {
 	case LIRC_GET_FEATURES:
@@ -416,173 +445,183 @@ static long ir_lirc_ioctl(struct file *file, unsigned int cmd,
 	/* mode support */
 	case LIRC_GET_REC_MODE:
 		if (dev->driver_type == RC_DRIVER_IR_RAW_TX)
-			return -ENOTTY;
-
-		val = fh->rec_mode;
+			ret = -ENOTTY;
+		else
+			val = fh->rec_mode;
 		break;
 
 	case LIRC_SET_REC_MODE:
 		switch (dev->driver_type) {
 		case RC_DRIVER_IR_RAW_TX:
-			return -ENOTTY;
+			ret = -ENOTTY;
+			break;
 		case RC_DRIVER_SCANCODE:
 			if (val != LIRC_MODE_SCANCODE)
-				return -EINVAL;
+				ret = -EINVAL;
 			break;
 		case RC_DRIVER_IR_RAW:
 			if (!(val == LIRC_MODE_MODE2 ||
 			      val == LIRC_MODE_SCANCODE))
-				return -EINVAL;
+				ret = -EINVAL;
 			break;
 		}
 
-		fh->rec_mode = val;
-		fh->poll_mode = val;
-		return 0;
+		if (!ret) {
+			fh->rec_mode = val;
+			fh->poll_mode = val;
+		}
+		break;
 
 	case LIRC_SET_POLL_MODES:
 		switch (dev->driver_type) {
 		case RC_DRIVER_IR_RAW_TX:
-			return -ENOTTY;
+			ret = -ENOTTY;
+			break;
 		case RC_DRIVER_SCANCODE:
 			if (val != LIRC_MODE_SCANCODE)
-				return -EINVAL;
+				ret = -EINVAL;
 			break;
 		case RC_DRIVER_IR_RAW:
 			if (val & ~(LIRC_MODE_MODE2 | LIRC_MODE_SCANCODE))
-				return -EINVAL;
+				ret = -EINVAL;
 			break;
 		}
 
-		fh->poll_mode = val;
-		return 0;
+		if (!ret)
+			fh->poll_mode = val;
+
+		break;
 
 	case LIRC_GET_SEND_MODE:
 		if (!dev->tx_ir)
-			return -ENOTTY;
-
-		val = fh->send_mode;
+			ret = -ENOTTY;
+		else
+			val = fh->send_mode;
 		break;
 
 	case LIRC_SET_SEND_MODE:
 		if (!dev->tx_ir)
-			return -ENOTTY;
-
-		if (!(val == LIRC_MODE_PULSE || val == LIRC_MODE_SCANCODE))
-			return -EINVAL;
-
-		fh->send_mode = val;
-		return 0;
+			ret = -ENOTTY;
+		else if (!(val == LIRC_MODE_PULSE || val == LIRC_MODE_SCANCODE))
+			ret = -EINVAL;
+		else
+			fh->send_mode = val;
+		break;
 
 	/* TX settings */
 	case LIRC_SET_TRANSMITTER_MASK:
 		if (!dev->s_tx_mask)
-			return -ENOTTY;
-
-		return dev->s_tx_mask(dev, val);
+			ret = -ENOTTY;
+		else
+			ret = dev->s_tx_mask(dev, val);
+		break;
 
 	case LIRC_SET_SEND_CARRIER:
 		if (!dev->s_tx_carrier)
-			return -ENOTTY;
-
-		return dev->s_tx_carrier(dev, val);
+			ret = -ENOTTY;
+		else
+			ret = dev->s_tx_carrier(dev, val);
+		break;
 
 	case LIRC_SET_SEND_DUTY_CYCLE:
 		if (!dev->s_tx_duty_cycle)
-			return -ENOTTY;
-
-		if (val <= 0 || val >= 100)
-			return -EINVAL;
-
-		return dev->s_tx_duty_cycle(dev, val);
+			ret = -ENOTTY;
+		else if (val <= 0 || val >= 100)
+			ret = -EINVAL;
+		else
+			ret = dev->s_tx_duty_cycle(dev, val);
+		break;
 
 	/* RX settings */
 	case LIRC_SET_REC_CARRIER:
 		if (!dev->s_rx_carrier_range)
-			return -ENOTTY;
-
-		if (val <= 0)
-			return -EINVAL;
-
-		return dev->s_rx_carrier_range(dev,
-					       fh->carrier_low,
-					       val);
+			ret = -ENOTTY;
+		else if (val <= 0)
+			ret = -EINVAL;
+		else
+			ret = dev->s_rx_carrier_range(dev, fh->carrier_low,
+						      val);
+		break;
 
 	case LIRC_SET_REC_CARRIER_RANGE:
 		if (!dev->s_rx_carrier_range)
-			return -ENOTTY;
-
-		if (val <= 0)
-			return -EINVAL;
-
-		fh->carrier_low = val;
-		return 0;
+			ret = -ENOTTY;
+		else if (val <= 0)
+			ret = -EINVAL;
+		else
+			fh->carrier_low = val;
+		break;
 
 	case LIRC_GET_REC_RESOLUTION:
 		if (!dev->rx_resolution)
-			return -ENOTTY;
-
-		val = dev->rx_resolution / 1000;
+			ret = -ENOTTY;
+		else
+			val = dev->rx_resolution / 1000;
 		break;
 
 	case LIRC_SET_WIDEBAND_RECEIVER:
 		if (!dev->s_learning_mode)
-			return -ENOTTY;
-
-		return dev->s_learning_mode(dev, !!val);
+			ret = -ENOTTY;
+		else
+			ret = dev->s_learning_mode(dev, !!val);
+		break;
 
 	case LIRC_SET_MEASURE_CARRIER_MODE:
 		if (!dev->s_carrier_report)
-			return -ENOTTY;
-
-		return dev->s_carrier_report(dev, !!val);
+			ret = -ENOTTY;
+		else
+			ret = dev->s_carrier_report(dev, !!val);
+		break;
 
 	/* Generic timeout support */
 	case LIRC_GET_MIN_TIMEOUT:
 		if (!dev->max_timeout)
-			return -ENOTTY;
-		val = DIV_ROUND_UP(dev->min_timeout, 1000);
+			ret = -ENOTTY;
+		else
+			val = DIV_ROUND_UP(dev->min_timeout, 1000);
 		break;
 
 	case LIRC_GET_MAX_TIMEOUT:
 		if (!dev->max_timeout)
-			return -ENOTTY;
-		val = dev->max_timeout / 1000;
+			ret = -ENOTTY;
+		else
+			val = dev->max_timeout / 1000;
 		break;
 
 	case LIRC_SET_REC_TIMEOUT:
-		if (!dev->max_timeout)
-			return -ENOTTY;
-
-		/* Check for multiply overflow */
-		if (val > U32_MAX / 1000)
-			return -EINVAL;
-
-		tmp = val * 1000;
-
-		if (tmp < dev->min_timeout || tmp > dev->max_timeout)
-			return -EINVAL;
-
-		if (dev->s_timeout)
-			ret = dev->s_timeout(dev, tmp);
-		if (!ret)
-			dev->timeout = tmp;
+		if (!dev->max_timeout) {
+			ret = -ENOTTY;
+		} else if (val > U32_MAX / 1000) {
+			/* Check for multiply overflow */
+			ret = -EINVAL;
+		} else {
+			u32 tmp = val * 1000;
+
+			if (tmp < dev->min_timeout || tmp > dev->max_timeout)
+				ret = -EINVAL;
+			else if (dev->s_timeout)
+				ret = dev->s_timeout(dev, tmp);
+			else if (!ret)
+				dev->timeout = tmp;
+		}
 		break;
 
 	case LIRC_SET_REC_TIMEOUT_REPORTS:
 		if (!dev->timeout)
-			return -ENOTTY;
-
-		fh->send_timeout_reports = !!val;
+			ret = -ENOTTY;
+		else
+			fh->send_timeout_reports = !!val;
 		break;
 
 	default:
-		return -ENOTTY;
+		ret = -ENOTTY;
 	}
 
-	if (_IOC_DIR(cmd) & _IOC_READ)
+	if (!ret && _IOC_DIR(cmd) & _IOC_READ)
 		ret = put_user(val, argp);
 
+out:
+	mutex_unlock(&dev->lock);
 	return ret;
 }
 
-- 
2.13.6

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 5/5] media: rc: iguanair: remove unnecessary locking
  2017-11-06 10:40 [PATCH 0/5] lirc improvements Sean Young
                   ` (3 preceding siblings ...)
  2017-11-06 10:40 ` [PATCH 4/5] media: lirc: improve locking Sean Young
@ 2017-11-06 10:40 ` Sean Young
  4 siblings, 0 replies; 6+ messages in thread
From: Sean Young @ 2017-11-06 10:40 UTC (permalink / raw)
  To: linux-media

Since lirc now correctly locks the rcdev, this locking is no longer
needed.

Signed-off-by: Sean Young <sean@mess.org>
---
 drivers/media/rc/iguanair.c | 28 ----------------------------
 1 file changed, 28 deletions(-)

diff --git a/drivers/media/rc/iguanair.c b/drivers/media/rc/iguanair.c
index 30e24da67226..64231efcc47a 100644
--- a/drivers/media/rc/iguanair.c
+++ b/drivers/media/rc/iguanair.c
@@ -36,8 +36,6 @@ struct iguanair {
 	uint8_t bufsize;
 	uint8_t cycle_overhead;
 
-	struct mutex lock;
-
 	/* receiver support */
 	bool receiver_on;
 	dma_addr_t dma_in, dma_out;
@@ -295,8 +293,6 @@ static int iguanair_set_tx_carrier(struct rc_dev *dev, uint32_t carrier)
 	if (carrier < 25000 || carrier > 150000)
 		return -EINVAL;
 
-	mutex_lock(&ir->lock);
-
 	if (carrier != ir->carrier) {
 		uint32_t cycles, fours, sevens;
 
@@ -325,8 +321,6 @@ static int iguanair_set_tx_carrier(struct rc_dev *dev, uint32_t carrier)
 		ir->packet->busy4 = 110 - fours;
 	}
 
-	mutex_unlock(&ir->lock);
-
 	return 0;
 }
 
@@ -337,9 +331,7 @@ static int iguanair_set_tx_mask(struct rc_dev *dev, uint32_t mask)
 	if (mask > 15)
 		return 4;
 
-	mutex_lock(&ir->lock);
 	ir->packet->channels = mask << 4;
-	mutex_unlock(&ir->lock);
 
 	return 0;
 }
@@ -351,7 +343,6 @@ static int iguanair_tx(struct rc_dev *dev, unsigned *txbuf, unsigned count)
 	unsigned i, size, periods, bytes;
 	int rc;
 
-	mutex_lock(&ir->lock);
 
 	/* convert from us to carrier periods */
 	for (i = space = size = 0; i < count; i++) {
@@ -382,8 +373,6 @@ static int iguanair_tx(struct rc_dev *dev, unsigned *txbuf, unsigned count)
 		rc = -EOVERFLOW;
 
 out:
-	mutex_unlock(&ir->lock);
-
 	return rc ? rc : count;
 }
 
@@ -392,14 +381,10 @@ static int iguanair_open(struct rc_dev *rdev)
 	struct iguanair *ir = rdev->priv;
 	int rc;
 
-	mutex_lock(&ir->lock);
-
 	rc = iguanair_receiver(ir, true);
 	if (rc == 0)
 		ir->receiver_on = true;
 
-	mutex_unlock(&ir->lock);
-
 	return rc;
 }
 
@@ -408,14 +393,10 @@ static void iguanair_close(struct rc_dev *rdev)
 	struct iguanair *ir = rdev->priv;
 	int rc;
 
-	mutex_lock(&ir->lock);
-
 	rc = iguanair_receiver(ir, false);
 	ir->receiver_on = false;
 	if (rc && rc != -ENODEV)
 		dev_warn(ir->dev, "failed to disable receiver: %d\n", rc);
-
-	mutex_unlock(&ir->lock);
 }
 
 static int iguanair_probe(struct usb_interface *intf,
@@ -456,7 +437,6 @@ static int iguanair_probe(struct usb_interface *intf,
 	ir->rc = rc;
 	ir->dev = &intf->dev;
 	ir->udev = udev;
-	mutex_init(&ir->lock);
 
 	init_completion(&ir->completion);
 	pipeout = usb_sndintpipe(udev,
@@ -553,8 +533,6 @@ static int iguanair_suspend(struct usb_interface *intf, pm_message_t message)
 	struct iguanair *ir = usb_get_intfdata(intf);
 	int rc = 0;
 
-	mutex_lock(&ir->lock);
-
 	if (ir->receiver_on) {
 		rc = iguanair_receiver(ir, false);
 		if (rc)
@@ -564,8 +542,6 @@ static int iguanair_suspend(struct usb_interface *intf, pm_message_t message)
 	usb_kill_urb(ir->urb_in);
 	usb_kill_urb(ir->urb_out);
 
-	mutex_unlock(&ir->lock);
-
 	return rc;
 }
 
@@ -574,8 +550,6 @@ static int iguanair_resume(struct usb_interface *intf)
 	struct iguanair *ir = usb_get_intfdata(intf);
 	int rc = 0;
 
-	mutex_lock(&ir->lock);
-
 	rc = usb_submit_urb(ir->urb_in, GFP_KERNEL);
 	if (rc)
 		dev_warn(&intf->dev, "failed to submit urb: %d\n", rc);
@@ -586,8 +560,6 @@ static int iguanair_resume(struct usb_interface *intf)
 			dev_warn(ir->dev, "failed to enable receiver after resume\n");
 	}
 
-	mutex_unlock(&ir->lock);
-
 	return rc;
 }
 
-- 
2.13.6

^ permalink raw reply related	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2017-11-06 10:40 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2017-11-06 10:40 [PATCH 0/5] lirc improvements Sean Young
2017-11-06 10:40 ` [PATCH 1/5] media: rc: move ir-lirc-codec.c contents into lirc_dev.c Sean Young
2017-11-06 10:40 ` [PATCH 2/5] media: rc: include <uapi/linux/lirc.h> rather than <media/lirc.h> Sean Young
2017-11-06 10:40 ` [PATCH 3/5] media: lirc: allow lirc device to opened more than once Sean Young
2017-11-06 10:40 ` [PATCH 4/5] media: lirc: improve locking Sean Young
2017-11-06 10:40 ` [PATCH 5/5] media: rc: iguanair: remove unnecessary locking Sean Young

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.