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