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 <da...@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);

--
To unsubscribe from this list: send the line "unsubscribe linux-media" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to