gpu: pvr: support for events
authorTopi Pohjolainen <topi.pohjolainen@nokia.com>
Tue, 27 Apr 2010 11:14:17 +0000 (13:14 +0200)
committerGrazvydas Ignotas <notasas@gmail.com>
Sun, 20 May 2012 18:09:41 +0000 (21:09 +0300)
In DRM vertical blank event style, introduce support for the
driver to issue and for the userspace to read events. Driver
file node is extended with a list holding unread events and
with a wait queue representing processes interested receiving
events.

Signed-off-by: Topi Pohjolainen <topi.pohjolainen@nokia.com>
Signed-off-by: Imre Deak <imre.deak@nokia.com>
pvr/module.c
pvr/private_data.h
pvr/pvr_events.c [new file with mode: 0644]
pvr/pvr_events.h [new file with mode: 0644]

index 3ba1aea..c79b0d9 100644 (file)
@@ -87,6 +87,10 @@ static int pvr_open(struct inode unref__ * inode, struct file *filp)
        priv->hBlockAlloc = block_alloc;
        filp->private_data = priv;
 
+       INIT_LIST_HEAD(&priv->event_list);
+       init_waitqueue_head(&priv->event_wait);
+       priv->event_space = 4096; /* set aside 4k for event buffer */
+
        ret = 0;
 err_unlock:
        pvr_unlock();
index d0192c1..b91b838 100644 (file)
 #ifndef __INCLUDED_PRIVATE_DATA_H_
 #define __INCLUDED_PRIVATE_DATA_H_
 
+#include <linux/wait.h>
+#include <linux/list.h>
+
 struct PVRSRV_FILE_PRIVATE_DATA {
        u32 ui32OpenPID;
        void *hBlockAlloc;
+
+       wait_queue_head_t event_wait;
+       struct list_head event_list;
+       int event_space;
 };
 
 #endif
diff --git a/pvr/pvr_events.c b/pvr/pvr_events.c
new file mode 100644 (file)
index 0000000..70c061b
--- /dev/null
@@ -0,0 +1,87 @@
+
+#include "pvr_events.h"
+#include "servicesint.h"
+
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/time.h>
+#include <linux/uaccess.h>
+#include <linux/fs.h>
+
+static spinlock_t event_lock;
+
+static bool pvr_dequeue_event(struct PVRSRV_FILE_PRIVATE_DATA *priv,
+               size_t total, size_t max, struct pvr_pending_event **out)
+{
+       struct pvr_pending_event *e;
+       unsigned long flags;
+       bool ret = false;
+
+       spin_lock_irqsave(&event_lock, flags);
+
+       *out = NULL;
+       if (list_empty(&priv->event_list))
+               goto err;
+
+       e = list_first_entry(&priv->event_list, struct pvr_pending_event, link);
+       if (e->event->length + total > max)
+               goto err;
+
+       priv->event_space += e->event->length;
+       list_del(&e->link);
+       *out = e;
+       ret = true;
+
+err:
+       spin_unlock_irqrestore(&event_lock, flags);
+
+       return ret;
+}
+
+ssize_t pvr_read(struct file *filp, char __user *buf, size_t count, loff_t *off)
+{
+       struct PVRSRV_FILE_PRIVATE_DATA *priv = filp->private_data;
+       struct pvr_pending_event *e;
+       size_t total;
+       ssize_t ret;
+
+       ret = wait_event_interruptible(priv->event_wait,
+                                       !list_empty(&priv->event_list));
+
+       if (ret < 0)
+               return ret;
+
+       total = 0;
+       while (pvr_dequeue_event(priv, total, count, &e)) {
+               if (copy_to_user(buf + total, e->event, e->event->length)) {
+                       total = -EFAULT;
+                       break;
+               }
+
+               total += e->event->length;
+               e->destroy(e);
+       }
+
+       return total;
+}
+
+void pvr_release_events(struct PVRSRV_FILE_PRIVATE_DATA *priv)
+{
+       struct pvr_pending_event *w;
+       struct pvr_pending_event *z;
+       unsigned long flags;
+
+       spin_lock_irqsave(&event_lock, flags);
+
+       /* Remove unconsumed events */
+       list_for_each_entry_safe(w, z, &priv->event_list, link)
+               w->destroy(w);
+
+       spin_unlock_irqrestore(&event_lock, flags);
+}
+
+void pvr_init_events(void)
+{
+       spin_lock_init(&event_lock);
+}
diff --git a/pvr/pvr_events.h b/pvr/pvr_events.h
new file mode 100644 (file)
index 0000000..f046cbf
--- /dev/null
@@ -0,0 +1,38 @@
+
+#ifndef PVR_EVENTS_H
+#define PVR_EVENTS_H
+
+#include "servicesext.h"
+#include "private_data.h"
+#include <linux/list.h>
+#include <linux/file.h>
+
+/*
+ * Header for events written back to userspace on the drm fd. The
+ * type defines the type of event, the length specifies the total
+ * length of the event (including the header), and user_data is
+ * typically a 64 bit value passed with the ioctl that triggered the
+ * event.  A read on the drm fd will always only return complete
+ * events, that is, if for example the read buffer is 100 bytes, and
+ * there are two 64 byte events pending, only one will be returned.
+ */
+struct pvr_event {
+       __u32 type;
+       __u32 length;
+};
+
+/* Event queued up for userspace to read */
+struct pvr_pending_event {
+       struct pvr_event *event;
+       struct list_head link;
+       struct PVRSRV_FILE_PRIVATE_DATA *file_priv;
+       void (*destroy)(struct pvr_pending_event *event);
+};
+
+void pvr_init_events(void);
+
+ssize_t pvr_read(struct file *filp, char __user *buf, size_t count,
+               loff_t *off);
+void pvr_release_events(struct PVRSRV_FILE_PRIVATE_DATA *priv);
+
+#endif /* PVR_EVENTS_H */