All of lore.kernel.org
 help / color / mirror / Atom feed
From: "David Härdeman" <david@hardeman.nu>
To: linux-media@vger.kernel.org
Cc: m.chehab@samsung.com
Subject: [PATCH 18/49] rc-core: allow chardev to be read
Date: Fri, 04 Apr 2014 01:32:46 +0200	[thread overview]
Message-ID: <20140403233246.27099.89520.stgit@zeus.muc.hardeman.nu> (raw)
In-Reply-To: <20140403232420.27099.94872.stgit@zeus.muc.hardeman.nu>

This patch is the first step towards making the rc chardev usable by adding
read functionality.

Basically the implementation mimics what evdev does. Userspace applications can
open the rc device and read rc_event structs with data. Only some basic events
are supported for now but later patches will add further events.

Signed-off-by: David Härdeman <david@hardeman.nu>
---
 drivers/media/rc/rc-main.c |  154 +++++++++++++++++++++++++++++++++++++++++++-
 include/media/rc-core.h    |   40 +++++++++++
 2 files changed, 189 insertions(+), 5 deletions(-)

diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c
index 9c7bdb8..5ce0cdb 100644
--- a/drivers/media/rc/rc-main.c
+++ b/drivers/media/rc/rc-main.c
@@ -22,12 +22,14 @@
 #include <linux/idr.h>
 #include <linux/device.h>
 #include <linux/module.h>
+#include <linux/poll.h>
 #include "rc-core-priv.h"
 
 /* Sizes are in bytes, 256 bytes allows for 32 entries on x64 */
-#define IR_TAB_MIN_SIZE	256
-#define IR_TAB_MAX_SIZE	8192
-#define RC_DEV_MAX	256
+#define IR_TAB_MIN_SIZE		256
+#define IR_TAB_MAX_SIZE		8192
+#define RC_DEV_MAX		256
+#define RC_RX_BUFFER_SIZE	1024
 
 /* FIXME: IR_KEYPRESS_TIMEOUT should be protocol specific */
 #define IR_KEYPRESS_TIMEOUT 250
@@ -44,16 +46,75 @@ static dev_t rc_devt;
 /**
  * struct rc_client - keeps track of processes which have opened a rc chardev
  * @dev: the &struct rc_dev which is being controlled
+ * @rxlock: protects the rxfifo
+ * @rxfifo: stores rx events which can be read by the process
  * @fasync: keeps track of the fasync queue
  * @node: list of current clients for the rc device (protected by client_lock
  *	in &struct rc_dev)
  */
 struct rc_client {
 	struct rc_dev *dev;
+	spinlock_t rxlock;
+	DECLARE_KFIFO(rxfifo, struct rc_event, RC_RX_BUFFER_SIZE);
 	struct fasync_struct *fasync;
 	struct list_head node;
 };
 
