Commit c53c2549 authored by Hans de Goede's avatar Hans de Goede Committed by Mauro Carvalho Chehab

[media] v4l2-event: Add v4l2_subscribed_event_ops

Just like with ctrl events, drivers may want to get called back on
listener add / remove for other event types too. Rather then special
casing all of this in subscribe / unsubscribe event it is better to
use ops for this.
Signed-off-by: default avatarHans de Goede <hdegoede@redhat.com>
Acked-by: default avatarHans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: default avatarLaurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent a22d85fe
...@@ -945,21 +945,35 @@ fast. ...@@ -945,21 +945,35 @@ fast.
Useful functions: Useful functions:
- v4l2_event_queue() void v4l2_event_queue(struct video_device *vdev, const struct v4l2_event *ev)
Queue events to video device. The driver's only responsibility is to fill Queue events to video device. The driver's only responsibility is to fill
in the type and the data fields. The other fields will be filled in by in the type and the data fields. The other fields will be filled in by
V4L2. V4L2.
- v4l2_event_subscribe() int v4l2_event_subscribe(struct v4l2_fh *fh,
struct v4l2_event_subscription *sub, unsigned elems,
const struct v4l2_subscribed_event_ops *ops)
The video_device->ioctl_ops->vidioc_subscribe_event must check the driver The video_device->ioctl_ops->vidioc_subscribe_event must check the driver
is able to produce events with specified event id. Then it calls is able to produce events with specified event id. Then it calls
v4l2_event_subscribe() to subscribe the event. The last argument is the v4l2_event_subscribe() to subscribe the event.
size of the event queue for this event. If it is 0, then the framework
will fill in a default value (this depends on the event type).
- v4l2_event_unsubscribe() The elems argument is the size of the event queue for this event. If it is 0,
then the framework will fill in a default value (this depends on the event
type).
The ops argument allows the driver to specify a number of callbacks:
* add: called when a new listener gets added (subscribing to the same
event twice will only cause this callback to get called once)
* del: called when a listener stops listening
* replace: replace event 'old' with event 'new'.
* merge: merge event 'old' into event 'new'.
All 4 callbacks are optional, if you don't want to specify any callbacks
the ops argument itself maybe NULL.
int v4l2_event_unsubscribe(struct v4l2_fh *fh,
struct v4l2_event_subscription *sub)
vidioc_unsubscribe_event in struct v4l2_ioctl_ops. A driver may use vidioc_unsubscribe_event in struct v4l2_ioctl_ops. A driver may use
v4l2_event_unsubscribe() directly unless it wants to be involved in v4l2_event_unsubscribe() directly unless it wants to be involved in
...@@ -968,7 +982,7 @@ Useful functions: ...@@ -968,7 +982,7 @@ Useful functions:
The special type V4L2_EVENT_ALL may be used to unsubscribe all events. The The special type V4L2_EVENT_ALL may be used to unsubscribe all events. The
drivers may want to handle this in a special way. drivers may want to handle this in a special way.
- v4l2_event_pending() int v4l2_event_pending(struct v4l2_fh *fh)
Returns the number of pending events. Useful when implementing poll. Returns the number of pending events. Useful when implementing poll.
......
...@@ -1469,7 +1469,7 @@ static int ivtv_subscribe_event(struct v4l2_fh *fh, struct v4l2_event_subscripti ...@@ -1469,7 +1469,7 @@ static int ivtv_subscribe_event(struct v4l2_fh *fh, struct v4l2_event_subscripti
case V4L2_EVENT_VSYNC: case V4L2_EVENT_VSYNC:
case V4L2_EVENT_EOS: case V4L2_EVENT_EOS:
case V4L2_EVENT_CTRL: case V4L2_EVENT_CTRL:
return v4l2_event_subscribe(fh, sub, 0); return v4l2_event_subscribe(fh, sub, 0, NULL);
default: default:
return -EINVAL; return -EINVAL;
} }
......
...@@ -1703,7 +1703,7 @@ static int ccdc_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, ...@@ -1703,7 +1703,7 @@ static int ccdc_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
if (sub->id != 0) if (sub->id != 0)
return -EINVAL; return -EINVAL;
return v4l2_event_subscribe(fh, sub, OMAP3ISP_CCDC_NEVENTS); return v4l2_event_subscribe(fh, sub, OMAP3ISP_CCDC_NEVENTS, NULL);
} }
static int ccdc_unsubscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, static int ccdc_unsubscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh,
......
...@@ -1032,7 +1032,7 @@ int omap3isp_stat_subscribe_event(struct v4l2_subdev *subdev, ...@@ -1032,7 +1032,7 @@ int omap3isp_stat_subscribe_event(struct v4l2_subdev *subdev,
if (sub->type != stat->event_type) if (sub->type != stat->event_type)
return -EINVAL; return -EINVAL;
return v4l2_event_subscribe(fh, sub, STAT_NEVENTS); return v4l2_event_subscribe(fh, sub, STAT_NEVENTS, NULL);
} }
int omap3isp_stat_unsubscribe_event(struct v4l2_subdev *subdev, int omap3isp_stat_unsubscribe_event(struct v4l2_subdev *subdev,
......
...@@ -2468,7 +2468,7 @@ int v4l2_ctrl_subscribe_event(struct v4l2_fh *fh, ...@@ -2468,7 +2468,7 @@ int v4l2_ctrl_subscribe_event(struct v4l2_fh *fh,
struct v4l2_event_subscription *sub) struct v4l2_event_subscription *sub)
{ {
if (sub->type == V4L2_EVENT_CTRL) if (sub->type == V4L2_EVENT_CTRL)
return v4l2_event_subscribe(fh, sub, 0); return v4l2_event_subscribe(fh, sub, 0, NULL);
return -EINVAL; return -EINVAL;
} }
EXPORT_SYMBOL(v4l2_ctrl_subscribe_event); EXPORT_SYMBOL(v4l2_ctrl_subscribe_event);
......
...@@ -120,6 +120,14 @@ static void __v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *e ...@@ -120,6 +120,14 @@ static void __v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *e
if (sev == NULL) if (sev == NULL)
return; 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. */ /* Increase event sequence number on fh. */
fh->sequence++; fh->sequence++;
...@@ -132,14 +140,14 @@ static void __v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *e ...@@ -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); sev->first = sev_pos(sev, 1);
fh->navailable--; fh->navailable--;
if (sev->elems == 1) { if (sev->elems == 1) {
if (sev->replace) { if (sev->ops && sev->ops->replace) {
sev->replace(&kev->event, ev); sev->ops->replace(&kev->event, ev);
copy_payload = false; copy_payload = false;
} }
} else if (sev->merge) { } else if (sev->ops && sev->ops->merge) {
struct v4l2_kevent *second_oldest = struct v4l2_kevent *second_oldest =
sev->events + sev_pos(sev, 0); 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) ...@@ -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; 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, 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_subscribed_event *sev, *found_ev;
struct v4l2_ctrl *ctrl = NULL; struct v4l2_ctrl *ctrl = NULL;
...@@ -236,10 +250,9 @@ int v4l2_event_subscribe(struct v4l2_fh *fh, ...@@ -236,10 +250,9 @@ int v4l2_event_subscribe(struct v4l2_fh *fh,
sev->id = sub->id; sev->id = sub->id;
sev->flags = sub->flags; sev->flags = sub->flags;
sev->fh = fh; sev->fh = fh;
sev->elems = elems; sev->ops = ops;
if (ctrl) { if (ctrl) {
sev->replace = ctrls_replace; sev->ops = &ctrl_ops;
sev->merge = ctrls_merge;
} }
spin_lock_irqsave(&fh->vdev->fh_lock, flags); spin_lock_irqsave(&fh->vdev->fh_lock, flags);
...@@ -248,12 +261,27 @@ int v4l2_event_subscribe(struct v4l2_fh *fh, ...@@ -248,12 +261,27 @@ int v4l2_event_subscribe(struct v4l2_fh *fh,
list_add(&sev->list, &fh->subscribed); list_add(&sev->list, &fh->subscribed);
spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); 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); 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); v4l2_ctrl_add_event(ctrl, sev);
/* Mark as ready for use */
sev->elems = elems;
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(v4l2_event_subscribe); EXPORT_SYMBOL_GPL(v4l2_event_subscribe);
...@@ -306,6 +334,10 @@ int v4l2_event_unsubscribe(struct v4l2_fh *fh, ...@@ -306,6 +334,10 @@ int v4l2_event_unsubscribe(struct v4l2_fh *fh,
} }
spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); 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) { if (sev && sev->type == V4L2_EVENT_CTRL) {
struct v4l2_ctrl *ctrl = v4l2_ctrl_find(fh->ctrl_handler, sev->id); struct v4l2_ctrl *ctrl = v4l2_ctrl_find(fh->ctrl_handler, sev->id);
......
...@@ -296,7 +296,7 @@ uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) ...@@ -296,7 +296,7 @@ uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
if (sub->type < UVC_EVENT_FIRST || sub->type > UVC_EVENT_LAST) if (sub->type < UVC_EVENT_FIRST || sub->type > UVC_EVENT_LAST)
return -EINVAL; return -EINVAL;
return v4l2_event_subscribe(&handle->vfh, arg, 2); return v4l2_event_subscribe(&handle->vfh, arg, 2, NULL);
} }
case VIDIOC_UNSUBSCRIBE_EVENT: case VIDIOC_UNSUBSCRIBE_EVENT:
......
...@@ -78,6 +78,19 @@ struct v4l2_kevent { ...@@ -78,6 +78,19 @@ struct v4l2_kevent {
struct v4l2_event event; struct v4l2_event event;
}; };
/** struct v4l2_subscribed_event_ops - Subscribed event operations.
* @add: Optional callback, called when a new listener is added
* @del: Optional callback, called when a listener stops listening
* @replace: Optional callback that can replace event 'old' with event 'new'.
* @merge: Optional callback that can merge event 'old' into event 'new'.
*/
struct v4l2_subscribed_event_ops {
int (*add)(struct v4l2_subscribed_event *sev);
void (*del)(struct v4l2_subscribed_event *sev);
void (*replace)(struct v4l2_event *old, const struct v4l2_event *new);
void (*merge)(const struct v4l2_event *old, struct v4l2_event *new);
};
/** struct v4l2_subscribed_event - Internal struct representing a subscribed event. /** struct v4l2_subscribed_event - Internal struct representing a subscribed event.
* @list: List node for the v4l2_fh->subscribed list. * @list: List node for the v4l2_fh->subscribed list.
* @type: Event type. * @type: Event type.
...@@ -85,8 +98,7 @@ struct v4l2_kevent { ...@@ -85,8 +98,7 @@ struct v4l2_kevent {
* @flags: Copy of v4l2_event_subscription->flags. * @flags: Copy of v4l2_event_subscription->flags.
* @fh: Filehandle that subscribed to this event. * @fh: Filehandle that subscribed to this event.
* @node: List node that hooks into the object's event list (if there is one). * @node: List node that hooks into the object's event list (if there is one).
* @replace: Optional callback that can replace event 'old' with event 'new'. * @ops: v4l2_subscribed_event_ops
* @merge: Optional callback that can merge event 'old' into event 'new'.
* @elems: The number of elements in the events array. * @elems: The number of elements in the events array.
* @first: The index of the events containing the oldest available event. * @first: The index of the events containing the oldest available event.
* @in_use: The number of queued events. * @in_use: The number of queued events.
...@@ -99,10 +111,7 @@ struct v4l2_subscribed_event { ...@@ -99,10 +111,7 @@ struct v4l2_subscribed_event {
u32 flags; u32 flags;
struct v4l2_fh *fh; struct v4l2_fh *fh;
struct list_head node; struct list_head node;
void (*replace)(struct v4l2_event *old, const struct v4l2_subscribed_event_ops *ops;
const struct v4l2_event *new);
void (*merge)(const struct v4l2_event *old,
struct v4l2_event *new);
unsigned elems; unsigned elems;
unsigned first; unsigned first;
unsigned in_use; unsigned in_use;
...@@ -115,7 +124,8 @@ void v4l2_event_queue(struct video_device *vdev, const struct v4l2_event *ev); ...@@ -115,7 +124,8 @@ void v4l2_event_queue(struct video_device *vdev, const struct v4l2_event *ev);
void v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *ev); void v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *ev);
int v4l2_event_pending(struct v4l2_fh *fh); int v4l2_event_pending(struct v4l2_fh *fh);
int v4l2_event_subscribe(struct v4l2_fh *fh, 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);
int v4l2_event_unsubscribe(struct v4l2_fh *fh, int v4l2_event_unsubscribe(struct v4l2_fh *fh,
struct v4l2_event_subscription *sub); struct v4l2_event_subscription *sub);
void v4l2_event_unsubscribe_all(struct v4l2_fh *fh); void v4l2_event_unsubscribe_all(struct v4l2_fh *fh);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment