[media] v4l2-event/ctrls/fh: allocate events per fh and per type instead of just per-fh

The driver had to decide how many events to allocate when the v4l2_fh struct
was created. It was possible to add more events afterwards, but there was no
way to ensure that you wouldn't miss important events if the event queue
would fill up for that filehandle.

In addition, once there were no more free events, any new events were simply
dropped on the floor.

For the control event in particular this made life very difficult since
control status/value changes could just be missed if the number of allocated
events and the speed at which the application read events was too low to keep
up with the number of generated events. The application would have no idea
what the latest state was for a control since it could have missed the latest
control change.

So this patch makes some major changes in how events are allocated. Instead
of allocating events per-filehandle they are now allocated when subscribing an
event. So for that particular event type N events (determined by the driver)
are allocated. Those events are reserved for that particular event type.
This ensures that you will not miss events for a particular type altogether.

In addition, if there are N events in use and a new event is raised, then
the oldest event is dropped and the new one is added. So the latest event
is always available.

This can be further improved by adding the ability to merge the state of
two events together, ensuring that no data is lost at all. This will be
added in the next patch.

This also makes it possible to allow the user to determine the number of
events that will be allocated. This is not implemented at the moment, but
would be trivial.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
diff --git a/include/media/v4l2-event.h b/include/media/v4l2-event.h
index eda17f8..8d681e5 100644
--- a/include/media/v4l2-event.h
+++ b/include/media/v4l2-event.h
@@ -30,10 +30,15 @@
 #include <linux/wait.h>
 
 struct v4l2_fh;
+struct v4l2_subscribed_event;
 struct video_device;
 
 struct v4l2_kevent {
+	/* list node for the v4l2_fh->available list */
 	struct list_head	list;
+	/* pointer to parent v4l2_subscribed_event */
+	struct v4l2_subscribed_event *sev;
+	/* event itself */
 	struct v4l2_event	event;
 };
 
@@ -50,18 +55,25 @@
 	struct v4l2_fh		*fh;
 	/* list node that hooks into the object's event list (if there is one) */
 	struct list_head	node;
+	/* the number of elements in the events array */
+	unsigned		elems;
+	/* the index of the events containing the oldest available event */
+	unsigned		first;
+	/* the number of queued events */
+	unsigned		in_use;
+	/* an array of elems events */
+	struct v4l2_kevent	events[];
 };
 
-int v4l2_event_alloc(struct v4l2_fh *fh, unsigned int n);
-void v4l2_event_free(struct v4l2_fh *fh);
 int v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event,
 		       int nonblocking);
 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);
 int v4l2_event_pending(struct v4l2_fh *fh);
 int v4l2_event_subscribe(struct v4l2_fh *fh,
-			 struct v4l2_event_subscription *sub);
+			 struct v4l2_event_subscription *sub, unsigned elems);
 int v4l2_event_unsubscribe(struct v4l2_fh *fh,
 			   struct v4l2_event_subscription *sub);
+void v4l2_event_unsubscribe_all(struct v4l2_fh *fh);
 
 #endif /* V4L2_EVENT_H */