[media] v4l2-event/ctrls/fh: allocate events per fh and per type instead of just...
[pandora-kernel.git] / drivers / media / video / v4l2-event.c
index dc68f60..9e325dd 100644 (file)
 #include <linux/sched.h>
 #include <linux/slab.h>
 
-static void v4l2_event_unsubscribe_all(struct v4l2_fh *fh);
-
-int v4l2_event_alloc(struct v4l2_fh *fh, unsigned int n)
-{
-       unsigned long flags;
-
-       while (fh->nallocated < n) {
-               struct v4l2_kevent *kev;
-
-               kev = kzalloc(sizeof(*kev), GFP_KERNEL);
-               if (kev == NULL)
-                       return -ENOMEM;
-
-               spin_lock_irqsave(&fh->vdev->fh_lock, flags);
-               list_add_tail(&kev->list, &fh->free);
-               fh->nallocated++;
-               spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
-       }
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(v4l2_event_alloc);
-
-#define list_kfree(list, type, member)                         \
-       while (!list_empty(list)) {                             \
-               type *hi;                                       \
-               hi = list_first_entry(list, type, member);      \
-               list_del(&hi->member);                          \
-               kfree(hi);                                      \
-       }
-
-void v4l2_event_free(struct v4l2_fh *fh)
+static unsigned sev_pos(const struct v4l2_subscribed_event *sev, unsigned idx)
 {
-       list_kfree(&fh->free, struct v4l2_kevent, list);
-       list_kfree(&fh->available, struct v4l2_kevent, list);
-       v4l2_event_unsubscribe_all(fh);
+       idx += sev->first;
+       return idx >= sev->elems ? idx - sev->elems : idx;
 }
-EXPORT_SYMBOL_GPL(v4l2_event_free);
 
 static int __v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event)
 {
@@ -84,11 +51,13 @@ static int __v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event)
        WARN_ON(fh->navailable == 0);
 
        kev = list_first_entry(&fh->available, struct v4l2_kevent, list);
-       list_move(&kev->list, &fh->free);
+       list_del(&kev->list);
        fh->navailable--;
 
        kev->event.pending = fh->navailable;
        *event = kev->event;
+       kev->sev->first = sev_pos(kev->sev, 1);
+       kev->sev->in_use--;
 
        spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
 
@@ -154,17 +123,24 @@ static void __v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *e
        fh->sequence++;
 
        /* Do we have any free events? */
-       if (list_empty(&fh->free))
-               return;
+       if (sev->in_use == sev->elems) {
+               /* no, remove the oldest one */
+               kev = sev->events + sev_pos(sev, 0);
+               list_del(&kev->list);
+               sev->in_use--;
+               sev->first = sev_pos(sev, 1);
+               fh->navailable--;
+       }
 
        /* Take one and fill it. */
-       kev = list_first_entry(&fh->free, struct v4l2_kevent, list);
+       kev = sev->events + sev_pos(sev, sev->in_use);
        kev->event.type = ev->type;
        kev->event.u = ev->u;
        kev->event.id = ev->id;
        kev->event.timestamp = *ts;
        kev->event.sequence = fh->sequence;
-       list_move_tail(&kev->list, &fh->available);
+       sev->in_use++;
+       list_add_tail(&kev->list, &fh->available);
 
        fh->navailable++;
 
@@ -209,38 +185,39 @@ int v4l2_event_pending(struct v4l2_fh *fh)
 EXPORT_SYMBOL_GPL(v4l2_event_pending);
 
 int v4l2_event_subscribe(struct v4l2_fh *fh,
-                        struct v4l2_event_subscription *sub)
+                        struct v4l2_event_subscription *sub, unsigned elems)
 {
        struct v4l2_subscribed_event *sev, *found_ev;
        struct v4l2_ctrl *ctrl = NULL;
        unsigned long flags;
+       unsigned i;
 
+       if (elems < 1)
+               elems = 1;
        if (sub->type == V4L2_EVENT_CTRL) {
                ctrl = v4l2_ctrl_find(fh->ctrl_handler, sub->id);
                if (ctrl == NULL)
                        return -EINVAL;
        }
 
-       sev = kzalloc(sizeof(*sev), GFP_KERNEL);
+       sev = kzalloc(sizeof(*sev) + sizeof(struct v4l2_kevent) * elems, GFP_KERNEL);
        if (!sev)
                return -ENOMEM;
+       for (i = 0; i < elems; i++)
+               sev->events[i].sev = sev;
+       sev->type = sub->type;
+       sev->id = sub->id;
+       sev->flags = sub->flags;
+       sev->fh = fh;
+       sev->elems = elems;
 
        spin_lock_irqsave(&fh->vdev->fh_lock, flags);
-
        found_ev = v4l2_event_subscribed(fh, sub->type, sub->id);
-       if (!found_ev) {
-               INIT_LIST_HEAD(&sev->list);
-               sev->type = sub->type;
-               sev->id = sub->id;
-               sev->fh = fh;
-               sev->flags = sub->flags;
-
+       if (!found_ev)
                list_add(&sev->list, &fh->subscribed);
-       }
-
        spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
 
-       /* v4l2_ctrl_add_fh uses a mutex, so do this outside the spin lock */
+       /* v4l2_ctrl_add_event uses a mutex, so do this outside the spin lock */
        if (found_ev)
                kfree(sev);
        else if (ctrl)
@@ -250,7 +227,7 @@ int v4l2_event_subscribe(struct v4l2_fh *fh,
 }
 EXPORT_SYMBOL_GPL(v4l2_event_subscribe);
 
-static void v4l2_event_unsubscribe_all(struct v4l2_fh *fh)
+void v4l2_event_unsubscribe_all(struct v4l2_fh *fh)
 {
        struct v4l2_event_subscription sub;
        struct v4l2_subscribed_event *sev;
@@ -271,6 +248,7 @@ static void v4l2_event_unsubscribe_all(struct v4l2_fh *fh)
                        v4l2_event_unsubscribe(fh, &sub);
        } while (sev);
 }
+EXPORT_SYMBOL_GPL(v4l2_event_unsubscribe_all);
 
 int v4l2_event_unsubscribe(struct v4l2_fh *fh,
                           struct v4l2_event_subscription *sub)