[media] v4l2-ctrls/event: remove struct v4l2_ctrl_fh, instead use v4l2_subscribed_event

The v4l2_ctrl_fh struct connected v4l2_ctrl with v4l2_fh so the control
would know which filehandles subscribed to it. However, it is much easier
to use struct v4l2_subscribed_event directly for that and get rid of that
intermediate struct.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c
index a532561..b63fe2a1 100644
--- a/drivers/media/video/v4l2-ctrls.c
+++ b/drivers/media/video/v4l2-ctrls.c
@@ -581,15 +581,15 @@
 static void send_event(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, u32 changes)
 {
 	struct v4l2_event ev;
-	struct v4l2_ctrl_fh *pos;
+	struct v4l2_subscribed_event *sev;
 
-	if (list_empty(&ctrl->fhs))
+	if (list_empty(&ctrl->ev_subs))
 			return;
 	fill_event(&ev, ctrl, changes);
 
-	list_for_each_entry(pos, &ctrl->fhs, node)
-		if (pos->fh != fh)
-			v4l2_event_queue_fh(pos->fh, &ev);
+	list_for_each_entry(sev, &ctrl->ev_subs, node)
+		if (sev->fh && sev->fh != fh)
+			v4l2_event_queue_fh(sev->fh, &ev);
 }
 
 /* Helper function: copy the current control value back to the caller */
@@ -867,7 +867,7 @@
 {
 	struct v4l2_ctrl_ref *ref, *next_ref;
 	struct v4l2_ctrl *ctrl, *next_ctrl;
-	struct v4l2_ctrl_fh *ctrl_fh, *next_ctrl_fh;
+	struct v4l2_subscribed_event *sev, *next_sev;
 
 	if (hdl == NULL || hdl->buckets == NULL)
 		return;
@@ -881,10 +881,8 @@
 	/* Free all controls owned by the handler */
 	list_for_each_entry_safe(ctrl, next_ctrl, &hdl->ctrls, node) {
 		list_del(&ctrl->node);
-		list_for_each_entry_safe(ctrl_fh, next_ctrl_fh, &ctrl->fhs, node) {
-			list_del(&ctrl_fh->node);
-			kfree(ctrl_fh);
-		}
+		list_for_each_entry_safe(sev, next_sev, &ctrl->ev_subs, node)
+			list_del(&sev->node);
 		kfree(ctrl);
 	}
 	kfree(hdl->buckets);
@@ -1084,7 +1082,7 @@
 	}
 
 	INIT_LIST_HEAD(&ctrl->node);
-	INIT_LIST_HEAD(&ctrl->fhs);
+	INIT_LIST_HEAD(&ctrl->ev_subs);
 	ctrl->handler = hdl;
 	ctrl->ops = ops;
 	ctrl->id = id;
@@ -2028,41 +2026,31 @@
 }
 EXPORT_SYMBOL(v4l2_ctrl_s_ctrl);
 
-void v4l2_ctrl_add_fh(struct v4l2_ctrl_handler *hdl,
-		struct v4l2_ctrl_fh *ctrl_fh,
-		struct v4l2_event_subscription *sub)
+void v4l2_ctrl_add_event(struct v4l2_ctrl *ctrl,
+				struct v4l2_subscribed_event *sev)
 {
-	struct v4l2_ctrl *ctrl = v4l2_ctrl_find(hdl, sub->id);
-
 	v4l2_ctrl_lock(ctrl);
-	list_add_tail(&ctrl_fh->node, &ctrl->fhs);
+	list_add_tail(&sev->node, &ctrl->ev_subs);
 	if (ctrl->type != V4L2_CTRL_TYPE_CTRL_CLASS &&
-	    (sub->flags & V4L2_EVENT_SUB_FL_SEND_INITIAL)) {
+	    (sev->flags & V4L2_EVENT_SUB_FL_SEND_INITIAL)) {
 		struct v4l2_event ev;
 
 		fill_event(&ev, ctrl, V4L2_EVENT_CTRL_CH_VALUE |
 			V4L2_EVENT_CTRL_CH_FLAGS);
-		v4l2_event_queue_fh(ctrl_fh->fh, &ev);
+		v4l2_event_queue_fh(sev->fh, &ev);
 	}
 	v4l2_ctrl_unlock(ctrl);
 }
-EXPORT_SYMBOL(v4l2_ctrl_add_fh);
+EXPORT_SYMBOL(v4l2_ctrl_add_event);
 
-void v4l2_ctrl_del_fh(struct v4l2_ctrl *ctrl, struct v4l2_fh *fh)
+void v4l2_ctrl_del_event(struct v4l2_ctrl *ctrl,
+				struct v4l2_subscribed_event *sev)
 {
-	struct v4l2_ctrl_fh *pos;
-
 	v4l2_ctrl_lock(ctrl);
-	list_for_each_entry(pos, &ctrl->fhs, node) {
-		if (pos->fh == fh) {
-			list_del(&pos->node);
-			kfree(pos);
-			break;
-		}
-	}
+	list_del(&sev->node);
 	v4l2_ctrl_unlock(ctrl);
 }
-EXPORT_SYMBOL(v4l2_ctrl_del_fh);
+EXPORT_SYMBOL(v4l2_ctrl_del_event);
 
 int v4l2_ctrl_subscribe_fh(struct v4l2_fh *fh,
 			struct v4l2_event_subscription *sub, unsigned n)