+/**
+ * rc_client_event() - passes an rc event to a specific client
+ * @client:	the &struct rc_client for this client
+ * @type:	the event type
+ * @code:	the event code (type specific)
+ * @val:	the event value (type and code specific)
+ *
+ * This function writes a &struct rc_event entry to the client kfifo
+ * for later reading from userspace.
+ */
+static void rc_client_event(struct rc_client *client, u16 type,
+			    u16 code, u32 val)
+{
+	unsigned long flags;
+	struct rc_event ev;
+
+	ev.type = type;
+	ev.code = code;
+	ev.val = val;
+
+	spin_lock_irqsave(&client->rxlock, flags);
+	if (kfifo_is_full(&client->rxfifo)) {
+		kfifo_skip(&client->rxfifo);
+		ev.type = RC_CORE;
+		ev.code = RC_CORE_DROPPED;
+		ev.val = 1;
+	}
+
+	kfifo_in(&client->rxfifo, &ev, 1);
+	kill_fasync(&client->fasync, SIGIO, POLL_IN);
+	spin_unlock_irqrestore(&client->rxlock, flags);
+}
+
+/**
+ * rc_event() - sends an rc_event to all listeners
+ * @dev:	the struct rc_dev of the device generating the event
+ * @type:	the event type
+ * @code:	the event code (type specific)
+ * @val:	the event value (type and code specific)
+ *
+ * This function passes an rc event to all clients.
+ */
+void rc_event(struct rc_dev *dev, u16 type, u16 code, u32 val)
+{
+	struct rc_client *client;
+
+	rcu_read_lock();
+	list_for_each_entry_rcu(client, &dev->client_list, node)
+		rc_client_event(client, type, code, val);
+	rcu_read_unlock();
+
+	wake_up_interruptible(&dev->rxwait);
+}
+EXPORT_SYMBOL_GPL(rc_event);
+
 static struct rc_map_list *seek_rc_map(const char *name)
 {
 	struct rc_map_list *map = NULL;
@@ -753,6 +814,7 @@ void rc_repeat(struct rc_dev *dev)
 
 	input_event(dev->input_dev, EV_MSC, MSC_SCAN, dev->last_scancode);
 	input_sync(dev->input_dev);
+	rc_event(dev, RC_KEY, RC_KEY_REPEAT, 1);
 
 	if (!dev->keypressed)
 		goto out;
@@ -788,6 +850,15 @@ static void ir_do_keydown(struct rc_dev *dev, enum rc_type protocol,
 		ir_do_keyup(dev, false);
 
 	input_event(dev->input_dev, EV_MSC, MSC_SCAN, scancode);
+	rc_event(dev, RC_KEY, RC_KEY_PROTOCOL, protocol);
+	/*
+	 * NOTE: If we ever get > 32 bit scancodes, we need to break the
+	 *	 scancode into 32 bit pieces and feed them to userspace
+	 *	 as one or more RC_KEY_SCANCODE_PART events followed
+	 *	 by a final RC_KEY_SCANCODE event.
+	 */
+	rc_event(dev, RC_KEY, RC_KEY_SCANCODE, scancode);
+	rc_event(dev, RC_KEY, RC_KEY_TOGGLE, toggle);
 
 	if (new_event && keycode != KEY_RESERVED) {
 		/* Register a keypress */
@@ -997,6 +1068,7 @@ static ssize_t show_protocols(struct device *device,
 	if (!dev)
 		return -EINVAL;
 
+	rc_event(dev, RC_KEY, RC_KEY_REPEAT, 1);
 	mutex_lock(&dev->lock);
 
 	if (fattr->type == RC_FILTER_NORMAL) {
@@ -1366,6 +1438,8 @@ static int rc_dev_open(struct inode *inode, struct file *file)
 		return -ENOMEM;
 
 	client->dev = dev;
+	spin_lock_init(&client->rxlock);
+	INIT_KFIFO(client->rxfifo);
 
 	rc_attach_client(dev, client);
 
@@ -1408,6 +1482,74 @@ static int rc_release(struct inode *inode, struct file *file)
 }
 
 /**
+ * rc_read() - allows userspace to read rc events
+ * @file:	the &struct file corresponding to the previous open()
+ * @buffer:	the userspace buffer to read data to
+ * @count:	the number of bytes to read
+ * @ppos:	the file offset
+ * @return:	the number of bytes read, or a negative error code
+ *
+ * This function (which implements read in &struct file_operations)
+ * allows userspace to read events from the rc device file.
+ */
+static ssize_t rc_read(struct file *file, char __user *buffer,
+		       size_t count, loff_t *ppos)
+{
+	struct rc_client *client = file->private_data;
+	struct rc_dev *dev = client->dev;
+	struct rc_event ev;
+	int ret;
+
+	if (count < sizeof(ev))
+		return -EINVAL;
+
+	if ((file->f_flags & O_NONBLOCK) &&
+	    kfifo_is_empty(&client->rxfifo) &&
+	    !dev->dead)
+		return -EAGAIN;
+
+	ret = wait_event_interruptible(dev->rxwait,
+				       !kfifo_is_empty(&client->rxfifo) ||
+				       dev->dead);
+
+	if (ret)
+		return ret;
+
+	if (dev->dead)
+		return -ENODEV;
+
+	for (ret = 0; ret + sizeof(ev) <= count; ret += sizeof(ev)) {
+		if (kfifo_out_spinlocked(&client->rxfifo, &ev, 1,
+					 &client->rxlock) != 1)
+			break;
+
+		if (copy_to_user(buffer + ret, &ev, sizeof(ev)))
+			return -EFAULT;
+	}
+
+	return ret;
+}
+
+/**
+ * rc_poll() - allows userspace to poll rc device files
+ * @file:	the &struct file corresponding to the previous open()
+ * @wait:	used to keep track of processes waiting for poll events
+ * @return:	a mask of poll events which have occurred
+ *
+ * This function (which implements poll in &struct file_operations)
+ * allows userspace to poll/select on the rc device file.
+ */
+static unsigned int rc_poll(struct file *file, poll_table *wait)
+{
+	struct rc_client *client = file->private_data;
+	struct rc_dev *dev = client->dev;
+
+	poll_wait(file, &dev->rxwait, wait);
+	return ((kfifo_is_empty(&client->rxfifo) ? 0 : (POLLIN | POLLRDNORM)) |
+		(!dev->dead ? 0 : (POLLHUP | POLLERR)));
+}
+
+/**
  * rc_fasync() - allows userspace to recieve asynchronous notifications
  * @fd:		the file descriptor corresponding to the opened rc device
  * @file:	the &struct file corresponding to the previous open()
@@ -1429,6 +1571,8 @@ static const struct file_operations rc_fops = {
 	.owner		= THIS_MODULE,
 	.open		= rc_dev_open,
 	.release	= rc_release,
+	.read		= rc_read,
+	.poll		= rc_poll,
 	.fasync		= rc_fasync,
 	.llseek		= no_llseek,
 };
@@ -1552,7 +1696,7 @@ struct rc_dev *rc_allocate_device(void)
 
 	INIT_LIST_HEAD(&dev->client_list);
 	spin_lock_init(&dev->client_lock);
-
+	init_waitqueue_head(&dev->rxwait);
 	spin_lock_init(&dev->rc_map.lock);
 	spin_lock_init(&dev->keylock);
 	mutex_init(&dev->lock);
@@ -1750,6 +1894,7 @@ void rc_unregister_device(struct rc_dev *dev)
 	list_for_each_entry(client, &dev->client_list, node)
 		kill_fasync(&client->fasync, SIGIO, POLL_HUP);
 	spin_unlock(&dev->client_lock);
+	wake_up_interruptible_all(&dev->rxwait);
 
 	cdev_del(&dev->cdev);
 
@@ -1771,7 +1916,6 @@ void rc_unregister_device(struct rc_dev *dev)
 	ida_simple_remove(&rc_ida, MINOR(dev->dev.devt));
 	put_device(&dev->dev);
 }
-
 EXPORT_SYMBOL_GPL(rc_unregister_device);
 
 static int __init rc_core_init(void)
diff --git a/include/media/rc-core.h b/include/media/rc-core.h
index e2615e7..d31ccdd 100644
--- a/include/media/rc-core.h
+++ b/include/media/rc-core.h
@@ -53,6 +53,43 @@ struct rc_keymap_entry {
 	};
 };
 
+/* rc_event.type value */
+#define RC_DEBUG		0x0
+#define RC_CORE			0x1
+#define RC_KEY			0x2
+#define RC_IR			0x3
+
+/* RC_CORE codes */
+#define RC_CORE_DROPPED		0x0
+
+/* RC_KEY codes */
+#define RC_KEY_REPEAT		0x0
+#define RC_KEY_PROTOCOL		0x1
+#define RC_KEY_SCANCODE		0x2
+#define RC_KEY_SCANCODE_PART	0x3
+#define RC_KEY_TOGGLE		0x4
+
+/* RC_IR codes */
+#define RC_IR_SPACE		0x0
+#define RC_IR_PULSE		0x1
+#define RC_IR_START		0x2
+#define RC_IR_STOP		0x3
+#define RC_IR_RESET		0x4
+#define RC_IR_CARRIER		0x5
+#define RC_IR_DUTY_CYCLE	0x6
+
+/**
+ * struct rc_event - used to communicate rc events to/from userspace
+ * @type:	the event type
+ * @code:	the event code (type specific)
+ * @val:	the event value (type and code specific)
+ */
+struct rc_event {
+	__u16 type;
+	__u16 code;
+	__u32 val;
+} __packed;
+
 /**
  * struct rc_scancode_filter - Filter scan codes.
  * @data:	Scancode data to match.
@@ -92,6 +129,7 @@ enum rc_filter_type {
  * @dead: used to determine if the device is still alive
  * @client_list: list of clients (processes which have opened the rc chardev)
  * @client_lock: protects client_list
+ * @rxwait: waitqueue for processes waiting for data to read
  * @raw: additional data for raw pulse/space devices
  * @input_dev: the input child device used to communicate events to userspace
  * @driver_type: specifies if protocol decoding is done in hardware or software
@@ -155,6 +193,7 @@ struct rc_dev {
 	bool				dead;
 	struct list_head		client_list;
 	spinlock_t			client_lock;
+	wait_queue_head_t		rxwait;
 	struct ir_raw_event_ctrl	*raw;
 	struct input_dev		*input_dev;
 	enum rc_driver_type		driver_type;
@@ -212,6 +251,7 @@ struct rc_dev *rc_allocate_device(void);
 void rc_free_device(struct rc_dev *dev);
 int rc_register_device(struct rc_dev *dev);
 void rc_unregister_device(struct rc_dev *dev);
+void rc_event(struct rc_dev *dev, u16 type, u16 code, u32 val);
 
 int rc_open(struct rc_dev *rdev);
 void rc_close(struct rc_dev *rdev);


  parent reply	other threads:[~2014-04-03 23:32 UTC|newest]

Thread overview: 68+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-04-03 23:31 [PATCH 00/49] rc-core: my current patch queue David Härdeman
2014-04-03 23:31 ` [PATCH 01/49] bt8xx: fixup RC5 decoding David Härdeman
2014-04-03 23:31 ` [PATCH 02/49] rc-core: improve ir-kbd-i2c get_key functions David Härdeman
2014-04-03 23:31 ` [PATCH 03/49] rc-core: document the protocol type David Härdeman
2014-04-03 23:31 ` [PATCH 04/49] rc-core: do not change 32bit NEC scancode format for now David Härdeman
2014-04-04 13:18   ` James Hogan
2014-04-03 23:31 ` [PATCH 05/49] rc-core: split dev->s_filter David Härdeman
2014-04-04 13:08   ` James Hogan
2014-04-03 23:31 ` [PATCH 06/49] rc-core: remove generic scancode filter David Härdeman
2014-04-04 13:30   ` James Hogan
2014-04-03 23:31 ` [PATCH 07/49] dib0700: NEC scancode cleanup David Härdeman
2014-04-03 23:31 ` [PATCH 08/49] lmedm04: " David Härdeman
2014-04-03 23:32 ` [PATCH 09/49] saa7134: NEC scancode fix David Härdeman
2014-04-03 23:32 ` [PATCH 10/49] [RFC] rc-core: use the full 32 bits for NEC scancodes David Härdeman
2014-04-03 23:32 ` [PATCH 11/49] [RFC] rc-core: don't throw away protocol information David Härdeman
2014-04-03 23:32 ` [PATCH 12/49] rc-core: simplify sysfs code David Härdeman
2014-04-03 23:32 ` [PATCH 13/49] rc-core: remove protocol arrays David Härdeman
2014-04-03 23:32 ` [PATCH 14/49] rc-core: rename dev->scanmask to dev->scancode_mask David Härdeman
2014-04-03 23:32 ` [PATCH 15/49] rc-core: merge rc5 and streamzap decoders David Härdeman
2014-04-03 23:32 ` [PATCH 16/49] rc-core: use an IDA rather than a bitmap David Härdeman
2014-07-25 22:39   ` Mauro Carvalho Chehab
2014-04-03 23:32 ` [PATCH 17/49] rc-core: add chardev David Härdeman
2014-04-03 23:32 ` David Härdeman [this message]
2014-04-03 23:32 ` [PATCH 19/49] rc-core: use a kfifo for TX data David Härdeman
2014-04-03 23:32 ` [PATCH 20/49] rc-core: allow chardev to be written David Härdeman
2014-04-03 23:33 ` [PATCH 21/49] rc-core: add ioctl support to the rc chardev David Härdeman
2014-04-03 23:33 ` [PATCH 22/49] rc-core: add an ioctl for getting IR RX settings David Härdeman
2014-04-03 23:33 ` [PATCH 23/49] rc-loopback: add RCIOCGIRRX ioctl support David Härdeman
2014-04-03 23:33 ` [PATCH 24/49] rc-core: add an ioctl for setting IR RX settings David Härdeman
2014-04-03 23:33 ` [PATCH 25/49] rc-loopback: add RCIOCSIRRX ioctl support David Härdeman
2014-04-03 23:33 ` [PATCH 26/49] rc-core: add an ioctl for getting IR TX settings David Härdeman
2014-04-03 23:33 ` [PATCH 27/49] rc-loopback: add RCIOCGIRTX ioctl support David Härdeman
2014-04-03 23:33 ` [PATCH 28/49] rc-core: add an ioctl for setting IR TX settings David Härdeman
2014-04-03 23:33 ` [PATCH 29/49] rc-loopback: add RCIOCSIRTX ioctl support David Härdeman
2014-04-03 23:33 ` [PATCH 30/49] rc-core: leave the internals of rc_dev alone David Härdeman
2014-07-24  1:50   ` Mauro Carvalho Chehab
2014-04-03 23:33 ` [PATCH 31/49] rc-core: split rc-main.c into rc-main.c and rc-keytable.c David Härdeman
2014-07-25 22:44   ` Mauro Carvalho Chehab
2014-04-03 23:33 ` [PATCH 32/49] rc-core: prepare for multiple keytables David Härdeman
2014-07-25 22:52   ` Mauro Carvalho Chehab
2014-04-03 23:34 ` [PATCH 33/49] rc-core: make the keytable of rc_dev an array David Härdeman
2014-04-03 23:34 ` [PATCH 34/49] rc-core: add ioctls for adding/removing keytables from userspace David Härdeman
2014-04-03 23:34 ` [PATCH 35/49] rc-core: remove redundant spinlock David Härdeman
2014-04-03 23:34 ` [PATCH 36/49] rc-core: make keytable RCU-friendly David Härdeman
2014-04-03 23:34 ` [PATCH 37/49] rc-core: allow empty keymaps David Härdeman
2014-07-25 22:58   ` Mauro Carvalho Chehab
2014-04-03 23:34 ` [PATCH 38/49] rc-core: rename ir-raw.c David Härdeman
2014-04-03 23:34 ` [PATCH 39/49] rc-core: make IR raw handling a separate module David Härdeman
2014-07-25 23:04   ` Mauro Carvalho Chehab
2014-04-03 23:34 ` [PATCH 40/49] rc-ir-raw: simplify locking David Härdeman
2014-07-25 23:08   ` Mauro Carvalho Chehab
2014-04-03 23:34 ` [PATCH 41/49] rc-core: rename mutex David Härdeman
2014-04-10 21:28   ` James Hogan
2014-07-25 23:12   ` Mauro Carvalho Chehab
2014-04-03 23:34 ` [PATCH 42/49] rc-ir-raw: atomic reads of protocols David Härdeman
2014-07-25 23:13   ` Mauro Carvalho Chehab
2014-04-03 23:34 ` [PATCH 43/49] rc-core: fix various sparse warnings David Härdeman
2014-04-03 23:34 ` [PATCH 44/49] rc-core: don't report scancodes via input devices David Härdeman
2014-07-25 23:16   ` Mauro Carvalho Chehab
2014-04-03 23:35 ` [PATCH 45/49] rc-ir-raw: add various rc_events David Härdeman
2014-07-25 23:16   ` Mauro Carvalho Chehab
2014-04-03 23:35 ` [PATCH 46/49] rc-core: use struct rc_event for all rc communication David Härdeman
2014-07-25 23:19   ` Mauro Carvalho Chehab
2014-04-03 23:35 ` [PATCH 47/49] rc-core: add keytable events David Härdeman
2014-04-03 23:35 ` [PATCH 48/49] rc-core: move remaining keytable functions David Härdeman
2014-04-03 23:35 ` [PATCH 49/49] rc-core: make rc-core.h userspace friendly David Härdeman
2014-04-04  2:05 ` [PATCH 00/49] rc-core: my current patch queue Mauro Carvalho Chehab
2014-06-26 20:07 ` David Härdeman

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20140403233246.27099.89520.stgit@zeus.muc.hardeman.nu \
    --to=david@hardeman.nu \
    --cc=linux-media@vger.kernel.org \
    --cc=m.chehab@samsung.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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.