diff options
author | Hans de Goede <hdegoede@redhat.com> | 2012-04-08 11:59:46 -0400 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@redhat.com> | 2012-05-07 15:28:35 -0400 |
commit | c53c2549333b340e2662dc64ec81323476b69a97 (patch) | |
tree | 4bd55780eba1302caaf7359631b996043dc1082f | |
parent | a22d85fea89744fad2cb215da1fe0c1ce226a613 (diff) |
[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: Hans de Goede <hdegoede@redhat.com>
Acked-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
-rw-r--r-- | Documentation/video4linux/v4l2-framework.txt | 28 | ||||
-rw-r--r-- | drivers/media/video/ivtv/ivtv-ioctl.c | 2 | ||||
-rw-r--r-- | drivers/media/video/omap3isp/ispccdc.c | 2 | ||||
-rw-r--r-- | drivers/media/video/omap3isp/ispstat.c | 2 | ||||
-rw-r--r-- | drivers/media/video/v4l2-ctrls.c | 2 | ||||
-rw-r--r-- | drivers/media/video/v4l2-event.c | 54 | ||||
-rw-r--r-- | drivers/usb/gadget/uvc_v4l2.c | 2 | ||||
-rw-r--r-- | include/media/v4l2-event.h | 24 |
8 files changed, 86 insertions, 30 deletions
diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt index e3dfc268d9c1..369d4bc87828 100644 --- a/Documentation/video4linux/v4l2-framework.txt +++ b/Documentation/video4linux/v4l2-framework.txt | |||
@@ -945,21 +945,35 @@ fast. | |||
945 | 945 | ||
946 | Useful functions: | 946 | Useful functions: |
947 | 947 | ||
948 | - v4l2_event_queue() | 948 | void v4l2_event_queue(struct video_device *vdev, const struct v4l2_event *ev) |
949 | 949 | ||
950 | Queue events to video device. The driver's only responsibility is to fill | 950 | Queue events to video device. The driver's only responsibility is to fill |
951 | in the type and the data fields. The other fields will be filled in by | 951 | in the type and the data fields. The other fields will be filled in by |
952 | V4L2. | 952 | V4L2. |
953 | 953 | ||
954 | - v4l2_event_subscribe() | 954 | int v4l2_event_subscribe(struct v4l2_fh *fh, |
955 | struct v4l2_event_subscription *sub, unsigned elems, | ||
956 | const struct v4l2_subscribed_event_ops *ops) | ||
955 | 957 | ||
956 | The video_device->ioctl_ops->vidioc_subscribe_event must check the driver | 958 | The video_device->ioctl_ops->vidioc_subscribe_event must check the driver |
957 | is able to produce events with specified event id. Then it calls | 959 | is able to produce events with specified event id. Then it calls |
958 | v4l2_event_subscribe() to subscribe the event. The last argument is the | 960 | v4l2_event_subscribe() to subscribe the event. |
959 | size of the event queue for this event. If it is 0, then the framework | ||
960 | will fill in a default value (this depends on the event type). | ||
961 | 961 | ||
962 | - v4l2_event_unsubscribe() | 962 | The elems argument is the size of the event queue for this event. If it is 0, |
963 | then the framework will fill in a default value (this depends on the event | ||
964 | type). | ||
965 | |||
966 | The ops argument allows the driver to specify a number of callbacks: | ||
967 | * add: called when a new listener gets added (subscribing to the same | ||
968 | event twice will only cause this callback to get called once) | ||
969 | * del: called when a listener stops listening | ||
970 | * replace: replace event 'old' with event 'new'. | ||
971 | * merge: merge event 'old' into event 'new'. | ||
972 | All 4 callbacks are optional, if you don't want to specify any callbacks | ||
973 | the ops argument itself maybe NULL. | ||
974 | |||
975 | int v4l2_event_unsubscribe(struct v4l2_fh *fh, | ||
976 | struct v4l2_event_subscription *sub) | ||
963 | 977 | ||
964 | vidioc_unsubscribe_event in struct v4l2_ioctl_ops. A driver may use | 978 | vidioc_unsubscribe_event in struct v4l2_ioctl_ops. A driver may use |
965 | v4l2_event_unsubscribe() directly unless it wants to be involved in | 979 | v4l2_event_unsubscribe() directly unless it wants to be involved in |
@@ -968,7 +982,7 @@ Useful functions: | |||
968 | The special type V4L2_EVENT_ALL may be used to unsubscribe all events. The | 982 | The special type V4L2_EVENT_ALL may be used to unsubscribe all events. The |
969 | drivers may want to handle this in a special way. | 983 | drivers may want to handle this in a special way. |
970 | 984 | ||
971 | - v4l2_event_pending() | 985 | int v4l2_event_pending(struct v4l2_fh *fh) |
972 | 986 | ||
973 | Returns the number of pending events. Useful when implementing poll. | 987 | Returns the number of pending events. Useful when implementing poll. |
974 | 988 | ||
diff --git a/drivers/media/video/ivtv/ivtv-ioctl.c b/drivers/media/video/ivtv/ivtv-ioctl.c index a151271f60e1..a7730fd4827f 100644 --- a/drivers/media/video/ivtv/ivtv-ioctl.c +++ b/drivers/media/video/ivtv/ivtv-ioctl.c | |||
@@ -1469,7 +1469,7 @@ static int ivtv_subscribe_event(struct v4l2_fh *fh, struct v4l2_event_subscripti | |||
1469 | case V4L2_EVENT_VSYNC: | 1469 | case V4L2_EVENT_VSYNC: |
1470 | case V4L2_EVENT_EOS: | 1470 | case V4L2_EVENT_EOS: |
1471 | case V4L2_EVENT_CTRL: | 1471 | case V4L2_EVENT_CTRL: |
1472 | return v4l2_event_subscribe(fh, sub, 0); | 1472 | return v4l2_event_subscribe(fh, sub, 0, NULL); |
1473 | default: | 1473 | default: |
1474 | return -EINVAL; | 1474 | return -EINVAL; |
1475 | } | 1475 | } |
diff --git a/drivers/media/video/omap3isp/ispccdc.c b/drivers/media/video/omap3isp/ispccdc.c index eaabc27f0fa2..1f3c16d8f0b4 100644 --- a/drivers/media/video/omap3isp/ispccdc.c +++ b/drivers/media/video/omap3isp/ispccdc.c | |||
@@ -1703,7 +1703,7 @@ static int ccdc_subscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, | |||
1703 | if (sub->id != 0) | 1703 | if (sub->id != 0) |
1704 | return -EINVAL; | 1704 | return -EINVAL; |
1705 | 1705 | ||
1706 | return v4l2_event_subscribe(fh, sub, OMAP3ISP_CCDC_NEVENTS); | 1706 | return v4l2_event_subscribe(fh, sub, OMAP3ISP_CCDC_NEVENTS, NULL); |
1707 | } | 1707 | } |
1708 | 1708 | ||
1709 | static int ccdc_unsubscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, | 1709 | static int ccdc_unsubscribe_event(struct v4l2_subdev *sd, struct v4l2_fh *fh, |
diff --git a/drivers/media/video/omap3isp/ispstat.c b/drivers/media/video/omap3isp/ispstat.c index 11871ecc6d25..b8640be692f1 100644 --- a/drivers/media/video/omap3isp/ispstat.c +++ b/drivers/media/video/omap3isp/ispstat.c | |||
@@ -1032,7 +1032,7 @@ int omap3isp_stat_subscribe_event(struct v4l2_subdev *subdev, | |||
1032 | if (sub->type != stat->event_type) | 1032 | if (sub->type != stat->event_type) |
1033 | return -EINVAL; | 1033 | return -EINVAL; |
1034 | 1034 | ||
1035 | return v4l2_event_subscribe(fh, sub, STAT_NEVENTS); | 1035 | return v4l2_event_subscribe(fh, sub, STAT_NEVENTS, NULL); |
1036 | } | 1036 | } |
1037 | 1037 | ||
1038 | int omap3isp_stat_unsubscribe_event(struct v4l2_subdev *subdev, | 1038 | int omap3isp_stat_unsubscribe_event(struct v4l2_subdev *subdev, |
diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c index c93a9796f1fb..91b197819fc2 100644 --- a/drivers/media/video/v4l2-ctrls.c +++ b/drivers/media/video/v4l2-ctrls.c | |||
@@ -2468,7 +2468,7 @@ int v4l2_ctrl_subscribe_event(struct v4l2_fh *fh, | |||
2468 | struct v4l2_event_subscription *sub) | 2468 | struct v4l2_event_subscription *sub) |
2469 | { | 2469 | { |
2470 | if (sub->type == V4L2_EVENT_CTRL) | 2470 | if (sub->type == V4L2_EVENT_CTRL) |
2471 | return v4l2_event_subscribe(fh, sub, 0); | 2471 | return v4l2_event_subscribe(fh, sub, 0, NULL); |
2472 | return -EINVAL; | 2472 | return -EINVAL; |
2473 | } | 2473 | } |
2474 | EXPORT_SYMBOL(v4l2_ctrl_subscribe_event); | 2474 | EXPORT_SYMBOL(v4l2_ctrl_subscribe_event); |
diff --git a/drivers/media/video/v4l2-event.c b/drivers/media/video/v4l2-event.c index c26ad9637143..0ba2dfa86d07 100644 --- a/drivers/media/video/v4l2-event.c +++ b/drivers/media/video/v4l2-event.c | |||
@@ -120,6 +120,14 @@ static void __v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *e | |||
120 | if (sev == NULL) | 120 | if (sev == NULL) |
121 | return; | 121 | return; |
122 | 122 | ||
123 | /* | ||
124 | * If the event has been added to the fh->subscribed list, but its | ||
125 | * add op has not completed yet elems will be 0, treat this as | ||
126 | * not being subscribed. | ||
127 | */ | ||
128 | if (!sev->elems) | ||
129 | return; | ||
130 | |||
123 | /* Increase event sequence number on fh. */ | 131 | /* Increase event sequence number on fh. */ |
124 | fh->sequence++; | 132 | fh->sequence++; |
125 | 133 | ||
@@ -132,14 +140,14 @@ static void __v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *e | |||
132 | sev->first = sev_pos(sev, 1); | 140 | sev->first = sev_pos(sev, 1); |
133 | fh->navailable--; | 141 | fh->navailable--; |
134 | if (sev->elems == 1) { | 142 | if (sev->elems == 1) { |
135 | if (sev->replace) { | 143 | if (sev->ops && sev->ops->replace) { |
136 | sev->replace(&kev->event, ev); | 144 | sev->ops->replace(&kev->event, ev); |
137 | copy_payload = false; | 145 | copy_payload = false; |
138 | } | 146 | } |
139 | } else if (sev->merge) { | 147 | } else if (sev->ops && sev->ops->merge) { |
140 | struct v4l2_kevent *second_oldest = | 148 | struct v4l2_kevent *second_oldest = |
141 | sev->events + sev_pos(sev, 0); | 149 | sev->events + sev_pos(sev, 0); |
142 | sev->merge(&kev->event, &second_oldest->event); | 150 | sev->ops->merge(&kev->event, &second_oldest->event); |
143 | } | 151 | } |
144 | } | 152 | } |
145 | 153 | ||
@@ -208,8 +216,14 @@ static void ctrls_merge(const struct v4l2_event *old, struct v4l2_event *new) | |||
208 | new->u.ctrl.changes |= old->u.ctrl.changes; | 216 | new->u.ctrl.changes |= old->u.ctrl.changes; |
209 | } | 217 | } |
210 | 218 | ||
219 | static const struct v4l2_subscribed_event_ops ctrl_ops = { | ||
220 | .replace = ctrls_replace, | ||
221 | .merge = ctrls_merge, | ||
222 | }; | ||
223 | |||
211 | int v4l2_event_subscribe(struct v4l2_fh *fh, | 224 | int v4l2_event_subscribe(struct v4l2_fh *fh, |
212 | struct v4l2_event_subscription *sub, unsigned elems) | 225 | struct v4l2_event_subscription *sub, unsigned elems, |
226 | const struct v4l2_subscribed_event_ops *ops) | ||
213 | { | 227 | { |
214 | struct v4l2_subscribed_event *sev, *found_ev; | 228 | struct v4l2_subscribed_event *sev, *found_ev; |
215 | struct v4l2_ctrl *ctrl = NULL; | 229 | struct v4l2_ctrl *ctrl = NULL; |
@@ -236,10 +250,9 @@ int v4l2_event_subscribe(struct v4l2_fh *fh, | |||
236 | sev->id = sub->id; | 250 | sev->id = sub->id; |
237 | sev->flags = sub->flags; | 251 | sev->flags = sub->flags; |
238 | sev->fh = fh; | 252 | sev->fh = fh; |
239 | sev->elems = elems; | 253 | sev->ops = ops; |
240 | if (ctrl) { | 254 | if (ctrl) { |
241 | sev->replace = ctrls_replace; | 255 | sev->ops = &ctrl_ops; |
242 | sev->merge = ctrls_merge; | ||
243 | } | 256 | } |
244 | 257 | ||
245 | spin_lock_irqsave(&fh->vdev->fh_lock, flags); | 258 | spin_lock_irqsave(&fh->vdev->fh_lock, flags); |
@@ -248,12 +261,27 @@ int v4l2_event_subscribe(struct v4l2_fh *fh, | |||
248 | list_add(&sev->list, &fh->subscribed); | 261 | list_add(&sev->list, &fh->subscribed); |
249 | spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); | 262 | spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); |
250 | 263 | ||
251 | /* v4l2_ctrl_add_event uses a mutex, so do this outside the spin lock */ | 264 | if (found_ev) { |
252 | if (found_ev) | ||
253 | kfree(sev); | 265 | kfree(sev); |
254 | else if (ctrl) | 266 | return 0; /* Already listening */ |
267 | } | ||
268 | |||
269 | if (sev->ops && sev->ops->add) { | ||
270 | int ret = sev->ops->add(sev); | ||
271 | if (ret) { | ||
272 | sev->ops = NULL; | ||
273 | v4l2_event_unsubscribe(fh, sub); | ||
274 | return ret; | ||
275 | } | ||
276 | } | ||
277 | |||
278 | /* v4l2_ctrl_add_event uses a mutex, so do this outside the spin lock */ | ||
279 | if (ctrl) | ||
255 | v4l2_ctrl_add_event(ctrl, sev); | 280 | v4l2_ctrl_add_event(ctrl, sev); |
256 | 281 | ||
282 | /* Mark as ready for use */ | ||
283 | sev->elems = elems; | ||
284 | |||
257 | return 0; | 285 | return 0; |
258 | } | 286 | } |
259 | EXPORT_SYMBOL_GPL(v4l2_event_subscribe); | 287 | EXPORT_SYMBOL_GPL(v4l2_event_subscribe); |
@@ -306,6 +334,10 @@ int v4l2_event_unsubscribe(struct v4l2_fh *fh, | |||
306 | } | 334 | } |
307 | 335 | ||
308 | spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); | 336 | spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); |
337 | |||
338 | if (sev && sev->ops && sev->ops->del) | ||
339 | sev->ops->del(sev); | ||
340 | |||
309 | if (sev && sev->type == V4L2_EVENT_CTRL) { | 341 | if (sev && sev->type == V4L2_EVENT_CTRL) { |
310 | struct v4l2_ctrl *ctrl = v4l2_ctrl_find(fh->ctrl_handler, sev->id); | 342 | struct v4l2_ctrl *ctrl = v4l2_ctrl_find(fh->ctrl_handler, sev->id); |
311 | 343 | ||
diff --git a/drivers/usb/gadget/uvc_v4l2.c b/drivers/usb/gadget/uvc_v4l2.c index f6e083b50191..90db5fe9c56e 100644 --- a/drivers/usb/gadget/uvc_v4l2.c +++ b/drivers/usb/gadget/uvc_v4l2.c | |||
@@ -296,7 +296,7 @@ uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg) | |||
296 | if (sub->type < UVC_EVENT_FIRST || sub->type > UVC_EVENT_LAST) | 296 | if (sub->type < UVC_EVENT_FIRST || sub->type > UVC_EVENT_LAST) |
297 | return -EINVAL; | 297 | return -EINVAL; |
298 | 298 | ||
299 | return v4l2_event_subscribe(&handle->vfh, arg, 2); | 299 | return v4l2_event_subscribe(&handle->vfh, arg, 2, NULL); |
300 | } | 300 | } |
301 | 301 | ||
302 | case VIDIOC_UNSUBSCRIBE_EVENT: | 302 | case VIDIOC_UNSUBSCRIBE_EVENT: |
diff --git a/include/media/v4l2-event.h b/include/media/v4l2-event.h index 5f14e8895ce2..88fa9a1e0df3 100644 --- a/include/media/v4l2-event.h +++ b/include/media/v4l2-event.h | |||
@@ -78,6 +78,19 @@ struct v4l2_kevent { | |||
78 | struct v4l2_event event; | 78 | struct v4l2_event event; |
79 | }; | 79 | }; |
80 | 80 | ||
81 | /** struct v4l2_subscribed_event_ops - Subscribed event operations. | ||
82 | * @add: Optional callback, called when a new listener is added | ||
83 | * @del: Optional callback, called when a listener stops listening | ||
84 | * @replace: Optional callback that can replace event 'old' with event 'new'. | ||
85 | * @merge: Optional callback that can merge event 'old' into event 'new'. | ||
86 | */ | ||
87 | struct v4l2_subscribed_event_ops { | ||
88 | int (*add)(struct v4l2_subscribed_event *sev); | ||
89 | void (*del)(struct v4l2_subscribed_event *sev); | ||
90 | void (*replace)(struct v4l2_event *old, const struct v4l2_event *new); | ||
91 | void (*merge)(const struct v4l2_event *old, struct v4l2_event *new); | ||
92 | }; | ||
93 | |||
81 | /** struct v4l2_subscribed_event - Internal struct representing a subscribed event. | 94 | /** struct v4l2_subscribed_event - Internal struct representing a subscribed event. |
82 | * @list: List node for the v4l2_fh->subscribed list. | 95 | * @list: List node for the v4l2_fh->subscribed list. |
83 | * @type: Event type. | 96 | * @type: Event type. |
@@ -85,8 +98,7 @@ struct v4l2_kevent { | |||
85 | * @flags: Copy of v4l2_event_subscription->flags. | 98 | * @flags: Copy of v4l2_event_subscription->flags. |
86 | * @fh: Filehandle that subscribed to this event. | 99 | * @fh: Filehandle that subscribed to this event. |
87 | * @node: List node that hooks into the object's event list (if there is one). | 100 | * @node: List node that hooks into the object's event list (if there is one). |
88 | * @replace: Optional callback that can replace event 'old' with event 'new'. | 101 | * @ops: v4l2_subscribed_event_ops |
89 | * @merge: Optional callback that can merge event 'old' into event 'new'. | ||
90 | * @elems: The number of elements in the events array. | 102 | * @elems: The number of elements in the events array. |
91 | * @first: The index of the events containing the oldest available event. | 103 | * @first: The index of the events containing the oldest available event. |
92 | * @in_use: The number of queued events. | 104 | * @in_use: The number of queued events. |
@@ -99,10 +111,7 @@ struct v4l2_subscribed_event { | |||
99 | u32 flags; | 111 | u32 flags; |
100 | struct v4l2_fh *fh; | 112 | struct v4l2_fh *fh; |
101 | struct list_head node; | 113 | struct list_head node; |
102 | void (*replace)(struct v4l2_event *old, | 114 | const struct v4l2_subscribed_event_ops *ops; |
103 | const struct v4l2_event *new); | ||
104 | void (*merge)(const struct v4l2_event *old, | ||
105 | struct v4l2_event *new); | ||
106 | unsigned elems; | 115 | unsigned elems; |
107 | unsigned first; | 116 | unsigned first; |
108 | unsigned in_use; | 117 | unsigned in_use; |
@@ -115,7 +124,8 @@ void v4l2_event_queue(struct video_device *vdev, const struct v4l2_event *ev); | |||
115 | void v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *ev); | 124 | void v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *ev); |
116 | int v4l2_event_pending(struct v4l2_fh *fh); | 125 | int v4l2_event_pending(struct v4l2_fh *fh); |
117 | int v4l2_event_subscribe(struct v4l2_fh *fh, | 126 | int v4l2_event_subscribe(struct v4l2_fh *fh, |
118 | struct v4l2_event_subscription *sub, unsigned elems); | 127 | struct v4l2_event_subscription *sub, unsigned elems, |
128 | const struct v4l2_subscribed_event_ops *ops); | ||
119 | int v4l2_event_unsubscribe(struct v4l2_fh *fh, | 129 | int v4l2_event_unsubscribe(struct v4l2_fh *fh, |
120 | struct v4l2_event_subscription *sub); | 130 | struct v4l2_event_subscription *sub); |
121 | void v4l2_event_unsubscribe_all(struct v4l2_fh *fh); | 131 | void v4l2_event_unsubscribe_all(struct v4l2_fh *fh); |