[media] v4l2-event: Add v4l2_subscribed_event_ops
[firefly-linux-kernel-4.4.55.git] / drivers / media / video / v4l2-event.c
index c26ad9637143bcc82bc0bb5618b207ccc07277ba..0ba2dfa86d07535a6159bc905c578a3d59dac0e0 100644 (file)
@@ -120,6 +120,14 @@ static void __v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *e
        if (sev == NULL)
                return;
 
+       /*
+        * If the event has been added to the fh->subscribed list, but its
+        * add op has not completed yet elems will be 0, treat this as
+        * not being subscribed.
+        */
+       if (!sev->elems)
+               return;
+
        /* Increase event sequence number on fh. */
        fh->sequence++;
 
@@ -132,14 +140,14 @@ static void __v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *e
                sev->first = sev_pos(sev, 1);
                fh->navailable--;
                if (sev->elems == 1) {
-                       if (sev->replace) {
-                               sev->replace(&kev->event, ev);
+                       if (sev->ops && sev->ops->replace) {
+                               sev->ops->replace(&kev->event, ev);
                                copy_payload = false;
                        }
-               } else if (sev->merge) {
+               } else if (sev->ops && sev->ops->merge) {
                        struct v4l2_kevent *second_oldest =
                                sev->events + sev_pos(sev, 0);
-                       sev->merge(&kev->event, &second_oldest->event);
+                       sev->ops->merge(&kev->event, &second_oldest->event);
                }
        }
 
@@ -208,8 +216,14 @@ static void ctrls_merge(const struct v4l2_event *old, struct v4l2_event *new)
        new->u.ctrl.changes |= old->u.ctrl.changes;
 }
 
+static const struct v4l2_subscribed_event_ops ctrl_ops = {
+       .replace = ctrls_replace,
+       .merge = ctrls_merge,
+};
+
 int v4l2_event_subscribe(struct v4l2_fh *fh,
-                        struct v4l2_event_subscription *sub, unsigned elems)
+                        struct v4l2_event_subscription *sub, unsigned elems,
+                        const struct v4l2_subscribed_event_ops *ops)
 {
        struct v4l2_subscribed_event *sev, *found_ev;
        struct v4l2_ctrl *ctrl = NULL;
@@ -236,10 +250,9 @@ int v4l2_event_subscribe(struct v4l2_fh *fh,
        sev->id = sub->id;
        sev->flags = sub->flags;
        sev->fh = fh;
-       sev->elems = elems;
+       sev->ops = ops;
        if (ctrl) {
-               sev->replace = ctrls_replace;
-               sev->merge = ctrls_merge;
+               sev->ops = &ctrl_ops;
        }
 
        spin_lock_irqsave(&fh->vdev->fh_lock, flags);
@@ -248,12 +261,27 @@ int v4l2_event_subscribe(struct v4l2_fh *fh,
                list_add(&sev->list, &fh->subscribed);
        spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
 
-       /* v4l2_ctrl_add_event uses a mutex, so do this outside the spin lock */
-       if (found_ev)
+       if (found_ev) {
                kfree(sev);
-       else if (ctrl)
+               return 0; /* Already listening */
+       }
+
+       if (sev->ops && sev->ops->add) {
+               int ret = sev->ops->add(sev);
+               if (ret) {
+                       sev->ops = NULL;
+                       v4l2_event_unsubscribe(fh, sub);
+                       return ret;
+               }
+       }
+
+       /* v4l2_ctrl_add_event uses a mutex, so do this outside the spin lock */
+       if (ctrl)
                v4l2_ctrl_add_event(ctrl, sev);
 
+       /* Mark as ready for use */
+       sev->elems = elems;
+
        return 0;
 }
 EXPORT_SYMBOL_GPL(v4l2_event_subscribe);
@@ -306,6 +334,10 @@ int v4l2_event_unsubscribe(struct v4l2_fh *fh,
        }
 
        spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
+
+       if (sev && sev->ops && sev->ops->del)
+               sev->ops->del(sev);
+
        if (sev && sev->type == V4L2_EVENT_CTRL) {
                struct v4l2_ctrl *ctrl = v4l2_ctrl_find(fh->ctrl_handler, sev->id);