diff --git a/drivers/media/video/v4l2-event.c b/drivers/media/video/v4l2-event.c
index 70fa82d..dc68f60 100644
--- a/drivers/media/video/v4l2-event.c
+++ b/drivers/media/video/v4l2-event.c
@@ -213,7 +213,6 @@
 {
 	struct v4l2_subscribed_event *sev, *found_ev;
 	struct v4l2_ctrl *ctrl = NULL;
-	struct v4l2_ctrl_fh *ctrl_fh = NULL;
 	unsigned long flags;
 
 	if (sub->type == V4L2_EVENT_CTRL) {
@@ -222,17 +221,9 @@
 			return -EINVAL;
 	}
 
-	sev = kmalloc(sizeof(*sev), GFP_KERNEL);
+	sev = kzalloc(sizeof(*sev), GFP_KERNEL);
 	if (!sev)
 		return -ENOMEM;
-	if (ctrl) {
-		ctrl_fh = kzalloc(sizeof(*ctrl_fh), GFP_KERNEL);
-		if (!ctrl_fh) {
-			kfree(sev);
-			return -ENOMEM;
-		}
-		ctrl_fh->fh = fh;
-	}
 
 	spin_lock_irqsave(&fh->vdev->fh_lock, flags);
 
@@ -241,22 +232,19 @@
 		INIT_LIST_HEAD(&sev->list);
 		sev->type = sub->type;
 		sev->id = sub->id;
+		sev->fh = fh;
+		sev->flags = sub->flags;
 
 		list_add(&sev->list, &fh->subscribed);
-		sev = NULL;
 	}
 
 	spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
 
 	/* v4l2_ctrl_add_fh uses a mutex, so do this outside the spin lock */
-	if (ctrl) {
-		if (found_ev)
-			kfree(ctrl_fh);
-		else
-			v4l2_ctrl_add_fh(fh->ctrl_handler, ctrl_fh, sub);
-	}
-
-	kfree(sev);
+	if (found_ev)
+		kfree(sev);
+	else if (ctrl)
+		v4l2_ctrl_add_event(ctrl, sev);
 
 	return 0;
 }
@@ -298,15 +286,17 @@
 	spin_lock_irqsave(&fh->vdev->fh_lock, flags);
 
 	sev = v4l2_event_subscribed(fh, sub->type, sub->id);
-	if (sev != NULL)
+	if (sev != NULL) {
 		list_del(&sev->list);
+		sev->fh = NULL;
+	}
 
 	spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
-	if (sev->type == V4L2_EVENT_CTRL) {
+	if (sev && sev->type == V4L2_EVENT_CTRL) {
 		struct v4l2_ctrl *ctrl = v4l2_ctrl_find(fh->ctrl_handler, sev->id);
 
 		if (ctrl)
-			v4l2_ctrl_del_fh(ctrl, fh);
+			v4l2_ctrl_del_event(ctrl, sev);
 	}
 
 	kfree(sev);
diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h
index 8f08c6e..a15e909 100644
--- a/include/media/v4l2-ctrls.h
+++ b/include/media/v4l2-ctrls.h
@@ -31,6 +31,7 @@
 struct video_device;
 struct v4l2_subdev;
 struct v4l2_event_subscription;
+struct v4l2_subscribed_event;
 struct v4l2_fh;
 
 /** struct v4l2_ctrl_ops - The control operations that the driver has to provide.
@@ -53,6 +54,7 @@
 
 /** struct v4l2_ctrl - The control structure.
   * @node:	The list node.
+  * @ev_subs:	The list of control event subscriptions.
   * @handler:	The handler that owns the control.
   * @cluster:	Point to start of cluster array.
   * @ncontrols:	Number of controls in cluster array.
@@ -108,7 +110,7 @@
 struct v4l2_ctrl {
 	/* Administrative fields */
 	struct list_head node;
-	struct list_head fhs;
+	struct list_head ev_subs;
 	struct v4l2_ctrl_handler *handler;
 	struct v4l2_ctrl **cluster;
 	unsigned ncontrols;
@@ -184,11 +186,6 @@
 	int error;
 };
 
-struct v4l2_ctrl_fh {
-	struct list_head node;
-	struct v4l2_fh *fh;
-};
-
 /** struct v4l2_ctrl_config - Control configuration structure.
   * @ops:	The control ops.
   * @id:	The control ID.
@@ -497,10 +494,10 @@
 int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val);
 
 /* Internal helper functions that deal with control events. */
-void v4l2_ctrl_add_fh(struct v4l2_ctrl_handler *hdl,
-		struct v4l2_ctrl_fh *ctrl_fh,
-		struct v4l2_event_subscription *sub);
-void v4l2_ctrl_del_fh(struct v4l2_ctrl *ctrl, struct v4l2_fh *fh);
+void v4l2_ctrl_add_event(struct v4l2_ctrl *ctrl,
+		struct v4l2_subscribed_event *sev);
+void v4l2_ctrl_del_event(struct v4l2_ctrl *ctrl,
+		struct v4l2_subscribed_event *sev);
 
 /** v4l2_ctrl_subscribe_fh() - Helper function that subscribes a control event.
   * @fh:	The file handler that subscribed the control event.
diff --git a/include/media/v4l2-event.h b/include/media/v4l2-event.h
index 042b893..eda17f8 100644
--- a/include/media/v4l2-event.h
+++ b/include/media/v4l2-event.h
@@ -38,9 +38,18 @@
 };
 
 struct v4l2_subscribed_event {
+	/* list node for the v4l2_fh->subscribed list */
 	struct list_head	list;
+	/* event type */
 	u32			type;
+	/* associated object ID (e.g. control ID) */
 	u32			id;
+	/* copy of v4l2_event_subscription->flags */
+	u32			flags;
+	/* filehandle that subscribed to this event */
+	struct v4l2_fh		*fh;
+	/* list node that hooks into the object's event list (if there is one) */
+	struct list_head	node;
 };
 
 int v4l2_event_alloc(struct v4l2_fh *fh, unsigned int n);