All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFCv1 PATCH 0/9] Control Events
@ 2011-04-04 11:51 Hans Verkuil
  2011-04-04 11:51 ` [RFCv1 PATCH 1/9] v4l2-ctrls: add new bitmask control type Hans Verkuil
  0 siblings, 1 reply; 30+ messages in thread
From: Hans Verkuil @ 2011-04-04 11:51 UTC (permalink / raw)
  To: linux-media

Before and during the Warsaw brainstorm meeting we discussed control events
and how those could be used for HDMI controls in addition to automatically
updating control panels.

This patch series implements this functionality.

Note that there are no official DocBook patches yet. I'd like to get some
feedback first.

But here is the informal documentation:

1) A new control type was added to represent bitmasks. This is needed by both
   HDMI controls and Flash controls. When defining a bitmask control the minimum
   and step fields are 0 and the maximum field is the bitmask value with all
   valid bits set to 1.

2) struct v4l2_fh now has a pointer to v4l2_ctrl_handler. This is needed for
   control events, but also for per-filehandle controls. If this pointer is NULL,
   then v4l2_fh will inherit the ctrl_handler of video_device.

3) Add a new 'id' field when (un)subscribing an event: this will identify a
   particular object from which you want to get an event. In the case of control
   events this is of course the control ID.

4) Add two new events:

#define V4L2_EVENT_CTRL_CH_VALUE                3
#define V4L2_EVENT_CTRL_CH_STATE                4

/* Payload for V4L2_EVENT_CTRL_CH_VALUE */
struct v4l2_event_ctrl_ch_value {
        __u32 type;
        union {
                __s32 value;
                __s64 value64;
        };
} __attribute__ ((packed));

/* Payload for V4L2_EVENT_CTRL_CH_STATE */
struct v4l2_event_ctrl_ch_state {
        __u32 type;
        __u32 flags;
} __attribute__ ((packed));

The first is sent when the value of a control changes (or when a button control
is activated), the second is sent when the flags field describing the state of a
control (active/grabbed/disabled) changes. The control type is also sent to make
it easier to interpret the value union and flags.

5) Added a new subscription flag field with currently one flag:

#define V4L2_EVENT_SUB_FL_SEND_INITIAL (1 << 0)

If set, then an initial event with the state at the time of subscription will
be sent. This avoids race conditions where an application has to get the current
state of a control, then subscribe an event. The application may have missed
state changes between the two.

This flag has no effect for events that are triggered by real events like
a Vsync.

I have thought about a 'FL_SKIP_SELF' event where the file handle that changes
the control value does not get an event in response. However, that is a bit
tricky and also does not seem to be necessary based on some tests with qv4l2.

So I won't implement that for now.

An updated qv4l2 can be found here:

http://git.linuxtv.org/hverkuil/v4l-utils.git?a=shortlog;h=refs/heads/core

It's based on a slightly older patch series, though.

6) Modified vb2_poll. See the commit log in that patch for the details.

Comments are welcome!

Regards,

	Hans



^ permalink raw reply	[flat|nested] 30+ messages in thread

* [RFCv1 PATCH 1/9] v4l2-ctrls: add new bitmask control type.
  2011-04-04 11:51 [RFCv1 PATCH 0/9] Control Events Hans Verkuil
@ 2011-04-04 11:51 ` Hans Verkuil
  2011-04-04 11:51   ` [RFCv1 PATCH 2/9] vivi: add bitmask test control Hans Verkuil
                     ` (8 more replies)
  0 siblings, 9 replies; 30+ messages in thread
From: Hans Verkuil @ 2011-04-04 11:51 UTC (permalink / raw)
  To: linux-media

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 drivers/media/video/v4l2-common.c |    3 +++
 drivers/media/video/v4l2-ctrls.c  |   17 ++++++++++++++++-
 include/linux/videodev2.h         |    1 +
 3 files changed, 20 insertions(+), 1 deletions(-)

diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c
index 06b9f9f..5c6100f 100644
--- a/drivers/media/video/v4l2-common.c
+++ b/drivers/media/video/v4l2-common.c
@@ -105,6 +105,9 @@ int v4l2_ctrl_check(struct v4l2_ext_control *ctrl, struct v4l2_queryctrl *qctrl,
 		    menu_items[ctrl->value][0] == '\0')
 			return -EINVAL;
 	}
+	if (qctrl->type == V4L2_CTRL_TYPE_BITMASK &&
+			(ctrl->value & ~qctrl->maximum))
+		return -ERANGE;
 	return 0;
 }
 EXPORT_SYMBOL(v4l2_ctrl_check);
diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c
index 2412f08..f75a1d4 100644
--- a/drivers/media/video/v4l2-ctrls.c
+++ b/drivers/media/video/v4l2-ctrls.c
@@ -726,6 +726,10 @@ static int validate_new(struct v4l2_ctrl *ctrl)
 			return -EINVAL;
 		return 0;
 
+	case V4L2_CTRL_TYPE_BITMASK:
+		ctrl->val &= ctrl->maximum;
+		return 0;
+
 	case V4L2_CTRL_TYPE_BUTTON:
 	case V4L2_CTRL_TYPE_CTRL_CLASS:
 		ctrl->val64 = 0;
@@ -962,13 +966,17 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
 
 	/* Sanity checks */
 	if (id == 0 || name == NULL || id >= V4L2_CID_PRIVATE_BASE ||
-	    max < min ||
 	    (type == V4L2_CTRL_TYPE_INTEGER && step == 0) ||
+	    (type == V4L2_CTRL_TYPE_BITMASK && max == 0) ||
 	    (type == V4L2_CTRL_TYPE_MENU && qmenu == NULL) ||
 	    (type == V4L2_CTRL_TYPE_STRING && max == 0)) {
 		handler_set_err(hdl, -ERANGE);
 		return NULL;
 	}
+	if (type != V4L2_CTRL_TYPE_BITMASK && max < min) {
+		handler_set_err(hdl, -ERANGE);
+		return NULL;
+	}
 	if ((type == V4L2_CTRL_TYPE_INTEGER ||
 	     type == V4L2_CTRL_TYPE_MENU ||
 	     type == V4L2_CTRL_TYPE_BOOLEAN) &&
@@ -976,6 +984,10 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
 		handler_set_err(hdl, -ERANGE);
 		return NULL;
 	}
+	if (type == V4L2_CTRL_TYPE_BITMASK && ((def & ~max) || min || step)) {
+		handler_set_err(hdl, -ERANGE);
+		return NULL;
+	}
 
 	if (type == V4L2_CTRL_TYPE_BUTTON)
 		flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
