Skip to contentSkip to conversation

lirc improvements

PATCH New Sean Young 1 participant 6 messages Nov 6, 2017

Subsystems (3)
lore
Share & tools
lore

Sean Young

author maintainer
#0 lore raw

cover lirc improvements

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
Signature · 1 line
2.13.6
author maintainer
#1 lore raw

PATCH 1/5 media: rc: move ir-lirc-codec.c contents into lirc_dev.c

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.

---
 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
drivers/media/rc/Makefile +1 -1
index a1ef86767aef..9d487988b78d 100644
@@ -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
drivers/media/rc/ir-lirc-codec.c deleted +0 -637

637 lines not on this page · raw message

drivers/media/rc/lirc_dev.c +618 -0

635 lines not on this page · raw message

drivers/media/rc/rc-core-priv.h +0 -2
index 950de264666f..063a8c0457bd 100644
@@ -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) {}
Signature · 1 line
2.13.6
author maintainer
#2 lore raw

PATCH 2/5 media: rc: include <uapi/linux/lirc.h> rather than <media/lirc.h>

In reply to Sean Young

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

---
 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
drivers/media/rc/lirc_dev.c +1 -1
index 5c0e6a3ea3d4..24e0c56c9892 100644
@@ -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
include/media/lirc.h deleted +0 -1
deleted file mode 100644
index 554988c860c1..000000000000
@@ -1 +0,0 @@
-#include <uapi/linux/lirc.h>
Signature · 1 line
2.13.6
author maintainer
#3 lore raw

PATCH 3/5 media: lirc: allow lirc device to opened more than once

In reply to Sean Young

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.

---
 drivers/media/rc/lirc_dev.c | 280 ++++++++++++++++++++++++--------------------
 include/media/rc-core.h     |  56 +++++----
 2 files changed, 185 insertions(+), 151 deletions(-)
drivers/media/rc/lirc_dev.c +151 -129
index 24e0c56c9892..32beecf103cc 100644
@@ -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)
include/media/rc-core.h +34 -22

80 lines not on this page · raw message

Signature · 1 line
2.13.6
author maintainer
#4 lore raw

PATCH 4/5 media: lirc: improve locking

In reply to Sean Young

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

---
 drivers/media/rc/lirc_dev.c | 255 +++++++++++++++++++++++++-------------------
 1 file changed, 147 insertions(+), 108 deletions(-)
drivers/media/rc/lirc_dev.c +147 -108
index 32beecf103cc..6b0053d4f041 100644
@@ -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;
 }
 
Signature · 1 line
2.13.6
author maintainer
#5 lore raw

PATCH 5/5 media: rc: iguanair: remove unnecessary locking

In reply to Sean Young

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

---
 drivers/media/rc/iguanair.c | 28 ----------------------------
 1 file changed, 28 deletions(-)
drivers/media/rc/iguanair.c +0 -28
index 30e24da67226..64231efcc47a 100644
@@ -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;
 }
 
Signature · 1 line
2.13.6