From a028d3f3f18aaee4722f7a90fec755d67ac2c75f Mon Sep 17 00:00:00 2001 From: Topi Pohjolainen Date: Tue, 27 Apr 2010 13:14:17 +0200 Subject: [PATCH] gpu: pvr: support for events 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 Signed-off-by: Imre Deak --- pvr/module.c | 4 +++ pvr/private_data.h | 7 ++++ pvr/pvr_events.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++ pvr/pvr_events.h | 38 ++++++++++++++++++++ 4 files changed, 136 insertions(+) create mode 100644 pvr/pvr_events.c create mode 100644 pvr/pvr_events.h diff --git a/pvr/module.c b/pvr/module.c index 3ba1aea..c79b0d9 100644 --- a/pvr/module.c +++ b/pvr/module.c @@ -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(); diff --git a/pvr/private_data.h b/pvr/private_data.h index d0192c1..b91b838 100644 --- a/pvr/private_data.h +++ b/pvr/private_data.h @@ -27,9 +27,16 @@ #ifndef __INCLUDED_PRIVATE_DATA_H_ #define __INCLUDED_PRIVATE_DATA_H_ +#include +#include + 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 index 0000000..70c061b --- /dev/null +++ b/pvr/pvr_events.c @@ -0,0 +1,87 @@ + +#include "pvr_events.h" +#include "servicesint.h" + +#include +#include +#include +#include +#include +#include + +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 index 0000000..f046cbf --- /dev/null +++ b/pvr/pvr_events.h @@ -0,0 +1,38 @@ + +#ifndef PVR_EVENTS_H +#define PVR_EVENTS_H + +#include "servicesext.h" +#include "private_data.h" +#include +#include + +/* + * 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 */ -- 2.39.5