@@ -1217,6 +1229,9 @@ static void log_ctrl(const struct v4l2_ctrl *ctrl,
 	case V4L2_CTRL_TYPE_MENU:
 		printk(KERN_CONT "%s", ctrl->qmenu[ctrl->cur.val]);
 		break;
+	case V4L2_CTRL_TYPE_BITMASK:
+		printk(KERN_CONT "0x%x", ctrl->cur.val);
+		break;
 	case V4L2_CTRL_TYPE_INTEGER64:
 		printk(KERN_CONT "%lld", ctrl->cur.val64);
 		break;
diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h
index aa6c393..92d2fdd 100644
--- a/include/linux/videodev2.h
+++ b/include/linux/videodev2.h
@@ -1034,6 +1034,7 @@ enum v4l2_ctrl_type {
 	V4L2_CTRL_TYPE_INTEGER64     = 5,
 	V4L2_CTRL_TYPE_CTRL_CLASS    = 6,
 	V4L2_CTRL_TYPE_STRING        = 7,
+	V4L2_CTRL_TYPE_BITMASK       = 8,
 };
 
 /*  Used in the VIDIOC_QUERYCTRL ioctl for querying controls */
-- 
1.7.1


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [RFCv1 PATCH 2/9] vivi: add bitmask test control.
  2011-04-04 11:51 ` [RFCv1 PATCH 1/9] v4l2-ctrls: add new bitmask control type Hans Verkuil
@ 2011-04-04 11:51   ` Hans Verkuil
  2011-04-04 11:51   ` [RFCv1 PATCH 3/9] v4l2-ioctl: add ctrl_handler to v4l2_fh Hans Verkuil
                     ` (7 subsequent siblings)
  8 siblings, 0 replies; 30+ messages in thread
From: Hans Verkuil @ 2011-04-04 11:51 UTC (permalink / raw)
  To: linux-media

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 drivers/media/video/vivi.c |   18 ++++++++++++++++--
 1 files changed, 16 insertions(+), 2 deletions(-)

diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c
index 2238a61..21d8f6a 100644
--- a/drivers/media/video/vivi.c
+++ b/drivers/media/video/vivi.c
@@ -174,6 +174,7 @@ struct vivi_dev {
 	struct v4l2_ctrl	   *int64;
 	struct v4l2_ctrl	   *menu;
 	struct v4l2_ctrl	   *string;
+	struct v4l2_ctrl	   *bitmask;
 
 	spinlock_t                 slock;
 	struct mutex		   mutex;
@@ -488,9 +489,10 @@ static void vivi_fillbuff(struct vivi_dev *dev, struct vivi_buffer *buf)
 	gen_text(dev, vbuf, line++ * 16, 16, str);
 	snprintf(str, sizeof(str), " volume %3d ", dev->volume->cur.val);
 	gen_text(dev, vbuf, line++ * 16, 16, str);
-	snprintf(str, sizeof(str), " int32 %d, int64 %lld ",
+	snprintf(str, sizeof(str), " int32 %d, int64 %lld, bitmask %08x ",
 			dev->int32->cur.val,
-			dev->int64->cur.val64);
+			dev->int64->cur.val64,
+			dev->bitmask->cur.val);
 	gen_text(dev, vbuf, line++ * 16, 16, str);
 	snprintf(str, sizeof(str), " boolean %d, menu %s, string \"%s\" ",
 			dev->boolean->cur.val,
@@ -1117,6 +1119,17 @@ static const struct v4l2_ctrl_config vivi_ctrl_string = {
 	.step = 1,
 };
 
+static const struct v4l2_ctrl_config vivi_ctrl_bitmask = {
+	.ops = &vivi_ctrl_ops,
+	.id = VIVI_CID_CUSTOM_BASE + 6,
+	.name = "Bitmask",
+	.type = V4L2_CTRL_TYPE_BITMASK,
+	.def = 0x80002000,
+	.min = 0,
+	.max = 0x80402010,
+	.step = 0,
+};
+
 static const struct v4l2_file_operations vivi_fops = {
 	.owner		= THIS_MODULE,
 	.open		= v4l2_fh_open,
@@ -1219,6 +1232,7 @@ static int __init vivi_create_instance(int inst)
 	dev->boolean = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_boolean, NULL);
 	dev->menu = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_menu, NULL);
 	dev->string = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_string, NULL);
+	dev->bitmask = v4l2_ctrl_new_custom(hdl, &vivi_ctrl_bitmask, NULL);
 	if (hdl->error) {
 		ret = hdl->error;
 		goto unreg_dev;
-- 
1.7.1


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [RFCv1 PATCH 3/9] v4l2-ioctl: add ctrl_handler to v4l2_fh
  2011-04-04 11:51 ` [RFCv1 PATCH 1/9] v4l2-ctrls: add new bitmask control type Hans Verkuil
  2011-04-04 11:51   ` [RFCv1 PATCH 2/9] vivi: add bitmask test control Hans Verkuil
@ 2011-04-04 11:51   ` Hans Verkuil
  2011-04-08 15:10     ` Laurent Pinchart
  2011-04-04 11:51   ` [RFCv1 PATCH 4/9] v4l2-ctrls: add per-control events Hans Verkuil
                     ` (6 subsequent siblings)
  8 siblings, 1 reply; 30+ messages in thread
From: Hans Verkuil @ 2011-04-04 11:51 UTC (permalink / raw)
  To: linux-media; +Cc: Hans Verkuil

From: Hans Verkuil <hverkuil@xs4all.nl>

This is required to implement control events and is also needed to allow
for per-filehandle control handlers.

Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
---
 drivers/media/video/v4l2-fh.c    |    2 ++
 drivers/media/video/v4l2-ioctl.c |   36 +++++++++++++++++++++++++++---------
 include/media/v4l2-fh.h          |    2 ++
 3 files changed, 31 insertions(+), 9 deletions(-)

diff --git a/drivers/media/video/v4l2-fh.c b/drivers/media/video/v4l2-fh.c
index 717f71e..8635011 100644
--- a/drivers/media/video/v4l2-fh.c
+++ b/drivers/media/video/v4l2-fh.c
@@ -32,6 +32,8 @@
 int v4l2_fh_init(struct v4l2_fh *fh, struct video_device *vdev)
 {
 	fh->vdev = vdev;
+	/* Inherit from video_device. May be overridden by the driver. */
+	fh->ctrl_handler = vdev->ctrl_handler;
 	INIT_LIST_HEAD(&fh->list);
 	set_bit(V4L2_FL_USES_V4L2_FH, &fh->vdev->flags);
 	fh->prio = V4L2_PRIORITY_UNSET;
diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c
index a01ed39..b5b875b 100644
--- a/drivers/media/video/v4l2-ioctl.c
+++ b/drivers/media/video/v4l2-ioctl.c
@@ -1418,7 +1418,9 @@ static long __video_do_ioctl(struct file *file,
 	{
 		struct v4l2_queryctrl *p = arg;
 
-		if (vfd->ctrl_handler)
+		if (vfh && vfh->ctrl_handler)
+			ret = v4l2_queryctrl(vfh->ctrl_handler, p);
+		else if (vfd->ctrl_handler)
 			ret = v4l2_queryctrl(vfd->ctrl_handler, p);
 		else if (ops->vidioc_queryctrl)
 			ret = ops->vidioc_queryctrl(file, fh, p);
@@ -1438,7 +1440,9 @@ static long __video_do_ioctl(struct file *file,
 	{
 		struct v4l2_control *p = arg;
 
-		if (vfd->ctrl_handler)
+		if (vfh && vfh->ctrl_handler)
+			ret = v4l2_g_ctrl(vfh->ctrl_handler, p);
+		else if (vfd->ctrl_handler)
 			ret = v4l2_g_ctrl(vfd->ctrl_handler, p);
 		else if (ops->vidioc_g_ctrl)
 			ret = ops->vidioc_g_ctrl(file, fh, p);
@@ -1470,12 +1474,16 @@ static long __video_do_ioctl(struct file *file,
 		struct v4l2_ext_controls ctrls;
 		struct v4l2_ext_control ctrl;
 
-		if (!vfd->ctrl_handler &&
+		if (!(vfh && vfh->ctrl_handler) && !vfd->ctrl_handler &&
 			!ops->vidioc_s_ctrl && !ops->vidioc_s_ext_ctrls)
 			break;
 
 		dbgarg(cmd, "id=0x%x, value=%d\n", p->id, p->value);
 
+		if (vfh && vfh->ctrl_handler) {
+			ret = v4l2_s_ctrl(vfh->ctrl_handler, p);
+			break;
+		}
 		if (vfd->ctrl_handler) {
 			ret = v4l2_s_ctrl(vfd->ctrl_handler, p);
 			break;
@@ -1501,7 +1509,9 @@ static long __video_do_ioctl(struct file *file,
 		struct v4l2_ext_controls *p = arg;
 
 		p->error_idx = p->count;
-		if (vfd->ctrl_handler)
+		if (vfh && vfh->ctrl_handler)
+			ret = v4l2_g_ext_ctrls(vfh->ctrl_handler, p);
+		else if (vfd->ctrl_handler)
 			ret = v4l2_g_ext_ctrls(vfd->ctrl_handler, p);
 		else if (ops->vidioc_g_ext_ctrls && check_ext_ctrls(p, 0))
 			ret = ops->vidioc_g_ext_ctrls(file, fh, p);
@@ -1515,10 +1525,13 @@ static long __video_do_ioctl(struct file *file,
 		struct v4l2_ext_controls *p = arg;
 
 		p->error_idx = p->count;
-		if (!vfd->ctrl_handler && !ops->vidioc_s_ext_ctrls)
+		if (!(vfh && vfh->ctrl_handler) && !vfd->ctrl_handler &&
+				!ops->vidioc_s_ext_ctrls)
 			break;
 		v4l_print_ext_ctrls(cmd, vfd, p, 1);
-		if (vfd->ctrl_handler)
+		if (vfh && vfh->ctrl_handler)
+			ret = v4l2_s_ext_ctrls(vfh->ctrl_handler, p);
+		else if (vfd->ctrl_handler)
 			ret = v4l2_s_ext_ctrls(vfd->ctrl_handler, p);
 		else if (check_ext_ctrls(p, 0))
 			ret = ops->vidioc_s_ext_ctrls(file, fh, p);
@@ -1529,10 +1542,13 @@ static long __video_do_ioctl(struct file *file,
 		struct v4l2_ext_controls *p = arg;
 
 		p->error_idx = p->count;
-		if (!vfd->ctrl_handler && !ops->vidioc_try_ext_ctrls)
+		if (!(vfh && vfh->ctrl_handler) && !vfd->ctrl_handler &&
+				!ops->vidioc_try_ext_ctrls)
 			break;
 		v4l_print_ext_ctrls(cmd, vfd, p, 1);
-		if (vfd->ctrl_handler)
+		if (vfh && vfh->ctrl_handler)
+			ret = v4l2_try_ext_ctrls(vfh->ctrl_handler, p);
+		else if (vfd->ctrl_handler)
 			ret = v4l2_try_ext_ctrls(vfd->ctrl_handler, p);
 		else if (check_ext_ctrls(p, 0))
 			ret = ops->vidioc_try_ext_ctrls(file, fh, p);
@@ -1542,7 +1558,9 @@ static long __video_do_ioctl(struct file *file,
 	{
 		struct v4l2_querymenu *p = arg;
 
-		if (vfd->ctrl_handler)
+		if (vfh && vfh->ctrl_handler)
+			ret = v4l2_querymenu(vfh->ctrl_handler, p);
+		else if (vfd->ctrl_handler)
 			ret = v4l2_querymenu(vfd->ctrl_handler, p);
 		else if (ops->vidioc_querymenu)
 			ret = ops->vidioc_querymenu(file, fh, p);
diff --git a/include/media/v4l2-fh.h b/include/media/v4l2-fh.h
index 0206aa5..d247111 100644
--- a/include/media/v4l2-fh.h
+++ b/include/media/v4l2-fh.h
@@ -30,11 +30,13 @@
 
 struct video_device;
 struct v4l2_events;
+struct v4l2_ctrl_handler;
 
 struct v4l2_fh {
 	struct list_head	list;
 	struct video_device	*vdev;
 	struct v4l2_events      *events; /* events, pending and subscribed */
+	struct v4l2_ctrl_handler *ctrl_handler;
 	enum v4l2_priority	prio;
 };
 
-- 
1.7.1


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [RFCv1 PATCH 4/9] v4l2-ctrls: add per-control events.
  2011-04-04 11:51 ` [RFCv1 PATCH 1/9] v4l2-ctrls: add new bitmask control type Hans Verkuil
  2011-04-04 11:51   ` [RFCv1 PATCH 2/9] vivi: add bitmask test control Hans Verkuil
  2011-04-04 11:51   ` [RFCv1 PATCH 3/9] v4l2-ioctl: add ctrl_handler to v4l2_fh Hans Verkuil
@ 2011-04-04 11:51   ` Hans Verkuil
  2011-04-11  7:29     ` Sakari Ailus
  2011-04-15 10:51     ` Sakari Ailus
  2011-04-04 11:51   ` [RFCv1 PATCH 5/9] vb2_poll: don't start DMA, leave that to the first read() Hans Verkuil
                     ` (5 subsequent siblings)
  8 siblings, 2 replies; 30+ messages in thread
From: Hans Verkuil @ 2011-04-04 11:51 UTC (permalink / raw)
  To: linux-media

Whenever a control changes value an event is sent to anyone that subscribed
to it.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 drivers/media/video/v4l2-ctrls.c |   59 ++++++++++++++++++
 drivers/media/video/v4l2-event.c |  126 +++++++++++++++++++++++++++-----------
 drivers/media/video/v4l2-fh.c    |    4 +-
 include/linux/videodev2.h        |   17 +++++-
 include/media/v4l2-ctrls.h       |    9 +++
 include/media/v4l2-event.h       |    2 +
 6 files changed, 177 insertions(+), 40 deletions(-)

diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c
index f75a1d4..163f412 100644
--- a/drivers/media/video/v4l2-ctrls.c
+++ b/drivers/media/video/v4l2-ctrls.c
@@ -23,6 +23,7 @@
 #include <media/v4l2-ioctl.h>
 #include <media/v4l2-device.h>
 #include <media/v4l2-ctrls.h>
+#include <media/v4l2-event.h>
 #include <media/v4l2-dev.h>
 
 /* Internal temporary helper struct, one for each v4l2_ext_control */
@@ -537,6 +538,16 @@ static bool type_is_int(const struct v4l2_ctrl *ctrl)
 	}
 }
 
+static void send_event(struct v4l2_ctrl *ctrl, struct v4l2_event *ev)
+{
+	struct v4l2_ctrl_fh *pos;
+
+	ev->id = ctrl->id;
+	list_for_each_entry(pos, &ctrl->fhs, node) {
+		v4l2_event_queue_fh(pos->fh, ev);
+	}
+}
+
 /* Helper function: copy the current control value back to the caller */
 static int cur_to_user(struct v4l2_ext_control *c,
 		       struct v4l2_ctrl *ctrl)
@@ -626,20 +637,38 @@ static int new_to_user(struct v4l2_ext_control *c,
 /* Copy the new value to the current value. */
 static void new_to_cur(struct v4l2_ctrl *ctrl)
 {
+	struct v4l2_event ev;
+	bool changed = false;
+
 	if (ctrl == NULL)
 		return;
 	switch (ctrl->type) {
+	case V4L2_CTRL_TYPE_BUTTON:
+		changed = true;
+		ev.u.ctrl_ch_value.value = 0;
+		break;
 	case V4L2_CTRL_TYPE_STRING:
 		/* strings are always 0-terminated */
+		changed = strcmp(ctrl->string, ctrl->cur.string);
 		strcpy(ctrl->cur.string, ctrl->string);
+		ev.u.ctrl_ch_value.value64 = 0;
 		break;
 	case V4L2_CTRL_TYPE_INTEGER64:
+		changed = ctrl->val64 != ctrl->cur.val64;
 		ctrl->cur.val64 = ctrl->val64;
+		ev.u.ctrl_ch_value.value64 = ctrl->val64;
 		break;
 	default:
+		changed = ctrl->val != ctrl->cur.val;
 		ctrl->cur.val = ctrl->val;
+		ev.u.ctrl_ch_value.value = ctrl->val;
 		break;
 	}
+	if (changed) {
+		ev.type = V4L2_EVENT_CTRL_CH_VALUE;
+		ev.u.ctrl_ch_value.type = ctrl->type;
+		send_event(ctrl, &ev);
+	}
 }
 
 /* Copy the current value to the new value */
@@ -784,6 +813,7 @@ void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl)
 {
 	struct v4l2_ctrl_ref *ref, *next_ref;
 	struct v4l2_ctrl *ctrl, *next_ctrl;
+	struct v4l2_ctrl_fh *ctrl_fh, *next_ctrl_fh;
 
 	if (hdl == NULL || hdl->buckets == NULL)
 		return;
@@ -797,6 +827,10 @@ void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl)
 	/* 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);
+		}
 		kfree(ctrl);
 	}
 	kfree(hdl->buckets);
@@ -1003,6 +1037,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
 	}
 
 	INIT_LIST_HEAD(&ctrl->node);
+	INIT_LIST_HEAD(&ctrl->fhs);
 	ctrl->handler = hdl;
 	ctrl->ops = ops;
 	ctrl->id = id;
@@ -1888,3 +1923,27 @@ int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val)
 	return set_ctrl(ctrl, &val);
 }
 EXPORT_SYMBOL(v4l2_ctrl_s_ctrl);
+
+void v4l2_ctrl_add_fh(struct v4l2_ctrl *ctrl, struct v4l2_ctrl_fh *ctrl_fh)
+{
+	v4l2_ctrl_lock(ctrl);
+	list_add_tail(&ctrl_fh->node, &ctrl->fhs);
+	v4l2_ctrl_unlock(ctrl);
+}
+EXPORT_SYMBOL(v4l2_ctrl_add_fh);
+
+void v4l2_ctrl_del_fh(struct v4l2_ctrl *ctrl, struct v4l2_fh *fh)
+{
+	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;
+		}
+	}
+	v4l2_ctrl_unlock(ctrl);
+}
+EXPORT_SYMBOL(v4l2_ctrl_del_fh);
diff --git a/drivers/media/video/v4l2-event.c b/drivers/media/video/v4l2-event.c
index 69fd343..c9251a5 100644
--- a/drivers/media/video/v4l2-event.c
+++ b/drivers/media/video/v4l2-event.c
@@ -25,10 +25,13 @@
 #include <media/v4l2-dev.h>
 #include <media/v4l2-fh.h>
 #include <media/v4l2-event.h>
+#include <media/v4l2-ctrls.h>
 
 #include <linux/sched.h>
 #include <linux/slab.h>
 
+static void v4l2_event_unsubscribe_all(struct v4l2_fh *fh);
+
 int v4l2_event_init(struct v4l2_fh *fh)
 {
 	fh->events = kzalloc(sizeof(*fh->events), GFP_KERNEL);
@@ -91,7 +94,7 @@ void v4l2_event_free(struct v4l2_fh *fh)
 
 	list_kfree(&events->free, struct v4l2_kevent, list);
 	list_kfree(&events->available, struct v4l2_kevent, list);
-	list_kfree(&events->subscribed, struct v4l2_subscribed_event, list);
+	v4l2_event_unsubscribe_all(fh);
 
 	kfree(events);
 	fh->events = NULL;
@@ -154,9 +157,9 @@ int v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event,
 }
 EXPORT_SYMBOL_GPL(v4l2_event_dequeue);
 
-/* Caller must hold fh->event->lock! */
+/* Caller must hold fh->vdev->fh_lock! */
 static struct v4l2_subscribed_event *v4l2_event_subscribed(
-	struct v4l2_fh *fh, u32 type)
+		struct v4l2_fh *fh, u32 type, u32 id)
 {
 	struct v4l2_events *events = fh->events;
 	struct v4l2_subscribed_event *sev;
@@ -164,13 +167,46 @@ static struct v4l2_subscribed_event *v4l2_event_subscribed(
 	assert_spin_locked(&fh->vdev->fh_lock);
 
 	list_for_each_entry(sev, &events->subscribed, list) {
-		if (sev->type == type)
+		if (sev->type == type && sev->id == id)
 			return sev;
 	}
 
 	return NULL;
 }
 
+static void __v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *ev,
+		const struct timespec *ts)
+{
+	struct v4l2_events *events = fh->events;
+	struct v4l2_subscribed_event *sev;
+	struct v4l2_kevent *kev;
+
+	/* Are we subscribed? */
+	sev = v4l2_event_subscribed(fh, ev->type, ev->id);
+	if (sev == NULL)
+		return;
+
+	/* Increase event sequence number on fh. */
+	events->sequence++;
+
+	/* Do we have any free events? */
+	if (list_empty(&events->free))
+		return;
+
+	/* Take one and fill it. */
+	kev = list_first_entry(&events->free, struct v4l2_kevent, list);
+	kev->event.type = ev->type;
+	kev->event.u = ev->u;
+	kev->event.id = ev->id;
+	kev->event.timestamp = *ts;
+	kev->event.sequence = events->sequence;
+	list_move_tail(&kev->list, &events->available);
+
+	events->navailable++;
+
+	wake_up_all(&events->wait);
+}
+
 void v4l2_event_queue(struct video_device *vdev, const struct v4l2_event *ev)
 {
 	struct v4l2_fh *fh;
@@ -182,37 +218,26 @@ void v4l2_event_queue(struct video_device *vdev, const struct v4l2_event *ev)
 	spin_lock_irqsave(&vdev->fh_lock, flags);
 
 	list_for_each_entry(fh, &vdev->fh_list, list) {
-		struct v4l2_events *events = fh->events;
-		struct v4l2_kevent *kev;
-
-		/* Are we subscribed? */
-		if (!v4l2_event_subscribed(fh, ev->type))
-			continue;
-
-		/* Increase event sequence number on fh. */
-		events->sequence++;
-
-		/* Do we have any free events? */
-		if (list_empty(&events->free))
-			continue;
-
-		/* Take one and fill it. */
-		kev = list_first_entry(&events->free, struct v4l2_kevent, list);
-		kev->event.type = ev->type;
-		kev->event.u = ev->u;
-		kev->event.timestamp = timestamp;
-		kev->event.sequence = events->sequence;
-		list_move_tail(&kev->list, &events->available);
-
-		events->navailable++;
-
-		wake_up_all(&events->wait);
+		__v4l2_event_queue_fh(fh, ev, &timestamp);
 	}
 
 	spin_unlock_irqrestore(&vdev->fh_lock, flags);
 }
 EXPORT_SYMBOL_GPL(v4l2_event_queue);
 
+void v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *ev)
+{
+	unsigned long flags;
+	struct timespec timestamp;
+
+	ktime_get_ts(&timestamp);
+
+	spin_lock_irqsave(&fh->vdev->fh_lock, flags);
+	__v4l2_event_queue_fh(fh, ev, &timestamp);
+	spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
+}
+EXPORT_SYMBOL_GPL(v4l2_event_queue_fh);
+
 int v4l2_event_pending(struct v4l2_fh *fh)
 {
 	return fh->events->navailable;
@@ -223,7 +248,9 @@ int v4l2_event_subscribe(struct v4l2_fh *fh,
 			 struct v4l2_event_subscription *sub)
 {
 	struct v4l2_events *events = fh->events;
-	struct v4l2_subscribed_event *sev;
+	struct v4l2_subscribed_event *sev, *found_ev;
+	struct v4l2_ctrl *ctrl = NULL;
+	struct v4l2_ctrl_fh *ctrl_fh = NULL;
 	unsigned long flags;
 
 	if (fh->events == NULL) {
@@ -231,15 +258,31 @@ int v4l2_event_subscribe(struct v4l2_fh *fh,
 		return -ENOMEM;
 	}
 
+	if (sub->type == V4L2_EVENT_CTRL_CH_VALUE) {
+		ctrl = v4l2_ctrl_find(fh->ctrl_handler, sub->id);
+		if (ctrl == NULL)
+			return -EINVAL;
+	}
+
 	sev = kmalloc(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);
 
-	if (v4l2_event_subscribed(fh, sub->type) == NULL) {
+	found_ev = v4l2_event_subscribed(fh, sub->type, sub->id);
+	if (!found_ev) {
 		INIT_LIST_HEAD(&sev->list);
 		sev->type = sub->type;
+		sev->id = sub->id;
 
 		list_add(&sev->list, &events->subscribed);
 		sev = NULL;
@@ -247,6 +290,10 @@ int v4l2_event_subscribe(struct v4l2_fh *fh,
 
 	spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
 
+	/* v4l2_ctrl_add_fh uses a mutex, so do this outside the spin lock */
+	if (!found_ev && ctrl)
+		v4l2_ctrl_add_fh(ctrl, ctrl_fh);
+
 	kfree(sev);
 
 	return 0;
@@ -256,6 +303,7 @@ EXPORT_SYMBOL_GPL(v4l2_event_subscribe);
 static void v4l2_event_unsubscribe_all(struct v4l2_fh *fh)
 {
 	struct v4l2_events *events = fh->events;
+	struct v4l2_event_subscription sub;
 	struct v4l2_subscribed_event *sev;
 	unsigned long flags;
 
@@ -265,11 +313,13 @@ static void v4l2_event_unsubscribe_all(struct v4l2_fh *fh)
 		spin_lock_irqsave(&fh->vdev->fh_lock, flags);
 		if (!list_empty(&events->subscribed)) {
 			sev = list_first_entry(&events->subscribed,
-				       struct v4l2_subscribed_event, list);
-			list_del(&sev->list);
+					struct v4l2_subscribed_event, list);
+			sub.type = sev->type;
+			sub.id = sev->id;
 		}
 		spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
-		kfree(sev);
+		if (sev)
+			v4l2_event_unsubscribe(fh, &sub);
 	} while (sev);
 }
 
@@ -286,11 +336,17 @@ int v4l2_event_unsubscribe(struct v4l2_fh *fh,
 
 	spin_lock_irqsave(&fh->vdev->fh_lock, flags);
 
-	sev = v4l2_event_subscribed(fh, sub->type);
+	sev = v4l2_event_subscribed(fh, sub->type, sub->id);
 	if (sev != NULL)
 		list_del(&sev->list);
 
 	spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
+	if (sev->type == V4L2_EVENT_CTRL_CH_VALUE) {
+		struct v4l2_ctrl *ctrl = v4l2_ctrl_find(fh->ctrl_handler, sev->id);
+
+		if (ctrl)
+			v4l2_ctrl_del_fh(ctrl, fh);
+	}
 
 	kfree(sev);
 
diff --git a/drivers/media/video/v4l2-fh.c b/drivers/media/video/v4l2-fh.c
index 8635011..c6aef84 100644
--- a/drivers/media/video/v4l2-fh.c
+++ b/drivers/media/video/v4l2-fh.c
@@ -93,10 +93,8 @@ void v4l2_fh_exit(struct v4l2_fh *fh)
 {
 	if (fh->vdev == NULL)
 		return;
-
-	fh->vdev = NULL;
-
 	v4l2_event_free(fh);
+	fh->vdev = NULL;
 }
 EXPORT_SYMBOL_GPL(v4l2_fh_exit);
 
diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h
index 92d2fdd..f7238c1 100644
--- a/include/linux/videodev2.h
+++ b/include/linux/videodev2.h
@@ -1787,6 +1787,7 @@ struct v4l2_streamparm {
 #define V4L2_EVENT_ALL				0
 #define V4L2_EVENT_VSYNC			1
 #define V4L2_EVENT_EOS				2
+#define V4L2_EVENT_CTRL_CH_VALUE		3
 #define V4L2_EVENT_PRIVATE_START		0x08000000
 
 /* Payload for V4L2_EVENT_VSYNC */
@@ -1795,21 +1796,33 @@ struct v4l2_event_vsync {
 	__u8 field;
 } __attribute__ ((packed));
 
+/* Payload for V4L2_EVENT_CTRL_CH_VALUE */
+struct v4l2_event_ctrl_ch_value {
+	__u32 type;
+	union {
+		__s32 value;
+		__s64 value64;
+	};
+} __attribute__ ((packed));
+
 struct v4l2_event {
 	__u32				type;
 	union {
 		struct v4l2_event_vsync vsync;
+		struct v4l2_event_ctrl_ch_value ctrl_ch_value;
 		__u8			data[64];
 	} u;
 	__u32				pending;
 	__u32				sequence;
 	struct timespec			timestamp;
-	__u32				reserved[9];
+	__u32				id;
+	__u32				reserved[8];
 };
 
 struct v4l2_event_subscription {
 	__u32				type;
-	__u32				reserved[7];
+	__u32				id;
+	__u32				reserved[6];
 };
 
 /*
diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h
index 97d0638..7ca45a5 100644
--- a/include/media/v4l2-ctrls.h
+++ b/include/media/v4l2-ctrls.h
@@ -30,6 +30,7 @@ struct v4l2_ctrl_handler;
 struct v4l2_ctrl;
 struct video_device;
 struct v4l2_subdev;
+struct v4l2_fh;
 
 /** struct v4l2_ctrl_ops - The control operations that the driver has to provide.
   * @g_volatile_ctrl: Get a new value for this control. Generally only relevant
@@ -97,6 +98,7 @@ struct v4l2_ctrl_ops {
 struct v4l2_ctrl {
 	/* Administrative fields */
 	struct list_head node;
+	struct list_head fhs;
 	struct v4l2_ctrl_handler *handler;
 	struct v4l2_ctrl **cluster;
 	unsigned ncontrols;
@@ -168,6 +170,11 @@ struct v4l2_ctrl_handler {
 	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.
@@ -440,6 +447,8 @@ s32 v4l2_ctrl_g_ctrl(struct v4l2_ctrl *ctrl);
   */
 int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val);
 
+void v4l2_ctrl_add_fh(struct v4l2_ctrl *ctrl, struct v4l2_ctrl_fh *ctrl_fh);
+void v4l2_ctrl_del_fh(struct v4l2_ctrl *ctrl, struct v4l2_fh *fh);
 
 /* Helpers for ioctl_ops. If hdl == NULL then they will all return -EINVAL. */
 int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc);
diff --git a/include/media/v4l2-event.h b/include/media/v4l2-event.h
index 3b86177..45e9c1e 100644
--- a/include/media/v4l2-event.h
+++ b/include/media/v4l2-event.h
@@ -40,6 +40,7 @@ struct v4l2_kevent {
 struct v4l2_subscribed_event {
 	struct list_head	list;
 	u32			type;
+	u32			id;
 };
 
 struct v4l2_events {
@@ -58,6 +59,7 @@ 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);
-- 
1.7.1


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [RFCv1 PATCH 5/9] vb2_poll: don't start DMA, leave that to the first read().
  2011-04-04 11:51 ` [RFCv1 PATCH 1/9] v4l2-ctrls: add new bitmask control type Hans Verkuil
                     ` (2 preceding siblings ...)
  2011-04-04 11:51   ` [RFCv1 PATCH 4/9] v4l2-ctrls: add per-control events Hans Verkuil
@ 2011-04-04 11:51   ` Hans Verkuil
  2011-04-08  7:00     ` Marek Szyprowski
  2011-04-04 11:51   ` [RFCv1 PATCH 6/9] vivi: add support for control events Hans Verkuil
                     ` (4 subsequent siblings)
  8 siblings, 1 reply; 30+ messages in thread
From: Hans Verkuil @ 2011-04-04 11:51 UTC (permalink / raw)
  To: linux-media

The vb2_poll function would start read DMA if called without any streaming
in progress. This unfortunately does not work if the application just wants
to poll for exceptions. This information of what the application is polling
for is sadly unavailable in the driver.

Andy Walls suggested to just return POLLIN | POLLRDNORM and let the first
call to read start the DMA. This initial read() call will return EAGAIN
since no actual data is available yet, but it does start the DMA.

Application are supposed to handle EAGAIN. MythTV does handle this correctly.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 drivers/media/video/videobuf2-core.c |   16 +++-------------
 1 files changed, 3 insertions(+), 13 deletions(-)

diff --git a/drivers/media/video/videobuf2-core.c b/drivers/media/video/videobuf2-core.c
index 6698c77..2dea57a 100644
--- a/drivers/media/video/videobuf2-core.c
+++ b/drivers/media/video/videobuf2-core.c
@@ -1372,20 +1372,10 @@ unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait)
 	 * Start file I/O emulator only if streaming API has not been used yet.
 	 */
 	if (q->num_buffers == 0 && q->fileio == NULL) {
-		if (!V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_READ)) {
-			ret = __vb2_init_fileio(q, 1);
-			if (ret)
-				return POLLERR;
-		}
-		if (V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_WRITE)) {
-			ret = __vb2_init_fileio(q, 0);
-			if (ret)
-				return POLLERR;
-			/*
-			 * Write to OUTPUT queue can be done immediately.
-			 */
+		if (!V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_READ))
+			return POLLIN | POLLRDNORM;
+		if (V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_WRITE))
 			return POLLOUT | POLLWRNORM;
-		}
 	}
 
 	/*
-- 
1.7.1


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [RFCv1 PATCH 6/9] vivi: add support for control events.
  2011-04-04 11:51 ` [RFCv1 PATCH 1/9] v4l2-ctrls: add new bitmask control type Hans Verkuil
                     ` (3 preceding siblings ...)
  2011-04-04 11:51   ` [RFCv1 PATCH 5/9] vb2_poll: don't start DMA, leave that to the first read() Hans Verkuil
@ 2011-04-04 11:51   ` Hans Verkuil
  2011-04-08 15:19     ` Laurent Pinchart
  2011-04-04 11:51   ` [RFCv1 PATCH 7/9] v4l2-ctrls: add new V4L2_EVENT_CTRL_CH_STATE event Hans Verkuil
                     ` (3 subsequent siblings)
  8 siblings, 1 reply; 30+ messages in thread
From: Hans Verkuil @ 2011-04-04 11:51 UTC (permalink / raw)
  To: linux-media

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 drivers/media/video/vivi.c |   41 +++++++++++++++++++++++++++++++++++++++--
 1 files changed, 39 insertions(+), 2 deletions(-)

diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c
index 21d8f6a..8790e03 100644
--- a/drivers/media/video/vivi.c
+++ b/drivers/media/video/vivi.c
@@ -32,6 +32,7 @@
 #include <media/v4l2-ioctl.h>
 #include <media/v4l2-ctrls.h>
 #include <media/v4l2-fh.h>
+#include <media/v4l2-event.h>
 #include <media/v4l2-common.h>
 
 #define VIVI_MODULE_NAME "vivi"
@@ -983,6 +984,14 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
 	return 0;
 }
 
+static int vidioc_subscribe_event(struct v4l2_fh *fh,
+				struct v4l2_event_subscription *sub)
+{
+	if (sub->type != V4L2_EVENT_CTRL_CH_VALUE)
+		return -EINVAL;
+	return v4l2_event_subscribe(fh, sub);
+}
+
 /* --- controls ---------------------------------------------- */
 
 static int vivi_s_ctrl(struct v4l2_ctrl *ctrl)
@@ -998,6 +1007,25 @@ static int vivi_s_ctrl(struct v4l2_ctrl *ctrl)
 	File operations for the device
    ------------------------------------------------------------------*/
 
+static int vivi_open(struct file *filp)
+{
+	int ret = v4l2_fh_open(filp);
+	struct v4l2_fh *fh;
+
+	if (ret)
+		return ret;
+	fh = filp->private_data;
+	ret = v4l2_event_init(fh);
+	if (ret)
+		goto rel_fh;
+	ret = v4l2_event_alloc(fh, 10);
+	if (!ret)
+		return ret;
+rel_fh:
+	v4l2_fh_release(filp);
+	return ret;
+}
+
 static ssize_t
 vivi_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
 {
@@ -1012,10 +1040,17 @@ static unsigned int
 vivi_poll(struct file *file, struct poll_table_struct *wait)
 {
 	struct vivi_dev *dev = video_drvdata(file);
+	struct v4l2_fh *fh = file->private_data;
 	struct vb2_queue *q = &dev->vb_vidq;
+	unsigned int res;
 
 	dprintk(dev, 1, "%s\n", __func__);
-	return vb2_poll(q, file, wait);
+	res = vb2_poll(q, file, wait);
+	if (v4l2_event_pending(fh))
+		res |= POLLPRI;
+	else
+		poll_wait(file, &fh->events->wait, wait);
+	return res;
 }
 
 static int vivi_close(struct file *file)
@@ -1132,7 +1167,7 @@ static const struct v4l2_ctrl_config vivi_ctrl_bitmask = {
 
 static const struct v4l2_file_operations vivi_fops = {
 	.owner		= THIS_MODULE,
-	.open		= v4l2_fh_open,
+	.open		= vivi_open,
 	.release        = vivi_close,
 	.read           = vivi_read,
 	.poll		= vivi_poll,
@@ -1156,6 +1191,8 @@ static const struct v4l2_ioctl_ops vivi_ioctl_ops = {
 	.vidioc_s_input       = vidioc_s_input,
 	.vidioc_streamon      = vidioc_streamon,
 	.vidioc_streamoff     = vidioc_streamoff,
+	.vidioc_subscribe_event = vidioc_subscribe_event,
+	.vidioc_unsubscribe_event = v4l2_event_unsubscribe,
 };
 
 static struct video_device vivi_template = {
-- 
1.7.1


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [RFCv1 PATCH 7/9] v4l2-ctrls: add new V4L2_EVENT_CTRL_CH_STATE event.
  2011-04-04 11:51 ` [RFCv1 PATCH 1/9] v4l2-ctrls: add new bitmask control type Hans Verkuil
                     ` (4 preceding siblings ...)
  2011-04-04 11:51   ` [RFCv1 PATCH 6/9] vivi: add support for control events Hans Verkuil
@ 2011-04-04 11:51   ` Hans Verkuil
  2011-04-04 11:51   ` [RFCv1 PATCH 8/9] vivi: add support for CTRL_CH_STATE events Hans Verkuil
                     ` (2 subsequent siblings)
  8 siblings, 0 replies; 30+ messages in thread
From: Hans Verkuil @ 2011-04-04 11:51 UTC (permalink / raw)
  To: linux-media

This event is triggered whenever the 'flags' field changes.

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 drivers/media/video/cx2341x.c    |   58 +++++++++++++++++++++++--------------
 drivers/media/video/saa7115.c    |    3 +-
 drivers/media/video/v4l2-ctrls.c |   38 ++++++++----------------
 include/linux/videodev2.h        |    8 +++++
 include/media/v4l2-ctrls.h       |   36 +++++++++++------------
 5 files changed, 76 insertions(+), 67 deletions(-)

diff --git a/drivers/media/video/cx2341x.c b/drivers/media/video/cx2341x.c
index 103ef6b..2781889 100644
--- a/drivers/media/video/cx2341x.c
+++ b/drivers/media/video/cx2341x.c
@@ -1307,6 +1307,12 @@ static int cx2341x_try_ctrl(struct v4l2_ctrl *ctrl)
 	return 0;
 }
 
+static void cx2341x_activate(struct v4l2_ctrl *ctrl, bool activate)
+{
+	v4l2_ctrl_flags(ctrl, V4L2_CTRL_FLAG_INACTIVE,
+			activate ? 0 : V4L2_CTRL_FLAG_INACTIVE);
+}
+
 static int cx2341x_s_ctrl(struct v4l2_ctrl *ctrl)
 {
 	static const int mpeg_stream_type[] = {
@@ -1380,10 +1386,10 @@ static int cx2341x_s_ctrl(struct v4l2_ctrl *ctrl)
 			int is_ac3 = hdl->audio_encoding->val ==
 						V4L2_MPEG_AUDIO_ENCODING_AC3;
 
-			v4l2_ctrl_activate(hdl->audio_ac3_bitrate, is_ac3);
-			v4l2_ctrl_activate(hdl->audio_l2_bitrate, !is_ac3);
+			cx2341x_activate(hdl->audio_ac3_bitrate, is_ac3);
+			cx2341x_activate(hdl->audio_l2_bitrate, !is_ac3);
 		}
-		v4l2_ctrl_activate(hdl->audio_mode_extension,
+		cx2341x_activate(hdl->audio_mode_extension,
 			hdl->audio_mode->val == V4L2_MPEG_AUDIO_MODE_JOINT_STEREO);
 		if (cx2341x_neq(hdl->audio_sampling_freq) &&
 		    hdl->ops && hdl->ops->s_audio_sampling_freq)
@@ -1413,9 +1419,9 @@ static int cx2341x_s_ctrl(struct v4l2_ctrl *ctrl)
 		if (err)
 			return err;
 
-		v4l2_ctrl_activate(hdl->video_bitrate_mode,
+		cx2341x_activate(hdl->video_bitrate_mode,
 			hdl->video_encoding->val != V4L2_MPEG_VIDEO_ENCODING_MPEG_1);
-		v4l2_ctrl_activate(hdl->video_bitrate_peak,
+		cx2341x_activate(hdl->video_bitrate_peak,
 			hdl->video_bitrate_mode->val != V4L2_MPEG_VIDEO_BITRATE_MODE_CBR);
 		if (cx2341x_neq(hdl->video_encoding) &&
 		    hdl->ops && hdl->ops->s_video_encoding)
@@ -1441,18 +1447,18 @@ static int cx2341x_s_ctrl(struct v4l2_ctrl *ctrl)
 
 		active_filter = hdl->video_spatial_filter_mode->val !=
 				V4L2_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE_AUTO;
-		v4l2_ctrl_activate(hdl->video_spatial_filter, active_filter);
-		v4l2_ctrl_activate(hdl->video_luma_spatial_filter_type, active_filter);
-		v4l2_ctrl_activate(hdl->video_chroma_spatial_filter_type, active_filter);
+		cx2341x_activate(hdl->video_spatial_filter, active_filter);
+		cx2341x_activate(hdl->video_luma_spatial_filter_type, active_filter);
+		cx2341x_activate(hdl->video_chroma_spatial_filter_type, active_filter);
 		active_filter = hdl->video_temporal_filter_mode->val !=
 				V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_AUTO;
-		v4l2_ctrl_activate(hdl->video_temporal_filter, active_filter);
+		cx2341x_activate(hdl->video_temporal_filter, active_filter);
 		active_filter = hdl->video_median_filter_type->val !=
 				V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF;
-		v4l2_ctrl_activate(hdl->video_luma_median_filter_bottom, active_filter);
-		v4l2_ctrl_activate(hdl->video_luma_median_filter_top, active_filter);
-		v4l2_ctrl_activate(hdl->video_chroma_median_filter_bottom, active_filter);
-		v4l2_ctrl_activate(hdl->video_chroma_median_filter_top, active_filter);
+		cx2341x_activate(hdl->video_luma_median_filter_bottom, active_filter);
+		cx2341x_activate(hdl->video_luma_median_filter_top, active_filter);
+		cx2341x_activate(hdl->video_chroma_median_filter_bottom, active_filter);
+		cx2341x_activate(hdl->video_chroma_median_filter_top, active_filter);
 		return 0;
 	}
 
@@ -1711,16 +1717,24 @@ int cx2341x_handler_setup(struct cx2341x_handler *cxhdl)
 }
 EXPORT_SYMBOL(cx2341x_handler_setup);
 
+static void cx2341x_grab(struct v4l2_ctrl *ctrl, bool busy)
+{
+	v4l2_ctrl_flags(ctrl, V4L2_CTRL_FLAG_GRABBED,
+			busy ? V4L2_CTRL_FLAG_GRABBED : 0);
+}
+
 void cx2341x_handler_set_busy(struct cx2341x_handler *cxhdl, int busy)
 {
-	v4l2_ctrl_grab(cxhdl->audio_sampling_freq, busy);
-	v4l2_ctrl_grab(cxhdl->audio_encoding, busy);
-	v4l2_ctrl_grab(cxhdl->audio_l2_bitrate, busy);
-	v4l2_ctrl_grab(cxhdl->audio_ac3_bitrate, busy);
-	v4l2_ctrl_grab(cxhdl->stream_vbi_fmt, busy);
-	v4l2_ctrl_grab(cxhdl->stream_type, busy);
-	v4l2_ctrl_grab(cxhdl->video_bitrate_mode, busy);
-	v4l2_ctrl_grab(cxhdl->video_bitrate, busy);
-	v4l2_ctrl_grab(cxhdl->video_bitrate_peak, busy);
+	mutex_lock(&cxhdl->hdl.lock);
+	cx2341x_grab(cxhdl->audio_sampling_freq, busy);
+	cx2341x_grab(cxhdl->audio_encoding, busy);
+	cx2341x_grab(cxhdl->audio_l2_bitrate, busy);
+	cx2341x_grab(cxhdl->audio_ac3_bitrate, busy);
+	cx2341x_grab(cxhdl->stream_vbi_fmt, busy);
+	cx2341x_grab(cxhdl->stream_type, busy);
+	cx2341x_grab(cxhdl->video_bitrate_mode, busy);
+	cx2341x_grab(cxhdl->video_bitrate, busy);
+	cx2341x_grab(cxhdl->video_bitrate_peak, busy);
+	mutex_unlock(&cxhdl->hdl.lock);
 }
 EXPORT_SYMBOL(cx2341x_handler_set_busy);
diff --git a/drivers/media/video/saa7115.c b/drivers/media/video/saa7115.c
index 0db9092..ae4b299 100644
--- a/drivers/media/video/saa7115.c
+++ b/drivers/media/video/saa7115.c
@@ -793,7 +793,8 @@ static int saa711x_s_ctrl(struct v4l2_ctrl *ctrl)
 			saa711x_write(sd, R_0F_CHROMA_GAIN_CNTL, state->gain->val);
 		else
 			saa711x_write(sd, R_0F_CHROMA_GAIN_CNTL, state->gain->val | 0x80);
-		v4l2_ctrl_activate(state->gain, !state->agc->val);
+		v4l2_ctrl_flags(state->gain, V4L2_CTRL_FLAG_INACTIVE,
+				state->agc->val ? V4L2_CTRL_FLAG_INACTIVE : 0);
 		break;
 
 	default:
diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c
index 163f412..122c6da 100644
--- a/drivers/media/video/v4l2-ctrls.c
+++ b/drivers/media/video/v4l2-ctrls.c
@@ -1205,40 +1205,28 @@ void v4l2_ctrl_cluster(unsigned ncontrols, struct v4l2_ctrl **controls)
 }
 EXPORT_SYMBOL(v4l2_ctrl_cluster);
 
-/* Activate/deactivate a control. */
-void v4l2_ctrl_activate(struct v4l2_ctrl *ctrl, bool active)
+void v4l2_ctrl_flags(struct v4l2_ctrl *ctrl, u32 clear_flags, u32 set_flags)
 {
+	struct v4l2_event ev;
+
 	if (ctrl == NULL)
 		return;
-
-	if (!active)
-		/* set V4L2_CTRL_FLAG_INACTIVE */
-		set_bit(4, &ctrl->flags);
-	else
-		/* clear V4L2_CTRL_FLAG_INACTIVE */
-		clear_bit(4, &ctrl->flags);
+	ctrl->flags = (ctrl->flags & ~clear_flags) | set_flags;
+	ev.u.ctrl_ch_state.flags = ctrl->flags;
+	ev.type = V4L2_EVENT_CTRL_CH_STATE;
+	send_event(ctrl, &ev);
 }
-EXPORT_SYMBOL(v4l2_ctrl_activate);
-
-/* Grab/ungrab a control.
-   Typically used when streaming starts and you want to grab controls,
-   preventing the user from changing them.
+EXPORT_SYMBOL(v4l2_ctrl_flags);
 
-   Just call this and the framework will block any attempts to change
-   these controls. */
-void v4l2_ctrl_grab(struct v4l2_ctrl *ctrl, bool grabbed)
+void v4l2_ctrl_flags_lock(struct v4l2_ctrl *ctrl, u32 clear_flags, u32 set_flags)
 {
 	if (ctrl == NULL)
 		return;
-
-	if (grabbed)
-		/* set V4L2_CTRL_FLAG_GRABBED */
-		set_bit(1, &ctrl->flags);
-	else
-		/* clear V4L2_CTRL_FLAG_GRABBED */
-		clear_bit(1, &ctrl->flags);
+	v4l2_ctrl_lock(ctrl);
+	v4l2_ctrl_flags(ctrl, clear_flags, set_flags);
+	v4l2_ctrl_unlock(ctrl);
 }
-EXPORT_SYMBOL(v4l2_ctrl_grab);
+EXPORT_SYMBOL(v4l2_ctrl_flags_lock);
 
 /* Log the control name and value */
 static void log_ctrl(const struct v4l2_ctrl *ctrl,
diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h
index f7238c1..eb56685 100644
--- a/include/linux/videodev2.h
+++ b/include/linux/videodev2.h
@@ -1788,6 +1788,7 @@ struct v4l2_streamparm {
 #define V4L2_EVENT_VSYNC			1
 #define V4L2_EVENT_EOS				2
 #define V4L2_EVENT_CTRL_CH_VALUE		3
+#define V4L2_EVENT_CTRL_CH_STATE		4
 #define V4L2_EVENT_PRIVATE_START		0x08000000
 
 /* Payload for V4L2_EVENT_VSYNC */
@@ -1805,11 +1806,18 @@ struct v4l2_event_ctrl_ch_value {
 	};
 } __attribute__ ((packed));
 
+/* Payload for V4L2_EVENT_CTRL_CH_STATE */
+struct v4l2_event_ctrl_ch_state {
+	__u32 type;
+	__u32 flags;
+} __attribute__ ((packed));
+
 struct v4l2_event {
 	__u32				type;
 	union {
 		struct v4l2_event_vsync vsync;
 		struct v4l2_event_ctrl_ch_value ctrl_ch_value;
+		struct v4l2_event_ctrl_ch_state ctrl_ch_state;
 		__u8			data[64];
 	} u;
 	__u32				pending;
diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h
index 7ca45a5..e6917f4 100644
--- a/include/media/v4l2-ctrls.h
+++ b/include/media/v4l2-ctrls.h
@@ -379,32 +379,30 @@ void v4l2_ctrl_cluster(unsigned ncontrols, struct v4l2_ctrl **controls);
   */
 struct v4l2_ctrl *v4l2_ctrl_find(struct v4l2_ctrl_handler *hdl, u32 id);
 
-/** v4l2_ctrl_activate() - Make the control active or inactive.
-  * @ctrl:	The control to (de)activate.
-  * @active:	True if the control should become active.
+/** v4l2_ctrl_flags() - Clear and set flags for a control.
+  * @ctrl:	The control whose flags should be changed.
+  * @clear_flags:	Mask out these flags.
+  * @set_flags:	Set these flags.
   *
-  * This sets or clears the V4L2_CTRL_FLAG_INACTIVE flag atomically.
-  * Does nothing if @ctrl == NULL.
-  * This will usually be called from within the s_ctrl op.
+  * This clears and sets flags. Does nothing if @ctrl == NULL.
+  * The V4L2_EVENT_CTRL_CH_STATE event will be generated afterwards.
   *
-  * This function can be called regardless of whether the control handler
-  * is locked or not.
+  * This function expects that the control handler is locked.
   */
-void v4l2_ctrl_activate(struct v4l2_ctrl *ctrl, bool active);
+void v4l2_ctrl_flags(struct v4l2_ctrl *ctrl, u32 clear_flags, u32 set_flags);
 
-/** v4l2_ctrl_grab() - Mark the control as grabbed or not grabbed.
-  * @ctrl:	The control to (de)activate.
-  * @grabbed:	True if the control should become grabbed.
+/** v4l2_ctrl_flags_lock() - Clear and set flags for a control.
+  * @ctrl:	The control whose flags should be changed.
+  * @clear_flags:	Mask out these flags.
+  * @set_flags:	Set these flags.
   *
-  * This sets or clears the V4L2_CTRL_FLAG_GRABBED flag atomically.
-  * Does nothing if @ctrl == NULL.
-  * This will usually be called when starting or stopping streaming in the
-  * driver.
+  * This clears and sets flags. Does nothing if @ctrl == NULL.
+  * The V4L2_EVENT_CTRL_CH_STATE event will be generated afterwards.
   *
-  * This function can be called regardless of whether the control handler
-  * is locked or not.
+  * This function expects that the control handler is unlocked and will lock
+  * it before changing flags.
   */
-void v4l2_ctrl_grab(struct v4l2_ctrl *ctrl, bool grabbed);
+void v4l2_ctrl_flags_lock(struct v4l2_ctrl *ctrl, u32 clear_flags, u32 set_flags);
 
 /** v4l2_ctrl_lock() - Helper function to lock the handler
   * associated with the control.
-- 
1.7.1


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [RFCv1 PATCH 8/9] vivi: add support for CTRL_CH_STATE events.
  2011-04-04 11:51 ` [RFCv1 PATCH 1/9] v4l2-ctrls: add new bitmask control type Hans Verkuil
                     ` (5 preceding siblings ...)
  2011-04-04 11:51   ` [RFCv1 PATCH 7/9] v4l2-ctrls: add new V4L2_EVENT_CTRL_CH_STATE event Hans Verkuil
@ 2011-04-04 11:51   ` Hans Verkuil
  2011-04-08 15:13     ` Laurent Pinchart
  2011-04-04 11:51   ` [RFCv1 PATCH 9/9] v4l2-ctrls: add new SEND_INITIAL flag to force an initial event Hans Verkuil
  2011-04-08 15:08   ` [RFCv1 PATCH 1/9] v4l2-ctrls: add new bitmask control type Laurent Pinchart
  8 siblings, 1 reply; 30+ messages in thread
From: Hans Verkuil @ 2011-04-04 11:51 UTC (permalink / raw)
  To: linux-media

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 drivers/media/video/v4l2-event.c |    6 ++++--
 drivers/media/video/vivi.c       |    8 ++++++--
 2 files changed, 10 insertions(+), 4 deletions(-)

diff --git a/drivers/media/video/v4l2-event.c b/drivers/media/video/v4l2-event.c
index c9251a5..9b503aa 100644
--- a/drivers/media/video/v4l2-event.c
+++ b/drivers/media/video/v4l2-event.c
@@ -258,7 +258,8 @@ int v4l2_event_subscribe(struct v4l2_fh *fh,
 		return -ENOMEM;
 	}
 
-	if (sub->type == V4L2_EVENT_CTRL_CH_VALUE) {
+	if (sub->type == V4L2_EVENT_CTRL_CH_VALUE ||
+			sub->type == V4L2_EVENT_CTRL_CH_STATE) {
 		ctrl = v4l2_ctrl_find(fh->ctrl_handler, sub->id);
 		if (ctrl == NULL)
 			return -EINVAL;
@@ -341,7 +342,8 @@ int v4l2_event_unsubscribe(struct v4l2_fh *fh,
 		list_del(&sev->list);
 
 	spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
-	if (sev->type == V4L2_EVENT_CTRL_CH_VALUE) {
+	if (sev->type == V4L2_EVENT_CTRL_CH_VALUE ||
+			sev->type == V4L2_EVENT_CTRL_CH_STATE) {
 		struct v4l2_ctrl *ctrl = v4l2_ctrl_find(fh->ctrl_handler, sev->id);
 
 		if (ctrl)
diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c
index 8790e03..a8d91ce 100644
--- a/drivers/media/video/vivi.c
+++ b/drivers/media/video/vivi.c
@@ -987,9 +987,13 @@ static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
 static int vidioc_subscribe_event(struct v4l2_fh *fh,
 				struct v4l2_event_subscription *sub)
 {
-	if (sub->type != V4L2_EVENT_CTRL_CH_VALUE)
+	switch (sub->type) {
+	case V4L2_EVENT_CTRL_CH_VALUE:
+	case V4L2_EVENT_CTRL_CH_STATE:
+		return v4l2_event_subscribe(fh, sub);
+	default:
 		return -EINVAL;
-	return v4l2_event_subscribe(fh, sub);
+	}
 }
 
 /* --- controls ---------------------------------------------- */
-- 
1.7.1


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* [RFCv1 PATCH 9/9] v4l2-ctrls: add new SEND_INITIAL flag to force an initial event.
  2011-04-04 11:51 ` [RFCv1 PATCH 1/9] v4l2-ctrls: add new bitmask control type Hans Verkuil
                     ` (6 preceding siblings ...)
  2011-04-04 11:51   ` [RFCv1 PATCH 8/9] vivi: add support for CTRL_CH_STATE events Hans Verkuil
@ 2011-04-04 11:51   ` Hans Verkuil
  2011-04-08 15:08   ` [RFCv1 PATCH 1/9] v4l2-ctrls: add new bitmask control type Laurent Pinchart
  8 siblings, 0 replies; 30+ messages in thread
From: Hans Verkuil @ 2011-04-04 11:51 UTC (permalink / raw)
  To: linux-media

Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
---
 drivers/media/video/v4l2-ctrls.c |   25 ++++++++++++++++++++++++-
 drivers/media/video/v4l2-event.c |    2 +-
 include/linux/videodev2.h        |    5 ++++-
 include/media/v4l2-ctrls.h       |    4 +++-
 4 files changed, 32 insertions(+), 4 deletions(-)

diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c
index 122c6da..e6fa9be 100644
--- a/drivers/media/video/v4l2-ctrls.c
+++ b/drivers/media/video/v4l2-ctrls.c
@@ -1912,10 +1912,33 @@ int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val)
 }
 EXPORT_SYMBOL(v4l2_ctrl_s_ctrl);
 
-void v4l2_ctrl_add_fh(struct v4l2_ctrl *ctrl, struct v4l2_ctrl_fh *ctrl_fh)
+void v4l2_ctrl_add_fh(struct v4l2_ctrl *ctrl, struct v4l2_ctrl_fh *ctrl_fh,
+		struct v4l2_event_subscription *sub)
 {
 	v4l2_ctrl_lock(ctrl);
 	list_add_tail(&ctrl_fh->node, &ctrl->fhs);
+	if (sub->flags & V4L2_EVENT_SUB_FL_SEND_INITIAL) {
+		struct v4l2_event ev;
+
+		ev.type = sub->type;
+		ev.id = ctrl->id;
+		switch (ev.type) {
+		case V4L2_EVENT_CTRL_CH_VALUE:
+			/* TODO: shouldn't be done for write-only or button/ctrl_class
+			   controls. */
+			if (ctrl->type == V4L2_CTRL_TYPE_INTEGER64)
+				ev.u.ctrl_ch_value.value64 = ctrl->cur.val64;
+			else
+				ev.u.ctrl_ch_value.value = ctrl->cur.val;
+			v4l2_event_queue_fh(ctrl_fh->fh, &ev);
+			break;
+		case V4L2_EVENT_CTRL_CH_STATE:
+			ev.u.ctrl_ch_state.flags = ctrl->flags;
+			v4l2_event_queue_fh(ctrl_fh->fh, &ev);
+		default:
+			break;
+		}
+	}
 	v4l2_ctrl_unlock(ctrl);
 }
 EXPORT_SYMBOL(v4l2_ctrl_add_fh);
diff --git a/drivers/media/video/v4l2-event.c b/drivers/media/video/v4l2-event.c
index 9b503aa..06608e7 100644
--- a/drivers/media/video/v4l2-event.c
+++ b/drivers/media/video/v4l2-event.c
@@ -293,7 +293,7 @@ int v4l2_event_subscribe(struct v4l2_fh *fh,
 
 	/* v4l2_ctrl_add_fh uses a mutex, so do this outside the spin lock */
 	if (!found_ev && ctrl)
-		v4l2_ctrl_add_fh(ctrl, ctrl_fh);
+		v4l2_ctrl_add_fh(ctrl, ctrl_fh, sub);
 
 	kfree(sev);
 
diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h
index eb56685..2a20dd9 100644
--- a/include/linux/videodev2.h
+++ b/include/linux/videodev2.h
@@ -1827,10 +1827,13 @@ struct v4l2_event {
 	__u32				reserved[8];
 };
 
+#define V4L2_EVENT_SUB_FL_SEND_INITIAL (1 << 0)
+
 struct v4l2_event_subscription {
 	__u32				type;
 	__u32				id;
-	__u32				reserved[6];
+	__u32				flags;
+	__u32				reserved[5];
 };
 
 /*
diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h
index e6917f4..27714c9 100644
--- a/include/media/v4l2-ctrls.h
+++ b/include/media/v4l2-ctrls.h
@@ -30,6 +30,7 @@ struct v4l2_ctrl_handler;
 struct v4l2_ctrl;
 struct video_device;
 struct v4l2_subdev;
+struct v4l2_event_subscription;
 struct v4l2_fh;
 
 /** struct v4l2_ctrl_ops - The control operations that the driver has to provide.
@@ -445,7 +446,8 @@ s32 v4l2_ctrl_g_ctrl(struct v4l2_ctrl *ctrl);
   */
 int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val);
 
-void v4l2_ctrl_add_fh(struct v4l2_ctrl *ctrl, struct v4l2_ctrl_fh *ctrl_fh);
+void v4l2_ctrl_add_fh(struct v4l2_ctrl *ctrl, struct v4l2_ctrl_fh *ctrl_fh,
+		struct v4l2_event_subscription *sub);
 void v4l2_ctrl_del_fh(struct v4l2_ctrl *ctrl, struct v4l2_fh *fh);
 
 /* Helpers for ioctl_ops. If hdl == NULL then they will all return -EINVAL. */
-- 
1.7.1


^ permalink raw reply related	[flat|nested] 30+ messages in thread

* RE: [RFCv1 PATCH 5/9] vb2_poll: don't start DMA, leave that to the first read().
  2011-04-04 11:51   ` [RFCv1 PATCH 5/9] vb2_poll: don't start DMA, leave that to the first read() Hans Verkuil
@ 2011-04-08  7:00     ` Marek Szyprowski
  2011-04-08  8:07       ` Hans Verkuil
  0 siblings, 1 reply; 30+ messages in thread
From: Marek Szyprowski @ 2011-04-08  7:00 UTC (permalink / raw)
  To: 'Hans Verkuil', linux-media; +Cc: Marek Szyprowski

Hello,

On Monday, April 04, 2011 1:52 PM Hans Verkuil wrote:

> The vb2_poll function would start read DMA if called without any streaming
> in progress. This unfortunately does not work if the application just wants
> to poll for exceptions. This information of what the application is polling
> for is sadly unavailable in the driver.
> 
> Andy Walls suggested to just return POLLIN | POLLRDNORM and let the first
> call to read start the DMA. This initial read() call will return EAGAIN
> since no actual data is available yet, but it does start the DMA.

The current implementation of vb2_read() will just start streaming on first
call without returning EAGAIN. Do you think this should be changed?

> 
> Application are supposed to handle EAGAIN. MythTV does handle this
> correctly.
> 
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> ---
>  drivers/media/video/videobuf2-core.c |   16 +++-------------
>  1 files changed, 3 insertions(+), 13 deletions(-)
> 
> diff --git a/drivers/media/video/videobuf2-core.c
> b/drivers/media/video/videobuf2-core.c
> index 6698c77..2dea57a 100644
> --- a/drivers/media/video/videobuf2-core.c
> +++ b/drivers/media/video/videobuf2-core.c
> @@ -1372,20 +1372,10 @@ unsigned int vb2_poll(struct vb2_queue *q, struct
> file *file, poll_table *wait)
>  	 * Start file I/O emulator only if streaming API has not been used
> yet.
>  	 */
>  	if (q->num_buffers == 0 && q->fileio == NULL) {
> -		if (!V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_READ))
> {
> -			ret = __vb2_init_fileio(q, 1);
> -			if (ret)
> -				return POLLERR;
> -		}
> -		if (V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_WRITE))
> {
> -			ret = __vb2_init_fileio(q, 0);
> -			if (ret)
> -				return POLLERR;
> -			/*
> -			 * Write to OUTPUT queue can be done immediately.
> -			 */
> +		if (!V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_READ))
> +			return POLLIN | POLLRDNORM;
> +		if (V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_WRITE))
>  			return POLLOUT | POLLWRNORM;
> -		}
>  	}
> 
>  	/*
> --

Best regards
-- 
Marek Szyprowski
Samsung Poland R&D Center


^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [RFCv1 PATCH 5/9] vb2_poll: don't start DMA, leave that to the first read().
  2011-04-08  7:00     ` Marek Szyprowski
@ 2011-04-08  8:07       ` Hans Verkuil
  2011-04-08  8:13         ` Marek Szyprowski
  0 siblings, 1 reply; 30+ messages in thread
From: Hans Verkuil @ 2011-04-08  8:07 UTC (permalink / raw)
  To: Marek Szyprowski; +Cc: 'Hans Verkuil', linux-media

On Friday, April 08, 2011 09:00:55 Marek Szyprowski wrote:
> Hello,
> 
> On Monday, April 04, 2011 1:52 PM Hans Verkuil wrote:
> 
> > The vb2_poll function would start read DMA if called without any streaming
> > in progress. This unfortunately does not work if the application just wants
> > to poll for exceptions. This information of what the application is polling
> > for is sadly unavailable in the driver.
> > 
> > Andy Walls suggested to just return POLLIN | POLLRDNORM and let the first
> > call to read start the DMA. This initial read() call will return EAGAIN
> > since no actual data is available yet, but it does start the DMA.
> 
> The current implementation of vb2_read() will just start streaming on first
> call without returning EAGAIN. Do you think this should be changed?

In the non-blocking case vb2_read will also return EAGAIN. Which is what
I meant. So nothing needs to be changed.

Regards,

        Hans

> 
> > 
> > Application are supposed to handle EAGAIN. MythTV does handle this
> > correctly.
> > 
> > Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> > ---
> >  drivers/media/video/videobuf2-core.c |   16 +++-------------
> >  1 files changed, 3 insertions(+), 13 deletions(-)
> > 
> > diff --git a/drivers/media/video/videobuf2-core.c
> > b/drivers/media/video/videobuf2-core.c
> > index 6698c77..2dea57a 100644
> > --- a/drivers/media/video/videobuf2-core.c
> > +++ b/drivers/media/video/videobuf2-core.c
> > @@ -1372,20 +1372,10 @@ unsigned int vb2_poll(struct vb2_queue *q, struct
> > file *file, poll_table *wait)
> >  	 * Start file I/O emulator only if streaming API has not been used
> > yet.
> >  	 */
> >  	if (q->num_buffers == 0 && q->fileio == NULL) {
> > -		if (!V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_READ))
> > {
> > -			ret = __vb2_init_fileio(q, 1);
> > -			if (ret)
> > -				return POLLERR;
> > -		}
> > -		if (V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_WRITE))
> > {
> > -			ret = __vb2_init_fileio(q, 0);
> > -			if (ret)
> > -				return POLLERR;
> > -			/*
> > -			 * Write to OUTPUT queue can be done immediately.
> > -			 */
> > +		if (!V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_READ))
> > +			return POLLIN | POLLRDNORM;
> > +		if (V4L2_TYPE_IS_OUTPUT(q->type) && (q->io_modes & VB2_WRITE))
> >  			return POLLOUT | POLLWRNORM;
> > -		}
> >  	}
> > 
> >  	/*
> > --
> 
> Best regards
> 

^ permalink raw reply	[flat|nested] 30+ messages in thread

* RE: [RFCv1 PATCH 5/9] vb2_poll: don't start DMA, leave that to the first read().
  2011-04-08  8:07       ` Hans Verkuil
@ 2011-04-08  8:13         ` Marek Szyprowski
  0 siblings, 0 replies; 30+ messages in thread
From: Marek Szyprowski @ 2011-04-08  8:13 UTC (permalink / raw)
  To: 'Hans Verkuil'; +Cc: 'Hans Verkuil', linux-media

Hello,

On Friday, April 08, 2011 10:08 AM Hans Verkuil wrote:

> On Friday, April 08, 2011 09:00:55 Marek Szyprowski wrote:
> > Hello,
> >
> > On Monday, April 04, 2011 1:52 PM Hans Verkuil wrote:
> >
> > > The vb2_poll function would start read DMA if called without any
> streaming
> > > in progress. This unfortunately does not work if the application just
> wants
> > > to poll for exceptions. This information of what the application is
> polling
> > > for is sadly unavailable in the driver.
> > >
> > > Andy Walls suggested to just return POLLIN | POLLRDNORM and let the
> first
> > > call to read start the DMA. This initial read() call will return EAGAIN
> > > since no actual data is available yet, but it does start the DMA.
> >
> > The current implementation of vb2_read() will just start streaming on
> first
> > call without returning EAGAIN. Do you think this should be changed?
> 
> In the non-blocking case vb2_read will also return EAGAIN. Which is what
> I meant. So nothing needs to be changed.

Right, I got confused again. vb2_read internally calls vb2_dqbuf, which 
in case of nonblocking io returns EAGAIN.

Acked-by: Marek Szyprowski <m.szyprowski@samsung.com>

Best regards
-- 
Marek Szyprowski
Samsung Poland R&D Center


^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [RFCv1 PATCH 1/9] v4l2-ctrls: add new bitmask control type.
  2011-04-04 11:51 ` [RFCv1 PATCH 1/9] v4l2-ctrls: add new bitmask control type Hans Verkuil
                     ` (7 preceding siblings ...)
  2011-04-04 11:51   ` [RFCv1 PATCH 9/9] v4l2-ctrls: add new SEND_INITIAL flag to force an initial event Hans Verkuil
@ 2011-04-08 15:08   ` Laurent Pinchart
  8 siblings, 0 replies; 30+ messages in thread
From: Laurent Pinchart @ 2011-04-08 15:08 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media

Hi Hans,

Thanks for the patch.

On Monday 04 April 2011 13:51:46 Hans Verkuil wrote:
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> ---
>  drivers/media/video/v4l2-common.c |    3 +++
>  drivers/media/video/v4l2-ctrls.c  |   17 ++++++++++++++++-
>  include/linux/videodev2.h         |    1 +
>  3 files changed, 20 insertions(+), 1 deletions(-)
> 
> diff --git a/drivers/media/video/v4l2-common.c
> b/drivers/media/video/v4l2-common.c index 06b9f9f..5c6100f 100644
> --- a/drivers/media/video/v4l2-common.c
> +++ b/drivers/media/video/v4l2-common.c
> @@ -105,6 +105,9 @@ int v4l2_ctrl_check(struct v4l2_ext_control *ctrl,
> struct v4l2_queryctrl *qctrl, menu_items[ctrl->value][0] == '\0')
>  			return -EINVAL;
>  	}
> +	if (qctrl->type == V4L2_CTRL_TYPE_BITMASK &&
> +			(ctrl->value & ~qctrl->maximum))
> +		return -ERANGE;
>  	return 0;
>  }
>  EXPORT_SYMBOL(v4l2_ctrl_check);
> diff --git a/drivers/media/video/v4l2-ctrls.c
> b/drivers/media/video/v4l2-ctrls.c index 2412f08..f75a1d4 100644
> --- a/drivers/media/video/v4l2-ctrls.c
> +++ b/drivers/media/video/v4l2-ctrls.c
> @@ -726,6 +726,10 @@ static int validate_new(struct v4l2_ctrl *ctrl)
>  			return -EINVAL;
>  		return 0;
> 
> +	case V4L2_CTRL_TYPE_BITMASK:
> +		ctrl->val &= ctrl->maximum;
> +		return 0;
> +
>  	case V4L2_CTRL_TYPE_BUTTON:
>  	case V4L2_CTRL_TYPE_CTRL_CLASS:
>  		ctrl->val64 = 0;
> @@ -962,13 +966,17 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct
> v4l2_ctrl_handler *hdl,
> 
>  	/* Sanity checks */
>  	if (id == 0 || name == NULL || id >= V4L2_CID_PRIVATE_BASE ||
> -	    max < min ||
>  	    (type == V4L2_CTRL_TYPE_INTEGER && step == 0) ||
> +	    (type == V4L2_CTRL_TYPE_BITMASK && max == 0) ||
>  	    (type == V4L2_CTRL_TYPE_MENU && qmenu == NULL) ||
>  	    (type == V4L2_CTRL_TYPE_STRING && max == 0)) {
>  		handler_set_err(hdl, -ERANGE);
>  		return NULL;
>  	}
> +	if (type != V4L2_CTRL_TYPE_BITMASK && max < min) {
> +		handler_set_err(hdl, -ERANGE);
> +		return NULL;
> +	}
>  	if ((type == V4L2_CTRL_TYPE_INTEGER ||
>  	     type == V4L2_CTRL_TYPE_MENU ||
>  	     type == V4L2_CTRL_TYPE_BOOLEAN) &&
> @@ -976,6 +984,10 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct
> v4l2_ctrl_handler *hdl, handler_set_err(hdl, -ERANGE);
>  		return NULL;
>  	}
> +	if (type == V4L2_CTRL_TYPE_BITMASK && ((def & ~max) || min || step)) {
> +		handler_set_err(hdl, -ERANGE);
> +		return NULL;
> +	}
> 
>  	if (type == V4L2_CTRL_TYPE_BUTTON)
>  		flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
> @@ -1217,6 +1229,9 @@ static void log_ctrl(const struct v4l2_ctrl *ctrl,
>  	case V4L2_CTRL_TYPE_MENU:
>  		printk(KERN_CONT "%s", ctrl->qmenu[ctrl->cur.val]);
>  		break;
> +	case V4L2_CTRL_TYPE_BITMASK:
> +		printk(KERN_CONT "0x%x", ctrl->cur.val);
> +		break;

What about 0x%08x instead ?

>  	case V4L2_CTRL_TYPE_INTEGER64:
>  		printk(KERN_CONT "%lld", ctrl->cur.val64);
>  		break;
> diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h
> index aa6c393..92d2fdd 100644
> --- a/include/linux/videodev2.h
> +++ b/include/linux/videodev2.h
> @@ -1034,6 +1034,7 @@ enum v4l2_ctrl_type {
>  	V4L2_CTRL_TYPE_INTEGER64     = 5,
>  	V4L2_CTRL_TYPE_CTRL_CLASS    = 6,
>  	V4L2_CTRL_TYPE_STRING        = 7,
> +	V4L2_CTRL_TYPE_BITMASK       = 8,
>  };
> 
>  /*  Used in the VIDIOC_QUERYCTRL ioctl for querying controls */

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [RFCv1 PATCH 3/9] v4l2-ioctl: add ctrl_handler to v4l2_fh
  2011-04-04 11:51   ` [RFCv1 PATCH 3/9] v4l2-ioctl: add ctrl_handler to v4l2_fh Hans Verkuil
@ 2011-04-08 15:10     ` Laurent Pinchart
  2011-04-08 15:39       ` Hans Verkuil
  0 siblings, 1 reply; 30+ messages in thread
From: Laurent Pinchart @ 2011-04-08 15:10 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media, Hans Verkuil

Hi Hans,

On Monday 04 April 2011 13:51:48 Hans Verkuil wrote:
> From: Hans Verkuil <hverkuil@xs4all.nl>
> 
> This is required to implement control events and is also needed to allow
> for per-filehandle control handlers.

Thanks for the patch.

Shouldn't you modify v4l2-subdev.c similarly ?

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [RFCv1 PATCH 8/9] vivi: add support for CTRL_CH_STATE events.
  2011-04-04 11:51   ` [RFCv1 PATCH 8/9] vivi: add support for CTRL_CH_STATE events Hans Verkuil
@ 2011-04-08 15:13     ` Laurent Pinchart
  0 siblings, 0 replies; 30+ messages in thread
From: Laurent Pinchart @ 2011-04-08 15:13 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media

Hi Hans,

Thanks for the patch.

On Monday 04 April 2011 13:51:53 Hans Verkuil wrote:
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> ---
>  drivers/media/video/v4l2-event.c |    6 ++++--
>  drivers/media/video/vivi.c       |    8 ++++++--
>  2 files changed, 10 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/media/video/v4l2-event.c
> b/drivers/media/video/v4l2-event.c index c9251a5..9b503aa 100644
> --- a/drivers/media/video/v4l2-event.c
> +++ b/drivers/media/video/v4l2-event.c
> @@ -258,7 +258,8 @@ int v4l2_event_subscribe(struct v4l2_fh *fh,
>  		return -ENOMEM;
>  	}
> 
> -	if (sub->type == V4L2_EVENT_CTRL_CH_VALUE) {
> +	if (sub->type == V4L2_EVENT_CTRL_CH_VALUE ||
> +			sub->type == V4L2_EVENT_CTRL_CH_STATE) {

Indentation looks weird here.

>  		ctrl = v4l2_ctrl_find(fh->ctrl_handler, sub->id);
>  		if (ctrl == NULL)
>  			return -EINVAL;
> @@ -341,7 +342,8 @@ int v4l2_event_unsubscribe(struct v4l2_fh *fh,
>  		list_del(&sev->list);
> 
>  	spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
> -	if (sev->type == V4L2_EVENT_CTRL_CH_VALUE) {
> +	if (sev->type == V4L2_EVENT_CTRL_CH_VALUE ||
> +			sev->type == V4L2_EVENT_CTRL_CH_STATE) {

And here.

>  		struct v4l2_ctrl *ctrl = v4l2_ctrl_find(fh->ctrl_handler, sev->id);
> 
>  		if (ctrl)

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [RFCv1 PATCH 6/9] vivi: add support for control events.
  2011-04-04 11:51   ` [RFCv1 PATCH 6/9] vivi: add support for control events Hans Verkuil
@ 2011-04-08 15:19     ` Laurent Pinchart
  2011-04-08 15:43       ` Hans Verkuil
  0 siblings, 1 reply; 30+ messages in thread
From: Laurent Pinchart @ 2011-04-08 15:19 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media

Hi Hans,

Thanks for the patch.
On Monday 04 April 2011 13:51:51 Hans Verkuil wrote:

[snip]

> diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c
> index 21d8f6a..8790e03 100644
> --- a/drivers/media/video/vivi.c
> +++ b/drivers/media/video/vivi.c
> @@ -998,6 +1007,25 @@ static int vivi_s_ctrl(struct v4l2_ctrl *ctrl)
>  	File operations for the device
>     ------------------------------------------------------------------*/
> 
> +static int vivi_open(struct file *filp)
> +{
> +	int ret = v4l2_fh_open(filp);
> +	struct v4l2_fh *fh;
> +
> +	if (ret)
> +		return ret;
> +	fh = filp->private_data;
> +	ret = v4l2_event_init(fh);
> +	if (ret)
> +		goto rel_fh;
> +	ret = v4l2_event_alloc(fh, 10);
> +	if (!ret)
> +		return ret;
> +rel_fh:
> +	v4l2_fh_release(filp);
> +	return ret;
> +}
> +

Should the V4L2 core provide a helper function that does just that ?

>  static ssize_t
>  vivi_read(struct file *file, char __user *data, size_t count, loff_t
> *ppos) {
> @@ -1012,10 +1040,17 @@ static unsigned int
>  vivi_poll(struct file *file, struct poll_table_struct *wait)
>  {
>  	struct vivi_dev *dev = video_drvdata(file);
> +	struct v4l2_fh *fh = file->private_data;
>  	struct vb2_queue *q = &dev->vb_vidq;
> +	unsigned int res;
> 
>  	dprintk(dev, 1, "%s\n", __func__);
> -	return vb2_poll(q, file, wait);
> +	res = vb2_poll(q, file, wait);
> +	if (v4l2_event_pending(fh))
> +		res |= POLLPRI;
> +	else
> +		poll_wait(file, &fh->events->wait, wait);

Don't you need to call poll_wait unconditionally ?

> +	return res;
>  }
> 
>  static int vivi_close(struct file *file)

[snip]

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [RFCv1 PATCH 3/9] v4l2-ioctl: add ctrl_handler to v4l2_fh
  2011-04-08 15:10     ` Laurent Pinchart
@ 2011-04-08 15:39       ` Hans Verkuil
  2011-04-11 14:54         ` Laurent Pinchart
  0 siblings, 1 reply; 30+ messages in thread
From: Hans Verkuil @ 2011-04-08 15:39 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: Hans Verkuil, linux-media

On Friday, April 08, 2011 17:10:32 Laurent Pinchart wrote:
> Hi Hans,
> 
> On Monday 04 April 2011 13:51:48 Hans Verkuil wrote:
> > From: Hans Verkuil <hverkuil@xs4all.nl>
> > 
> > This is required to implement control events and is also needed to allow
> > for per-filehandle control handlers.
> 
> Thanks for the patch.
> 
> Shouldn't you modify v4l2-subdev.c similarly ?
> 
> 

Good question. Does it make sense to have per-filehandle controls for a
sub-device? On the other hand, does it make sense NOT to have it?

I'm inclined to add this functionality if nobody objects. Although a
use-case for this would be nice bonus.

Regards,

	Hans

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [RFCv1 PATCH 6/9] vivi: add support for control events.
  2011-04-08 15:19     ` Laurent Pinchart
@ 2011-04-08 15:43       ` Hans Verkuil
  0 siblings, 0 replies; 30+ messages in thread
From: Hans Verkuil @ 2011-04-08 15:43 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: Hans Verkuil, linux-media

On Friday, April 08, 2011 17:19:40 Laurent Pinchart wrote:
> Hi Hans,
> 
> Thanks for the patch.
> On Monday 04 April 2011 13:51:51 Hans Verkuil wrote:
> 
> [snip]
> 
> > diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c
> > index 21d8f6a..8790e03 100644
> > --- a/drivers/media/video/vivi.c
> > +++ b/drivers/media/video/vivi.c
> > @@ -998,6 +1007,25 @@ static int vivi_s_ctrl(struct v4l2_ctrl *ctrl)
> >  	File operations for the device
> >     ------------------------------------------------------------------*/
> > 
> > +static int vivi_open(struct file *filp)
> > +{
> > +	int ret = v4l2_fh_open(filp);
> > +	struct v4l2_fh *fh;
> > +
> > +	if (ret)
> > +		return ret;
> > +	fh = filp->private_data;
> > +	ret = v4l2_event_init(fh);
> > +	if (ret)
> > +		goto rel_fh;
> > +	ret = v4l2_event_alloc(fh, 10);
> > +	if (!ret)
> > +		return ret;
> > +rel_fh:
> > +	v4l2_fh_release(filp);
> > +	return ret;
> > +}
> > +
> 
> Should the V4L2 core provide a helper function that does just that ?

Good idea.

> 
> >  static ssize_t
> >  vivi_read(struct file *file, char __user *data, size_t count, loff_t
> > *ppos) {
> > @@ -1012,10 +1040,17 @@ static unsigned int
> >  vivi_poll(struct file *file, struct poll_table_struct *wait)
> >  {
> >  	struct vivi_dev *dev = video_drvdata(file);
> > +	struct v4l2_fh *fh = file->private_data;
> >  	struct vb2_queue *q = &dev->vb_vidq;
> > +	unsigned int res;
> > 
> >  	dprintk(dev, 1, "%s\n", __func__);
> > -	return vb2_poll(q, file, wait);
> > +	res = vb2_poll(q, file, wait);
> > +	if (v4l2_event_pending(fh))
> > +		res |= POLLPRI;
> > +	else
> > +		poll_wait(file, &fh->events->wait, wait);
> 
> Don't you need to call poll_wait unconditionally ?

No, setting POLLPRI will have poll() exit without waiting.
On the other hand, it may be more understandable if I do it unconditionally.
I've been back and forth about this a few times already :-)

I suspect that auditing the way drivers implement poll() would be a great
janitorial project. It's not the greatest API in the world...

Regards,

	Hans

> 
> > +	return res;
> >  }
> > 
> >  static int vivi_close(struct file *file)
> 
> [snip]
> 
> 

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [RFCv1 PATCH 4/9] v4l2-ctrls: add per-control events.
  2011-04-04 11:51   ` [RFCv1 PATCH 4/9] v4l2-ctrls: add per-control events Hans Verkuil
@ 2011-04-11  7:29     ` Sakari Ailus
  2011-04-11 14:57       ` Hans Verkuil
  2011-04-15 10:51     ` Sakari Ailus
  1 sibling, 1 reply; 30+ messages in thread
From: Sakari Ailus @ 2011-04-11  7:29 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media

Hi Hans,

Thanks for the patchset! This looks really nice!

Hans Verkuil wrote:
> Whenever a control changes value an event is sent to anyone that subscribed
> to it.
> 
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> ---
>  drivers/media/video/v4l2-ctrls.c |   59 ++++++++++++++++++
>  drivers/media/video/v4l2-event.c |  126 +++++++++++++++++++++++++++-----------
>  drivers/media/video/v4l2-fh.c    |    4 +-
>  include/linux/videodev2.h        |   17 +++++-
>  include/media/v4l2-ctrls.h       |    9 +++
>  include/media/v4l2-event.h       |    2 +
>  6 files changed, 177 insertions(+), 40 deletions(-)
> 
> diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c
> index f75a1d4..163f412 100644
> --- a/drivers/media/video/v4l2-ctrls.c
> +++ b/drivers/media/video/v4l2-ctrls.c
> @@ -23,6 +23,7 @@
>  #include <media/v4l2-ioctl.h>
>  #include <media/v4l2-device.h>
>  #include <media/v4l2-ctrls.h>
> +#include <media/v4l2-event.h>
>  #include <media/v4l2-dev.h>
>  
>  /* Internal temporary helper struct, one for each v4l2_ext_control */
> @@ -537,6 +538,16 @@ static bool type_is_int(const struct v4l2_ctrl *ctrl)
>  	}
>  }
>  
> +static void send_event(struct v4l2_ctrl *ctrl, struct v4l2_event *ev)
> +{
> +	struct v4l2_ctrl_fh *pos;
> +
> +	ev->id = ctrl->id;
> +	list_for_each_entry(pos, &ctrl->fhs, node) {
> +		v4l2_event_queue_fh(pos->fh, ev);
> +	}

No need for braces here.

> +}
> +
>  /* Helper function: copy the current control value back to the caller */
>  static int cur_to_user(struct v4l2_ext_control *c,
>  		       struct v4l2_ctrl *ctrl)
> @@ -626,20 +637,38 @@ static int new_to_user(struct v4l2_ext_control *c,
>  /* Copy the new value to the current value. */
>  static void new_to_cur(struct v4l2_ctrl *ctrl)
>  {
> +	struct v4l2_event ev;
> +	bool changed = false;
> +
>  	if (ctrl == NULL)
>  		return;
>  	switch (ctrl->type) {
> +	case V4L2_CTRL_TYPE_BUTTON:
> +		changed = true;
> +		ev.u.ctrl_ch_value.value = 0;
> +		break;
>  	case V4L2_CTRL_TYPE_STRING:
>  		/* strings are always 0-terminated */
> +		changed = strcmp(ctrl->string, ctrl->cur.string);
>  		strcpy(ctrl->cur.string, ctrl->string);
> +		ev.u.ctrl_ch_value.value64 = 0;
>  		break;
>  	case V4L2_CTRL_TYPE_INTEGER64:
> +		changed = ctrl->val64 != ctrl->cur.val64;
>  		ctrl->cur.val64 = ctrl->val64;
> +		ev.u.ctrl_ch_value.value64 = ctrl->val64;
>  		break;
>  	default:
> +		changed = ctrl->val != ctrl->cur.val;
>  		ctrl->cur.val = ctrl->val;
> +		ev.u.ctrl_ch_value.value = ctrl->val;
>  		break;
>  	}
> +	if (changed) {
> +		ev.type = V4L2_EVENT_CTRL_CH_VALUE;
> +		ev.u.ctrl_ch_value.type = ctrl->type;
> +		send_event(ctrl, &ev);
> +	}
>  }
>  
>  /* Copy the current value to the new value */
> @@ -784,6 +813,7 @@ void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl)
>  {
>  	struct v4l2_ctrl_ref *ref, *next_ref;
>  	struct v4l2_ctrl *ctrl, *next_ctrl;
> +	struct v4l2_ctrl_fh *ctrl_fh, *next_ctrl_fh;
>  
>  	if (hdl == NULL || hdl->buckets == NULL)
>  		return;
> @@ -797,6 +827,10 @@ void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl)
>  	/* 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);
> +		}
>  		kfree(ctrl);
>  	}
>  	kfree(hdl->buckets);
> @@ -1003,6 +1037,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
>  	}
>  
>  	INIT_LIST_HEAD(&ctrl->node);
> +	INIT_LIST_HEAD(&ctrl->fhs);
>  	ctrl->handler = hdl;
>  	ctrl->ops = ops;
>  	ctrl->id = id;
> @@ -1888,3 +1923,27 @@ int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val)
>  	return set_ctrl(ctrl, &val);
>  }
>  EXPORT_SYMBOL(v4l2_ctrl_s_ctrl);
> +
> +void v4l2_ctrl_add_fh(struct v4l2_ctrl *ctrl, struct v4l2_ctrl_fh *ctrl_fh)
> +{
> +	v4l2_ctrl_lock(ctrl);
> +	list_add_tail(&ctrl_fh->node, &ctrl->fhs);
> +	v4l2_ctrl_unlock(ctrl);
> +}
> +EXPORT_SYMBOL(v4l2_ctrl_add_fh);
> +
> +void v4l2_ctrl_del_fh(struct v4l2_ctrl *ctrl, struct v4l2_fh *fh)
> +{
> +	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;
> +		}
> +	}
> +	v4l2_ctrl_unlock(ctrl);
> +}
> +EXPORT_SYMBOL(v4l2_ctrl_del_fh);
> diff --git a/drivers/media/video/v4l2-event.c b/drivers/media/video/v4l2-event.c
> index 69fd343..c9251a5 100644
> --- a/drivers/media/video/v4l2-event.c
> +++ b/drivers/media/video/v4l2-event.c
> @@ -25,10 +25,13 @@
>  #include <media/v4l2-dev.h>
>  #include <media/v4l2-fh.h>
>  #include <media/v4l2-event.h>
> +#include <media/v4l2-ctrls.h>
>  
>  #include <linux/sched.h>
>  #include <linux/slab.h>
>  
> +static void v4l2_event_unsubscribe_all(struct v4l2_fh *fh);
> +
>  int v4l2_event_init(struct v4l2_fh *fh)
>  {
>  	fh->events = kzalloc(sizeof(*fh->events), GFP_KERNEL);
> @@ -91,7 +94,7 @@ void v4l2_event_free(struct v4l2_fh *fh)
>  
>  	list_kfree(&events->free, struct v4l2_kevent, list);
>  	list_kfree(&events->available, struct v4l2_kevent, list);
> -	list_kfree(&events->subscribed, struct v4l2_subscribed_event, list);
> +	v4l2_event_unsubscribe_all(fh);
>  
>  	kfree(events);
>  	fh->events = NULL;
> @@ -154,9 +157,9 @@ int v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event,
>  }
>  EXPORT_SYMBOL_GPL(v4l2_event_dequeue);
>  
> -/* Caller must hold fh->event->lock! */
> +/* Caller must hold fh->vdev->fh_lock! */
>  static struct v4l2_subscribed_event *v4l2_event_subscribed(
> -	struct v4l2_fh *fh, u32 type)
> +		struct v4l2_fh *fh, u32 type, u32 id)
>  {
>  	struct v4l2_events *events = fh->events;
>  	struct v4l2_subscribed_event *sev;
> @@ -164,13 +167,46 @@ static struct v4l2_subscribed_event *v4l2_event_subscribed(
>  	assert_spin_locked(&fh->vdev->fh_lock);
>  
>  	list_for_each_entry(sev, &events->subscribed, list) {
> -		if (sev->type == type)
> +		if (sev->type == type && sev->id == id)
>  			return sev;
>  	}
>  
>  	return NULL;
>  }
>  
> +static void __v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *ev,
> +		const struct timespec *ts)
> +{
> +	struct v4l2_events *events = fh->events;
> +	struct v4l2_subscribed_event *sev;
> +	struct v4l2_kevent *kev;
> +
> +	/* Are we subscribed? */
> +	sev = v4l2_event_subscribed(fh, ev->type, ev->id);
> +	if (sev == NULL)
> +		return;
> +
> +	/* Increase event sequence number on fh. */
> +	events->sequence++;
> +
> +	/* Do we have any free events? */
> +	if (list_empty(&events->free))
> +		return;
> +
> +	/* Take one and fill it. */
> +	kev = list_first_entry(&events->free, struct v4l2_kevent, list);
> +	kev->event.type = ev->type;
> +	kev->event.u = ev->u;
> +	kev->event.id = ev->id;
> +	kev->event.timestamp = *ts;
> +	kev->event.sequence = events->sequence;
> +	list_move_tail(&kev->list, &events->available);
> +
> +	events->navailable++;
> +
> +	wake_up_all(&events->wait);
> +}
> +
>  void v4l2_event_queue(struct video_device *vdev, const struct v4l2_event *ev)
>  {
>  	struct v4l2_fh *fh;
> @@ -182,37 +218,26 @@ void v4l2_event_queue(struct video_device *vdev, const struct v4l2_event *ev)
>  	spin_lock_irqsave(&vdev->fh_lock, flags);
>  
>  	list_for_each_entry(fh, &vdev->fh_list, list) {
> -		struct v4l2_events *events = fh->events;
> -		struct v4l2_kevent *kev;
> -
> -		/* Are we subscribed? */
> -		if (!v4l2_event_subscribed(fh, ev->type))
> -			continue;
> -
> -		/* Increase event sequence number on fh. */
> -		events->sequence++;
> -
> -		/* Do we have any free events? */
> -		if (list_empty(&events->free))
> -			continue;
> -
> -		/* Take one and fill it. */
> -		kev = list_first_entry(&events->free, struct v4l2_kevent, list);
> -		kev->event.type = ev->type;
> -		kev->event.u = ev->u;
> -		kev->event.timestamp = timestamp;
> -		kev->event.sequence = events->sequence;
> -		list_move_tail(&kev->list, &events->available);
> -
> -		events->navailable++;
> -
> -		wake_up_all(&events->wait);
> +		__v4l2_event_queue_fh(fh, ev, &timestamp);
>  	}
>  
>  	spin_unlock_irqrestore(&vdev->fh_lock, flags);
>  }
>  EXPORT_SYMBOL_GPL(v4l2_event_queue);
>  
> +void v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *ev)
> +{
> +	unsigned long flags;
> +	struct timespec timestamp;
> +
> +	ktime_get_ts(&timestamp);
> +
> +	spin_lock_irqsave(&fh->vdev->fh_lock, flags);
> +	__v4l2_event_queue_fh(fh, ev, &timestamp);
> +	spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
> +}
> +EXPORT_SYMBOL_GPL(v4l2_event_queue_fh);
> +
>  int v4l2_event_pending(struct v4l2_fh *fh)
>  {
>  	return fh->events->navailable;
> @@ -223,7 +248,9 @@ int v4l2_event_subscribe(struct v4l2_fh *fh,
>  			 struct v4l2_event_subscription *sub)
>  {
>  	struct v4l2_events *events = fh->events;
> -	struct v4l2_subscribed_event *sev;
> +	struct v4l2_subscribed_event *sev, *found_ev;
> +	struct v4l2_ctrl *ctrl = NULL;
> +	struct v4l2_ctrl_fh *ctrl_fh = NULL;
>  	unsigned long flags;
>  
>  	if (fh->events == NULL) {
> @@ -231,15 +258,31 @@ int v4l2_event_subscribe(struct v4l2_fh *fh,
>  		return -ENOMEM;
>  	}
>  
> +	if (sub->type == V4L2_EVENT_CTRL_CH_VALUE) {
> +		ctrl = v4l2_ctrl_find(fh->ctrl_handler, sub->id);
> +		if (ctrl == NULL)
> +			return -EINVAL;
> +	}
> +
>  	sev = kmalloc(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);
>  
> -	if (v4l2_event_subscribed(fh, sub->type) == NULL) {
> +	found_ev = v4l2_event_subscribed(fh, sub->type, sub->id);
> +	if (!found_ev) {
>  		INIT_LIST_HEAD(&sev->list);
>  		sev->type = sub->type;
> +		sev->id = sub->id;
>  
>  		list_add(&sev->list, &events->subscribed);
>  		sev = NULL;
> @@ -247,6 +290,10 @@ int v4l2_event_subscribe(struct v4l2_fh *fh,
>  
>  	spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
>  
> +	/* v4l2_ctrl_add_fh uses a mutex, so do this outside the spin lock */
> +	if (!found_ev && ctrl)
> +		v4l2_ctrl_add_fh(ctrl, ctrl_fh);
> +
>  	kfree(sev);
>  
>  	return 0;
> @@ -256,6 +303,7 @@ EXPORT_SYMBOL_GPL(v4l2_event_subscribe);
>  static void v4l2_event_unsubscribe_all(struct v4l2_fh *fh)
>  {
>  	struct v4l2_events *events = fh->events;
> +	struct v4l2_event_subscription sub;
>  	struct v4l2_subscribed_event *sev;
>  	unsigned long flags;
>  
> @@ -265,11 +313,13 @@ static void v4l2_event_unsubscribe_all(struct v4l2_fh *fh)
>  		spin_lock_irqsave(&fh->vdev->fh_lock, flags);
>  		if (!list_empty(&events->subscribed)) {
>  			sev = list_first_entry(&events->subscribed,
> -				       struct v4l2_subscribed_event, list);
> -			list_del(&sev->list);
> +					struct v4l2_subscribed_event, list);
> +			sub.type = sev->type;
> +			sub.id = sev->id;
>  		}
>  		spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
> -		kfree(sev);
> +		if (sev)
> +			v4l2_event_unsubscribe(fh, &sub);
>  	} while (sev);
>  }
>  
> @@ -286,11 +336,17 @@ int v4l2_event_unsubscribe(struct v4l2_fh *fh,
>  
>  	spin_lock_irqsave(&fh->vdev->fh_lock, flags);
>  
> -	sev = v4l2_event_subscribed(fh, sub->type);
> +	sev = v4l2_event_subscribed(fh, sub->type, sub->id);
>  	if (sev != NULL)
>  		list_del(&sev->list);
>  
>  	spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
> +	if (sev->type == V4L2_EVENT_CTRL_CH_VALUE) {
> +		struct v4l2_ctrl *ctrl = v4l2_ctrl_find(fh->ctrl_handler, sev->id);
> +
> +		if (ctrl)
> +			v4l2_ctrl_del_fh(ctrl, fh);
> +	}
>  
>  	kfree(sev);
>  
> diff --git a/drivers/media/video/v4l2-fh.c b/drivers/media/video/v4l2-fh.c
> index 8635011..c6aef84 100644
> --- a/drivers/media/video/v4l2-fh.c
> +++ b/drivers/media/video/v4l2-fh.c
> @@ -93,10 +93,8 @@ void v4l2_fh_exit(struct v4l2_fh *fh)
>  {
>  	if (fh->vdev == NULL)
>  		return;
> -
> -	fh->vdev = NULL;
> -
>  	v4l2_event_free(fh);
> +	fh->vdev = NULL;

This looks like a bugfix.

>  }
>  EXPORT_SYMBOL_GPL(v4l2_fh_exit);
>  
> diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h
> index 92d2fdd..f7238c1 100644
> --- a/include/linux/videodev2.h
> +++ b/include/linux/videodev2.h
> @@ -1787,6 +1787,7 @@ struct v4l2_streamparm {
>  #define V4L2_EVENT_ALL				0
>  #define V4L2_EVENT_VSYNC			1
>  #define V4L2_EVENT_EOS				2
> +#define V4L2_EVENT_CTRL_CH_VALUE		3
>  #define V4L2_EVENT_PRIVATE_START		0x08000000
>  
>  /* Payload for V4L2_EVENT_VSYNC */
> @@ -1795,21 +1796,33 @@ struct v4l2_event_vsync {
>  	__u8 field;
>  } __attribute__ ((packed));
>  
> +/* Payload for V4L2_EVENT_CTRL_CH_VALUE */
> +struct v4l2_event_ctrl_ch_value {
> +	__u32 type;

type is enum v4l2_ctrl_type in struct v4l2_ctrl and struct v4l2_queryctrl.

> +	union {
> +		__s32 value;
> +		__s64 value64;
> +	};
> +} __attribute__ ((packed));
> +
>  struct v4l2_event {
>  	__u32				type;
>  	union {
>  		struct v4l2_event_vsync vsync;
> +		struct v4l2_event_ctrl_ch_value ctrl_ch_value;
>  		__u8			data[64];
>  	} u;
>  	__u32				pending;
>  	__u32				sequence;
>  	struct timespec			timestamp;
> -	__u32				reserved[9];
> +	__u32				id;

id is valid only for control related events. Shouldn't it be part of the
control related structures instead, or another union for control related
event types? E.g.

struct {
	enum v4l2_ctrl_type	id;
	union {
		struct v4l2_event_ctrl_ch_value ch_value;
	};
} ctrl;

> +	__u32				reserved[8];
>  };
>  
>  struct v4l2_event_subscription {
>  	__u32				type;
> -	__u32				reserved[7];
> +	__u32				id;
> +	__u32				reserved[6];
>  };

Similar situation here, but no existing union where id would fit in.

>  /*
> diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h
> index 97d0638..7ca45a5 100644
> --- a/include/media/v4l2-ctrls.h
> +++ b/include/media/v4l2-ctrls.h
> @@ -30,6 +30,7 @@ struct v4l2_ctrl_handler;
>  struct v4l2_ctrl;
>  struct video_device;
>  struct v4l2_subdev;
> +struct v4l2_fh;
>  
>  /** struct v4l2_ctrl_ops - The control operations that the driver has to provide.
>    * @g_volatile_ctrl: Get a new value for this control. Generally only relevant
> @@ -97,6 +98,7 @@ struct v4l2_ctrl_ops {
>  struct v4l2_ctrl {
>  	/* Administrative fields */
>  	struct list_head node;
> +	struct list_head fhs;
>  	struct v4l2_ctrl_handler *handler;
>  	struct v4l2_ctrl **cluster;
>  	unsigned ncontrols;
> @@ -168,6 +170,11 @@ struct v4l2_ctrl_handler {
>  	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.
> @@ -440,6 +447,8 @@ s32 v4l2_ctrl_g_ctrl(struct v4l2_ctrl *ctrl);
>    */
>  int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val);
>  
> +void v4l2_ctrl_add_fh(struct v4l2_ctrl *ctrl, struct v4l2_ctrl_fh *ctrl_fh);
> +void v4l2_ctrl_del_fh(struct v4l2_ctrl *ctrl, struct v4l2_fh *fh);
>  
>  /* Helpers for ioctl_ops. If hdl == NULL then they will all return -EINVAL. */
>  int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc);
> diff --git a/include/media/v4l2-event.h b/include/media/v4l2-event.h
> index 3b86177..45e9c1e 100644
> --- a/include/media/v4l2-event.h
> +++ b/include/media/v4l2-event.h
> @@ -40,6 +40,7 @@ struct v4l2_kevent {
>  struct v4l2_subscribed_event {
>  	struct list_head	list;
>  	u32			type;
> +	u32			id;
>  };

And here.

>  struct v4l2_events {
> @@ -58,6 +59,7 @@ 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);

Regards,

-- 
Sakari Ailus
sakari.ailus@maxwell.research.nokia.com

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [RFCv1 PATCH 3/9] v4l2-ioctl: add ctrl_handler to v4l2_fh
  2011-04-08 15:39       ` Hans Verkuil
@ 2011-04-11 14:54         ` Laurent Pinchart
  0 siblings, 0 replies; 30+ messages in thread
From: Laurent Pinchart @ 2011-04-11 14:54 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: Hans Verkuil, linux-media

Hi Hans,

On Friday 08 April 2011 17:39:01 Hans Verkuil wrote:
> On Friday, April 08, 2011 17:10:32 Laurent Pinchart wrote:
> > On Monday 04 April 2011 13:51:48 Hans Verkuil wrote:
> > > From: Hans Verkuil <hverkuil@xs4all.nl>
> > > 
> > > This is required to implement control events and is also needed to
> > > allow for per-filehandle control handlers.
> > 
> > Thanks for the patch.
> > 
> > Shouldn't you modify v4l2-subdev.c similarly ?
> 
> Good question. Does it make sense to have per-filehandle controls for a
> sub-device? On the other hand, does it make sense NOT to have it?

Yes, I think it does. I wrote support for per-file handler controls for 
subdevs and submitted an RFC patch to the list some time ago to implement 
bandwidth managemeng in the OMAP3 ISP driver (this has been put on hold due to 
missing information about the OMAP3 ISP though).

> I'm inclined to add this functionality if nobody objects. Although a
> use-case for this would be nice bonus.

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [RFCv1 PATCH 4/9] v4l2-ctrls: add per-control events.
  2011-04-11  7:29     ` Sakari Ailus
@ 2011-04-11 14:57       ` Hans Verkuil
  2011-04-12  8:12         ` Sakari Ailus
  0 siblings, 1 reply; 30+ messages in thread
From: Hans Verkuil @ 2011-04-11 14:57 UTC (permalink / raw)
  To: Sakari Ailus; +Cc: Hans Verkuil, linux-media

> Hi Hans,
>
> Thanks for the patchset! This looks really nice!
>
> Hans Verkuil wrote:
>> Whenever a control changes value an event is sent to anyone that
>> subscribed
>> to it.
>>
>> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
>> ---
>>  drivers/media/video/v4l2-ctrls.c |   59 ++++++++++++++++++
>>  drivers/media/video/v4l2-event.c |  126
>> +++++++++++++++++++++++++++-----------
>>  drivers/media/video/v4l2-fh.c    |    4 +-
>>  include/linux/videodev2.h        |   17 +++++-
>>  include/media/v4l2-ctrls.h       |    9 +++
>>  include/media/v4l2-event.h       |    2 +
>>  6 files changed, 177 insertions(+), 40 deletions(-)
>>
>> diff --git a/drivers/media/video/v4l2-ctrls.c
>> b/drivers/media/video/v4l2-ctrls.c
>> index f75a1d4..163f412 100644
>> --- a/drivers/media/video/v4l2-ctrls.c
>> +++ b/drivers/media/video/v4l2-ctrls.c
>> @@ -23,6 +23,7 @@
>>  #include <media/v4l2-ioctl.h>
>>  #include <media/v4l2-device.h>
>>  #include <media/v4l2-ctrls.h>
>> +#include <media/v4l2-event.h>
>>  #include <media/v4l2-dev.h>
>>
>>  /* Internal temporary helper struct, one for each v4l2_ext_control */
>> @@ -537,6 +538,16 @@ static bool type_is_int(const struct v4l2_ctrl
>> *ctrl)
>>  	}
>>  }
>>
>> +static void send_event(struct v4l2_ctrl *ctrl, struct v4l2_event *ev)
>> +{
>> +	struct v4l2_ctrl_fh *pos;
>> +
>> +	ev->id = ctrl->id;
>> +	list_for_each_entry(pos, &ctrl->fhs, node) {
>> +		v4l2_event_queue_fh(pos->fh, ev);
>> +	}
>
> No need for braces here.
>
>> +}
>> +
>>  /* Helper function: copy the current control value back to the caller
>> */
>>  static int cur_to_user(struct v4l2_ext_control *c,
>>  		       struct v4l2_ctrl *ctrl)
>> @@ -626,20 +637,38 @@ static int new_to_user(struct v4l2_ext_control *c,
>>  /* Copy the new value to the current value. */
>>  static void new_to_cur(struct v4l2_ctrl *ctrl)
>>  {
>> +	struct v4l2_event ev;
>> +	bool changed = false;
>> +
>>  	if (ctrl == NULL)
>>  		return;
>>  	switch (ctrl->type) {
>> +	case V4L2_CTRL_TYPE_BUTTON:
>> +		changed = true;
>> +		ev.u.ctrl_ch_value.value = 0;
>> +		break;
>>  	case V4L2_CTRL_TYPE_STRING:
>>  		/* strings are always 0-terminated */
>> +		changed = strcmp(ctrl->string, ctrl->cur.string);
>>  		strcpy(ctrl->cur.string, ctrl->string);
>> +		ev.u.ctrl_ch_value.value64 = 0;
>>  		break;
>>  	case V4L2_CTRL_TYPE_INTEGER64:
>> +		changed = ctrl->val64 != ctrl->cur.val64;
>>  		ctrl->cur.val64 = ctrl->val64;
>> +		ev.u.ctrl_ch_value.value64 = ctrl->val64;
>>  		break;
>>  	default:
>> +		changed = ctrl->val != ctrl->cur.val;
>>  		ctrl->cur.val = ctrl->val;
>> +		ev.u.ctrl_ch_value.value = ctrl->val;
>>  		break;
>>  	}
>> +	if (changed) {
>> +		ev.type = V4L2_EVENT_CTRL_CH_VALUE;
>> +		ev.u.ctrl_ch_value.type = ctrl->type;
>> +		send_event(ctrl, &ev);
>> +	}
>>  }
>>
>>  /* Copy the current value to the new value */
>> @@ -784,6 +813,7 @@ void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler
>> *hdl)
>>  {
>>  	struct v4l2_ctrl_ref *ref, *next_ref;
>>  	struct v4l2_ctrl *ctrl, *next_ctrl;
>> +	struct v4l2_ctrl_fh *ctrl_fh, *next_ctrl_fh;
>>
>>  	if (hdl == NULL || hdl->buckets == NULL)
>>  		return;
>> @@ -797,6 +827,10 @@ void v4l2_ctrl_handler_free(struct
>> v4l2_ctrl_handler *hdl)
>>  	/* 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);
>> +		}
>>  		kfree(ctrl);
>>  	}
>>  	kfree(hdl->buckets);
>> @@ -1003,6 +1037,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct
>> v4l2_ctrl_handler *hdl,
>>  	}
>>
>>  	INIT_LIST_HEAD(&ctrl->node);
>> +	INIT_LIST_HEAD(&ctrl->fhs);
>>  	ctrl->handler = hdl;
>>  	ctrl->ops = ops;
>>  	ctrl->id = id;
>> @@ -1888,3 +1923,27 @@ int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32
>> val)
>>  	return set_ctrl(ctrl, &val);
>>  }
>>  EXPORT_SYMBOL(v4l2_ctrl_s_ctrl);
>> +
>> +void v4l2_ctrl_add_fh(struct v4l2_ctrl *ctrl, struct v4l2_ctrl_fh
>> *ctrl_fh)
>> +{
>> +	v4l2_ctrl_lock(ctrl);
>> +	list_add_tail(&ctrl_fh->node, &ctrl->fhs);
>> +	v4l2_ctrl_unlock(ctrl);
>> +}
>> +EXPORT_SYMBOL(v4l2_ctrl_add_fh);
>> +
>> +void v4l2_ctrl_del_fh(struct v4l2_ctrl *ctrl, struct v4l2_fh *fh)
>> +{
>> +	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;
>> +		}
>> +	}
>> +	v4l2_ctrl_unlock(ctrl);
>> +}
>> +EXPORT_SYMBOL(v4l2_ctrl_del_fh);
>> diff --git a/drivers/media/video/v4l2-event.c
>> b/drivers/media/video/v4l2-event.c
>> index 69fd343..c9251a5 100644
>> --- a/drivers/media/video/v4l2-event.c
>> +++ b/drivers/media/video/v4l2-event.c
>> @@ -25,10 +25,13 @@
>>  #include <media/v4l2-dev.h>
>>  #include <media/v4l2-fh.h>
>>  #include <media/v4l2-event.h>
>> +#include <media/v4l2-ctrls.h>
>>
>>  #include <linux/sched.h>
>>  #include <linux/slab.h>
>>
>> +static void v4l2_event_unsubscribe_all(struct v4l2_fh *fh);
>> +
>>  int v4l2_event_init(struct v4l2_fh *fh)
>>  {
>>  	fh->events = kzalloc(sizeof(*fh->events), GFP_KERNEL);
>> @@ -91,7 +94,7 @@ void v4l2_event_free(struct v4l2_fh *fh)
>>
>>  	list_kfree(&events->free, struct v4l2_kevent, list);
>>  	list_kfree(&events->available, struct v4l2_kevent, list);
>> -	list_kfree(&events->subscribed, struct v4l2_subscribed_event, list);
>> +	v4l2_event_unsubscribe_all(fh);
>>
>>  	kfree(events);
>>  	fh->events = NULL;
>> @@ -154,9 +157,9 @@ int v4l2_event_dequeue(struct v4l2_fh *fh, struct
>> v4l2_event *event,
>>  }
>>  EXPORT_SYMBOL_GPL(v4l2_event_dequeue);
>>
>> -/* Caller must hold fh->event->lock! */
>> +/* Caller must hold fh->vdev->fh_lock! */
>>  static struct v4l2_subscribed_event *v4l2_event_subscribed(
>> -	struct v4l2_fh *fh, u32 type)
>> +		struct v4l2_fh *fh, u32 type, u32 id)
>>  {
>>  	struct v4l2_events *events = fh->events;
>>  	struct v4l2_subscribed_event *sev;
>> @@ -164,13 +167,46 @@ static struct v4l2_subscribed_event
>> *v4l2_event_subscribed(
>>  	assert_spin_locked(&fh->vdev->fh_lock);
>>
>>  	list_for_each_entry(sev, &events->subscribed, list) {
>> -		if (sev->type == type)
>> +		if (sev->type == type && sev->id == id)
>>  			return sev;
>>  	}
>>
>>  	return NULL;
>>  }
>>
>> +static void __v4l2_event_queue_fh(struct v4l2_fh *fh, const struct
>> v4l2_event *ev,
>> +		const struct timespec *ts)
>> +{
>> +	struct v4l2_events *events = fh->events;
>> +	struct v4l2_subscribed_event *sev;
>> +	struct v4l2_kevent *kev;
>> +
>> +	/* Are we subscribed? */
>> +	sev = v4l2_event_subscribed(fh, ev->type, ev->id);
>> +	if (sev == NULL)
>> +		return;
>> +
>> +	/* Increase event sequence number on fh. */
>> +	events->sequence++;
>> +
>> +	/* Do we have any free events? */
>> +	if (list_empty(&events->free))
>> +		return;
>> +
>> +	/* Take one and fill it. */
>> +	kev = list_first_entry(&events->free, struct v4l2_kevent, list);
>> +	kev->event.type = ev->type;
>> +	kev->event.u = ev->u;
>> +	kev->event.id = ev->id;
>> +	kev->event.timestamp = *ts;
>> +	kev->event.sequence = events->sequence;
>> +	list_move_tail(&kev->list, &events->available);
>> +
>> +	events->navailable++;
>> +
>> +	wake_up_all(&events->wait);
>> +}
>> +
>>  void v4l2_event_queue(struct video_device *vdev, const struct
>> v4l2_event *ev)
>>  {
>>  	struct v4l2_fh *fh;
>> @@ -182,37 +218,26 @@ void v4l2_event_queue(struct video_device *vdev,
>> const struct v4l2_event *ev)
>>  	spin_lock_irqsave(&vdev->fh_lock, flags);
>>
>>  	list_for_each_entry(fh, &vdev->fh_list, list) {
>> -		struct v4l2_events *events = fh->events;
>> -		struct v4l2_kevent *kev;
>> -
>> -		/* Are we subscribed? */
>> -		if (!v4l2_event_subscribed(fh, ev->type))
>> -			continue;
>> -
>> -		/* Increase event sequence number on fh. */
>> -		events->sequence++;
>> -
>> -		/* Do we have any free events? */
>> -		if (list_empty(&events->free))
>> -			continue;
>> -
>> -		/* Take one and fill it. */
>> -		kev = list_first_entry(&events->free, struct v4l2_kevent, list);
>> -		kev->event.type = ev->type;
>> -		kev->event.u = ev->u;
>> -		kev->event.timestamp = timestamp;
>> -		kev->event.sequence = events->sequence;
>> -		list_move_tail(&kev->list, &events->available);
>> -
>> -		events->navailable++;
>> -
>> -		wake_up_all(&events->wait);
>> +		__v4l2_event_queue_fh(fh, ev, &timestamp);
>>  	}
>>
>>  	spin_unlock_irqrestore(&vdev->fh_lock, flags);
>>  }
>>  EXPORT_SYMBOL_GPL(v4l2_event_queue);
>>
>> +void v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event
>> *ev)
>> +{
>> +	unsigned long flags;
>> +	struct timespec timestamp;
>> +
>> +	ktime_get_ts(&timestamp);
>> +
>> +	spin_lock_irqsave(&fh->vdev->fh_lock, flags);
>> +	__v4l2_event_queue_fh(fh, ev, &timestamp);
>> +	spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
>> +}
>> +EXPORT_SYMBOL_GPL(v4l2_event_queue_fh);
>> +
>>  int v4l2_event_pending(struct v4l2_fh *fh)
>>  {
>>  	return fh->events->navailable;
>> @@ -223,7 +248,9 @@ int v4l2_event_subscribe(struct v4l2_fh *fh,
>>  			 struct v4l2_event_subscription *sub)
>>  {
>>  	struct v4l2_events *events = fh->events;
>> -	struct v4l2_subscribed_event *sev;
>> +	struct v4l2_subscribed_event *sev, *found_ev;
>> +	struct v4l2_ctrl *ctrl = NULL;
>> +	struct v4l2_ctrl_fh *ctrl_fh = NULL;
>>  	unsigned long flags;
>>
>>  	if (fh->events == NULL) {
>> @@ -231,15 +258,31 @@ int v4l2_event_subscribe(struct v4l2_fh *fh,
>>  		return -ENOMEM;
>>  	}
>>
>> +	if (sub->type == V4L2_EVENT_CTRL_CH_VALUE) {
>> +		ctrl = v4l2_ctrl_find(fh->ctrl_handler, sub->id);
>> +		if (ctrl == NULL)
>> +			return -EINVAL;
>> +	}
>> +
>>  	sev = kmalloc(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);
>>
>> -	if (v4l2_event_subscribed(fh, sub->type) == NULL) {
>> +	found_ev = v4l2_event_subscribed(fh, sub->type, sub->id);
>> +	if (!found_ev) {
>>  		INIT_LIST_HEAD(&sev->list);
>>  		sev->type = sub->type;
>> +		sev->id = sub->id;
>>
>>  		list_add(&sev->list, &events->subscribed);
>>  		sev = NULL;
>> @@ -247,6 +290,10 @@ int v4l2_event_subscribe(struct v4l2_fh *fh,
>>
>>  	spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
>>
>> +	/* v4l2_ctrl_add_fh uses a mutex, so do this outside the spin lock */
>> +	if (!found_ev && ctrl)
>> +		v4l2_ctrl_add_fh(ctrl, ctrl_fh);
>> +
>>  	kfree(sev);
>>
>>  	return 0;
>> @@ -256,6 +303,7 @@ EXPORT_SYMBOL_GPL(v4l2_event_subscribe);
>>  static void v4l2_event_unsubscribe_all(struct v4l2_fh *fh)
>>  {
>>  	struct v4l2_events *events = fh->events;
>> +	struct v4l2_event_subscription sub;
>>  	struct v4l2_subscribed_event *sev;
>>  	unsigned long flags;
>>
>> @@ -265,11 +313,13 @@ static void v4l2_event_unsubscribe_all(struct
>> v4l2_fh *fh)
>>  		spin_lock_irqsave(&fh->vdev->fh_lock, flags);
>>  		if (!list_empty(&events->subscribed)) {
>>  			sev = list_first_entry(&events->subscribed,
>> -				       struct v4l2_subscribed_event, list);
>> -			list_del(&sev->list);
>> +					struct v4l2_subscribed_event, list);
>> +			sub.type = sev->type;
>> +			sub.id = sev->id;
>>  		}
>>  		spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
>> -		kfree(sev);
>> +		if (sev)
>> +			v4l2_event_unsubscribe(fh, &sub);
>>  	} while (sev);
>>  }
>>
>> @@ -286,11 +336,17 @@ int v4l2_event_unsubscribe(struct v4l2_fh *fh,
>>
>>  	spin_lock_irqsave(&fh->vdev->fh_lock, flags);
>>
>> -	sev = v4l2_event_subscribed(fh, sub->type);
>> +	sev = v4l2_event_subscribed(fh, sub->type, sub->id);
>>  	if (sev != NULL)
>>  		list_del(&sev->list);
>>
>>  	spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
>> +	if (sev->type == V4L2_EVENT_CTRL_CH_VALUE) {
>> +		struct v4l2_ctrl *ctrl = v4l2_ctrl_find(fh->ctrl_handler, sev->id);
>> +
>> +		if (ctrl)
>> +			v4l2_ctrl_del_fh(ctrl, fh);
>> +	}
>>
>>  	kfree(sev);
>>
>> diff --git a/drivers/media/video/v4l2-fh.c
>> b/drivers/media/video/v4l2-fh.c
>> index 8635011..c6aef84 100644
>> --- a/drivers/media/video/v4l2-fh.c
>> +++ b/drivers/media/video/v4l2-fh.c
>> @@ -93,10 +93,8 @@ void v4l2_fh_exit(struct v4l2_fh *fh)
>>  {
>>  	if (fh->vdev == NULL)
>>  		return;
>> -
>> -	fh->vdev = NULL;
>> -
>>  	v4l2_event_free(fh);
>> +	fh->vdev = NULL;
>
> This looks like a bugfix.

But it isn't :-)

v4l2_event_free didn't use fh->vdev in the past, but now it does so the
order had to be swapped.

>
>>  }
>>  EXPORT_SYMBOL_GPL(v4l2_fh_exit);
>>
>> diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h
>> index 92d2fdd..f7238c1 100644
>> --- a/include/linux/videodev2.h
>> +++ b/include/linux/videodev2.h
>> @@ -1787,6 +1787,7 @@ struct v4l2_streamparm {
>>  #define V4L2_EVENT_ALL				0
>>  #define V4L2_EVENT_VSYNC			1
>>  #define V4L2_EVENT_EOS				2
>> +#define V4L2_EVENT_CTRL_CH_VALUE		3
>>  #define V4L2_EVENT_PRIVATE_START		0x08000000
>>
>>  /* Payload for V4L2_EVENT_VSYNC */
>> @@ -1795,21 +1796,33 @@ struct v4l2_event_vsync {
>>  	__u8 field;
>>  } __attribute__ ((packed));
>>
>> +/* Payload for V4L2_EVENT_CTRL_CH_VALUE */
>> +struct v4l2_event_ctrl_ch_value {
>> +	__u32 type;
>
> type is enum v4l2_ctrl_type in struct v4l2_ctrl and struct v4l2_queryctrl.

Yes, but enum's are frowned upon these days in the public API.

>
>> +	union {
>> +		__s32 value;
>> +		__s64 value64;
>> +	};
>> +} __attribute__ ((packed));
>> +
>>  struct v4l2_event {
>>  	__u32				type;
>>  	union {
>>  		struct v4l2_event_vsync vsync;
>> +		struct v4l2_event_ctrl_ch_value ctrl_ch_value;
>>  		__u8			data[64];
>>  	} u;
>>  	__u32				pending;
>>  	__u32				sequence;
>>  	struct timespec			timestamp;
>> -	__u32				reserved[9];
>> +	__u32				id;
>
> id is valid only for control related events. Shouldn't it be part of the
> control related structures instead, or another union for control related
> event types? E.g.
>
> struct {
> 	enum v4l2_ctrl_type	id;
> 	union {
> 		struct v4l2_event_ctrl_ch_value ch_value;
> 	};
> } ctrl;

The problem with making this dependent of the type is that the core event
code would have to have a switch on the event type when it comes to
matching identifiers. By making it a core field it doesn't have to do
this. Also, this makes it available for use by private events as well.

It is important to keep the send-event core code as fast as possible since
it can be called from interrupt context.

So this is by design.

Regards,

       Hans


^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [RFCv1 PATCH 4/9] v4l2-ctrls: add per-control events.
  2011-04-11 14:57       ` Hans Verkuil
@ 2011-04-12  8:12         ` Sakari Ailus
  2011-04-12 13:40           ` Hans Verkuil
  0 siblings, 1 reply; 30+ messages in thread
From: Sakari Ailus @ 2011-04-12  8:12 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: Hans Verkuil, linux-media

Hi Hans,

Hans Verkuil wrote:
[clip]
>>> diff --git a/drivers/media/video/v4l2-fh.c
>>> b/drivers/media/video/v4l2-fh.c
>>> index 8635011..c6aef84 100644
>>> --- a/drivers/media/video/v4l2-fh.c
>>> +++ b/drivers/media/video/v4l2-fh.c
>>> @@ -93,10 +93,8 @@ void v4l2_fh_exit(struct v4l2_fh *fh)
>>>  {
>>>  	if (fh->vdev == NULL)
>>>  		return;
>>> -
>>> -	fh->vdev = NULL;
>>> -
>>>  	v4l2_event_free(fh);
>>> +	fh->vdev = NULL;
>>
>> This looks like a bugfix.
> 
> But it isn't :-)
> 
> v4l2_event_free didn't use fh->vdev in the past, but now it does so the
> order had to be swapped.

Ok.

>>
>>>  }
>>>  EXPORT_SYMBOL_GPL(v4l2_fh_exit);
>>>
>>> diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h
>>> index 92d2fdd..f7238c1 100644
>>> --- a/include/linux/videodev2.h
>>> +++ b/include/linux/videodev2.h
>>> @@ -1787,6 +1787,7 @@ struct v4l2_streamparm {
>>>  #define V4L2_EVENT_ALL				0
>>>  #define V4L2_EVENT_VSYNC			1
>>>  #define V4L2_EVENT_EOS				2
>>> +#define V4L2_EVENT_CTRL_CH_VALUE		3
>>>  #define V4L2_EVENT_PRIVATE_START		0x08000000
>>>
>>>  /* Payload for V4L2_EVENT_VSYNC */
>>> @@ -1795,21 +1796,33 @@ struct v4l2_event_vsync {
>>>  	__u8 field;
>>>  } __attribute__ ((packed));
>>>
>>> +/* Payload for V4L2_EVENT_CTRL_CH_VALUE */
>>> +struct v4l2_event_ctrl_ch_value {
>>> +	__u32 type;
>>
>> type is enum v4l2_ctrl_type in struct v4l2_ctrl and struct v4l2_queryctrl.
> 
> Yes, but enum's are frowned upon these days in the public API.

Agreed.

>>
>>> +	union {
>>> +		__s32 value;
>>> +		__s64 value64;
>>> +	};
>>> +} __attribute__ ((packed));
>>> +
>>>  struct v4l2_event {
>>>  	__u32				type;
>>>  	union {
>>>  		struct v4l2_event_vsync vsync;
>>> +		struct v4l2_event_ctrl_ch_value ctrl_ch_value;
>>>  		__u8			data[64];
>>>  	} u;
>>>  	__u32				pending;
>>>  	__u32				sequence;
>>>  	struct timespec			timestamp;
>>> -	__u32				reserved[9];
>>> +	__u32				id;
>>
>> id is valid only for control related events. Shouldn't it be part of the
>> control related structures instead, or another union for control related
>> event types? E.g.
>>
>> struct {
>> 	enum v4l2_ctrl_type	id;
>> 	union {
>> 		struct v4l2_event_ctrl_ch_value ch_value;
>> 	};
>> } ctrl;
> 
> The problem with making this dependent of the type is that the core event
> code would have to have a switch on the event type when it comes to
> matching identifiers. By making it a core field it doesn't have to do
> this. Also, this makes it available for use by private events as well.
> 
> It is important to keep the send-event core code as fast as possible since
> it can be called from interrupt context.
> 
> So this is by design.

I understand your point, and agree with it. There would be a few places
in the v4l2-event.c that one would have to know if the event is
control-related or not.

As the id field is handled in the v4l2-event.c the way it is, it must be
zero when the event is not a control-related one. This makes it
impossible to use it for other purposes, at least for now.

What about renaming the field to ctrl_id? Or do you think we could have
other use for the field outside the scope of the control-related events?

The benefit of the current name is still that it does not suggest
anything on the implementation.

Cheers,

-- 
Sakari Ailus
sakari.ailus@maxwell.research.nokia.com

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [RFCv1 PATCH 4/9] v4l2-ctrls: add per-control events.
  2011-04-12  8:12         ` Sakari Ailus
@ 2011-04-12 13:40           ` Hans Verkuil
  0 siblings, 0 replies; 30+ messages in thread
From: Hans Verkuil @ 2011-04-12 13:40 UTC (permalink / raw)
  To: Sakari Ailus; +Cc: Hans Verkuil, linux-media

> Hi Hans,
>
> Hans Verkuil wrote:
>>>
>>>> +	union {
>>>> +		__s32 value;
>>>> +		__s64 value64;
>>>> +	};
>>>> +} __attribute__ ((packed));
>>>> +
>>>>  struct v4l2_event {
>>>>  	__u32				type;
>>>>  	union {
>>>>  		struct v4l2_event_vsync vsync;
>>>> +		struct v4l2_event_ctrl_ch_value ctrl_ch_value;
>>>>  		__u8			data[64];
>>>>  	} u;
>>>>  	__u32				pending;
>>>>  	__u32				sequence;
>>>>  	struct timespec			timestamp;
>>>> -	__u32				reserved[9];
>>>> +	__u32				id;
>>>
>>> id is valid only for control related events. Shouldn't it be part of
>>> the
>>> control related structures instead, or another union for control
>>> related
>>> event types? E.g.
>>>
>>> struct {
>>> 	enum v4l2_ctrl_type	id;
>>> 	union {
>>> 		struct v4l2_event_ctrl_ch_value ch_value;
>>> 	};
>>> } ctrl;
>>
>> The problem with making this dependent of the type is that the core
>> event
>> code would have to have a switch on the event type when it comes to
>> matching identifiers. By making it a core field it doesn't have to do
>> this. Also, this makes it available for use by private events as well.
>>
>> It is important to keep the send-event core code as fast as possible
>> since
>> it can be called from interrupt context.
>>
>> So this is by design.
>
> I understand your point, and agree with it. There would be a few places
> in the v4l2-event.c that one would have to know if the event is
> control-related or not.
>
> As the id field is handled in the v4l2-event.c the way it is, it must be
> zero when the event is not a control-related one. This makes it
> impossible to use it for other purposes, at least for now.
>
> What about renaming the field to ctrl_id? Or do you think we could have
> other use for the field outside the scope of the control-related events?

I believe so, yes. For example per-input or per-output events (where id
refers to the input/output index) or per-pad events (where id refers to
the pad id). In general, the id field can be used as an object identifier.

If there are no 'objects' related to the event, then it has to be 0 as you
said.

Regards,

       Hans

>
> The benefit of the current name is still that it does not suggest
> anything on the implementation.
>
> Cheers,
>
> --
> Sakari Ailus
> sakari.ailus@maxwell.research.nokia.com
> --
> To unsubscribe from this list: send the line "unsubscribe linux-media" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>



^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [RFCv1 PATCH 4/9] v4l2-ctrls: add per-control events.
  2011-04-04 11:51   ` [RFCv1 PATCH 4/9] v4l2-ctrls: add per-control events Hans Verkuil
  2011-04-11  7:29     ` Sakari Ailus
@ 2011-04-15 10:51     ` Sakari Ailus
  2011-04-15 16:25       ` Hans Verkuil
  1 sibling, 1 reply; 30+ messages in thread
From: Sakari Ailus @ 2011-04-15 10:51 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: linux-media

Hi Hans,

I have some more comments below. :-)

Hans Verkuil wrote:
> Whenever a control changes value an event is sent to anyone that subscribed
> to it.
> 
> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> ---
>  drivers/media/video/v4l2-ctrls.c |   59 ++++++++++++++++++
>  drivers/media/video/v4l2-event.c |  126 +++++++++++++++++++++++++++-----------
>  drivers/media/video/v4l2-fh.c    |    4 +-
>  include/linux/videodev2.h        |   17 +++++-
>  include/media/v4l2-ctrls.h       |    9 +++
>  include/media/v4l2-event.h       |    2 +
>  6 files changed, 177 insertions(+), 40 deletions(-)
> 
> diff --git a/drivers/media/video/v4l2-ctrls.c b/drivers/media/video/v4l2-ctrls.c
> index f75a1d4..163f412 100644
> --- a/drivers/media/video/v4l2-ctrls.c
> +++ b/drivers/media/video/v4l2-ctrls.c
> @@ -23,6 +23,7 @@
>  #include <media/v4l2-ioctl.h>
>  #include <media/v4l2-device.h>
>  #include <media/v4l2-ctrls.h>
> +#include <media/v4l2-event.h>
>  #include <media/v4l2-dev.h>
>  
>  /* Internal temporary helper struct, one for each v4l2_ext_control */
> @@ -537,6 +538,16 @@ static bool type_is_int(const struct v4l2_ctrl *ctrl)
>  	}
>  }
>  
> +static void send_event(struct v4l2_ctrl *ctrl, struct v4l2_event *ev)
> +{
> +	struct v4l2_ctrl_fh *pos;
> +
> +	ev->id = ctrl->id;
> +	list_for_each_entry(pos, &ctrl->fhs, node) {
> +		v4l2_event_queue_fh(pos->fh, ev);
> +	}

Shouldn't we do v4l2_ctrl_lock(ctrl) here? Or does something prevent
changes to the file handle list while we loop over it?

v4l2_ctrl_lock() locks a mutex. Events often arrive from interrupt
context, which would mean the drivers would need to create a work queue
to tell about changes to control values.

> +}
> +
>  /* Helper function: copy the current control value back to the caller */
>  static int cur_to_user(struct v4l2_ext_control *c,
>  		       struct v4l2_ctrl *ctrl)
> @@ -626,20 +637,38 @@ static int new_to_user(struct v4l2_ext_control *c,
>  /* Copy the new value to the current value. */
>  static void new_to_cur(struct v4l2_ctrl *ctrl)
>  {
> +	struct v4l2_event ev;
> +	bool changed = false;
> +
>  	if (ctrl == NULL)
>  		return;
>  	switch (ctrl->type) {
> +	case V4L2_CTRL_TYPE_BUTTON:
> +		changed = true;
> +		ev.u.ctrl_ch_value.value = 0;
> +		break;
>  	case V4L2_CTRL_TYPE_STRING:
>  		/* strings are always 0-terminated */
> +		changed = strcmp(ctrl->string, ctrl->cur.string);
>  		strcpy(ctrl->cur.string, ctrl->string);
> +		ev.u.ctrl_ch_value.value64 = 0;
>  		break;
>  	case V4L2_CTRL_TYPE_INTEGER64:
> +		changed = ctrl->val64 != ctrl->cur.val64;
>  		ctrl->cur.val64 = ctrl->val64;
> +		ev.u.ctrl_ch_value.value64 = ctrl->val64;
>  		break;
>  	default:
> +		changed = ctrl->val != ctrl->cur.val;
>  		ctrl->cur.val = ctrl->val;
> +		ev.u.ctrl_ch_value.value = ctrl->val;
>  		break;
>  	}
> +	if (changed) {
> +		ev.type = V4L2_EVENT_CTRL_CH_VALUE;
> +		ev.u.ctrl_ch_value.type = ctrl->type;
> +		send_event(ctrl, &ev);
> +	}
>  }
>  
>  /* Copy the current value to the new value */
> @@ -784,6 +813,7 @@ void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl)
>  {
>  	struct v4l2_ctrl_ref *ref, *next_ref;
>  	struct v4l2_ctrl *ctrl, *next_ctrl;
> +	struct v4l2_ctrl_fh *ctrl_fh, *next_ctrl_fh;
>  
>  	if (hdl == NULL || hdl->buckets == NULL)
>  		return;
> @@ -797,6 +827,10 @@ void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler *hdl)
>  	/* 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);
> +		}

Wouldn't this also require holding v4l2_ctrl_lock(ctrl), since otherwise
the file handle list may change in the middle of doing this?

Then we'd hold two mutexes at once. I'd guess it's fine as long as we're
certain never do the locking of the two in a different order.

>  		kfree(ctrl);
>  	}
>  	kfree(hdl->buckets);
> @@ -1003,6 +1037,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl,
>  	}
>  
>  	INIT_LIST_HEAD(&ctrl->node);
> +	INIT_LIST_HEAD(&ctrl->fhs);
>  	ctrl->handler = hdl;
>  	ctrl->ops = ops;
>  	ctrl->id = id;
> @@ -1888,3 +1923,27 @@ int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val)
>  	return set_ctrl(ctrl, &val);
>  }
>  EXPORT_SYMBOL(v4l2_ctrl_s_ctrl);
> +
> +void v4l2_ctrl_add_fh(struct v4l2_ctrl *ctrl, struct v4l2_ctrl_fh *ctrl_fh)
> +{
> +	v4l2_ctrl_lock(ctrl);
> +	list_add_tail(&ctrl_fh->node, &ctrl->fhs);
> +	v4l2_ctrl_unlock(ctrl);
> +}
> +EXPORT_SYMBOL(v4l2_ctrl_add_fh);
> +
> +void v4l2_ctrl_del_fh(struct v4l2_ctrl *ctrl, struct v4l2_fh *fh)
> +{
> +	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;
> +		}
> +	}
> +	v4l2_ctrl_unlock(ctrl);
> +}
> +EXPORT_SYMBOL(v4l2_ctrl_del_fh);
> diff --git a/drivers/media/video/v4l2-event.c b/drivers/media/video/v4l2-event.c
> index 69fd343..c9251a5 100644
> --- a/drivers/media/video/v4l2-event.c
> +++ b/drivers/media/video/v4l2-event.c
> @@ -25,10 +25,13 @@
>  #include <media/v4l2-dev.h>
>  #include <media/v4l2-fh.h>
>  #include <media/v4l2-event.h>
> +#include <media/v4l2-ctrls.h>
>  
>  #include <linux/sched.h>
>  #include <linux/slab.h>
>  
> +static void v4l2_event_unsubscribe_all(struct v4l2_fh *fh);
> +
>  int v4l2_event_init(struct v4l2_fh *fh)
>  {
>  	fh->events = kzalloc(sizeof(*fh->events), GFP_KERNEL);
> @@ -91,7 +94,7 @@ void v4l2_event_free(struct v4l2_fh *fh)
>  
>  	list_kfree(&events->free, struct v4l2_kevent, list);
>  	list_kfree(&events->available, struct v4l2_kevent, list);
> -	list_kfree(&events->subscribed, struct v4l2_subscribed_event, list);
> +	v4l2_event_unsubscribe_all(fh);
>  
>  	kfree(events);
>  	fh->events = NULL;
> @@ -154,9 +157,9 @@ int v4l2_event_dequeue(struct v4l2_fh *fh, struct v4l2_event *event,
>  }
>  EXPORT_SYMBOL_GPL(v4l2_event_dequeue);
>  
> -/* Caller must hold fh->event->lock! */
> +/* Caller must hold fh->vdev->fh_lock! */
>  static struct v4l2_subscribed_event *v4l2_event_subscribed(
> -	struct v4l2_fh *fh, u32 type)
> +		struct v4l2_fh *fh, u32 type, u32 id)
>  {
>  	struct v4l2_events *events = fh->events;
>  	struct v4l2_subscribed_event *sev;
> @@ -164,13 +167,46 @@ static struct v4l2_subscribed_event *v4l2_event_subscribed(
>  	assert_spin_locked(&fh->vdev->fh_lock);
>  
>  	list_for_each_entry(sev, &events->subscribed, list) {
> -		if (sev->type == type)
> +		if (sev->type == type && sev->id == id)
>  			return sev;
>  	}
>  
>  	return NULL;
>  }
>  
> +static void __v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *ev,
> +		const struct timespec *ts)
> +{
> +	struct v4l2_events *events = fh->events;
> +	struct v4l2_subscribed_event *sev;
> +	struct v4l2_kevent *kev;
> +
> +	/* Are we subscribed? */
> +	sev = v4l2_event_subscribed(fh, ev->type, ev->id);
> +	if (sev == NULL)
> +		return;
> +
> +	/* Increase event sequence number on fh. */
> +	events->sequence++;
> +
> +	/* Do we have any free events? */
> +	if (list_empty(&events->free))
> +		return;
> +
> +	/* Take one and fill it. */
> +	kev = list_first_entry(&events->free, struct v4l2_kevent, list);
> +	kev->event.type = ev->type;
> +	kev->event.u = ev->u;
> +	kev->event.id = ev->id;
> +	kev->event.timestamp = *ts;
> +	kev->event.sequence = events->sequence;
> +	list_move_tail(&kev->list, &events->available);
> +
> +	events->navailable++;
> +
> +	wake_up_all(&events->wait);
> +}
> +
>  void v4l2_event_queue(struct video_device *vdev, const struct v4l2_event *ev)
>  {
>  	struct v4l2_fh *fh;
> @@ -182,37 +218,26 @@ void v4l2_event_queue(struct video_device *vdev, const struct v4l2_event *ev)
>  	spin_lock_irqsave(&vdev->fh_lock, flags);
>  
>  	list_for_each_entry(fh, &vdev->fh_list, list) {
> -		struct v4l2_events *events = fh->events;
> -		struct v4l2_kevent *kev;
> -
> -		/* Are we subscribed? */
> -		if (!v4l2_event_subscribed(fh, ev->type))
> -			continue;
> -
> -		/* Increase event sequence number on fh. */
> -		events->sequence++;
> -
> -		/* Do we have any free events? */
> -		if (list_empty(&events->free))
> -			continue;
> -
> -		/* Take one and fill it. */
> -		kev = list_first_entry(&events->free, struct v4l2_kevent, list);
> -		kev->event.type = ev->type;
> -		kev->event.u = ev->u;
> -		kev->event.timestamp = timestamp;
> -		kev->event.sequence = events->sequence;
> -		list_move_tail(&kev->list, &events->available);
> -
> -		events->navailable++;
> -
> -		wake_up_all(&events->wait);
> +		__v4l2_event_queue_fh(fh, ev, &timestamp);
>  	}
>  
>  	spin_unlock_irqrestore(&vdev->fh_lock, flags);
>  }
>  EXPORT_SYMBOL_GPL(v4l2_event_queue);
>  
> +void v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event *ev)
> +{
> +	unsigned long flags;
> +	struct timespec timestamp;
> +
> +	ktime_get_ts(&timestamp);
> +
> +	spin_lock_irqsave(&fh->vdev->fh_lock, flags);
> +	__v4l2_event_queue_fh(fh, ev, &timestamp);
> +	spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
> +}
> +EXPORT_SYMBOL_GPL(v4l2_event_queue_fh);
> +
>  int v4l2_event_pending(struct v4l2_fh *fh)
>  {
>  	return fh->events->navailable;
> @@ -223,7 +248,9 @@ int v4l2_event_subscribe(struct v4l2_fh *fh,
>  			 struct v4l2_event_subscription *sub)
>  {
>  	struct v4l2_events *events = fh->events;
> -	struct v4l2_subscribed_event *sev;
> +	struct v4l2_subscribed_event *sev, *found_ev;
> +	struct v4l2_ctrl *ctrl = NULL;
> +	struct v4l2_ctrl_fh *ctrl_fh = NULL;
>  	unsigned long flags;
>  
>  	if (fh->events == NULL) {
> @@ -231,15 +258,31 @@ int v4l2_event_subscribe(struct v4l2_fh *fh,
>  		return -ENOMEM;
>  	}
>  
> +	if (sub->type == V4L2_EVENT_CTRL_CH_VALUE) {
> +		ctrl = v4l2_ctrl_find(fh->ctrl_handler, sub->id);
> +		if (ctrl == NULL)
> +			return -EINVAL;
> +	}
> +
>  	sev = kmalloc(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);
>  
> -	if (v4l2_event_subscribed(fh, sub->type) == NULL) {
> +	found_ev = v4l2_event_subscribed(fh, sub->type, sub->id);
> +	if (!found_ev) {
>  		INIT_LIST_HEAD(&sev->list);
>  		sev->type = sub->type;
> +		sev->id = sub->id;
>  
>  		list_add(&sev->list, &events->subscribed);
>  		sev = NULL;
> @@ -247,6 +290,10 @@ int v4l2_event_subscribe(struct v4l2_fh *fh,
>  
>  	spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
>  
> +	/* v4l2_ctrl_add_fh uses a mutex, so do this outside the spin lock */
> +	if (!found_ev && ctrl)
> +		v4l2_ctrl_add_fh(ctrl, ctrl_fh);

Doesn't this allow adding two events for the same control id, if the
v4l2_event_subscribed() call is performed when another is checking for
the same control id before it has been added to the list in ctrl by
v4l2_ctrl_add_fh()?

...

Regards,

-- 
Sakari Ailus
sakari.ailus@maxwell.research.nokia.com

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [RFCv1 PATCH 4/9] v4l2-ctrls: add per-control events.
  2011-04-15 10:51     ` Sakari Ailus
@ 2011-04-15 16:25       ` Hans Verkuil
  2011-04-16  8:51         ` Sakari Ailus
  0 siblings, 1 reply; 30+ messages in thread
From: Hans Verkuil @ 2011-04-15 16:25 UTC (permalink / raw)
  To: Sakari Ailus; +Cc: Hans Verkuil, linux-media

> Hi Hans,
>
> I have some more comments below. :-)
>
> Hans Verkuil wrote:
>> Whenever a control changes value an event is sent to anyone that
>> subscribed
>> to it.
>>
>> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
>> ---
>>  drivers/media/video/v4l2-ctrls.c |   59 ++++++++++++++++++
>>  drivers/media/video/v4l2-event.c |  126
>> +++++++++++++++++++++++++++-----------
>>  drivers/media/video/v4l2-fh.c    |    4 +-
>>  include/linux/videodev2.h        |   17 +++++-
>>  include/media/v4l2-ctrls.h       |    9 +++
>>  include/media/v4l2-event.h       |    2 +
>>  6 files changed, 177 insertions(+), 40 deletions(-)
>>
>> diff --git a/drivers/media/video/v4l2-ctrls.c
>> b/drivers/media/video/v4l2-ctrls.c
>> index f75a1d4..163f412 100644
>> --- a/drivers/media/video/v4l2-ctrls.c
>> +++ b/drivers/media/video/v4l2-ctrls.c
>> @@ -23,6 +23,7 @@
>>  #include <media/v4l2-ioctl.h>
>>  #include <media/v4l2-device.h>
>>  #include <media/v4l2-ctrls.h>
>> +#include <media/v4l2-event.h>
>>  #include <media/v4l2-dev.h>
>>
>>  /* Internal temporary helper struct, one for each v4l2_ext_control */
>> @@ -537,6 +538,16 @@ static bool type_is_int(const struct v4l2_ctrl
>> *ctrl)
>>  	}
>>  }
>>
>> +static void send_event(struct v4l2_ctrl *ctrl, struct v4l2_event *ev)
>> +{
>> +	struct v4l2_ctrl_fh *pos;
>> +
>> +	ev->id = ctrl->id;
>> +	list_for_each_entry(pos, &ctrl->fhs, node) {
>> +		v4l2_event_queue_fh(pos->fh, ev);
>> +	}
>
> Shouldn't we do v4l2_ctrl_lock(ctrl) here? Or does something prevent
> changes to the file handle list while we loop over it?

This function is always called with the lock taken.

> v4l2_ctrl_lock() locks a mutex. Events often arrive from interrupt
> context, which would mean the drivers would need to create a work queue
> to tell about changes to control values.

I will have to check whether it is possible to make a function that can be
called from interrupt context. I have my doubts though whether it is 1)
possible and 2) desirable. At least in the area of HDMI
receivers/transmitters you will want to have a workqueue anyway.

>
>> +}
>> +
>>  /* Helper function: copy the current control value back to the caller
>> */
>>  static int cur_to_user(struct v4l2_ext_control *c,
>>  		       struct v4l2_ctrl *ctrl)
>> @@ -626,20 +637,38 @@ static int new_to_user(struct v4l2_ext_control *c,
>>  /* Copy the new value to the current value. */
>>  static void new_to_cur(struct v4l2_ctrl *ctrl)
>>  {
>> +	struct v4l2_event ev;
>> +	bool changed = false;
>> +
>>  	if (ctrl == NULL)
>>  		return;
>>  	switch (ctrl->type) {
>> +	case V4L2_CTRL_TYPE_BUTTON:
>> +		changed = true;
>> +		ev.u.ctrl_ch_value.value = 0;
>> +		break;
>>  	case V4L2_CTRL_TYPE_STRING:
>>  		/* strings are always 0-terminated */
>> +		changed = strcmp(ctrl->string, ctrl->cur.string);
>>  		strcpy(ctrl->cur.string, ctrl->string);
>> +		ev.u.ctrl_ch_value.value64 = 0;
>>  		break;
>>  	case V4L2_CTRL_TYPE_INTEGER64:
>> +		changed = ctrl->val64 != ctrl->cur.val64;
>>  		ctrl->cur.val64 = ctrl->val64;
>> +		ev.u.ctrl_ch_value.value64 = ctrl->val64;
>>  		break;
>>  	default:
>> +		changed = ctrl->val != ctrl->cur.val;
>>  		ctrl->cur.val = ctrl->val;
>> +		ev.u.ctrl_ch_value.value = ctrl->val;
>>  		break;
>>  	}
>> +	if (changed) {
>> +		ev.type = V4L2_EVENT_CTRL_CH_VALUE;
>> +		ev.u.ctrl_ch_value.type = ctrl->type;
>> +		send_event(ctrl, &ev);
>> +	}
>>  }
>>
>>  /* Copy the current value to the new value */
>> @@ -784,6 +813,7 @@ void v4l2_ctrl_handler_free(struct v4l2_ctrl_handler
>> *hdl)
>>  {
>>  	struct v4l2_ctrl_ref *ref, *next_ref;
>>  	struct v4l2_ctrl *ctrl, *next_ctrl;
>> +	struct v4l2_ctrl_fh *ctrl_fh, *next_ctrl_fh;
>>
>>  	if (hdl == NULL || hdl->buckets == NULL)
>>  		return;
>> @@ -797,6 +827,10 @@ void v4l2_ctrl_handler_free(struct
>> v4l2_ctrl_handler *hdl)
>>  	/* 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);
>> +		}
>
> Wouldn't this also require holding v4l2_ctrl_lock(ctrl), since otherwise
> the file handle list may change in the middle of doing this?
>
> Then we'd hold two mutexes at once. I'd guess it's fine as long as we're
> certain never do the locking of the two in a different order.

The lock is already taken. Note that the locking is done at the control
handler level. So there is no per-control lock. v4l2_ctrl_lock will
actually lock ctrl->handler->lock.

>
>>  		kfree(ctrl);
>>  	}
>>  	kfree(hdl->buckets);
>> @@ -1003,6 +1037,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct
>> v4l2_ctrl_handler *hdl,
>>  	}
>>
>>  	INIT_LIST_HEAD(&ctrl->node);
>> +	INIT_LIST_HEAD(&ctrl->fhs);
>>  	ctrl->handler = hdl;
>>  	ctrl->ops = ops;
>>  	ctrl->id = id;
>> @@ -1888,3 +1923,27 @@ int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32
>> val)
>>  	return set_ctrl(ctrl, &val);
>>  }
>>  EXPORT_SYMBOL(v4l2_ctrl_s_ctrl);
>> +
>> +void v4l2_ctrl_add_fh(struct v4l2_ctrl *ctrl, struct v4l2_ctrl_fh
>> *ctrl_fh)
>> +{
>> +	v4l2_ctrl_lock(ctrl);
>> +	list_add_tail(&ctrl_fh->node, &ctrl->fhs);
>> +	v4l2_ctrl_unlock(ctrl);
>> +}
>> +EXPORT_SYMBOL(v4l2_ctrl_add_fh);
>> +
>> +void v4l2_ctrl_del_fh(struct v4l2_ctrl *ctrl, struct v4l2_fh *fh)
>> +{
>> +	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;
>> +		}
>> +	}
>> +	v4l2_ctrl_unlock(ctrl);
>> +}
>> +EXPORT_SYMBOL(v4l2_ctrl_del_fh);
>> diff --git a/drivers/media/video/v4l2-event.c
>> b/drivers/media/video/v4l2-event.c
>> index 69fd343..c9251a5 100644
>> --- a/drivers/media/video/v4l2-event.c
>> +++ b/drivers/media/video/v4l2-event.c
>> @@ -25,10 +25,13 @@
>>  #include <media/v4l2-dev.h>
>>  #include <media/v4l2-fh.h>
>>  #include <media/v4l2-event.h>
>> +#include <media/v4l2-ctrls.h>
>>
>>  #include <linux/sched.h>
>>  #include <linux/slab.h>
>>
>> +static void v4l2_event_unsubscribe_all(struct v4l2_fh *fh);
>> +
>>  int v4l2_event_init(struct v4l2_fh *fh)
>>  {
>>  	fh->events = kzalloc(sizeof(*fh->events), GFP_KERNEL);
>> @@ -91,7 +94,7 @@ void v4l2_event_free(struct v4l2_fh *fh)
>>
>>  	list_kfree(&events->free, struct v4l2_kevent, list);
>>  	list_kfree(&events->available, struct v4l2_kevent, list);
>> -	list_kfree(&events->subscribed, struct v4l2_subscribed_event, list);
>> +	v4l2_event_unsubscribe_all(fh);
>>
>>  	kfree(events);
>>  	fh->events = NULL;
>> @@ -154,9 +157,9 @@ int v4l2_event_dequeue(struct v4l2_fh *fh, struct
>> v4l2_event *event,
>>  }
>>  EXPORT_SYMBOL_GPL(v4l2_event_dequeue);
>>
>> -/* Caller must hold fh->event->lock! */
>> +/* Caller must hold fh->vdev->fh_lock! */
>>  static struct v4l2_subscribed_event *v4l2_event_subscribed(
>> -	struct v4l2_fh *fh, u32 type)
>> +		struct v4l2_fh *fh, u32 type, u32 id)
>>  {
>>  	struct v4l2_events *events = fh->events;
>>  	struct v4l2_subscribed_event *sev;
>> @@ -164,13 +167,46 @@ static struct v4l2_subscribed_event
>> *v4l2_event_subscribed(
>>  	assert_spin_locked(&fh->vdev->fh_lock);
>>
>>  	list_for_each_entry(sev, &events->subscribed, list) {
>> -		if (sev->type == type)
>> +		if (sev->type == type && sev->id == id)
>>  			return sev;
>>  	}
>>
>>  	return NULL;
>>  }
>>
>> +static void __v4l2_event_queue_fh(struct v4l2_fh *fh, const struct
>> v4l2_event *ev,
>> +		const struct timespec *ts)
>> +{
>> +	struct v4l2_events *events = fh->events;
>> +	struct v4l2_subscribed_event *sev;
>> +	struct v4l2_kevent *kev;
>> +
>> +	/* Are we subscribed? */
>> +	sev = v4l2_event_subscribed(fh, ev->type, ev->id);
>> +	if (sev == NULL)
>> +		return;
>> +
>> +	/* Increase event sequence number on fh. */
>> +	events->sequence++;
>> +
>> +	/* Do we have any free events? */
>> +	if (list_empty(&events->free))
>> +		return;
>> +
>> +	/* Take one and fill it. */
>> +	kev = list_first_entry(&events->free, struct v4l2_kevent, list);
>> +	kev->event.type = ev->type;
>> +	kev->event.u = ev->u;
>> +	kev->event.id = ev->id;
>> +	kev->event.timestamp = *ts;
>> +	kev->event.sequence = events->sequence;
>> +	list_move_tail(&kev->list, &events->available);
>> +
>> +	events->navailable++;
>> +
>> +	wake_up_all(&events->wait);
>> +}
>> +
>>  void v4l2_event_queue(struct video_device *vdev, const struct
>> v4l2_event *ev)
>>  {
>>  	struct v4l2_fh *fh;
>> @@ -182,37 +218,26 @@ void v4l2_event_queue(struct video_device *vdev,
>> const struct v4l2_event *ev)
>>  	spin_lock_irqsave(&vdev->fh_lock, flags);
>>
>>  	list_for_each_entry(fh, &vdev->fh_list, list) {
>> -		struct v4l2_events *events = fh->events;
>> -		struct v4l2_kevent *kev;
>> -
>> -		/* Are we subscribed? */
>> -		if (!v4l2_event_subscribed(fh, ev->type))
>> -			continue;
>> -
>> -		/* Increase event sequence number on fh. */
>> -		events->sequence++;
>> -
>> -		/* Do we have any free events? */
>> -		if (list_empty(&events->free))
>> -			continue;
>> -
>> -		/* Take one and fill it. */
>> -		kev = list_first_entry(&events->free, struct v4l2_kevent, list);
>> -		kev->event.type = ev->type;
>> -		kev->event.u = ev->u;
>> -		kev->event.timestamp = timestamp;
>> -		kev->event.sequence = events->sequence;
>> -		list_move_tail(&kev->list, &events->available);
>> -
>> -		events->navailable++;
>> -
>> -		wake_up_all(&events->wait);
>> +		__v4l2_event_queue_fh(fh, ev, &timestamp);
>>  	}
>>
>>  	spin_unlock_irqrestore(&vdev->fh_lock, flags);
>>  }
>>  EXPORT_SYMBOL_GPL(v4l2_event_queue);
>>
>> +void v4l2_event_queue_fh(struct v4l2_fh *fh, const struct v4l2_event
>> *ev)
>> +{
>> +	unsigned long flags;
>> +	struct timespec timestamp;
>> +
>> +	ktime_get_ts(&timestamp);
>> +
>> +	spin_lock_irqsave(&fh->vdev->fh_lock, flags);
>> +	__v4l2_event_queue_fh(fh, ev, &timestamp);
>> +	spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
>> +}
>> +EXPORT_SYMBOL_GPL(v4l2_event_queue_fh);
>> +
>>  int v4l2_event_pending(struct v4l2_fh *fh)
>>  {
>>  	return fh->events->navailable;
>> @@ -223,7 +248,9 @@ int v4l2_event_subscribe(struct v4l2_fh *fh,
>>  			 struct v4l2_event_subscription *sub)
>>  {
>>  	struct v4l2_events *events = fh->events;
>> -	struct v4l2_subscribed_event *sev;
>> +	struct v4l2_subscribed_event *sev, *found_ev;
>> +	struct v4l2_ctrl *ctrl = NULL;
>> +	struct v4l2_ctrl_fh *ctrl_fh = NULL;
>>  	unsigned long flags;
>>
>>  	if (fh->events == NULL) {
>> @@ -231,15 +258,31 @@ int v4l2_event_subscribe(struct v4l2_fh *fh,
>>  		return -ENOMEM;
>>  	}
>>
>> +	if (sub->type == V4L2_EVENT_CTRL_CH_VALUE) {
>> +		ctrl = v4l2_ctrl_find(fh->ctrl_handler, sub->id);
>> +		if (ctrl == NULL)
>> +			return -EINVAL;
>> +	}
>> +
>>  	sev = kmalloc(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);
>>
>> -	if (v4l2_event_subscribed(fh, sub->type) == NULL) {
>> +	found_ev = v4l2_event_subscribed(fh, sub->type, sub->id);
>> +	if (!found_ev) {
>>  		INIT_LIST_HEAD(&sev->list);
>>  		sev->type = sub->type;
>> +		sev->id = sub->id;
>>
>>  		list_add(&sev->list, &events->subscribed);
>>  		sev = NULL;
>> @@ -247,6 +290,10 @@ int v4l2_event_subscribe(struct v4l2_fh *fh,
>>
>>  	spin_unlock_irqrestore(&fh->vdev->fh_lock, flags);
>>
>> +	/* v4l2_ctrl_add_fh uses a mutex, so do this outside the spin lock */
>> +	if (!found_ev && ctrl)
>> +		v4l2_ctrl_add_fh(ctrl, ctrl_fh);
>
> Doesn't this allow adding two events for the same control id, if the
> v4l2_event_subscribed() call is performed when another is checking for
> the same control id before it has been added to the list in ctrl by
> v4l2_ctrl_add_fh()?

No, because only one of the two will get a found_ev == NULL result. The
check whether the event is already subscribed and adding the new event is
done with the spinlock held, so that is atomic.

Regards,

        Hans

>
> ...
>
> Regards,
>
> --
> Sakari Ailus
> sakari.ailus@maxwell.research.nokia.com
> --
> To unsubscribe from this list: send the line "unsubscribe linux-media" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>



^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [RFCv1 PATCH 4/9] v4l2-ctrls: add per-control events.
  2011-04-15 16:25       ` Hans Verkuil
@ 2011-04-16  8:51         ` Sakari Ailus
  2011-04-19 12:19           ` Laurent Pinchart
  0 siblings, 1 reply; 30+ messages in thread
From: Sakari Ailus @ 2011-04-16  8:51 UTC (permalink / raw)
  To: Hans Verkuil; +Cc: Hans Verkuil, linux-media

Hi Hans,

Thanks for the reply.

Hans Verkuil wrote:
>> Hi Hans,
>>
>> I have some more comments below. :-)
>>
>> Hans Verkuil wrote:
>>> Whenever a control changes value an event is sent to anyone that
>>> subscribed
>>> to it.
>>>
>>> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
>>> ---
>>>  drivers/media/video/v4l2-ctrls.c |   59 ++++++++++++++++++
>>>  drivers/media/video/v4l2-event.c |  126
>>> +++++++++++++++++++++++++++-----------
>>>  drivers/media/video/v4l2-fh.c    |    4 +-
>>>  include/linux/videodev2.h        |   17 +++++-
>>>  include/media/v4l2-ctrls.h       |    9 +++
>>>  include/media/v4l2-event.h       |    2 +
>>>  6 files changed, 177 insertions(+), 40 deletions(-)
>>>
>>> diff --git a/drivers/media/video/v4l2-ctrls.c
>>> b/drivers/media/video/v4l2-ctrls.c
>>> index f75a1d4..163f412 100644
>>> --- a/drivers/media/video/v4l2-ctrls.c
>>> +++ b/drivers/media/video/v4l2-ctrls.c
>>> @@ -23,6 +23,7 @@
>>>  #include <media/v4l2-ioctl.h>
>>>  #include <media/v4l2-device.h>
>>>  #include <media/v4l2-ctrls.h>
>>> +#include <media/v4l2-event.h>
>>>  #include <media/v4l2-dev.h>
>>>
>>>  /* Internal temporary helper struct, one for each v4l2_ext_control */
>>> @@ -537,6 +538,16 @@ static bool type_is_int(const struct v4l2_ctrl
>>> *ctrl)
>>>  	}
>>>  }
>>>
>>> +static void send_event(struct v4l2_ctrl *ctrl, struct v4l2_event *ev)
>>> +{
>>> +	struct v4l2_ctrl_fh *pos;
>>> +
>>> +	ev->id = ctrl->id;
>>> +	list_for_each_entry(pos, &ctrl->fhs, node) {
>>> +		v4l2_event_queue_fh(pos->fh, ev);
>>> +	}
>>
>> Shouldn't we do v4l2_ctrl_lock(ctrl) here? Or does something prevent
>> changes to the file handle list while we loop over it?
> 
> This function is always called with the lock taken.

Yes, you're right.

>> v4l2_ctrl_lock() locks a mutex. Events often arrive from interrupt
>> context, which would mean the drivers would need to create a work queue
>> to tell about changes to control values.
> 
> I will have to check whether it is possible to make a function that can be
> called from interrupt context. I have my doubts though whether it is 1)
> possible and 2) desirable. At least in the area of HDMI
> receivers/transmitters you will want to have a workqueue anyway.

I wonder if there could be a more generic mechanism than to implement
this in a driver itself. In some cases it may also be harmful that
events are lost, and if there's just a single event for the workqueue,
it happens too easily in my opinion.

What do you think; could/should there be a queue for control events that
arrive from interrupt context, or should that be implemented in the
drivers themselves?

Another issue with this is that workqueues require to be scheduled so
sending the event to user space gets delayed by that. One of the
important aspects of events is latency and it would be nice to be able
to minimise that --- that's one reason why events use a spinlock rather
than a mutex, the other being that they can be easily sent from
interrupt context where they mostly arrive from.

It would be nice to have the same properties for control events.

There are use cases where a user space control process would run on a
real time priority to avoid scheduling latencies caused by other
processes, and such control process receiving control events would be
affected by the low priority of the work queues.

I agree with all your responses below on locking.

Thanks.

Regards,

-- 
Sakari Ailus
sakari.ailus@maxwell.research.nokia.com

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [RFCv1 PATCH 4/9] v4l2-ctrls: add per-control events.
  2011-04-16  8:51         ` Sakari Ailus
@ 2011-04-19 12:19           ` Laurent Pinchart
  2011-04-21 11:41             ` Sakari Ailus
  0 siblings, 1 reply; 30+ messages in thread
From: Laurent Pinchart @ 2011-04-19 12:19 UTC (permalink / raw)
  To: Sakari Ailus; +Cc: Hans Verkuil, Hans Verkuil, linux-media

Hi Sakari,

On Saturday 16 April 2011 10:51:27 Sakari Ailus wrote:
> Hans Verkuil wrote:
> >> Hans Verkuil wrote:
> >>> Whenever a control changes value an event is sent to anyone that
> >>> subscribed
> >>> to it.
> >>> 
> >>> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> >>> ---
> >>> 
> >>>  drivers/media/video/v4l2-ctrls.c |   59 ++++++++++++++++++
> >>>  drivers/media/video/v4l2-event.c |  126
> >>> 
> >>> +++++++++++++++++++++++++++-----------
> >>> 
> >>>  drivers/media/video/v4l2-fh.c    |    4 +-
> >>>  include/linux/videodev2.h        |   17 +++++-
> >>>  include/media/v4l2-ctrls.h       |    9 +++
> >>>  include/media/v4l2-event.h       |    2 +
> >>>  6 files changed, 177 insertions(+), 40 deletions(-)
> >>> 
> >>> diff --git a/drivers/media/video/v4l2-ctrls.c
> >>> b/drivers/media/video/v4l2-ctrls.c
> >>> index f75a1d4..163f412 100644
> >>> --- a/drivers/media/video/v4l2-ctrls.c
> >>> +++ b/drivers/media/video/v4l2-ctrls.c
> >>> @@ -23,6 +23,7 @@
> >>> 
> >>>  #include <media/v4l2-ioctl.h>
> >>>  #include <media/v4l2-device.h>
> >>>  #include <media/v4l2-ctrls.h>
> >>> 
> >>> +#include <media/v4l2-event.h>
> >>> 
> >>>  #include <media/v4l2-dev.h>
> >>>  
> >>>  /* Internal temporary helper struct, one for each v4l2_ext_control */
> >>> 
> >>> @@ -537,6 +538,16 @@ static bool type_is_int(const struct v4l2_ctrl
> >>> *ctrl)
> >>> 
> >>>  	}
> >>>  
> >>>  }
> >>> 
> >>> +static void send_event(struct v4l2_ctrl *ctrl, struct v4l2_event *ev)
> >>> +{
> >>> +	struct v4l2_ctrl_fh *pos;
> >>> +
> >>> +	ev->id = ctrl->id;
> >>> +	list_for_each_entry(pos, &ctrl->fhs, node) {
> >>> +		v4l2_event_queue_fh(pos->fh, ev);
> >>> +	}
> >> 
> >> Shouldn't we do v4l2_ctrl_lock(ctrl) here? Or does something prevent
> >> changes to the file handle list while we loop over it?
> > 
> > This function is always called with the lock taken.
> 
> Yes, you're right.
> 
> >> v4l2_ctrl_lock() locks a mutex. Events often arrive from interrupt
> >> context, which would mean the drivers would need to create a work queue
> >> to tell about changes to control values.
> > 
> > I will have to check whether it is possible to make a function that can
> > be called from interrupt context. I have my doubts though whether it is
> > 1) possible and 2) desirable. At least in the area of HDMI
> > receivers/transmitters you will want to have a workqueue anyway.
> 
> I wonder if there could be a more generic mechanism than to implement
> this in a driver itself. In some cases it may also be harmful that
> events are lost, and if there's just a single event for the workqueue,
> it happens too easily in my opinion.
> 
> What do you think; could/should there be a queue for control events that
> arrive from interrupt context, or should that be implemented in the
> drivers themselves?

I expect most drivers to generate control events from interrupt context, so 
pushing the workqueue down to individual drivers is probably not a good idea.

On a somehow related note, applications will often want to be informed of 
control changes initiated by the device, but won't need to receive events for 
control changes initiated by the application itself. Is that something we 
support ?

> Another issue with this is that workqueues require to be scheduled so
> sending the event to user space gets delayed by that. One of the
> important aspects of events is latency and it would be nice to be able
> to minimise that --- that's one reason why events use a spinlock rather
> than a mutex, the other being that they can be easily sent from
> interrupt context where they mostly arrive from.
> 
> It would be nice to have the same properties for control events.
> 
> There are use cases where a user space control process would run on a
> real time priority to avoid scheduling latencies caused by other
> processes, and such control process receiving control events would be
> affected by the low priority of the work queues.

I would also prefer a solution implemented in the control framework that would 
be interrupt context-friendly, without requiring a work queue.

> I agree with all your responses below on locking.
> 
> Thanks.
> 
> Regards,

-- 
Regards,

Laurent Pinchart

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [RFCv1 PATCH 4/9] v4l2-ctrls: add per-control events.
  2011-04-19 12:19           ` Laurent Pinchart
@ 2011-04-21 11:41             ` Sakari Ailus
  2011-04-21 12:16               ` Hans Verkuil
  0 siblings, 1 reply; 30+ messages in thread
From: Sakari Ailus @ 2011-04-21 11:41 UTC (permalink / raw)
  To: Laurent Pinchart; +Cc: Hans Verkuil, Hans Verkuil, linux-media

Laurent Pinchart wrote:
> Hi Sakari,

Hi Laurent,

> On Saturday 16 April 2011 10:51:27 Sakari Ailus wrote:
>> Hans Verkuil wrote:
>>>> Hans Verkuil wrote:
>>>>> Whenever a control changes value an event is sent to anyone that
>>>>> subscribed
>>>>> to it.
>>>>>
>>>>> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
>>>>> ---
>>>>>
>>>>>  drivers/media/video/v4l2-ctrls.c |   59 ++++++++++++++++++
>>>>>  drivers/media/video/v4l2-event.c |  126
>>>>>
>>>>> +++++++++++++++++++++++++++-----------
>>>>>
>>>>>  drivers/media/video/v4l2-fh.c    |    4 +-
>>>>>  include/linux/videodev2.h        |   17 +++++-
>>>>>  include/media/v4l2-ctrls.h       |    9 +++
>>>>>  include/media/v4l2-event.h       |    2 +
>>>>>  6 files changed, 177 insertions(+), 40 deletions(-)
>>>>>
>>>>> diff --git a/drivers/media/video/v4l2-ctrls.c
>>>>> b/drivers/media/video/v4l2-ctrls.c
>>>>> index f75a1d4..163f412 100644
>>>>> --- a/drivers/media/video/v4l2-ctrls.c
>>>>> +++ b/drivers/media/video/v4l2-ctrls.c
>>>>> @@ -23,6 +23,7 @@
>>>>>
>>>>>  #include <media/v4l2-ioctl.h>
>>>>>  #include <media/v4l2-device.h>
>>>>>  #include <media/v4l2-ctrls.h>
>>>>>
>>>>> +#include <media/v4l2-event.h>
>>>>>
>>>>>  #include <media/v4l2-dev.h>
>>>>>  
>>>>>  /* Internal temporary helper struct, one for each v4l2_ext_control */
>>>>>
>>>>> @@ -537,6 +538,16 @@ static bool type_is_int(const struct v4l2_ctrl
>>>>> *ctrl)
>>>>>
>>>>>  	}
>>>>>  
>>>>>  }
>>>>>
>>>>> +static void send_event(struct v4l2_ctrl *ctrl, struct v4l2_event *ev)
>>>>> +{
>>>>> +	struct v4l2_ctrl_fh *pos;
>>>>> +
>>>>> +	ev->id = ctrl->id;
>>>>> +	list_for_each_entry(pos, &ctrl->fhs, node) {
>>>>> +		v4l2_event_queue_fh(pos->fh, ev);
>>>>> +	}
>>>>
>>>> Shouldn't we do v4l2_ctrl_lock(ctrl) here? Or does something prevent
>>>> changes to the file handle list while we loop over it?
>>>
>>> This function is always called with the lock taken.
>>
>> Yes, you're right.
>>
>>>> v4l2_ctrl_lock() locks a mutex. Events often arrive from interrupt
>>>> context, which would mean the drivers would need to create a work queue
>>>> to tell about changes to control values.
>>>
>>> I will have to check whether it is possible to make a function that can
>>> be called from interrupt context. I have my doubts though whether it is
>>> 1) possible and 2) desirable. At least in the area of HDMI
>>> receivers/transmitters you will want to have a workqueue anyway.
>>
>> I wonder if there could be a more generic mechanism than to implement
>> this in a driver itself. In some cases it may also be harmful that
>> events are lost, and if there's just a single event for the workqueue,
>> it happens too easily in my opinion.
>>
>> What do you think; could/should there be a queue for control events that
>> arrive from interrupt context, or should that be implemented in the
>> drivers themselves?
> 
> I expect most drivers to generate control events from interrupt context, so 
> pushing the workqueue down to individual drivers is probably not a good idea.
> 
> On a somehow related note, applications will often want to be informed of 
> control changes initiated by the device, but won't need to receive events for 
> control changes initiated by the application itself. Is that something we 
> support ?

I think this is a very good question.

For example, in the flash interface RFC the V4L2_CID_FLASH_STROBE would
be such a control. It is changed by both the user and the driver, and
the user might want to have an event on the changes made by the driver.

Besides the drivers, other applications may also change control values.

Do you think it would be okay just not to send the event of control
changes to the file handle where it originated?

>> Another issue with this is that workqueues require to be scheduled so
>> sending the event to user space gets delayed by that. One of the
>> important aspects of events is latency and it would be nice to be able
>> to minimise that --- that's one reason why events use a spinlock rather
>> than a mutex, the other being that they can be easily sent from
>> interrupt context where they mostly arrive from.
>>
>> It would be nice to have the same properties for control events.
>>
>> There are use cases where a user space control process would run on a
>> real time priority to avoid scheduling latencies caused by other
>> processes, and such control process receiving control events would be
>> affected by the low priority of the work queues.
> 
> I would also prefer a solution implemented in the control framework that would 
> be interrupt context-friendly, without requiring a work queue.

Actually control events are not an issue, the issue is in the control
framework itself. Changing control values from interrupt context isn't
supported either.

That could perhaps be a topic for another RFC / patchset? :-)

Regards,

-- 
Sakari Ailus
sakari.ailus@maxwell.research.nokia.com

^ permalink raw reply	[flat|nested] 30+ messages in thread

* Re: [RFCv1 PATCH 4/9] v4l2-ctrls: add per-control events.
  2011-04-21 11:41             ` Sakari Ailus
@ 2011-04-21 12:16               ` Hans Verkuil
  0 siblings, 0 replies; 30+ messages in thread
From: Hans Verkuil @ 2011-04-21 12:16 UTC (permalink / raw)
  To: Sakari Ailus; +Cc: Laurent Pinchart, Hans Verkuil, linux-media

On Thursday, April 21, 2011 13:41:01 Sakari Ailus wrote:
> Laurent Pinchart wrote:
> > Hi Sakari,
> 
> Hi Laurent,
> 
> > On Saturday 16 April 2011 10:51:27 Sakari Ailus wrote:
> >> Hans Verkuil wrote:
> >>>> Hans Verkuil wrote:
> >>>>> Whenever a control changes value an event is sent to anyone that
> >>>>> subscribed
> >>>>> to it.
> >>>>>
> >>>>> Signed-off-by: Hans Verkuil <hans.verkuil@cisco.com>
> >>>>> ---
> >>>>>
> >>>>>  drivers/media/video/v4l2-ctrls.c |   59 ++++++++++++++++++
> >>>>>  drivers/media/video/v4l2-event.c |  126
> >>>>>
> >>>>> +++++++++++++++++++++++++++-----------
> >>>>>
> >>>>>  drivers/media/video/v4l2-fh.c    |    4 +-
> >>>>>  include/linux/videodev2.h        |   17 +++++-
> >>>>>  include/media/v4l2-ctrls.h       |    9 +++
> >>>>>  include/media/v4l2-event.h       |    2 +
> >>>>>  6 files changed, 177 insertions(+), 40 deletions(-)
> >>>>>
> >>>>> diff --git a/drivers/media/video/v4l2-ctrls.c
> >>>>> b/drivers/media/video/v4l2-ctrls.c
> >>>>> index f75a1d4..163f412 100644
> >>>>> --- a/drivers/media/video/v4l2-ctrls.c
> >>>>> +++ b/drivers/media/video/v4l2-ctrls.c
> >>>>> @@ -23,6 +23,7 @@
> >>>>>
> >>>>>  #include <media/v4l2-ioctl.h>
> >>>>>  #include <media/v4l2-device.h>
> >>>>>  #include <media/v4l2-ctrls.h>
> >>>>>
> >>>>> +#include <media/v4l2-event.h>
> >>>>>
> >>>>>  #include <media/v4l2-dev.h>
> >>>>>  
> >>>>>  /* Internal temporary helper struct, one for each v4l2_ext_control */
> >>>>>
> >>>>> @@ -537,6 +538,16 @@ static bool type_is_int(const struct v4l2_ctrl
> >>>>> *ctrl)
> >>>>>
> >>>>>  	}
> >>>>>  
> >>>>>  }
> >>>>>
> >>>>> +static void send_event(struct v4l2_ctrl *ctrl, struct v4l2_event *ev)
> >>>>> +{
> >>>>> +	struct v4l2_ctrl_fh *pos;
> >>>>> +
> >>>>> +	ev->id = ctrl->id;
> >>>>> +	list_for_each_entry(pos, &ctrl->fhs, node) {
> >>>>> +		v4l2_event_queue_fh(pos->fh, ev);
> >>>>> +	}
> >>>>
> >>>> Shouldn't we do v4l2_ctrl_lock(ctrl) here? Or does something prevent
> >>>> changes to the file handle list while we loop over it?
> >>>
> >>> This function is always called with the lock taken.
> >>
> >> Yes, you're right.
> >>
> >>>> v4l2_ctrl_lock() locks a mutex. Events often arrive from interrupt
> >>>> context, which would mean the drivers would need to create a work queue
> >>>> to tell about changes to control values.
> >>>
> >>> I will have to check whether it is possible to make a function that can
> >>> be called from interrupt context. I have my doubts though whether it is
> >>> 1) possible and 2) desirable. At least in the area of HDMI
> >>> receivers/transmitters you will want to have a workqueue anyway.
> >>
> >> I wonder if there could be a more generic mechanism than to implement
> >> this in a driver itself. In some cases it may also be harmful that
> >> events are lost, and if there's just a single event for the workqueue,
> >> it happens too easily in my opinion.
> >>
> >> What do you think; could/should there be a queue for control events that
> >> arrive from interrupt context, or should that be implemented in the
> >> drivers themselves?
> > 
> > I expect most drivers to generate control events from interrupt context, so 
> > pushing the workqueue down to individual drivers is probably not a good idea.
> > 
> > On a somehow related note, applications will often want to be informed of 
> > control changes initiated by the device, but won't need to receive events for 
> > control changes initiated by the application itself. Is that something we 
> > support ?
> 
> I think this is a very good question.
> 
> For example, in the flash interface RFC the V4L2_CID_FLASH_STROBE would
> be such a control. It is changed by both the user and the driver, and
> the user might want to have an event on the changes made by the driver.
> 
> Besides the drivers, other applications may also change control values.
> 
> Do you think it would be okay just not to send the event of control
> changes to the file handle where it originated?

I have thought about this and it can be done. However, it would mean some
core changes since the originating fh needs to be passed into the control
framework. If we decide to do this, then I definitely want to postpone that
work until the first round of control fw patches is in.

> >> Another issue with this is that workqueues require to be scheduled so
> >> sending the event to user space gets delayed by that. One of the
> >> important aspects of events is latency and it would be nice to be able
> >> to minimise that --- that's one reason why events use a spinlock rather
> >> than a mutex, the other being that they can be easily sent from
> >> interrupt context where they mostly arrive from.
> >>
> >> It would be nice to have the same properties for control events.
> >>
> >> There are use cases where a user space control process would run on a
> >> real time priority to avoid scheduling latencies caused by other
> >> processes, and such control process receiving control events would be
> >> affected by the low priority of the work queues.
> > 
> > I would also prefer a solution implemented in the control framework that would 
> > be interrupt context-friendly, without requiring a work queue.
> 
> Actually control events are not an issue, the issue is in the control
> framework itself. Changing control values from interrupt context isn't
> supported either.

This can't be done in general, but I think it should be possible to create
'interrupt-safe' controls. Changing such controls should of course not involve
any i2c accesses. I want to look at this in the next several days.

Regards,

	Hans

^ permalink raw reply	[flat|nested] 30+ messages in thread

end of thread, other threads:[~2011-04-21 12:16 UTC | newest]

Thread overview: 30+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-04-04 11:51 [RFCv1 PATCH 0/9] Control Events Hans Verkuil
2011-04-04 11:51 ` [RFCv1 PATCH 1/9] v4l2-ctrls: add new bitmask control type Hans Verkuil
2011-04-04 11:51   ` [RFCv1 PATCH 2/9] vivi: add bitmask test control Hans Verkuil
2011-04-04 11:51   ` [RFCv1 PATCH 3/9] v4l2-ioctl: add ctrl_handler to v4l2_fh Hans Verkuil
2011-04-08 15:10     ` Laurent Pinchart
2011-04-08 15:39       ` Hans Verkuil
2011-04-11 14:54         ` Laurent Pinchart
2011-04-04 11:51   ` [RFCv1 PATCH 4/9] v4l2-ctrls: add per-control events Hans Verkuil
2011-04-11  7:29     ` Sakari Ailus
2011-04-11 14:57       ` Hans Verkuil
2011-04-12  8:12         ` Sakari Ailus
2011-04-12 13:40           ` Hans Verkuil
2011-04-15 10:51     ` Sakari Ailus
2011-04-15 16:25       ` Hans Verkuil
2011-04-16  8:51         ` Sakari Ailus
2011-04-19 12:19           ` Laurent Pinchart
2011-04-21 11:41             ` Sakari Ailus
2011-04-21 12:16               ` Hans Verkuil
2011-04-04 11:51   ` [RFCv1 PATCH 5/9] vb2_poll: don't start DMA, leave that to the first read() Hans Verkuil
2011-04-08  7:00     ` Marek Szyprowski
2011-04-08  8:07       ` Hans Verkuil
2011-04-08  8:13         ` Marek Szyprowski
2011-04-04 11:51   ` [RFCv1 PATCH 6/9] vivi: add support for control events Hans Verkuil
2011-04-08 15:19     ` Laurent Pinchart
2011-04-08 15:43       ` Hans Verkuil
2011-04-04 11:51   ` [RFCv1 PATCH 7/9] v4l2-ctrls: add new V4L2_EVENT_CTRL_CH_STATE event Hans Verkuil
2011-04-04 11:51   ` [RFCv1 PATCH 8/9] vivi: add support for CTRL_CH_STATE events Hans Verkuil
2011-04-08 15:13     ` Laurent Pinchart
2011-04-04 11:51   ` [RFCv1 PATCH 9/9] v4l2-ctrls: add new SEND_INITIAL flag to force an initial event Hans Verkuil
2011-04-08 15:08   ` [RFCv1 PATCH 1/9] v4l2-ctrls: add new bitmask control type Laurent Pinchart

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.