From a85fd20fb62e5720ea4831b132334abc618972dd Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Mon, 26 May 2014 10:49:22 -0300 Subject: [media] mem2mem: make queue lock in v4l2_m2m_poll interruptible This patch makes the queue lock taken in v4l2_m2m_poll interruptible. Signed-off-by: Philipp Zabel Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-mem2mem.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index 178ce96..97defed 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -568,8 +568,12 @@ unsigned int v4l2_m2m_poll(struct file *file, struct v4l2_m2m_ctx *m2m_ctx, if (m2m_ctx->m2m_dev->m2m_ops->lock) m2m_ctx->m2m_dev->m2m_ops->lock(m2m_ctx->priv); - else if (m2m_ctx->q_lock) - mutex_lock(m2m_ctx->q_lock); + else if (m2m_ctx->q_lock) { + if (mutex_lock_interruptible(m2m_ctx->q_lock)) { + rc |= POLLERR; + goto end; + } + } spin_lock_irqsave(&src_q->done_lock, flags); if (!list_empty(&src_q->done_list)) -- cgit v1.1 From 6bbd4fec0cd93823fe651fa3907bee2bc6c814f6 Mon Sep 17 00:00:00 2001 From: Philipp Zabel Date: Mon, 26 May 2014 11:17:32 -0300 Subject: [media] videobuf2-dma-contig: allow to vmap contiguous dma buffers This allows drivers to vmap contiguous dma buffers so they can inspect the buffer contents with the CPU. This will be needed for the CODA driver's JPEG handling. On CODA960, the header parsing has to be done on the CPU. The hardware modules can only process the entropy coded segment after all registers and tables are set up. Signed-off-by: Philipp Zabel Acked-by: Marek Szyprowski Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/videobuf2-dma-contig.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/videobuf2-dma-contig.c b/drivers/media/v4l2-core/videobuf2-dma-contig.c index 880be07..6b254b8 100644 --- a/drivers/media/v4l2-core/videobuf2-dma-contig.c +++ b/drivers/media/v4l2-core/videobuf2-dma-contig.c @@ -98,6 +98,9 @@ static void *vb2_dc_vaddr(void *buf_priv) { struct vb2_dc_buf *buf = buf_priv; + if (!buf->vaddr && buf->db_attach) + buf->vaddr = dma_buf_vmap(buf->db_attach->dmabuf); + return buf->vaddr; } @@ -735,6 +738,7 @@ static int vb2_dc_map_dmabuf(void *mem_priv) buf->dma_addr = sg_dma_address(sgt->sgl); buf->dma_sgt = sgt; + buf->vaddr = NULL; return 0; } @@ -754,6 +758,10 @@ static void vb2_dc_unmap_dmabuf(void *mem_priv) return; } + if (buf->vaddr) { + dma_buf_vunmap(buf->db_attach->dmabuf, buf->vaddr); + buf->vaddr = NULL; + } dma_buf_unmap_attachment(buf->db_attach, sgt, buf->dma_dir); buf->dma_addr = 0; -- cgit v1.1 From b7284bb0be49441805ae7db0fc419ab7ee882bff Mon Sep 17 00:00:00 2001 From: Ramakrishnan Muthukrishnan Date: Thu, 19 Jun 2014 14:22:57 -0300 Subject: [media] media: v4l2-core: remove the use of V4L2_FL_USE_FH_PRIO flag Since all the drivers that use `struct v4l2_fh' use the core priority checking instead of doing it themselves, this flag can be removed. This patch removes the usage of the flag from v4l2-core. Signed-off-by: Ramakrishnan Muthukrishnan Reviewed-by: Hans Verkuil Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-dev.c | 6 ++---- drivers/media/v4l2-core/v4l2-fh.c | 13 +++++++++---- drivers/media/v4l2-core/v4l2-ioctl.c | 9 +++------ 3 files changed, 14 insertions(+), 14 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c index 634d863..35698aa 100644 --- a/drivers/media/v4l2-core/v4l2-dev.c +++ b/drivers/media/v4l2-core/v4l2-dev.c @@ -563,11 +563,9 @@ static void determine_valid_ioctls(struct video_device *vdev) /* vfl_type and vfl_dir independent ioctls */ SET_VALID_IOCTL(ops, VIDIOC_QUERYCAP, vidioc_querycap); - if (ops->vidioc_g_priority || - test_bit(V4L2_FL_USE_FH_PRIO, &vdev->flags)) + if (ops->vidioc_g_priority) set_bit(_IOC_NR(VIDIOC_G_PRIORITY), valid_ioctls); - if (ops->vidioc_s_priority || - test_bit(V4L2_FL_USE_FH_PRIO, &vdev->flags)) + if (ops->vidioc_s_priority) set_bit(_IOC_NR(VIDIOC_S_PRIORITY), valid_ioctls); SET_VALID_IOCTL(ops, VIDIOC_STREAMON, vidioc_streamon); SET_VALID_IOCTL(ops, VIDIOC_STREAMOFF, vidioc_streamoff); diff --git a/drivers/media/v4l2-core/v4l2-fh.c b/drivers/media/v4l2-core/v4l2-fh.c index e57c002..c97067a 100644 --- a/drivers/media/v4l2-core/v4l2-fh.c +++ b/drivers/media/v4l2-core/v4l2-fh.c @@ -37,6 +37,13 @@ void v4l2_fh_init(struct v4l2_fh *fh, struct video_device *vdev) fh->ctrl_handler = vdev->ctrl_handler; INIT_LIST_HEAD(&fh->list); set_bit(V4L2_FL_USES_V4L2_FH, &fh->vdev->flags); + /* + * determine_valid_ioctls() does not know if struct v4l2_fh + * is used by this driver, but here we do. So enable the + * prio ioctls here. + */ + set_bit(_IOC_NR(VIDIOC_G_PRIORITY), vdev->valid_ioctls); + set_bit(_IOC_NR(VIDIOC_S_PRIORITY), vdev->valid_ioctls); fh->prio = V4L2_PRIORITY_UNSET; init_waitqueue_head(&fh->wait); INIT_LIST_HEAD(&fh->available); @@ -49,8 +56,7 @@ void v4l2_fh_add(struct v4l2_fh *fh) { unsigned long flags; - if (test_bit(V4L2_FL_USE_FH_PRIO, &fh->vdev->flags)) - v4l2_prio_open(fh->vdev->prio, &fh->prio); + v4l2_prio_open(fh->vdev->prio, &fh->prio); spin_lock_irqsave(&fh->vdev->fh_lock, flags); list_add(&fh->list, &fh->vdev->fh_list); spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); @@ -78,8 +84,7 @@ void v4l2_fh_del(struct v4l2_fh *fh) spin_lock_irqsave(&fh->vdev->fh_lock, flags); list_del_init(&fh->list); spin_unlock_irqrestore(&fh->vdev->fh_lock, flags); - if (test_bit(V4L2_FL_USE_FH_PRIO, &fh->vdev->flags)) - v4l2_prio_close(fh->vdev->prio, fh->prio); + v4l2_prio_close(fh->vdev->prio, fh->prio); } EXPORT_SYMBOL_GPL(v4l2_fh_del); diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 16bffd8..8d4a25d 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -2190,7 +2190,6 @@ static long __video_do_ioctl(struct file *file, const struct v4l2_ioctl_info *info; void *fh = file->private_data; struct v4l2_fh *vfh = NULL; - int use_fh_prio = 0; int debug = vfd->debug; long ret = -ENOTTY; @@ -2200,10 +2199,8 @@ static long __video_do_ioctl(struct file *file, return ret; } - if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) { + if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) vfh = file->private_data; - use_fh_prio = test_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags); - } if (v4l2_is_known_ioctl(cmd)) { info = &v4l2_ioctls[_IOC_NR(cmd)]; @@ -2212,7 +2209,7 @@ static long __video_do_ioctl(struct file *file, !((info->flags & INFO_FL_CTRL) && vfh && vfh->ctrl_handler)) goto done; - if (use_fh_prio && (info->flags & INFO_FL_PRIO)) { + if (vfh && (info->flags & INFO_FL_PRIO)) { ret = v4l2_prio_check(vfd->prio, vfh->prio); if (ret) goto done; @@ -2237,7 +2234,7 @@ static long __video_do_ioctl(struct file *file, ret = -ENOTTY; } else { ret = ops->vidioc_default(file, fh, - use_fh_prio ? v4l2_prio_check(vfd->prio, vfh->prio) >= 0 : 0, + vfh ? v4l2_prio_check(vfd->prio, vfh->prio) >= 0 : 0, cmd, arg); } -- cgit v1.1 From 0ba2aeb6dab80920edd9cf5b93b1ea4d6913b8f3 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Wed, 16 Apr 2014 09:41:25 -0300 Subject: [media] v4l2-ctrls: increase internal min/max/step/def to 64 bit While VIDIOC_QUERYCTRL is limited to 32 bit min/max/step/def values for controls, the upcoming VIDIOC_QUERY_EXT_CTRL isn't. So increase the internal representation to 64 bits in preparation. Because of these changes the msi3101 driver has been modified slightly to fix a formatting issue (%d becomes %lld), vivi had to be modified as well to cope with the new 64-bit min/max values and the PIXEL_RATE control in a few sensor drivers required proper min/max/def values. Signed-off-by: Hans Verkuil Reviewed-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-common.c | 6 ++- drivers/media/v4l2-core/v4l2-ctrls.c | 93 +++++++++++++++++++++-------------- 2 files changed, 62 insertions(+), 37 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-common.c b/drivers/media/v4l2-core/v4l2-common.c index 433d6d7..ccaa38f 100644 --- a/drivers/media/v4l2-core/v4l2-common.c +++ b/drivers/media/v4l2-core/v4l2-common.c @@ -111,9 +111,13 @@ int v4l2_ctrl_check(struct v4l2_ext_control *ctrl, struct v4l2_queryctrl *qctrl, EXPORT_SYMBOL(v4l2_ctrl_check); /* Fill in a struct v4l2_queryctrl */ -int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 step, s32 def) +int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 _min, s32 _max, s32 _step, s32 _def) { const char *name; + s64 min = _min; + s64 max = _max; + u64 step = _step; + s64 def = _def; v4l2_ctrl_fill(qctrl->id, &name, &qctrl->type, &min, &max, &step, &def, &qctrl->flags); diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 55c6832..e132fa2 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -877,7 +877,7 @@ const char *v4l2_ctrl_get_name(u32 id) EXPORT_SYMBOL(v4l2_ctrl_get_name); void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, - s32 *min, s32 *max, s32 *step, s32 *def, u32 *flags) + s64 *min, s64 *max, u64 *step, s64 *def, u32 *flags) { *name = v4l2_ctrl_get_name(id); *flags = 0; @@ -1041,14 +1041,23 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, *type = V4L2_CTRL_TYPE_INTEGER; *flags |= V4L2_CTRL_FLAG_READ_ONLY; break; - case V4L2_CID_MPEG_VIDEO_DEC_FRAME: case V4L2_CID_MPEG_VIDEO_DEC_PTS: - *flags |= V4L2_CTRL_FLAG_VOLATILE; - /* Fall through */ + *type = V4L2_CTRL_TYPE_INTEGER64; + *flags |= V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY; + *min = *def = 0; + *max = 0x1ffffffffLL; + *step = 1; + break; + case V4L2_CID_MPEG_VIDEO_DEC_FRAME: + *type = V4L2_CTRL_TYPE_INTEGER64; + *flags |= V4L2_CTRL_FLAG_VOLATILE | V4L2_CTRL_FLAG_READ_ONLY; + *min = *def = 0; + *max = 0x7fffffffffffffffLL; + *step = 1; + break; case V4L2_CID_PIXEL_RATE: *type = V4L2_CTRL_TYPE_INTEGER64; *flags |= V4L2_CTRL_FLAG_READ_ONLY; - *min = *max = *step = *def = 0; break; default: *type = V4L2_CTRL_TYPE_INTEGER; @@ -1352,7 +1361,7 @@ static int cluster_changed(struct v4l2_ctrl *master) /* Control range checking */ static int check_range(enum v4l2_ctrl_type type, - s32 min, s32 max, u32 step, s32 def) + s64 min, s64 max, u64 step, s64 def) { switch (type) { case V4L2_CTRL_TYPE_BOOLEAN: @@ -1360,7 +1369,8 @@ static int check_range(enum v4l2_ctrl_type type, return -ERANGE; /* fall through */ case V4L2_CTRL_TYPE_INTEGER: - if (step <= 0 || min > max || def < min || def > max) + case V4L2_CTRL_TYPE_INTEGER64: + if (step == 0 || min > max || def < min || def > max) return -ERANGE; return 0; case V4L2_CTRL_TYPE_BITMASK: @@ -1385,23 +1395,30 @@ static int check_range(enum v4l2_ctrl_type type, } } +/* Round towards the closest legal value */ +#define ROUND_TO_RANGE(val, offset_type, ctrl) \ +({ \ + offset_type offset; \ + val += (ctrl)->step / 2; \ + val = clamp_t(typeof(val), val, \ + (ctrl)->minimum, (ctrl)->maximum); \ + offset = (val) - (ctrl)->minimum; \ + offset = (ctrl)->step * (offset / (ctrl)->step); \ + val = (ctrl)->minimum + offset; \ + 0; \ +}) + /* Validate a new control */ static int validate_new(const struct v4l2_ctrl *ctrl, struct v4l2_ext_control *c) { size_t len; - u32 offset; - s32 val; switch (ctrl->type) { case V4L2_CTRL_TYPE_INTEGER: - /* Round towards the closest legal value */ - val = c->value + ctrl->step / 2; - val = clamp(val, ctrl->minimum, ctrl->maximum); - offset = val - ctrl->minimum; - offset = ctrl->step * (offset / ctrl->step); - c->value = ctrl->minimum + offset; - return 0; + return ROUND_TO_RANGE(*(s32 *)&c->value, u32, ctrl); + case V4L2_CTRL_TYPE_INTEGER64: + return ROUND_TO_RANGE(*(s64 *)&c->value64, u64, ctrl); case V4L2_CTRL_TYPE_BOOLEAN: c->value = !!c->value; @@ -1427,9 +1444,6 @@ static int validate_new(const struct v4l2_ctrl *ctrl, c->value = 0; return 0; - case V4L2_CTRL_TYPE_INTEGER64: - return 0; - case V4L2_CTRL_TYPE_STRING: len = strlen(c->string); if (len < ctrl->minimum) @@ -1653,7 +1667,7 @@ unlock: static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, const struct v4l2_ctrl_ops *ops, u32 id, const char *name, enum v4l2_ctrl_type type, - s32 min, s32 max, u32 step, s32 def, + s64 min, s64 max, u64 step, s64 def, u32 flags, const char * const *qmenu, const s64 *qmenu_int, void *priv) { @@ -1738,10 +1752,10 @@ struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl, const s64 *qmenu_int = cfg->qmenu_int; enum v4l2_ctrl_type type = cfg->type; u32 flags = cfg->flags; - s32 min = cfg->min; - s32 max = cfg->max; - u32 step = cfg->step; - s32 def = cfg->def; + s64 min = cfg->min; + s64 max = cfg->max; + u64 step = cfg->step; + s64 def = cfg->def; if (name == NULL) v4l2_ctrl_fill(cfg->id, &name, &type, &min, &max, &step, @@ -1774,7 +1788,7 @@ EXPORT_SYMBOL(v4l2_ctrl_new_custom); /* Helper function for standard non-menu controls */ struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl, const struct v4l2_ctrl_ops *ops, - u32 id, s32 min, s32 max, u32 step, s32 def) + u32 id, s64 min, s64 max, u64 step, s64 def) { const char *name; enum v4l2_ctrl_type type; @@ -1794,15 +1808,17 @@ EXPORT_SYMBOL(v4l2_ctrl_new_std); /* Helper function for standard menu controls */ struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl, const struct v4l2_ctrl_ops *ops, - u32 id, s32 max, s32 mask, s32 def) + u32 id, u8 _max, u64 mask, u8 _def) { const char * const *qmenu = NULL; const s64 *qmenu_int = NULL; unsigned int qmenu_int_len = 0; const char *name; enum v4l2_ctrl_type type; - s32 min; - s32 step; + s64 min; + s64 max = _max; + s64 def = _def; + u64 step; u32 flags; v4l2_ctrl_fill(id, &name, &type, &min, &max, &step, &def, &flags); @@ -1823,14 +1839,16 @@ EXPORT_SYMBOL(v4l2_ctrl_new_std_menu); /* Helper function for standard menu controls with driver defined menu */ struct v4l2_ctrl *v4l2_ctrl_new_std_menu_items(struct v4l2_ctrl_handler *hdl, - const struct v4l2_ctrl_ops *ops, u32 id, s32 max, - s32 mask, s32 def, const char * const *qmenu) + const struct v4l2_ctrl_ops *ops, u32 id, u8 _max, + u64 mask, u8 _def, const char * const *qmenu) { enum v4l2_ctrl_type type; const char *name; u32 flags; - s32 step; - s32 min; + u64 step; + s64 min; + s64 max = _max; + s64 def = _def; /* v4l2_ctrl_new_std_menu_items() should only be called for * standard controls without a standard menu. @@ -1854,12 +1872,14 @@ EXPORT_SYMBOL(v4l2_ctrl_new_std_menu_items); /* Helper function for standard integer menu controls */ struct v4l2_ctrl *v4l2_ctrl_new_int_menu(struct v4l2_ctrl_handler *hdl, const struct v4l2_ctrl_ops *ops, - u32 id, s32 max, s32 def, const s64 *qmenu_int) + u32 id, u8 _max, u8 _def, const s64 *qmenu_int) { const char *name; enum v4l2_ctrl_type type; - s32 min; - s32 step; + s64 min; + u64 step; + s64 max = _max; + s64 def = _def; u32 flags; v4l2_ctrl_fill(id, &name, &type, &min, &max, &step, &def, &flags); @@ -2887,13 +2907,14 @@ void v4l2_ctrl_notify(struct v4l2_ctrl *ctrl, v4l2_ctrl_notify_fnc notify, void EXPORT_SYMBOL(v4l2_ctrl_notify); int v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl, - s32 min, s32 max, u32 step, s32 def) + s64 min, s64 max, u64 step, s64 def) { int ret = check_range(ctrl->type, min, max, step, def); struct v4l2_ext_control c; switch (ctrl->type) { case V4L2_CTRL_TYPE_INTEGER: + case V4L2_CTRL_TYPE_INTEGER64: case V4L2_CTRL_TYPE_BOOLEAN: case V4L2_CTRL_TYPE_MENU: case V4L2_CTRL_TYPE_INTEGER_MENU: -- cgit v1.1 From 7eafbce91b50317ef92d43c61b30a46cebe49f03 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 16 Dec 2013 10:12:17 -0300 Subject: [media] v4l2-ctrls: use pr_info/cont instead of printk Codingstyle fix. Signed-off-by: Hans Verkuil Reviewed-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index e132fa2..365884b 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -2068,45 +2068,45 @@ static void log_ctrl(const struct v4l2_ctrl *ctrl, if (ctrl->type == V4L2_CTRL_TYPE_CTRL_CLASS) return; - printk(KERN_INFO "%s%s%s: ", prefix, colon, ctrl->name); + pr_info("%s%s%s: ", prefix, colon, ctrl->name); switch (ctrl->type) { case V4L2_CTRL_TYPE_INTEGER: - printk(KERN_CONT "%d", ctrl->cur.val); + pr_cont("%d", ctrl->cur.val); break; case V4L2_CTRL_TYPE_BOOLEAN: - printk(KERN_CONT "%s", ctrl->cur.val ? "true" : "false"); + pr_cont("%s", ctrl->cur.val ? "true" : "false"); break; case V4L2_CTRL_TYPE_MENU: - printk(KERN_CONT "%s", ctrl->qmenu[ctrl->cur.val]); + pr_cont("%s", ctrl->qmenu[ctrl->cur.val]); break; case V4L2_CTRL_TYPE_INTEGER_MENU: - printk(KERN_CONT "%lld", ctrl->qmenu_int[ctrl->cur.val]); + pr_cont("%lld", ctrl->qmenu_int[ctrl->cur.val]); break; case V4L2_CTRL_TYPE_BITMASK: - printk(KERN_CONT "0x%08x", ctrl->cur.val); + pr_cont("0x%08x", ctrl->cur.val); break; case V4L2_CTRL_TYPE_INTEGER64: - printk(KERN_CONT "%lld", ctrl->cur.val64); + pr_cont("%lld", ctrl->cur.val64); break; case V4L2_CTRL_TYPE_STRING: - printk(KERN_CONT "%s", ctrl->cur.string); + pr_cont("%s", ctrl->cur.string); break; default: - printk(KERN_CONT "unknown type %d", ctrl->type); + pr_cont("unknown type %d", ctrl->type); break; } if (ctrl->flags & (V4L2_CTRL_FLAG_INACTIVE | V4L2_CTRL_FLAG_GRABBED | V4L2_CTRL_FLAG_VOLATILE)) { if (ctrl->flags & V4L2_CTRL_FLAG_INACTIVE) - printk(KERN_CONT " inactive"); + pr_cont(" inactive"); if (ctrl->flags & V4L2_CTRL_FLAG_GRABBED) - printk(KERN_CONT " grabbed"); + pr_cont(" grabbed"); if (ctrl->flags & V4L2_CTRL_FLAG_VOLATILE) - printk(KERN_CONT " volatile"); + pr_cont(" volatile"); } - printk(KERN_CONT "\n"); + pr_cont("\n"); } /* Log all controls owned by the handler */ -- cgit v1.1 From d9a2547150245f34a050f744ea46542c44792652 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 12 Jun 2014 07:54:16 -0300 Subject: [media] v4l2-ctrls: add support for compound types This patch implements initial support for compound types. The changes are fairly obvious: basic support for is_ptr types, the type_is_int function is replaced by a is_int bitfield, and v4l2_query_ext_ctrl is added. Note that this patch does not yet add support for N-dimensional arrays, that comes later. So v4l2_query_ext_ctrl just sets elems to 1 and nr_of_dims and dims[] are all zero. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 224 ++++++++++++++++++++++++++--------- 1 file changed, 168 insertions(+), 56 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 365884b..d5cd1aa 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -1124,20 +1124,6 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, } EXPORT_SYMBOL(v4l2_ctrl_fill); -/* Helper function to determine whether the control type is compatible with - VIDIOC_G/S_CTRL. */ -static bool type_is_int(const struct v4l2_ctrl *ctrl) -{ - switch (ctrl->type) { - case V4L2_CTRL_TYPE_INTEGER64: - case V4L2_CTRL_TYPE_STRING: - /* Nope, these need v4l2_ext_control */ - return false; - default: - return true; - } -} - static void fill_event(struct v4l2_event *ev, struct v4l2_ctrl *ctrl, u32 changes) { memset(ev->reserved, 0, sizeof(ev->reserved)); @@ -1146,7 +1132,7 @@ static void fill_event(struct v4l2_event *ev, struct v4l2_ctrl *ctrl, u32 change ev->u.ctrl.changes = changes; ev->u.ctrl.type = ctrl->type; ev->u.ctrl.flags = ctrl->flags; - if (ctrl->type == V4L2_CTRL_TYPE_STRING) + if (ctrl->is_ptr) ev->u.ctrl.value64 = 0; else ev->u.ctrl.value64 = ctrl->cur.val64; @@ -1181,6 +1167,9 @@ static int cur_to_user(struct v4l2_ext_control *c, { u32 len; + if (ctrl->is_ptr && !ctrl->is_string) + return copy_to_user(c->ptr, ctrl->cur.p, ctrl->elem_size); + switch (ctrl->type) { case V4L2_CTRL_TYPE_STRING: len = strlen(ctrl->cur.string); @@ -1208,6 +1197,9 @@ static int user_to_new(struct v4l2_ext_control *c, u32 size; ctrl->is_new = 1; + if (ctrl->is_ptr && !ctrl->is_string) + return copy_from_user(ctrl->p, c->ptr, ctrl->elem_size); + switch (ctrl->type) { case V4L2_CTRL_TYPE_INTEGER64: ctrl->val64 = c->value64; @@ -1242,6 +1234,9 @@ static int new_to_user(struct v4l2_ext_control *c, { u32 len; + if (ctrl->is_ptr && !ctrl->is_string) + return copy_to_user(c->ptr, ctrl->p, ctrl->elem_size); + switch (ctrl->type) { case V4L2_CTRL_TYPE_STRING: len = strlen(ctrl->string); @@ -1268,6 +1263,7 @@ static void new_to_cur(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, u32 ch_flags) if (ctrl == NULL) return; + switch (ctrl->type) { case V4L2_CTRL_TYPE_BUTTON: changed = true; @@ -1282,8 +1278,13 @@ static void new_to_cur(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, u32 ch_flags) ctrl->cur.val64 = ctrl->val64; break; default: - changed = ctrl->val != ctrl->cur.val; - ctrl->cur.val = ctrl->val; + if (ctrl->is_ptr) { + changed = memcmp(ctrl->p, ctrl->cur.p, ctrl->elem_size); + memcpy(ctrl->cur.p, ctrl->p, ctrl->elem_size); + } else { + changed = ctrl->val != ctrl->cur.val; + ctrl->cur.val = ctrl->val; + } break; } if (ch_flags & V4L2_EVENT_CTRL_CH_FLAGS) { @@ -1323,7 +1324,10 @@ static void cur_to_new(struct v4l2_ctrl *ctrl) ctrl->val64 = ctrl->cur.val64; break; default: - ctrl->val = ctrl->cur.val; + if (ctrl->is_ptr) + memcpy(ctrl->p, ctrl->cur.p, ctrl->elem_size); + else + ctrl->val = ctrl->cur.val; break; } } @@ -1536,7 +1540,7 @@ static struct v4l2_ctrl_ref *find_private_ref( VIDIOC_G/S_CTRL. */ if (V4L2_CTRL_ID2CLASS(ref->ctrl->id) == V4L2_CTRL_CLASS_USER && V4L2_CTRL_DRIVER_PRIV(ref->ctrl->id)) { - if (!type_is_int(ref->ctrl)) + if (!ref->ctrl->is_int) continue; if (id == 0) return ref; @@ -1606,8 +1610,12 @@ static int handler_new_ref(struct v4l2_ctrl_handler *hdl, u32 class_ctrl = V4L2_CTRL_ID2CLASS(id) | 1; int bucket = id % hdl->nr_of_buckets; /* which bucket to use */ - /* Automatically add the control class if it is not yet present. */ - if (id != class_ctrl && find_ref_lock(hdl, class_ctrl) == NULL) + /* + * Automatically add the control class if it is not yet present and + * the new control is not a compound control. + */ + if (ctrl->type < V4L2_CTRL_COMPOUND_TYPES && + id != class_ctrl && find_ref_lock(hdl, class_ctrl) == NULL) if (!v4l2_ctrl_new_std(hdl, NULL, class_ctrl, 0, 0, 0, 0)) return hdl->error; @@ -1668,18 +1676,28 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, const struct v4l2_ctrl_ops *ops, u32 id, const char *name, enum v4l2_ctrl_type type, s64 min, s64 max, u64 step, s64 def, + u32 elem_size, u32 flags, const char * const *qmenu, const s64 *qmenu_int, void *priv) { struct v4l2_ctrl *ctrl; - unsigned sz_extra = 0; + unsigned sz_extra; + void *data; int err; if (hdl->error) return NULL; + if (type == V4L2_CTRL_TYPE_INTEGER64) + elem_size = sizeof(s64); + else if (type == V4L2_CTRL_TYPE_STRING) + elem_size = max + 1; + else if (type < V4L2_CTRL_COMPOUND_TYPES) + elem_size = sizeof(s32); + /* Sanity checks */ if (id == 0 || name == NULL || id >= V4L2_CID_PRIVATE_BASE || + elem_size == 0 || (type == V4L2_CTRL_TYPE_MENU && qmenu == NULL) || (type == V4L2_CTRL_TYPE_INTEGER_MENU && qmenu_int == NULL)) { handler_set_err(hdl, -ERANGE); @@ -1695,12 +1713,13 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, return NULL; } + sz_extra = 0; if (type == V4L2_CTRL_TYPE_BUTTON) flags |= V4L2_CTRL_FLAG_WRITE_ONLY; else if (type == V4L2_CTRL_TYPE_CTRL_CLASS) flags |= V4L2_CTRL_FLAG_READ_ONLY; - else if (type == V4L2_CTRL_TYPE_STRING) - sz_extra += 2 * (max + 1); + else if (type == V4L2_CTRL_TYPE_STRING || type >= V4L2_CTRL_COMPOUND_TYPES) + sz_extra += 2 * elem_size; ctrl = kzalloc(sizeof(*ctrl) + sz_extra, GFP_KERNEL); if (ctrl == NULL) { @@ -1719,18 +1738,28 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, ctrl->minimum = min; ctrl->maximum = max; ctrl->step = step; + ctrl->default_value = def; + ctrl->is_string = type == V4L2_CTRL_TYPE_STRING; + ctrl->is_ptr = type >= V4L2_CTRL_COMPOUND_TYPES || ctrl->is_string; + ctrl->is_int = !ctrl->is_ptr && type != V4L2_CTRL_TYPE_INTEGER64; + ctrl->elem_size = elem_size; if (type == V4L2_CTRL_TYPE_MENU) ctrl->qmenu = qmenu; else if (type == V4L2_CTRL_TYPE_INTEGER_MENU) ctrl->qmenu_int = qmenu_int; ctrl->priv = priv; - ctrl->cur.val = ctrl->val = ctrl->default_value = def; + ctrl->cur.val = ctrl->val = def; + data = &ctrl->cur + 1; + + if (ctrl->is_string) { + ctrl->string = data; + ctrl->cur.string = data + elem_size; - if (ctrl->type == V4L2_CTRL_TYPE_STRING) { - ctrl->cur.string = (char *)&ctrl[1] + sz_extra - (max + 1); - ctrl->string = (char *)&ctrl[1] + sz_extra - 2 * (max + 1); if (ctrl->minimum) memset(ctrl->cur.string, ' ', ctrl->minimum); + } else if (ctrl->is_ptr) { + ctrl->p = data; + ctrl->cur.p = data + elem_size; } if (handler_new_ref(hdl, ctrl)) { kfree(ctrl); @@ -1778,7 +1807,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl, ctrl = v4l2_ctrl_new(hdl, cfg->ops, cfg->id, name, type, min, max, is_menu ? cfg->menu_skip_mask : step, - def, flags, qmenu, qmenu_int, priv); + def, cfg->elem_size, + flags, qmenu, qmenu_int, priv); if (ctrl) ctrl->is_private = cfg->is_private; return ctrl; @@ -1795,13 +1825,15 @@ struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl, u32 flags; v4l2_ctrl_fill(id, &name, &type, &min, &max, &step, &def, &flags); - if (type == V4L2_CTRL_TYPE_MENU - || type == V4L2_CTRL_TYPE_INTEGER_MENU) { + if (type == V4L2_CTRL_TYPE_MENU || + type == V4L2_CTRL_TYPE_INTEGER_MENU || + type >= V4L2_CTRL_COMPOUND_TYPES) { handler_set_err(hdl, -EINVAL); return NULL; } return v4l2_ctrl_new(hdl, ops, id, name, type, - min, max, step, def, flags, NULL, NULL, NULL); + min, max, step, def, 0, + flags, NULL, NULL, NULL); } EXPORT_SYMBOL(v4l2_ctrl_new_std); @@ -1833,7 +1865,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl, return NULL; } return v4l2_ctrl_new(hdl, ops, id, name, type, - 0, max, mask, def, flags, qmenu, qmenu_int, NULL); + 0, max, mask, def, 0, + flags, qmenu, qmenu_int, NULL); } EXPORT_SYMBOL(v4l2_ctrl_new_std_menu); @@ -1864,7 +1897,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu_items(struct v4l2_ctrl_handler *hdl, return NULL; } return v4l2_ctrl_new(hdl, ops, id, name, type, 0, max, mask, def, - flags, qmenu, NULL, NULL); + 0, flags, qmenu, NULL, NULL); } EXPORT_SYMBOL(v4l2_ctrl_new_std_menu_items); @@ -1888,7 +1921,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_int_menu(struct v4l2_ctrl_handler *hdl, return NULL; } return v4l2_ctrl_new(hdl, ops, id, name, type, - 0, max, 0, def, flags, NULL, qmenu_int, NULL); + 0, max, 0, def, 0, + flags, NULL, qmenu_int, NULL); } EXPORT_SYMBOL(v4l2_ctrl_new_int_menu); @@ -2177,9 +2211,10 @@ int v4l2_ctrl_handler_setup(struct v4l2_ctrl_handler *hdl) } EXPORT_SYMBOL(v4l2_ctrl_handler_setup); -/* Implement VIDIOC_QUERYCTRL */ -int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc) +/* Implement VIDIOC_QUERY_EXT_CTRL */ +int v4l2_query_ext_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_query_ext_ctrl *qc) { + const unsigned next_flags = V4L2_CTRL_FLAG_NEXT_CTRL | V4L2_CTRL_FLAG_NEXT_COMPOUND; u32 id = qc->id & V4L2_CTRL_ID_MASK; struct v4l2_ctrl_ref *ref; struct v4l2_ctrl *ctrl; @@ -2192,7 +2227,20 @@ int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc) /* Try to find it */ ref = find_ref(hdl, id); - if ((qc->id & V4L2_CTRL_FLAG_NEXT_CTRL) && !list_empty(&hdl->ctrl_refs)) { + if ((qc->id & next_flags) && !list_empty(&hdl->ctrl_refs)) { + bool is_compound; + /* Match any control that is not hidden */ + unsigned mask = 1; + bool match = false; + + if ((qc->id & next_flags) == V4L2_CTRL_FLAG_NEXT_COMPOUND) { + /* Match any hidden control */ + match = true; + } else if ((qc->id & next_flags) == next_flags) { + /* Match any control, compound or not */ + mask = 0; + } + /* Find the next control with ID > qc->id */ /* Did we reach the end of the control list? */ @@ -2200,19 +2248,34 @@ int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc) ref = NULL; /* Yes, so there is no next control */ } else if (ref) { /* We found a control with the given ID, so just get - the next one in the list. */ - ref = list_entry(ref->node.next, typeof(*ref), node); + the next valid one in the list. */ + list_for_each_entry_continue(ref, &hdl->ctrl_refs, node) { + is_compound = + ref->ctrl->type >= V4L2_CTRL_COMPOUND_TYPES; + if (id < ref->ctrl->id && + (is_compound & mask) == match) + break; + } + if (&ref->node == &hdl->ctrl_refs) + ref = NULL; } else { /* No control with the given ID exists, so start searching for the next largest ID. We know there is one, otherwise the first 'if' above would have been true. */ - list_for_each_entry(ref, &hdl->ctrl_refs, node) - if (id < ref->ctrl->id) + list_for_each_entry(ref, &hdl->ctrl_refs, node) { + is_compound = + ref->ctrl->type >= V4L2_CTRL_COMPOUND_TYPES; + if (id < ref->ctrl->id && + (is_compound & mask) == match) break; + } + if (&ref->node == &hdl->ctrl_refs) + ref = NULL; } } mutex_unlock(hdl->lock); + if (!ref) return -EINVAL; @@ -2223,6 +2286,12 @@ int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc) else qc->id = ctrl->id; strlcpy(qc->name, ctrl->name, sizeof(qc->name)); + qc->flags = ctrl->flags; + qc->type = ctrl->type; + if (ctrl->is_ptr) + qc->flags |= V4L2_CTRL_FLAG_HAS_PAYLOAD; + qc->elem_size = ctrl->elem_size; + qc->elems = 1; qc->minimum = ctrl->minimum; qc->maximum = ctrl->maximum; qc->default_value = ctrl->default_value; @@ -2231,15 +2300,50 @@ int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc) qc->step = 1; else qc->step = ctrl->step; - qc->flags = ctrl->flags; - qc->type = ctrl->type; + return 0; +} +EXPORT_SYMBOL(v4l2_query_ext_ctrl); + +/* Implement VIDIOC_QUERYCTRL */ +int v4l2_queryctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_queryctrl *qc) +{ + struct v4l2_query_ext_ctrl qec = { qc->id }; + int rc; + + rc = v4l2_query_ext_ctrl(hdl, &qec); + if (rc) + return rc; + + qc->id = qec.id; + qc->type = qec.type; + qc->flags = qec.flags; + strlcpy(qc->name, qec.name, sizeof(qc->name)); + switch (qc->type) { + case V4L2_CTRL_TYPE_INTEGER: + case V4L2_CTRL_TYPE_BOOLEAN: + case V4L2_CTRL_TYPE_MENU: + case V4L2_CTRL_TYPE_INTEGER_MENU: + case V4L2_CTRL_TYPE_STRING: + case V4L2_CTRL_TYPE_BITMASK: + qc->minimum = qec.minimum; + qc->maximum = qec.maximum; + qc->step = qec.step; + qc->default_value = qec.default_value; + break; + default: + qc->minimum = 0; + qc->maximum = 0; + qc->step = 0; + qc->default_value = 0; + break; + } return 0; } EXPORT_SYMBOL(v4l2_queryctrl); int v4l2_subdev_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc) { - if (qc->id & V4L2_CTRL_FLAG_NEXT_CTRL) + if (qc->id & (V4L2_CTRL_FLAG_NEXT_CTRL | V4L2_CTRL_FLAG_NEXT_COMPOUND)) return -EINVAL; return v4l2_queryctrl(sd->ctrl_handler, qc); } @@ -2339,7 +2443,8 @@ EXPORT_SYMBOL(v4l2_subdev_querymenu); Find the controls in the control array and do some basic checks. */ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs, - struct v4l2_ctrl_helper *helpers) + struct v4l2_ctrl_helper *helpers, + bool get) { struct v4l2_ctrl_helper *h; bool have_clusters = false; @@ -2371,6 +2476,13 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl, have_clusters = true; if (ctrl->cluster[0] != ctrl) ref = find_ref_lock(hdl, ctrl->cluster[0]->id); + if (ctrl->is_ptr && !ctrl->is_string && c->size < ctrl->elem_size) { + if (get) { + c->size = ctrl->elem_size; + return -ENOSPC; + } + return -EFAULT; + } /* Store the ref to the master control of the cluster */ h->mref = ref; h->ctrl = ctrl; @@ -2451,7 +2563,7 @@ int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs return -ENOMEM; } - ret = prepare_ext_ctrls(hdl, cs, helpers); + ret = prepare_ext_ctrls(hdl, cs, helpers, true); cs->error_idx = cs->count; for (i = 0; !ret && i < cs->count; i++) @@ -2513,11 +2625,11 @@ static int get_ctrl(struct v4l2_ctrl *ctrl, struct v4l2_ext_control *c) int ret = 0; int i; - /* String controls are not supported. The new_to_user() and + /* Compound controls are not supported. The new_to_user() and * cur_to_user() calls below would need to be modified not to access * userspace memory when called from get_ctrl(). */ - if (ctrl->type == V4L2_CTRL_TYPE_STRING) + if (!ctrl->is_int) return -EINVAL; if (ctrl->flags & V4L2_CTRL_FLAG_WRITE_ONLY) @@ -2543,7 +2655,7 @@ int v4l2_g_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_control *control) struct v4l2_ext_control c; int ret; - if (ctrl == NULL || !type_is_int(ctrl)) + if (ctrl == NULL || !ctrl->is_int) return -EINVAL; ret = get_ctrl(ctrl, &c); control->value = c.value; @@ -2562,7 +2674,7 @@ s32 v4l2_ctrl_g_ctrl(struct v4l2_ctrl *ctrl) struct v4l2_ext_control c; /* It's a driver bug if this happens. */ - WARN_ON(!type_is_int(ctrl)); + WARN_ON(!ctrl->is_int); c.value = 0; get_ctrl(ctrl, &c); return c.value; @@ -2698,7 +2810,7 @@ static int try_set_ext_ctrls(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl, if (!helpers) return -ENOMEM; } - ret = prepare_ext_ctrls(hdl, cs, helpers); + ret = prepare_ext_ctrls(hdl, cs, helpers, false); if (!ret) ret = validate_ctrls(cs, helpers, set); if (ret && set) @@ -2803,11 +2915,11 @@ static int set_ctrl(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, struct v4l2_ctrl *master = ctrl->cluster[0]; int i; - /* String controls are not supported. The user_to_new() and + /* Compound controls are not supported. The user_to_new() and * cur_to_user() calls below would need to be modified not to access * userspace memory when called from set_ctrl(). */ - if (ctrl->type == V4L2_CTRL_TYPE_STRING) + if (ctrl->is_ptr) return -EINVAL; /* Reset the 'is_new' flags of the cluster */ @@ -2849,7 +2961,7 @@ int v4l2_s_ctrl(struct v4l2_fh *fh, struct v4l2_ctrl_handler *hdl, struct v4l2_ext_control c; int ret; - if (ctrl == NULL || !type_is_int(ctrl)) + if (ctrl == NULL || !ctrl->is_int) return -EINVAL; if (ctrl->flags & V4L2_CTRL_FLAG_READ_ONLY) @@ -2873,7 +2985,7 @@ int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val) struct v4l2_ext_control c; /* It's a driver bug if this happens. */ - WARN_ON(!type_is_int(ctrl)); + WARN_ON(!ctrl->is_int); c.value = val; return set_ctrl_lock(NULL, ctrl, &c); } -- cgit v1.1 From e6bee3685e732df82f48698254a36754cf15f0b0 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 10 Jun 2014 04:22:06 -0300 Subject: [media] v4l2: integrate support for VIDIOC_QUERY_EXT_CTRL Add the v4l2 core plumbing for the new VIDIOC_QUERY_EXT_CTRL ioctl. Signed-off-by: Hans Verkuil Reviewed-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-dev.c | 2 ++ drivers/media/v4l2-core/v4l2-ioctl.c | 33 +++++++++++++++++++++++++++++++++ drivers/media/v4l2-core/v4l2-subdev.c | 3 +++ 3 files changed, 38 insertions(+) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c index 35698aa..1cfdbdd 100644 --- a/drivers/media/v4l2-core/v4l2-dev.c +++ b/drivers/media/v4l2-core/v4l2-dev.c @@ -575,6 +575,8 @@ static void determine_valid_ioctls(struct video_device *vdev) be valid if the filehandle passed the control handler. */ if (vdev->ctrl_handler || ops->vidioc_queryctrl) set_bit(_IOC_NR(VIDIOC_QUERYCTRL), valid_ioctls); + if (vdev->ctrl_handler || ops->vidioc_query_ext_ctrl) + set_bit(_IOC_NR(VIDIOC_QUERY_EXT_CTRL), valid_ioctls); if (vdev->ctrl_handler || ops->vidioc_g_ctrl || ops->vidioc_g_ext_ctrls) set_bit(_IOC_NR(VIDIOC_G_CTRL), valid_ioctls); if (vdev->ctrl_handler || ops->vidioc_s_ctrl || ops->vidioc_s_ext_ctrls) diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 8d4a25d..c38a620 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -525,6 +525,21 @@ static void v4l_print_queryctrl(const void *arg, bool write_only) p->step, p->default_value, p->flags); } +static void v4l_print_query_ext_ctrl(const void *arg, bool write_only) +{ + const struct v4l2_query_ext_ctrl *p = arg; + + pr_cont("id=0x%x, type=%d, name=%.*s, min/max=%lld/%lld, " + "step=%lld, default=%lld, flags=0x%08x, elem_size=%u, elems=%u, " + "nr_of_dims=%u, dims=%u,%u,%u,%u,%u,%u,%u,%u\n", + p->id, p->type, (int)sizeof(p->name), p->name, + p->minimum, p->maximum, + p->step, p->default_value, p->flags, + p->elem_size, p->elems, p->nr_of_dims, + p->dims[0], p->dims[1], p->dims[2], p->dims[3], + p->dims[4], p->dims[5], p->dims[6], p->dims[7]); +} + static void v4l_print_querymenu(const void *arg, bool write_only) { const struct v4l2_querymenu *p = arg; @@ -1561,6 +1576,23 @@ static int v4l_queryctrl(const struct v4l2_ioctl_ops *ops, return -ENOTTY; } +static int v4l_query_ext_ctrl(const struct v4l2_ioctl_ops *ops, + struct file *file, void *fh, void *arg) +{ + struct video_device *vfd = video_devdata(file); + struct v4l2_query_ext_ctrl *p = arg; + struct v4l2_fh *vfh = + test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL; + + if (vfh && vfh->ctrl_handler) + return v4l2_query_ext_ctrl(vfh->ctrl_handler, p); + if (vfd->ctrl_handler) + return v4l2_query_ext_ctrl(vfd->ctrl_handler, p); + if (ops->vidioc_query_ext_ctrl) + return ops->vidioc_query_ext_ctrl(file, fh, p); + return -ENOTTY; +} + static int v4l_querymenu(const struct v4l2_ioctl_ops *ops, struct file *file, void *fh, void *arg) { @@ -2121,6 +2153,7 @@ static struct v4l2_ioctl_info v4l2_ioctls[] = { IOCTL_INFO_STD(VIDIOC_DV_TIMINGS_CAP, vidioc_dv_timings_cap, v4l_print_dv_timings_cap, INFO_FL_CLEAR(v4l2_dv_timings_cap, type)), IOCTL_INFO_FNC(VIDIOC_ENUM_FREQ_BANDS, v4l_enum_freq_bands, v4l_print_freq_band, 0), IOCTL_INFO_FNC(VIDIOC_DBG_G_CHIP_INFO, v4l_dbg_g_chip_info, v4l_print_dbg_chip_info, INFO_FL_CLEAR(v4l2_dbg_chip_info, match)), + IOCTL_INFO_FNC(VIDIOC_QUERY_EXT_CTRL, v4l_query_ext_ctrl, v4l_print_query_ext_ctrl, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_query_ext_ctrl, id)), }; #define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls) diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index 058c1a6..b984f33d 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -139,6 +139,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) case VIDIOC_QUERYCTRL: return v4l2_queryctrl(vfh->ctrl_handler, arg); + case VIDIOC_QUERY_EXT_CTRL: + return v4l2_query_ext_ctrl(vfh->ctrl_handler, arg); + case VIDIOC_QUERYMENU: return v4l2_querymenu(vfh->ctrl_handler, arg); -- cgit v1.1 From 0176077a813933a547b7a913377a87d615b7c108 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 27 Apr 2014 03:22:17 -0300 Subject: [media] v4l2-ctrls: create type_ops Since compound controls can have non-standard types we need to be able to do type-specific checks etc. In order to make that easy type operations are added. There are four operations: - equal: check if two values are equal - init: initialize a value - log: log the value - validate: validate a new value The v4l2_ctrl struct adds p_new and p_cur unions at the end of the struct. This union provides a standard way of accessing control types through a pointer, which greatly simplifies internal control processing. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 273 ++++++++++++++++++++++------------- drivers/media/v4l2-core/v4l2-ioctl.c | 5 +- 2 files changed, 175 insertions(+), 103 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index d5cd1aa..09e2c3a 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -1161,6 +1161,149 @@ static void send_event(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, u32 changes) v4l2_event_queue_fh(sev->fh, &ev); } +static bool std_equal(const struct v4l2_ctrl *ctrl, + union v4l2_ctrl_ptr ptr1, + union v4l2_ctrl_ptr ptr2) +{ + switch (ctrl->type) { + case V4L2_CTRL_TYPE_BUTTON: + return false; + case V4L2_CTRL_TYPE_STRING: + /* strings are always 0-terminated */ + return !strcmp(ptr1.p_char, ptr2.p_char); + case V4L2_CTRL_TYPE_INTEGER64: + return *ptr1.p_s64 == *ptr2.p_s64; + default: + if (ctrl->is_ptr) + return !memcmp(ptr1.p, ptr2.p, ctrl->elem_size); + return *ptr1.p_s32 == *ptr2.p_s32; + } +} + +static void std_init(const struct v4l2_ctrl *ctrl, + union v4l2_ctrl_ptr ptr) +{ + switch (ctrl->type) { + case V4L2_CTRL_TYPE_STRING: + memset(ptr.p_char, ' ', ctrl->minimum); + ptr.p_char[ctrl->minimum] = '\0'; + break; + case V4L2_CTRL_TYPE_INTEGER64: + *ptr.p_s64 = ctrl->default_value; + break; + case V4L2_CTRL_TYPE_INTEGER: + case V4L2_CTRL_TYPE_INTEGER_MENU: + case V4L2_CTRL_TYPE_MENU: + case V4L2_CTRL_TYPE_BITMASK: + case V4L2_CTRL_TYPE_BOOLEAN: + *ptr.p_s32 = ctrl->default_value; + break; + default: + break; + } +} + +static void std_log(const struct v4l2_ctrl *ctrl) +{ + union v4l2_ctrl_ptr ptr = ctrl->p_cur; + + switch (ctrl->type) { + case V4L2_CTRL_TYPE_INTEGER: + pr_cont("%d", *ptr.p_s32); + break; + case V4L2_CTRL_TYPE_BOOLEAN: + pr_cont("%s", *ptr.p_s32 ? "true" : "false"); + break; + case V4L2_CTRL_TYPE_MENU: + pr_cont("%s", ctrl->qmenu[*ptr.p_s32]); + break; + case V4L2_CTRL_TYPE_INTEGER_MENU: + pr_cont("%lld", ctrl->qmenu_int[*ptr.p_s32]); + break; + case V4L2_CTRL_TYPE_BITMASK: + pr_cont("0x%08x", *ptr.p_s32); + break; + case V4L2_CTRL_TYPE_INTEGER64: + pr_cont("%lld", *ptr.p_s64); + break; + case V4L2_CTRL_TYPE_STRING: + pr_cont("%s", ptr.p_char); + break; + default: + pr_cont("unknown type %d", ctrl->type); + break; + } +} + +/* Round towards the closest legal value */ +#define ROUND_TO_RANGE(val, offset_type, ctrl) \ +({ \ + offset_type offset; \ + val += (ctrl)->step / 2; \ + val = clamp_t(typeof(val), val, \ + (ctrl)->minimum, (ctrl)->maximum); \ + offset = (val) - (ctrl)->minimum; \ + offset = (ctrl)->step * (offset / (ctrl)->step); \ + val = (ctrl)->minimum + offset; \ + 0; \ +}) + +/* Validate a new control */ +static int std_validate(const struct v4l2_ctrl *ctrl, + union v4l2_ctrl_ptr ptr) +{ + size_t len; + + switch (ctrl->type) { + case V4L2_CTRL_TYPE_INTEGER: + return ROUND_TO_RANGE(*ptr.p_s32, u32, ctrl); + case V4L2_CTRL_TYPE_INTEGER64: + return ROUND_TO_RANGE(*ptr.p_s64, u64, ctrl); + + case V4L2_CTRL_TYPE_BOOLEAN: + *ptr.p_s32 = !!*ptr.p_s32; + return 0; + + case V4L2_CTRL_TYPE_MENU: + case V4L2_CTRL_TYPE_INTEGER_MENU: + if (*ptr.p_s32 < ctrl->minimum || *ptr.p_s32 > ctrl->maximum) + return -ERANGE; + if (ctrl->menu_skip_mask & (1 << *ptr.p_s32)) + return -EINVAL; + if (ctrl->type == V4L2_CTRL_TYPE_MENU && + ctrl->qmenu[*ptr.p_s32][0] == '\0') + return -EINVAL; + return 0; + + case V4L2_CTRL_TYPE_BITMASK: + *ptr.p_s32 &= ctrl->maximum; + return 0; + + case V4L2_CTRL_TYPE_BUTTON: + case V4L2_CTRL_TYPE_CTRL_CLASS: + *ptr.p_s32 = 0; + return 0; + + case V4L2_CTRL_TYPE_STRING: + len = strlen(ptr.p_char); + if (len < ctrl->minimum) + return -ERANGE; + if ((len - ctrl->minimum) % ctrl->step) + return -ERANGE; + return 0; + + default: + return -EINVAL; + } +} + +static const struct v4l2_ctrl_type_ops std_type_ops = { + .equal = std_equal, + .init = std_init, + .log = std_log, + .validate = std_validate, +}; + /* 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) @@ -1344,21 +1487,7 @@ static int cluster_changed(struct v4l2_ctrl *master) if (ctrl == NULL) continue; - switch (ctrl->type) { - case V4L2_CTRL_TYPE_BUTTON: - /* Button controls are always 'different' */ - return 1; - case V4L2_CTRL_TYPE_STRING: - /* strings are always 0-terminated */ - diff = strcmp(ctrl->string, ctrl->cur.string); - break; - case V4L2_CTRL_TYPE_INTEGER64: - diff = ctrl->val64 != ctrl->cur.val64; - break; - default: - diff = ctrl->val != ctrl->cur.val; - break; - } + diff = !ctrl->type_ops->equal(ctrl, ctrl->p_cur, ctrl->p_new); } return diff; } @@ -1399,65 +1528,30 @@ static int check_range(enum v4l2_ctrl_type type, } } -/* Round towards the closest legal value */ -#define ROUND_TO_RANGE(val, offset_type, ctrl) \ -({ \ - offset_type offset; \ - val += (ctrl)->step / 2; \ - val = clamp_t(typeof(val), val, \ - (ctrl)->minimum, (ctrl)->maximum); \ - offset = (val) - (ctrl)->minimum; \ - offset = (ctrl)->step * (offset / (ctrl)->step); \ - val = (ctrl)->minimum + offset; \ - 0; \ -}) - /* Validate a new control */ static int validate_new(const struct v4l2_ctrl *ctrl, struct v4l2_ext_control *c) { - size_t len; + union v4l2_ctrl_ptr ptr; switch (ctrl->type) { case V4L2_CTRL_TYPE_INTEGER: - return ROUND_TO_RANGE(*(s32 *)&c->value, u32, ctrl); - case V4L2_CTRL_TYPE_INTEGER64: - return ROUND_TO_RANGE(*(s64 *)&c->value64, u64, ctrl); - - case V4L2_CTRL_TYPE_BOOLEAN: - c->value = !!c->value; - return 0; - - case V4L2_CTRL_TYPE_MENU: case V4L2_CTRL_TYPE_INTEGER_MENU: - if (c->value < ctrl->minimum || c->value > ctrl->maximum) - return -ERANGE; - if (ctrl->menu_skip_mask & (1 << c->value)) - return -EINVAL; - if (ctrl->type == V4L2_CTRL_TYPE_MENU && - ctrl->qmenu[c->value][0] == '\0') - return -EINVAL; - return 0; - + case V4L2_CTRL_TYPE_MENU: case V4L2_CTRL_TYPE_BITMASK: - c->value &= ctrl->maximum; - return 0; - + case V4L2_CTRL_TYPE_BOOLEAN: case V4L2_CTRL_TYPE_BUTTON: case V4L2_CTRL_TYPE_CTRL_CLASS: - c->value = 0; - return 0; + ptr.p_s32 = &c->value; + return ctrl->type_ops->validate(ctrl, ptr); - case V4L2_CTRL_TYPE_STRING: - len = strlen(c->string); - if (len < ctrl->minimum) - return -ERANGE; - if ((len - ctrl->minimum) % ctrl->step) - return -ERANGE; - return 0; + case V4L2_CTRL_TYPE_INTEGER64: + ptr.p_s64 = &c->value64; + return ctrl->type_ops->validate(ctrl, ptr); default: - return -EINVAL; + ptr.p = c->ptr; + return ctrl->type_ops->validate(ctrl, ptr); } } @@ -1674,6 +1768,7 @@ unlock: /* Add a new control */ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, const struct v4l2_ctrl_ops *ops, + const struct v4l2_ctrl_type_ops *type_ops, u32 id, const char *name, enum v4l2_ctrl_type type, s64 min, s64 max, u64 step, s64 def, u32 elem_size, @@ -1731,6 +1826,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, INIT_LIST_HEAD(&ctrl->ev_subs); ctrl->handler = hdl; ctrl->ops = ops; + ctrl->type_ops = type_ops ? type_ops : &std_type_ops; ctrl->id = id; ctrl->name = name; ctrl->type = type; @@ -1751,16 +1847,16 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, ctrl->cur.val = ctrl->val = def; data = &ctrl->cur + 1; - if (ctrl->is_string) { - ctrl->string = data; - ctrl->cur.string = data + elem_size; - - if (ctrl->minimum) - memset(ctrl->cur.string, ' ', ctrl->minimum); - } else if (ctrl->is_ptr) { - ctrl->p = data; - ctrl->cur.p = data + elem_size; + if (ctrl->is_ptr) { + ctrl->p = ctrl->p_new.p = data; + ctrl->p_cur.p = data + elem_size; + } else { + ctrl->p_new.p = &ctrl->val; + ctrl->p_cur.p = &ctrl->cur.val; } + ctrl->type_ops->init(ctrl, ctrl->p_cur); + ctrl->type_ops->init(ctrl, ctrl->p_new); + if (handler_new_ref(hdl, ctrl)) { kfree(ctrl); return NULL; @@ -1804,7 +1900,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl, return NULL; } - ctrl = v4l2_ctrl_new(hdl, cfg->ops, cfg->id, name, + ctrl = v4l2_ctrl_new(hdl, cfg->ops, cfg->type_ops, cfg->id, name, type, min, max, is_menu ? cfg->menu_skip_mask : step, def, cfg->elem_size, @@ -1831,7 +1927,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl, handler_set_err(hdl, -EINVAL); return NULL; } - return v4l2_ctrl_new(hdl, ops, id, name, type, + return v4l2_ctrl_new(hdl, ops, NULL, id, name, type, min, max, step, def, 0, flags, NULL, NULL, NULL); } @@ -1864,7 +1960,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl, handler_set_err(hdl, -EINVAL); return NULL; } - return v4l2_ctrl_new(hdl, ops, id, name, type, + return v4l2_ctrl_new(hdl, ops, NULL, id, name, type, 0, max, mask, def, 0, flags, qmenu, qmenu_int, NULL); } @@ -1896,7 +1992,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu_items(struct v4l2_ctrl_handler *hdl, handler_set_err(hdl, -EINVAL); return NULL; } - return v4l2_ctrl_new(hdl, ops, id, name, type, 0, max, mask, def, + return v4l2_ctrl_new(hdl, ops, NULL, id, name, type, + 0, max, mask, def, 0, flags, qmenu, NULL, NULL); } @@ -1920,7 +2017,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_int_menu(struct v4l2_ctrl_handler *hdl, handler_set_err(hdl, -EINVAL); return NULL; } - return v4l2_ctrl_new(hdl, ops, id, name, type, + return v4l2_ctrl_new(hdl, ops, NULL, id, name, type, 0, max, 0, def, 0, flags, NULL, qmenu_int, NULL); } @@ -2104,32 +2201,8 @@ static void log_ctrl(const struct v4l2_ctrl *ctrl, pr_info("%s%s%s: ", prefix, colon, ctrl->name); - switch (ctrl->type) { - case V4L2_CTRL_TYPE_INTEGER: - pr_cont("%d", ctrl->cur.val); - break; - case V4L2_CTRL_TYPE_BOOLEAN: - pr_cont("%s", ctrl->cur.val ? "true" : "false"); - break; - case V4L2_CTRL_TYPE_MENU: - pr_cont("%s", ctrl->qmenu[ctrl->cur.val]); - break; - case V4L2_CTRL_TYPE_INTEGER_MENU: - pr_cont("%lld", ctrl->qmenu_int[ctrl->cur.val]); - break; - case V4L2_CTRL_TYPE_BITMASK: - pr_cont("0x%08x", ctrl->cur.val); - break; - case V4L2_CTRL_TYPE_INTEGER64: - pr_cont("%lld", ctrl->cur.val64); - break; - case V4L2_CTRL_TYPE_STRING: - pr_cont("%s", ctrl->cur.string); - break; - default: - pr_cont("unknown type %d", ctrl->type); - break; - } + ctrl->type_ops->log(ctrl); + if (ctrl->flags & (V4L2_CTRL_FLAG_INACTIVE | V4L2_CTRL_FLAG_GRABBED | V4L2_CTRL_FLAG_VOLATILE)) { diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index c38a620..96bc117 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -531,13 +531,12 @@ static void v4l_print_query_ext_ctrl(const void *arg, bool write_only) pr_cont("id=0x%x, type=%d, name=%.*s, min/max=%lld/%lld, " "step=%lld, default=%lld, flags=0x%08x, elem_size=%u, elems=%u, " - "nr_of_dims=%u, dims=%u,%u,%u,%u,%u,%u,%u,%u\n", + "nr_of_dims=%u, dims=%u,%u,%u,%u\n", p->id, p->type, (int)sizeof(p->name), p->name, p->minimum, p->maximum, p->step, p->default_value, p->flags, p->elem_size, p->elems, p->nr_of_dims, - p->dims[0], p->dims[1], p->dims[2], p->dims[3], - p->dims[4], p->dims[5], p->dims[6], p->dims[7]); + p->dims[0], p->dims[1], p->dims[2], p->dims[3]); } static void v4l_print_querymenu(const void *arg, bool write_only) -- cgit v1.1 From 000e4f9a5bcf86fb52914c445ce5634b65e910a2 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 27 Apr 2014 03:26:30 -0300 Subject: [media] v4l2-ctrls: rewrite copy routines to operate on union v4l2_ctrl_ptr In order to implement array support and (for the future) configuration stores we need to have more generic copy routines that all operate on the v4l2_ctrl_ptr union. So instead of e.g. using ctrl->cur.string it uses ptr.p_char. This makes e.g. cur_to_user generic so it can be used to copy any v4l2_ctrl_ptr value to userspace, not just the (hardcoded) current value. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 129 +++++++++++++++-------------------- 1 file changed, 56 insertions(+), 73 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 09e2c3a..e7e0bea 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -1304,48 +1304,64 @@ static const struct v4l2_ctrl_type_ops std_type_ops = { .validate = std_validate, }; -/* 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) +/* Helper function: copy the given control value back to the caller */ +static int ptr_to_user(struct v4l2_ext_control *c, + struct v4l2_ctrl *ctrl, + union v4l2_ctrl_ptr ptr) { u32 len; if (ctrl->is_ptr && !ctrl->is_string) - return copy_to_user(c->ptr, ctrl->cur.p, ctrl->elem_size); + return copy_to_user(c->ptr, ptr.p, ctrl->elem_size); switch (ctrl->type) { case V4L2_CTRL_TYPE_STRING: - len = strlen(ctrl->cur.string); + len = strlen(ptr.p_char); if (c->size < len + 1) { c->size = len + 1; return -ENOSPC; } - return copy_to_user(c->string, ctrl->cur.string, - len + 1) ? -EFAULT : 0; + return copy_to_user(c->string, ptr.p_char, len + 1) ? + -EFAULT : 0; case V4L2_CTRL_TYPE_INTEGER64: - c->value64 = ctrl->cur.val64; + c->value64 = *ptr.p_s64; break; default: - c->value = ctrl->cur.val; + c->value = *ptr.p_s32; break; } return 0; } -/* Helper function: copy the caller-provider value as the new control value */ -static int user_to_new(struct v4l2_ext_control *c, +/* 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) { + return ptr_to_user(c, ctrl, ctrl->p_cur); +} + +/* Helper function: copy the new control value back to the caller */ +static int new_to_user(struct v4l2_ext_control *c, + struct v4l2_ctrl *ctrl) +{ + return ptr_to_user(c, ctrl, ctrl->p_new); +} + +/* Helper function: copy the caller-provider value to the given control value */ +static int user_to_ptr(struct v4l2_ext_control *c, + struct v4l2_ctrl *ctrl, + union v4l2_ctrl_ptr ptr) +{ int ret; u32 size; ctrl->is_new = 1; if (ctrl->is_ptr && !ctrl->is_string) - return copy_from_user(ctrl->p, c->ptr, ctrl->elem_size); + return copy_from_user(ptr.p, c->ptr, ctrl->elem_size); switch (ctrl->type) { case V4L2_CTRL_TYPE_INTEGER64: - ctrl->val64 = c->value64; + *ptr.p_s64 = c->value64; break; case V4L2_CTRL_TYPE_STRING: size = c->size; @@ -1353,83 +1369,64 @@ static int user_to_new(struct v4l2_ext_control *c, return -ERANGE; if (size > ctrl->maximum + 1) size = ctrl->maximum + 1; - ret = copy_from_user(ctrl->string, c->string, size); + ret = copy_from_user(ptr.p_char, c->string, size); if (!ret) { - char last = ctrl->string[size - 1]; + char last = ptr.p_char[size - 1]; - ctrl->string[size - 1] = 0; + ptr.p_char[size - 1] = 0; /* If the string was longer than ctrl->maximum, then return an error. */ - if (strlen(ctrl->string) == ctrl->maximum && last) + if (strlen(ptr.p_char) == ctrl->maximum && last) return -ERANGE; } return ret ? -EFAULT : 0; default: - ctrl->val = c->value; + *ptr.p_s32 = c->value; break; } return 0; } -/* Helper function: copy the new control value back to the caller */ -static int new_to_user(struct v4l2_ext_control *c, +/* Helper function: copy the caller-provider value as the new control value */ +static int user_to_new(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl) { - u32 len; - - if (ctrl->is_ptr && !ctrl->is_string) - return copy_to_user(c->ptr, ctrl->p, ctrl->elem_size); + return user_to_ptr(c, ctrl, ctrl->p_new); +} +/* Copy the one value to another. */ +static void ptr_to_ptr(struct v4l2_ctrl *ctrl, + union v4l2_ctrl_ptr from, union v4l2_ctrl_ptr to) +{ + if (ctrl == NULL) + return; switch (ctrl->type) { case V4L2_CTRL_TYPE_STRING: - len = strlen(ctrl->string); - if (c->size < len + 1) { - c->size = ctrl->maximum + 1; - return -ENOSPC; - } - return copy_to_user(c->string, ctrl->string, - len + 1) ? -EFAULT : 0; + /* strings are always 0-terminated */ + strcpy(to.p_char, from.p_char); + break; case V4L2_CTRL_TYPE_INTEGER64: - c->value64 = ctrl->val64; + *to.p_s64 = *from.p_s64; break; default: - c->value = ctrl->val; + if (ctrl->is_ptr) + memcpy(to.p, from.p, ctrl->elem_size); + else + *to.p_s32 = *from.p_s32; break; } - return 0; } /* Copy the new value to the current value. */ static void new_to_cur(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, u32 ch_flags) { - bool changed = false; + bool changed; if (ctrl == NULL) return; + changed = !ctrl->type_ops->equal(ctrl, ctrl->p_cur, ctrl->p_new); + ptr_to_ptr(ctrl, ctrl->p_new, ctrl->p_cur); - switch (ctrl->type) { - case V4L2_CTRL_TYPE_BUTTON: - changed = true; - break; - case V4L2_CTRL_TYPE_STRING: - /* strings are always 0-terminated */ - changed = strcmp(ctrl->string, ctrl->cur.string); - strcpy(ctrl->cur.string, ctrl->string); - break; - case V4L2_CTRL_TYPE_INTEGER64: - changed = ctrl->val64 != ctrl->cur.val64; - ctrl->cur.val64 = ctrl->val64; - break; - default: - if (ctrl->is_ptr) { - changed = memcmp(ctrl->p, ctrl->cur.p, ctrl->elem_size); - memcpy(ctrl->cur.p, ctrl->p, ctrl->elem_size); - } else { - changed = ctrl->val != ctrl->cur.val; - ctrl->cur.val = ctrl->val; - } - break; - } if (ch_flags & V4L2_EVENT_CTRL_CH_FLAGS) { /* Note: CH_FLAGS is only set for auto clusters. */ ctrl->flags &= @@ -1458,21 +1455,7 @@ static void cur_to_new(struct v4l2_ctrl *ctrl) { if (ctrl == NULL) return; - switch (ctrl->type) { - case V4L2_CTRL_TYPE_STRING: - /* strings are always 0-terminated */ - strcpy(ctrl->string, ctrl->cur.string); - break; - case V4L2_CTRL_TYPE_INTEGER64: - ctrl->val64 = ctrl->cur.val64; - break; - default: - if (ctrl->is_ptr) - memcpy(ctrl->p, ctrl->cur.p, ctrl->elem_size); - else - ctrl->val = ctrl->cur.val; - break; - } + ptr_to_ptr(ctrl, ctrl->p_cur, ctrl->p_new); } /* Return non-zero if one or more of the controls in the cluster has a new -- cgit v1.1 From 9ea1b7a4b66fddfab9e65e243b72d18371f8d9a5 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 17 Jan 2014 08:25:26 -0300 Subject: [media] v4l2-ctrls: compare values only once When setting a control the control's new value is compared to the current value twice: once by new_to_cur(), once by cluster_changed(). Not a big deal when dealing with simple values, but it can be a problem when dealing with compound types or arrays. So fix this: cluster_changed() sets the has_changed flag, which is used by new_to_cur() instead of having to do another compare. Signed-off-by: Hans Verkuil Reviewed-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index e7e0bea..1b4d37c 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -1424,8 +1424,11 @@ static void new_to_cur(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, u32 ch_flags) if (ctrl == NULL) return; - changed = !ctrl->type_ops->equal(ctrl, ctrl->p_cur, ctrl->p_new); - ptr_to_ptr(ctrl, ctrl->p_new, ctrl->p_cur); + + /* has_changed is set by cluster_changed */ + changed = ctrl->has_changed; + if (changed) + ptr_to_ptr(ctrl, ctrl->p_new, ctrl->p_cur); if (ch_flags & V4L2_EVENT_CTRL_CH_FLAGS) { /* Note: CH_FLAGS is only set for auto clusters. */ @@ -1462,17 +1465,19 @@ static void cur_to_new(struct v4l2_ctrl *ctrl) value that differs from the current value. */ static int cluster_changed(struct v4l2_ctrl *master) { - int diff = 0; + bool changed = false; int i; - for (i = 0; !diff && i < master->ncontrols; i++) { + for (i = 0; i < master->ncontrols; i++) { struct v4l2_ctrl *ctrl = master->cluster[i]; if (ctrl == NULL) continue; - diff = !ctrl->type_ops->equal(ctrl, ctrl->p_cur, ctrl->p_new); + ctrl->has_changed = !ctrl->type_ops->equal(ctrl, + ctrl->p_cur, ctrl->p_new); + changed |= ctrl->has_changed; } - return diff; + return changed; } /* Control range checking */ -- cgit v1.1 From 2a9ec3731137f973c6289698de6566a25418b96f Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 27 Apr 2014 03:38:13 -0300 Subject: [media] v4l2-ctrls: use ptrs for all but the s32 type Rather than having two unions for all types just keep 'val' and 'cur.val' and use the p_cur and p_new unions to access all others. The only reason for keeping 'val' and 'cur.val' is that it is used all over, so converting this as well would be a huge job. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 1b4d37c..4b10571 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -1135,7 +1135,7 @@ static void fill_event(struct v4l2_event *ev, struct v4l2_ctrl *ctrl, u32 change if (ctrl->is_ptr) ev->u.ctrl.value64 = 0; else - ev->u.ctrl.value64 = ctrl->cur.val64; + ev->u.ctrl.value64 = *ctrl->p_cur.p_s64; ev->u.ctrl.minimum = ctrl->minimum; ev->u.ctrl.maximum = ctrl->maximum; if (ctrl->type == V4L2_CTRL_TYPE_MENU @@ -1801,7 +1801,9 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, flags |= V4L2_CTRL_FLAG_WRITE_ONLY; else if (type == V4L2_CTRL_TYPE_CTRL_CLASS) flags |= V4L2_CTRL_FLAG_READ_ONLY; - else if (type == V4L2_CTRL_TYPE_STRING || type >= V4L2_CTRL_COMPOUND_TYPES) + else if (type == V4L2_CTRL_TYPE_INTEGER64 || + type == V4L2_CTRL_TYPE_STRING || + type >= V4L2_CTRL_COMPOUND_TYPES) sz_extra += 2 * elem_size; ctrl = kzalloc(sizeof(*ctrl) + sz_extra, GFP_KERNEL); @@ -1833,10 +1835,10 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, ctrl->qmenu_int = qmenu_int; ctrl->priv = priv; ctrl->cur.val = ctrl->val = def; - data = &ctrl->cur + 1; + data = &ctrl[1]; - if (ctrl->is_ptr) { - ctrl->p = ctrl->p_new.p = data; + if (!ctrl->is_int) { + ctrl->p_new.p = data; ctrl->p_cur.p = data + elem_size; } else { ctrl->p_new.p = &ctrl->val; @@ -3103,10 +3105,10 @@ int v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl, ctrl->maximum = max; ctrl->step = step; ctrl->default_value = def; - c.value = ctrl->cur.val; + c.value = *ctrl->p_cur.p_s32; if (validate_new(ctrl, &c)) c.value = def; - if (c.value != ctrl->cur.val) + if (c.value != *ctrl->p_cur.p_s32) ret = set_ctrl(NULL, ctrl, &c, V4L2_EVENT_CTRL_CH_RANGE); else send_event(NULL, ctrl, V4L2_EVENT_CTRL_CH_RANGE); -- cgit v1.1 From 20d88eef66a86989ea3cffe2a4e0d16cbf2d4563 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 12 Jun 2014 07:55:21 -0300 Subject: [media] v4l2-ctrls: prepare for array support Add dims, nr_of_dims and elems fields to the core control structures in preparation for N-dimensional array support. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 33 ++++++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 9 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 4b10571..e6c98a7 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -1759,18 +1759,27 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, const struct v4l2_ctrl_type_ops *type_ops, u32 id, const char *name, enum v4l2_ctrl_type type, s64 min, s64 max, u64 step, s64 def, - u32 elem_size, + const u32 dims[V4L2_CTRL_MAX_DIMS], u32 elem_size, u32 flags, const char * const *qmenu, const s64 *qmenu_int, void *priv) { struct v4l2_ctrl *ctrl; unsigned sz_extra; + unsigned nr_of_dims = 0; + unsigned elems = 1; void *data; int err; if (hdl->error) return NULL; + while (dims && dims[nr_of_dims]) { + elems *= dims[nr_of_dims]; + nr_of_dims++; + if (nr_of_dims == V4L2_CTRL_MAX_DIMS) + break; + } + if (type == V4L2_CTRL_TYPE_INTEGER64) elem_size = sizeof(s64); else if (type == V4L2_CTRL_TYPE_STRING) @@ -1828,6 +1837,10 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, ctrl->is_string = type == V4L2_CTRL_TYPE_STRING; ctrl->is_ptr = type >= V4L2_CTRL_COMPOUND_TYPES || ctrl->is_string; ctrl->is_int = !ctrl->is_ptr && type != V4L2_CTRL_TYPE_INTEGER64; + ctrl->elems = elems; + ctrl->nr_of_dims = nr_of_dims; + if (nr_of_dims) + memcpy(ctrl->dims, dims, nr_of_dims * sizeof(dims[0])); ctrl->elem_size = elem_size; if (type == V4L2_CTRL_TYPE_MENU) ctrl->qmenu = qmenu; @@ -1892,8 +1905,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_custom(struct v4l2_ctrl_handler *hdl, ctrl = v4l2_ctrl_new(hdl, cfg->ops, cfg->type_ops, cfg->id, name, type, min, max, - is_menu ? cfg->menu_skip_mask : step, - def, cfg->elem_size, + is_menu ? cfg->menu_skip_mask : step, def, + cfg->dims, cfg->elem_size, flags, qmenu, qmenu_int, priv); if (ctrl) ctrl->is_private = cfg->is_private; @@ -1918,7 +1931,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_std(struct v4l2_ctrl_handler *hdl, return NULL; } return v4l2_ctrl_new(hdl, ops, NULL, id, name, type, - min, max, step, def, 0, + min, max, step, def, NULL, 0, flags, NULL, NULL, NULL); } EXPORT_SYMBOL(v4l2_ctrl_new_std); @@ -1951,7 +1964,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu(struct v4l2_ctrl_handler *hdl, return NULL; } return v4l2_ctrl_new(hdl, ops, NULL, id, name, type, - 0, max, mask, def, 0, + 0, max, mask, def, NULL, 0, flags, qmenu, qmenu_int, NULL); } EXPORT_SYMBOL(v4l2_ctrl_new_std_menu); @@ -1983,8 +1996,8 @@ struct v4l2_ctrl *v4l2_ctrl_new_std_menu_items(struct v4l2_ctrl_handler *hdl, return NULL; } return v4l2_ctrl_new(hdl, ops, NULL, id, name, type, - 0, max, mask, def, - 0, flags, qmenu, NULL, NULL); + 0, max, mask, def, NULL, 0, + flags, qmenu, NULL, NULL); } EXPORT_SYMBOL(v4l2_ctrl_new_std_menu_items); @@ -2008,7 +2021,7 @@ struct v4l2_ctrl *v4l2_ctrl_new_int_menu(struct v4l2_ctrl_handler *hdl, return NULL; } return v4l2_ctrl_new(hdl, ops, NULL, id, name, type, - 0, max, 0, def, 0, + 0, max, 0, def, NULL, 0, flags, NULL, qmenu_int, NULL); } EXPORT_SYMBOL(v4l2_ctrl_new_int_menu); @@ -2354,7 +2367,9 @@ int v4l2_query_ext_ctrl(struct v4l2_ctrl_handler *hdl, struct v4l2_query_ext_ctr if (ctrl->is_ptr) qc->flags |= V4L2_CTRL_FLAG_HAS_PAYLOAD; qc->elem_size = ctrl->elem_size; - qc->elems = 1; + qc->elems = ctrl->elems; + qc->nr_of_dims = ctrl->nr_of_dims; + memcpy(qc->dims, ctrl->dims, qc->nr_of_dims * sizeof(qc->dims[0])); qc->minimum = ctrl->minimum; qc->maximum = ctrl->maximum; qc->default_value = ctrl->default_value; -- cgit v1.1 From 998e7659150760c0f4871ee20de2ef2276e3f80a Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 10 Jun 2014 07:55:00 -0300 Subject: [media] v4l2-ctrls: prepare for array support Add core support for N-dimensional arrays. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 58 ++++++++++++++++++++++++------------ 1 file changed, 39 insertions(+), 19 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index e6c98a7..6ed2d56 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -1161,7 +1161,7 @@ static void send_event(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, u32 changes) v4l2_event_queue_fh(sev->fh, &ev); } -static bool std_equal(const struct v4l2_ctrl *ctrl, +static bool std_equal(const struct v4l2_ctrl *ctrl, u32 idx, union v4l2_ctrl_ptr ptr1, union v4l2_ctrl_ptr ptr2) { @@ -1180,7 +1180,7 @@ static bool std_equal(const struct v4l2_ctrl *ctrl, } } -static void std_init(const struct v4l2_ctrl *ctrl, +static void std_init(const struct v4l2_ctrl *ctrl, u32 idx, union v4l2_ctrl_ptr ptr) { switch (ctrl->type) { @@ -1207,6 +1207,14 @@ static void std_log(const struct v4l2_ctrl *ctrl) { union v4l2_ctrl_ptr ptr = ctrl->p_cur; + if (ctrl->is_array) { + unsigned i; + + for (i = 0; i < ctrl->nr_of_dims; i++) + pr_cont("[%u]", ctrl->dims[i]); + pr_cont(" "); + } + switch (ctrl->type) { case V4L2_CTRL_TYPE_INTEGER: pr_cont("%d", *ptr.p_s32); @@ -1249,7 +1257,7 @@ static void std_log(const struct v4l2_ctrl *ctrl) }) /* Validate a new control */ -static int std_validate(const struct v4l2_ctrl *ctrl, +static int std_validate(const struct v4l2_ctrl *ctrl, u32 idx, union v4l2_ctrl_ptr ptr) { size_t len; @@ -1473,8 +1481,8 @@ static int cluster_changed(struct v4l2_ctrl *master) if (ctrl == NULL) continue; - ctrl->has_changed = !ctrl->type_ops->equal(ctrl, - ctrl->p_cur, ctrl->p_new); + ctrl->has_changed = !ctrl->type_ops->equal(ctrl, 0, + ctrl->p_cur, ctrl->p_new); changed |= ctrl->has_changed; } return changed; @@ -1531,15 +1539,15 @@ static int validate_new(const struct v4l2_ctrl *ctrl, case V4L2_CTRL_TYPE_BUTTON: case V4L2_CTRL_TYPE_CTRL_CLASS: ptr.p_s32 = &c->value; - return ctrl->type_ops->validate(ctrl, ptr); + return ctrl->type_ops->validate(ctrl, 0, ptr); case V4L2_CTRL_TYPE_INTEGER64: ptr.p_s64 = &c->value64; - return ctrl->type_ops->validate(ctrl, ptr); + return ctrl->type_ops->validate(ctrl, 0, ptr); default: ptr.p = c->ptr; - return ctrl->type_ops->validate(ctrl, ptr); + return ctrl->type_ops->validate(ctrl, 0, ptr); } } @@ -1767,6 +1775,8 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, unsigned sz_extra; unsigned nr_of_dims = 0; unsigned elems = 1; + bool is_array; + unsigned tot_ctrl_size; void *data; int err; @@ -1779,6 +1789,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, if (nr_of_dims == V4L2_CTRL_MAX_DIMS) break; } + is_array = nr_of_dims > 0; if (type == V4L2_CTRL_TYPE_INTEGER64) elem_size = sizeof(s64); @@ -1786,10 +1797,11 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, elem_size = max + 1; else if (type < V4L2_CTRL_COMPOUND_TYPES) elem_size = sizeof(s32); + tot_ctrl_size = elem_size * elems; /* Sanity checks */ - if (id == 0 || name == NULL || id >= V4L2_CID_PRIVATE_BASE || - elem_size == 0 || + if (id == 0 || name == NULL || !elem_size || + id >= V4L2_CID_PRIVATE_BASE || (type == V4L2_CTRL_TYPE_MENU && qmenu == NULL) || (type == V4L2_CTRL_TYPE_INTEGER_MENU && qmenu_int == NULL)) { handler_set_err(hdl, -ERANGE); @@ -1804,6 +1816,12 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, handler_set_err(hdl, -ERANGE); return NULL; } + if (is_array && + (type == V4L2_CTRL_TYPE_BUTTON || + type == V4L2_CTRL_TYPE_CTRL_CLASS)) { + handler_set_err(hdl, -EINVAL); + return NULL; + } sz_extra = 0; if (type == V4L2_CTRL_TYPE_BUTTON) @@ -1812,8 +1830,9 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, flags |= V4L2_CTRL_FLAG_READ_ONLY; else if (type == V4L2_CTRL_TYPE_INTEGER64 || type == V4L2_CTRL_TYPE_STRING || - type >= V4L2_CTRL_COMPOUND_TYPES) - sz_extra += 2 * elem_size; + type >= V4L2_CTRL_COMPOUND_TYPES || + is_array) + sz_extra += 2 * tot_ctrl_size; ctrl = kzalloc(sizeof(*ctrl) + sz_extra, GFP_KERNEL); if (ctrl == NULL) { @@ -1834,9 +1853,10 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, ctrl->maximum = max; ctrl->step = step; ctrl->default_value = def; - ctrl->is_string = type == V4L2_CTRL_TYPE_STRING; - ctrl->is_ptr = type >= V4L2_CTRL_COMPOUND_TYPES || ctrl->is_string; + ctrl->is_string = !is_array && type == V4L2_CTRL_TYPE_STRING; + ctrl->is_ptr = is_array || type >= V4L2_CTRL_COMPOUND_TYPES || ctrl->is_string; ctrl->is_int = !ctrl->is_ptr && type != V4L2_CTRL_TYPE_INTEGER64; + ctrl->is_array = is_array; ctrl->elems = elems; ctrl->nr_of_dims = nr_of_dims; if (nr_of_dims) @@ -1852,13 +1872,13 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, if (!ctrl->is_int) { ctrl->p_new.p = data; - ctrl->p_cur.p = data + elem_size; + ctrl->p_cur.p = data + tot_ctrl_size; } else { ctrl->p_new.p = &ctrl->val; ctrl->p_cur.p = &ctrl->cur.val; } - ctrl->type_ops->init(ctrl, ctrl->p_cur); - ctrl->type_ops->init(ctrl, ctrl->p_new); + ctrl->type_ops->init(ctrl, 0, ctrl->p_cur); + ctrl->type_ops->init(ctrl, 0, ctrl->p_new); if (handler_new_ref(hdl, ctrl)) { kfree(ctrl); @@ -2764,7 +2784,7 @@ s64 v4l2_ctrl_g_ctrl_int64(struct v4l2_ctrl *ctrl) struct v4l2_ext_control c; /* It's a driver bug if this happens. */ - WARN_ON(ctrl->type != V4L2_CTRL_TYPE_INTEGER64); + WARN_ON(ctrl->is_ptr || ctrl->type != V4L2_CTRL_TYPE_INTEGER64); c.value = 0; get_ctrl(ctrl, &c); return c.value; @@ -3074,7 +3094,7 @@ int v4l2_ctrl_s_ctrl_int64(struct v4l2_ctrl *ctrl, s64 val) struct v4l2_ext_control c; /* It's a driver bug if this happens. */ - WARN_ON(ctrl->type != V4L2_CTRL_TYPE_INTEGER64); + WARN_ON(ctrl->is_ptr || ctrl->type != V4L2_CTRL_TYPE_INTEGER64); c.value64 = val; return set_ctrl_lock(NULL, ctrl, &c); } -- cgit v1.1 From 265c7f8a72e972a707246a2bb2c492524aa95ea4 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sun, 5 Jan 2014 11:06:05 -0300 Subject: [media] v4l2-ctrls: type_ops can handle array elements Extend the control type operations to handle N-dimensional array elements. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 40 ++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 18 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 6ed2d56..f6ac927 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -1169,14 +1169,16 @@ static bool std_equal(const struct v4l2_ctrl *ctrl, u32 idx, case V4L2_CTRL_TYPE_BUTTON: return false; case V4L2_CTRL_TYPE_STRING: + idx *= ctrl->elem_size; /* strings are always 0-terminated */ - return !strcmp(ptr1.p_char, ptr2.p_char); + return !strcmp(ptr1.p_char + idx, ptr2.p_char + idx); case V4L2_CTRL_TYPE_INTEGER64: - return *ptr1.p_s64 == *ptr2.p_s64; + return ptr1.p_s64[idx] == ptr2.p_s64[idx]; default: - if (ctrl->is_ptr) - return !memcmp(ptr1.p, ptr2.p, ctrl->elem_size); - return *ptr1.p_s32 == *ptr2.p_s32; + if (ctrl->is_int) + return ptr1.p_s32[idx] == ptr2.p_s32[idx]; + idx *= ctrl->elem_size; + return !memcmp(ptr1.p + idx, ptr2.p + idx, ctrl->elem_size); } } @@ -1185,18 +1187,19 @@ static void std_init(const struct v4l2_ctrl *ctrl, u32 idx, { switch (ctrl->type) { case V4L2_CTRL_TYPE_STRING: - memset(ptr.p_char, ' ', ctrl->minimum); - ptr.p_char[ctrl->minimum] = '\0'; + idx *= ctrl->elem_size; + memset(ptr.p_char + idx, ' ', ctrl->minimum); + ptr.p_char[idx + ctrl->minimum] = '\0'; break; case V4L2_CTRL_TYPE_INTEGER64: - *ptr.p_s64 = ctrl->default_value; + ptr.p_s64[idx] = ctrl->default_value; break; case V4L2_CTRL_TYPE_INTEGER: case V4L2_CTRL_TYPE_INTEGER_MENU: case V4L2_CTRL_TYPE_MENU: case V4L2_CTRL_TYPE_BITMASK: case V4L2_CTRL_TYPE_BOOLEAN: - *ptr.p_s32 = ctrl->default_value; + ptr.p_s32[idx] = ctrl->default_value; break; default: break; @@ -1264,36 +1267,37 @@ static int std_validate(const struct v4l2_ctrl *ctrl, u32 idx, switch (ctrl->type) { case V4L2_CTRL_TYPE_INTEGER: - return ROUND_TO_RANGE(*ptr.p_s32, u32, ctrl); + return ROUND_TO_RANGE(ptr.p_s32[idx], u32, ctrl); case V4L2_CTRL_TYPE_INTEGER64: - return ROUND_TO_RANGE(*ptr.p_s64, u64, ctrl); + return ROUND_TO_RANGE(ptr.p_s64[idx], u64, ctrl); case V4L2_CTRL_TYPE_BOOLEAN: - *ptr.p_s32 = !!*ptr.p_s32; + ptr.p_s32[idx] = !!ptr.p_s32[idx]; return 0; case V4L2_CTRL_TYPE_MENU: case V4L2_CTRL_TYPE_INTEGER_MENU: - if (*ptr.p_s32 < ctrl->minimum || *ptr.p_s32 > ctrl->maximum) + if (ptr.p_s32[idx] < ctrl->minimum || ptr.p_s32[idx] > ctrl->maximum) return -ERANGE; - if (ctrl->menu_skip_mask & (1 << *ptr.p_s32)) + if (ctrl->menu_skip_mask & (1 << ptr.p_s32[idx])) return -EINVAL; if (ctrl->type == V4L2_CTRL_TYPE_MENU && - ctrl->qmenu[*ptr.p_s32][0] == '\0') + ctrl->qmenu[ptr.p_s32[idx]][0] == '\0') return -EINVAL; return 0; case V4L2_CTRL_TYPE_BITMASK: - *ptr.p_s32 &= ctrl->maximum; + ptr.p_s32[idx] &= ctrl->maximum; return 0; case V4L2_CTRL_TYPE_BUTTON: case V4L2_CTRL_TYPE_CTRL_CLASS: - *ptr.p_s32 = 0; + ptr.p_s32[idx] = 0; return 0; case V4L2_CTRL_TYPE_STRING: - len = strlen(ptr.p_char); + idx *= ctrl->elem_size; + len = strlen(ptr.p_char + idx); if (len < ctrl->minimum) return -ERANGE; if ((len - ctrl->minimum) % ctrl->step) -- cgit v1.1 From 302ab7ce2daba8cdd82a6809adb42d117a683f06 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 10 Jun 2014 07:06:50 -0300 Subject: [media] v4l2-ctrls: add array support Finish the userspace-facing array support. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 109 ++++++++++++++++++++--------------- 1 file changed, 63 insertions(+), 46 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index f6ac927..b3ab8a9 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -1202,6 +1202,8 @@ static void std_init(const struct v4l2_ctrl *ctrl, u32 idx, ptr.p_s32[idx] = ctrl->default_value; break; default: + idx *= ctrl->elem_size; + memset(ptr.p + idx, 0, ctrl->elem_size); break; } } @@ -1324,7 +1326,7 @@ static int ptr_to_user(struct v4l2_ext_control *c, u32 len; if (ctrl->is_ptr && !ctrl->is_string) - return copy_to_user(c->ptr, ptr.p, ctrl->elem_size); + return copy_to_user(c->ptr, ptr.p, c->size); switch (ctrl->type) { case V4L2_CTRL_TYPE_STRING: @@ -1368,8 +1370,16 @@ static int user_to_ptr(struct v4l2_ext_control *c, u32 size; ctrl->is_new = 1; - if (ctrl->is_ptr && !ctrl->is_string) - return copy_from_user(ptr.p, c->ptr, ctrl->elem_size); + if (ctrl->is_ptr && !ctrl->is_string) { + unsigned idx; + + ret = copy_from_user(ptr.p, c->ptr, c->size); + if (ret || !ctrl->is_array) + return ret; + for (idx = c->size / ctrl->elem_size; idx < ctrl->elems; idx++) + ctrl->type_ops->init(ctrl, idx, ptr); + return 0; + } switch (ctrl->type) { case V4L2_CTRL_TYPE_INTEGER64: @@ -1412,21 +1422,7 @@ static void ptr_to_ptr(struct v4l2_ctrl *ctrl, { if (ctrl == NULL) return; - switch (ctrl->type) { - case V4L2_CTRL_TYPE_STRING: - /* strings are always 0-terminated */ - strcpy(to.p_char, from.p_char); - break; - case V4L2_CTRL_TYPE_INTEGER64: - *to.p_s64 = *from.p_s64; - break; - default: - if (ctrl->is_ptr) - memcpy(to.p, from.p, ctrl->elem_size); - else - *to.p_s32 = *from.p_s32; - break; - } + memcpy(to.p, from.p, ctrl->elems * ctrl->elem_size); } /* Copy the new value to the current value. */ @@ -1478,15 +1474,19 @@ static void cur_to_new(struct v4l2_ctrl *ctrl) static int cluster_changed(struct v4l2_ctrl *master) { bool changed = false; + unsigned idx; int i; for (i = 0; i < master->ncontrols; i++) { struct v4l2_ctrl *ctrl = master->cluster[i]; + bool ctrl_changed = false; if (ctrl == NULL) continue; - ctrl->has_changed = !ctrl->type_ops->equal(ctrl, 0, + for (idx = 0; !ctrl_changed && idx < ctrl->elems; idx++) + ctrl_changed = !ctrl->type_ops->equal(ctrl, idx, ctrl->p_cur, ctrl->p_new); + ctrl->has_changed = ctrl_changed; changed |= ctrl->has_changed; } return changed; @@ -1533,26 +1533,32 @@ static int validate_new(const struct v4l2_ctrl *ctrl, struct v4l2_ext_control *c) { union v4l2_ctrl_ptr ptr; - - switch (ctrl->type) { - case V4L2_CTRL_TYPE_INTEGER: - case V4L2_CTRL_TYPE_INTEGER_MENU: - case V4L2_CTRL_TYPE_MENU: - case V4L2_CTRL_TYPE_BITMASK: - case V4L2_CTRL_TYPE_BOOLEAN: - case V4L2_CTRL_TYPE_BUTTON: - case V4L2_CTRL_TYPE_CTRL_CLASS: - ptr.p_s32 = &c->value; - return ctrl->type_ops->validate(ctrl, 0, ptr); - - case V4L2_CTRL_TYPE_INTEGER64: - ptr.p_s64 = &c->value64; - return ctrl->type_ops->validate(ctrl, 0, ptr); - - default: - ptr.p = c->ptr; - return ctrl->type_ops->validate(ctrl, 0, ptr); + unsigned idx; + int err = 0; + + if (!ctrl->is_ptr) { + switch (ctrl->type) { + case V4L2_CTRL_TYPE_INTEGER: + case V4L2_CTRL_TYPE_INTEGER_MENU: + case V4L2_CTRL_TYPE_MENU: + case V4L2_CTRL_TYPE_BITMASK: + case V4L2_CTRL_TYPE_BOOLEAN: + case V4L2_CTRL_TYPE_BUTTON: + case V4L2_CTRL_TYPE_CTRL_CLASS: + ptr.p_s32 = &c->value; + return ctrl->type_ops->validate(ctrl, 0, ptr); + + case V4L2_CTRL_TYPE_INTEGER64: + ptr.p_s64 = &c->value64; + return ctrl->type_ops->validate(ctrl, 0, ptr); + default: + break; + } } + ptr.p = c->ptr; + for (idx = 0; !err && idx < c->size / ctrl->elem_size; idx++) + err = ctrl->type_ops->validate(ctrl, idx, ptr); + return err; } static inline u32 node2id(struct list_head *node) @@ -1781,6 +1787,7 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, unsigned elems = 1; bool is_array; unsigned tot_ctrl_size; + unsigned idx; void *data; int err; @@ -1881,8 +1888,10 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, ctrl->p_new.p = &ctrl->val; ctrl->p_cur.p = &ctrl->cur.val; } - ctrl->type_ops->init(ctrl, 0, ctrl->p_cur); - ctrl->type_ops->init(ctrl, 0, ctrl->p_new); + for (idx = 0; idx < elems; idx++) { + ctrl->type_ops->init(ctrl, idx, ctrl->p_cur); + ctrl->type_ops->init(ctrl, idx, ctrl->p_new); + } if (handler_new_ref(hdl, ctrl)) { kfree(ctrl); @@ -2578,12 +2587,17 @@ static int prepare_ext_ctrls(struct v4l2_ctrl_handler *hdl, have_clusters = true; if (ctrl->cluster[0] != ctrl) ref = find_ref_lock(hdl, ctrl->cluster[0]->id); - if (ctrl->is_ptr && !ctrl->is_string && c->size < ctrl->elem_size) { - if (get) { - c->size = ctrl->elem_size; - return -ENOSPC; + if (ctrl->is_ptr && !ctrl->is_string) { + unsigned tot_size = ctrl->elems * ctrl->elem_size; + + if (c->size < tot_size) { + if (get) { + c->size = tot_size; + return -ENOSPC; + } + return -EFAULT; } - return -EFAULT; + c->size = tot_size; } /* Store the ref to the master control of the cluster */ h->mref = ref; @@ -3123,7 +3137,7 @@ EXPORT_SYMBOL(v4l2_ctrl_notify); int v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl, s64 min, s64 max, u64 step, s64 def) { - int ret = check_range(ctrl->type, min, max, step, def); + int ret; struct v4l2_ext_control c; switch (ctrl->type) { @@ -3133,6 +3147,9 @@ int v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl, case V4L2_CTRL_TYPE_MENU: case V4L2_CTRL_TYPE_INTEGER_MENU: case V4L2_CTRL_TYPE_BITMASK: + if (ctrl->is_array) + return -EINVAL; + ret = check_range(ctrl->type, min, max, step, def); if (ret) return ret; break; -- cgit v1.1 From c336f75e1e822658122b96d874da01f5f4df994d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 18 Jan 2014 06:06:01 -0300 Subject: [media] v4l2-ctrls: return elem_size instead of strlen When getting a string and the size given by the application is too short return the max length the string can have (elem_size) instead of the string length + 1. That makes more sense. Signed-off-by: Hans Verkuil Reviewed-by: Sylwester Nawrocki Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index b3ab8a9..e6e33b3 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -1332,7 +1332,7 @@ static int ptr_to_user(struct v4l2_ext_control *c, case V4L2_CTRL_TYPE_STRING: len = strlen(ptr.p_char); if (c->size < len + 1) { - c->size = len + 1; + c->size = ctrl->elem_size; return -ENOSPC; } return copy_to_user(c->string, ptr.p_char, len + 1) ? -- cgit v1.1 From 40265bbe42933920684136ff759e6340eaf72774 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 10 Jun 2014 07:07:38 -0300 Subject: [media] v4l2-ctrl: fix error return of copy_to/from_user copy_to/from_user returns the number of bytes not copied, it does not return a 'normal' linux error code. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index e6e33b3..1086ae3 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -1326,7 +1326,8 @@ static int ptr_to_user(struct v4l2_ext_control *c, u32 len; if (ctrl->is_ptr && !ctrl->is_string) - return copy_to_user(c->ptr, ptr.p, c->size); + return copy_to_user(c->ptr, ptr.p, c->size) ? + -EFAULT : 0; switch (ctrl->type) { case V4L2_CTRL_TYPE_STRING: @@ -1336,7 +1337,7 @@ static int ptr_to_user(struct v4l2_ext_control *c, return -ENOSPC; } return copy_to_user(c->string, ptr.p_char, len + 1) ? - -EFAULT : 0; + -EFAULT : 0; case V4L2_CTRL_TYPE_INTEGER64: c->value64 = *ptr.p_s64; break; @@ -1373,7 +1374,7 @@ static int user_to_ptr(struct v4l2_ext_control *c, if (ctrl->is_ptr && !ctrl->is_string) { unsigned idx; - ret = copy_from_user(ptr.p, c->ptr, c->size); + ret = copy_from_user(ptr.p, c->ptr, c->size) ? -EFAULT : 0; if (ret || !ctrl->is_array) return ret; for (idx = c->size / ctrl->elem_size; idx < ctrl->elems; idx++) @@ -1391,7 +1392,7 @@ static int user_to_ptr(struct v4l2_ext_control *c, return -ERANGE; if (size > ctrl->maximum + 1) size = ctrl->maximum + 1; - ret = copy_from_user(ptr.p_char, c->string, size); + ret = copy_from_user(ptr.p_char, c->string, size) ? -EFAULT : 0; if (!ret) { char last = ptr.p_char[size - 1]; @@ -1401,7 +1402,7 @@ static int user_to_ptr(struct v4l2_ext_control *c, if (strlen(ptr.p_char) == ctrl->maximum && last) return -ERANGE; } - return ret ? -EFAULT : 0; + return ret; default: *ptr.p_s32 = c->value; break; -- cgit v1.1 From dda4a4d5ea245591b788b70116fb52b0d145fb33 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Tue, 10 Jun 2014 07:30:04 -0300 Subject: [media] v4l2-ctrls/videodev2.h: add u8 and u16 types These are needed by the upcoming patches for the motion detection matrices. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 45 ++++++++++++++++++++++++++++++++---- 1 file changed, 41 insertions(+), 4 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 1086ae3..adf5485 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -1174,6 +1174,10 @@ static bool std_equal(const struct v4l2_ctrl *ctrl, u32 idx, return !strcmp(ptr1.p_char + idx, ptr2.p_char + idx); case V4L2_CTRL_TYPE_INTEGER64: return ptr1.p_s64[idx] == ptr2.p_s64[idx]; + case V4L2_CTRL_TYPE_U8: + return ptr1.p_u8[idx] == ptr2.p_u8[idx]; + case V4L2_CTRL_TYPE_U16: + return ptr1.p_u16[idx] == ptr2.p_u16[idx]; default: if (ctrl->is_int) return ptr1.p_s32[idx] == ptr2.p_s32[idx]; @@ -1201,6 +1205,12 @@ static void std_init(const struct v4l2_ctrl *ctrl, u32 idx, case V4L2_CTRL_TYPE_BOOLEAN: ptr.p_s32[idx] = ctrl->default_value; break; + case V4L2_CTRL_TYPE_U8: + ptr.p_u8[idx] = ctrl->default_value; + break; + case V4L2_CTRL_TYPE_U16: + ptr.p_u16[idx] = ctrl->default_value; + break; default: idx *= ctrl->elem_size; memset(ptr.p + idx, 0, ctrl->elem_size); @@ -1242,6 +1252,12 @@ static void std_log(const struct v4l2_ctrl *ctrl) case V4L2_CTRL_TYPE_STRING: pr_cont("%s", ptr.p_char); break; + case V4L2_CTRL_TYPE_U8: + pr_cont("%u", (unsigned)*ptr.p_u8); + break; + case V4L2_CTRL_TYPE_U16: + pr_cont("%u", (unsigned)*ptr.p_u16); + break; default: pr_cont("unknown type %d", ctrl->type); break; @@ -1272,6 +1288,10 @@ static int std_validate(const struct v4l2_ctrl *ctrl, u32 idx, return ROUND_TO_RANGE(ptr.p_s32[idx], u32, ctrl); case V4L2_CTRL_TYPE_INTEGER64: return ROUND_TO_RANGE(ptr.p_s64[idx], u64, ctrl); + case V4L2_CTRL_TYPE_U8: + return ROUND_TO_RANGE(ptr.p_u8[idx], u8, ctrl); + case V4L2_CTRL_TYPE_U16: + return ROUND_TO_RANGE(ptr.p_u16[idx], u16, ctrl); case V4L2_CTRL_TYPE_BOOLEAN: ptr.p_s32[idx] = !!ptr.p_s32[idx]; @@ -1502,6 +1522,8 @@ static int check_range(enum v4l2_ctrl_type type, if (step != 1 || max > 1 || min < 0) return -ERANGE; /* fall through */ + case V4L2_CTRL_TYPE_U8: + case V4L2_CTRL_TYPE_U16: case V4L2_CTRL_TYPE_INTEGER: case V4L2_CTRL_TYPE_INTEGER64: if (step == 0 || min > max || def < min || def > max) @@ -1803,12 +1825,25 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, } is_array = nr_of_dims > 0; - if (type == V4L2_CTRL_TYPE_INTEGER64) + /* Prefill elem_size for all types handled by std_type_ops */ + switch (type) { + case V4L2_CTRL_TYPE_INTEGER64: elem_size = sizeof(s64); - else if (type == V4L2_CTRL_TYPE_STRING) + break; + case V4L2_CTRL_TYPE_STRING: elem_size = max + 1; - else if (type < V4L2_CTRL_COMPOUND_TYPES) - elem_size = sizeof(s32); + break; + case V4L2_CTRL_TYPE_U8: + elem_size = sizeof(u8); + break; + case V4L2_CTRL_TYPE_U16: + elem_size = sizeof(u16); + break; + default: + if (type < V4L2_CTRL_COMPOUND_TYPES) + elem_size = sizeof(s32); + break; + } tot_ctrl_size = elem_size * elems; /* Sanity checks */ @@ -3148,6 +3183,8 @@ int v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl, case V4L2_CTRL_TYPE_MENU: case V4L2_CTRL_TYPE_INTEGER_MENU: case V4L2_CTRL_TYPE_BITMASK: + case V4L2_CTRL_TYPE_U8: + case V4L2_CTRL_TYPE_U16: if (ctrl->is_array) return -EINVAL; ret = check_range(ctrl->type, min, max, step, def); -- cgit v1.1 From 59253f29c0f3a8b2f6e5108610037150acade9e9 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 27 Jan 2014 11:03:34 -0300 Subject: [media] v4l2-ctrls: fix comments Various comments referred to videodev2.h, but the control definitions have been moved to v4l2-controls.h. Also add the same reminder message to each class of controls. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index adf5485..5aaf15e 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -592,7 +592,7 @@ const char *v4l2_ctrl_get_name(u32 id) { switch (id) { /* USER controls */ - /* Keep the order of the 'case's the same as in videodev2.h! */ + /* Keep the order of the 'case's the same as in v4l2-controls.h! */ case V4L2_CID_USER_CLASS: return "User Controls"; case V4L2_CID_BRIGHTNESS: return "Brightness"; case V4L2_CID_CONTRAST: return "Contrast"; @@ -754,7 +754,7 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_MPEG_VIDEO_VPX_PROFILE: return "VPX Profile"; /* CAMERA controls */ - /* Keep the order of the 'case's the same as in videodev2.h! */ + /* Keep the order of the 'case's the same as in v4l2-controls.h! */ case V4L2_CID_CAMERA_CLASS: return "Camera Controls"; case V4L2_CID_EXPOSURE_AUTO: return "Auto Exposure"; case V4L2_CID_EXPOSURE_ABSOLUTE: return "Exposure Time, Absolute"; @@ -788,8 +788,8 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_AUTO_FOCUS_STATUS: return "Auto Focus, Status"; case V4L2_CID_AUTO_FOCUS_RANGE: return "Auto Focus, Range"; - /* FM Radio Modulator control */ - /* Keep the order of the 'case's the same as in videodev2.h! */ + /* FM Radio Modulator controls */ + /* Keep the order of the 'case's the same as in v4l2-controls.h! */ case V4L2_CID_FM_TX_CLASS: return "FM Radio Modulator Controls"; case V4L2_CID_RDS_TX_DEVIATION: return "RDS Signal Deviation"; case V4L2_CID_RDS_TX_PI: return "RDS Program ID"; @@ -812,6 +812,7 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_TUNE_ANTENNA_CAPACITOR: return "Tune Antenna Capacitor"; /* Flash controls */ + /* Keep the order of the 'case's the same as in v4l2-controls.h! */ case V4L2_CID_FLASH_CLASS: return "Flash Controls"; case V4L2_CID_FLASH_LED_MODE: return "LED Mode"; case V4L2_CID_FLASH_STROBE_SOURCE: return "Strobe Source"; @@ -827,7 +828,7 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_FLASH_READY: return "Ready to Strobe"; /* JPEG encoder controls */ - /* Keep the order of the 'case's the same as in videodev2.h! */ + /* Keep the order of the 'case's the same as in v4l2-controls.h! */ case V4L2_CID_JPEG_CLASS: return "JPEG Compression Controls"; case V4L2_CID_JPEG_CHROMA_SUBSAMPLING: return "Chroma Subsampling"; case V4L2_CID_JPEG_RESTART_INTERVAL: return "Restart Interval"; @@ -835,18 +836,21 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_JPEG_ACTIVE_MARKER: return "Active Markers"; /* Image source controls */ + /* Keep the order of the 'case's the same as in v4l2-controls.h! */ case V4L2_CID_IMAGE_SOURCE_CLASS: return "Image Source Controls"; case V4L2_CID_VBLANK: return "Vertical Blanking"; case V4L2_CID_HBLANK: return "Horizontal Blanking"; case V4L2_CID_ANALOGUE_GAIN: return "Analogue Gain"; /* Image processing controls */ + /* Keep the order of the 'case's the same as in v4l2-controls.h! */ case V4L2_CID_IMAGE_PROC_CLASS: return "Image Processing Controls"; case V4L2_CID_LINK_FREQ: return "Link Frequency"; case V4L2_CID_PIXEL_RATE: return "Pixel Rate"; case V4L2_CID_TEST_PATTERN: return "Test Pattern"; /* DV controls */ + /* Keep the order of the 'case's the same as in v4l2-controls.h! */ case V4L2_CID_DV_CLASS: return "Digital Video Controls"; case V4L2_CID_DV_TX_HOTPLUG: return "Hotplug Present"; case V4L2_CID_DV_TX_RXSENSE: return "RxSense Present"; -- cgit v1.1 From a77b4fc0bc328b0989f83220aba1c268e087107f Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 28 Mar 2014 13:00:08 -0300 Subject: [media] v4l2-ctrls/v4l2-controls.h: add MD controls Add the 'Detect' control class and the new motion detection controls. Those controls will be used by the solo6x10 and go7007 drivers. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 5aaf15e..5c3b8de 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -462,6 +462,13 @@ const char * const *v4l2_ctrl_get_menu(u32 id) "RGB full range (0-255)", NULL, }; + static const char * const detect_md_mode[] = { + "Disabled", + "Global", + "Threshold Grid", + "Region Grid", + NULL, + }; switch (id) { @@ -553,6 +560,8 @@ const char * const *v4l2_ctrl_get_menu(u32 id) case V4L2_CID_DV_TX_RGB_RANGE: case V4L2_CID_DV_RX_RGB_RANGE: return dv_rgb_range; + case V4L2_CID_DETECT_MD_MODE: + return detect_md_mode; default: return NULL; @@ -874,6 +883,15 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_RF_TUNER_BANDWIDTH_AUTO: return "Bandwidth, Auto"; case V4L2_CID_RF_TUNER_BANDWIDTH: return "Bandwidth"; case V4L2_CID_RF_TUNER_PLL_LOCK: return "PLL Lock"; + + /* Detection controls */ + /* Keep the order of the 'case's the same as in v4l2-controls.h! */ + case V4L2_CID_DETECT_CLASS: return "Detection Controls"; + case V4L2_CID_DETECT_MD_MODE: return "Motion Detection Mode"; + case V4L2_CID_DETECT_MD_GLOBAL_THRESHOLD: return "MD Global Threshold"; + case V4L2_CID_DETECT_MD_THRESHOLD_GRID: return "MD Threshold Grid"; + case V4L2_CID_DETECT_MD_REGION_GRID: return "MD Region Grid"; + default: return NULL; } @@ -992,6 +1010,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_TEST_PATTERN: case V4L2_CID_TUNE_DEEMPHASIS: case V4L2_CID_MPEG_VIDEO_VPX_GOLDEN_FRAME_SEL: + case V4L2_CID_DETECT_MD_MODE: *type = V4L2_CTRL_TYPE_MENU; break; case V4L2_CID_LINK_FREQ: @@ -1018,6 +1037,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_DV_CLASS: case V4L2_CID_FM_RX_CLASS: case V4L2_CID_RF_TUNER_CLASS: + case V4L2_CID_DETECT_CLASS: *type = V4L2_CTRL_TYPE_CTRL_CLASS; /* You can neither read not write these */ *flags |= V4L2_CTRL_FLAG_READ_ONLY | V4L2_CTRL_FLAG_WRITE_ONLY; @@ -1063,6 +1083,12 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, *type = V4L2_CTRL_TYPE_INTEGER64; *flags |= V4L2_CTRL_FLAG_READ_ONLY; break; + case V4L2_CID_DETECT_MD_REGION_GRID: + *type = V4L2_CTRL_TYPE_U8; + break; + case V4L2_CID_DETECT_MD_THRESHOLD_GRID: + *type = V4L2_CTRL_TYPE_U16; + break; default: *type = V4L2_CTRL_TYPE_INTEGER; break; @@ -1103,6 +1129,7 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_RF_TUNER_MIXER_GAIN: case V4L2_CID_RF_TUNER_IF_GAIN: case V4L2_CID_RF_TUNER_BANDWIDTH: + case V4L2_CID_DETECT_MD_GLOBAL_THRESHOLD: *flags |= V4L2_CTRL_FLAG_SLIDER; break; case V4L2_CID_PAN_RELATIVE: -- cgit v1.1 From 0d5e8c4313c83dc2d60519a219d517a13ba8a432 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 17 Jul 2014 12:31:23 -0300 Subject: [media] Fix 64-bit division fall-out from 64-bit control ranges Commit 0ba2aeb6dab80920edd9cf5b93b1ea4d6913b8f3 increased the internal control ranges to 64 bit, but that caused problems in drivers that use the minimum/maximum/step/default_value control values in a division or modulus operations since not all architectures support those natively. Luckily, in almost all cases it is possible to just cast to 32 bits (the control value is known to be 32 bits, so it is safe to cast). Only in v4l2-ctrls.c was it necessary to use do_div in one function. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 5c3b8de..8552c83 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -1303,7 +1303,7 @@ static void std_log(const struct v4l2_ctrl *ctrl) val = clamp_t(typeof(val), val, \ (ctrl)->minimum, (ctrl)->maximum); \ offset = (val) - (ctrl)->minimum; \ - offset = (ctrl)->step * (offset / (ctrl)->step); \ + offset = (ctrl)->step * (offset / (s32)(ctrl)->step); \ val = (ctrl)->minimum + offset; \ 0; \ }) @@ -1313,12 +1313,24 @@ static int std_validate(const struct v4l2_ctrl *ctrl, u32 idx, union v4l2_ctrl_ptr ptr) { size_t len; + u64 offset; + s64 val; switch (ctrl->type) { case V4L2_CTRL_TYPE_INTEGER: return ROUND_TO_RANGE(ptr.p_s32[idx], u32, ctrl); case V4L2_CTRL_TYPE_INTEGER64: - return ROUND_TO_RANGE(ptr.p_s64[idx], u64, ctrl); + /* + * We can't use the ROUND_TO_RANGE define here due to + * the u64 divide that needs special care. + */ + val = ptr.p_s64[idx]; + val += ctrl->step / 2; + val = clamp_t(s64, val, ctrl->minimum, ctrl->maximum); + offset = val - ctrl->minimum; + do_div(offset, ctrl->step); + ptr.p_s64[idx] = ctrl->minimum + offset * ctrl->step; + return 0; case V4L2_CTRL_TYPE_U8: return ROUND_TO_RANGE(ptr.p_u8[idx], u8, ctrl); case V4L2_CTRL_TYPE_U16: @@ -1353,7 +1365,7 @@ static int std_validate(const struct v4l2_ctrl *ctrl, u32 idx, len = strlen(ptr.p_char + idx); if (len < ctrl->minimum) return -ERANGE; - if ((len - ctrl->minimum) % ctrl->step) + if ((len - (u32)ctrl->minimum) % (u32)ctrl->step) return -ERANGE; return 0; -- cgit v1.1 From d52e23813672c3c72f92e7b39c7408d4b9a40a96 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 27 May 2014 09:41:05 -0300 Subject: [media] v4l: Support extending the v4l2_pix_format structure The v4l2_pix_format structure has no reserved field. It is embedded in the v4l2_framebuffer structure which has no reserved fields either, and in the v4l2_format structure which has reserved fields that were not previously required to be zeroed out by applications. To allow extending v4l2_pix_format, inline it in the v4l2_framebuffer structure, and use the priv field as a magic value to indicate that the application has set all v4l2_pix_format extended fields and zeroed all reserved fields following the v4l2_pix_format field in the v4l2_format structure. The availability of this API extension is reported to userspace through the new V4L2_CAP_EXT_PIX_FORMAT capability flag. Just checking that the priv field is still set to the magic value at [GS]_FMT return wouldn't be enough, as older kernels don't zero the priv field on return. To simplify the internal API towards drivers zero the extended fields and set the priv field to the magic value for applications not aware of the extensions. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-compat-ioctl32.c | 19 +++++--- drivers/media/v4l2-core/v4l2-ioctl.c | 65 +++++++++++++++++++++++++-- 2 files changed, 75 insertions(+), 9 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c index 7e2411c..cca6c2f 100644 --- a/drivers/media/v4l2-core/v4l2-compat-ioctl32.c +++ b/drivers/media/v4l2-core/v4l2-compat-ioctl32.c @@ -540,7 +540,16 @@ struct v4l2_framebuffer32 { __u32 capability; __u32 flags; compat_caddr_t base; - struct v4l2_pix_format fmt; + struct { + __u32 width; + __u32 height; + __u32 pixelformat; + __u32 field; + __u32 bytesperline; + __u32 sizeimage; + __u32 colorspace; + __u32 priv; + } fmt; }; static int get_v4l2_framebuffer32(struct v4l2_framebuffer *kp, struct v4l2_framebuffer32 __user *up) @@ -550,10 +559,10 @@ static int get_v4l2_framebuffer32(struct v4l2_framebuffer *kp, struct v4l2_frame if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_framebuffer32)) || get_user(tmp, &up->base) || get_user(kp->capability, &up->capability) || - get_user(kp->flags, &up->flags)) + get_user(kp->flags, &up->flags) || + copy_from_user(&kp->fmt, &up->fmt, sizeof(up->fmt))) return -EFAULT; kp->base = compat_ptr(tmp); - get_v4l2_pix_format(&kp->fmt, &up->fmt); return 0; } @@ -564,9 +573,9 @@ static int put_v4l2_framebuffer32(struct v4l2_framebuffer *kp, struct v4l2_frame if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_framebuffer32)) || put_user(tmp, &up->base) || put_user(kp->capability, &up->capability) || - put_user(kp->flags, &up->flags)) + put_user(kp->flags, &up->flags) || + copy_to_user(&up->fmt, &kp->fmt, sizeof(up->fmt))) return -EFAULT; - put_v4l2_pix_format(&kp->fmt, &up->fmt); return 0; } diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 96bc117..2e63000 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -973,13 +973,48 @@ static int check_fmt(struct file *file, enum v4l2_buf_type type) return -EINVAL; } +static void v4l_sanitize_format(struct v4l2_format *fmt) +{ + unsigned int offset; + + /* + * The v4l2_pix_format structure has been extended with fields that were + * not previously required to be set to zero by applications. The priv + * field, when set to a magic value, indicates the the extended fields + * are valid. Otherwise they will contain undefined values. To simplify + * the API towards drivers zero the extended fields and set the priv + * field to the magic value when the extended pixel format structure + * isn't used by applications. + */ + + if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE && + fmt->type != V4L2_BUF_TYPE_VIDEO_OUTPUT) + return; + + if (fmt->fmt.pix.priv == V4L2_PIX_FMT_PRIV_MAGIC) + return; + + fmt->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC; + + offset = offsetof(struct v4l2_pix_format, priv) + + sizeof(fmt->fmt.pix.priv); + memset(((void *)&fmt->fmt.pix) + offset, 0, + sizeof(fmt->fmt.pix) - offset); +} + static int v4l_querycap(const struct v4l2_ioctl_ops *ops, struct file *file, void *fh, void *arg) { struct v4l2_capability *cap = (struct v4l2_capability *)arg; + int ret; cap->version = LINUX_VERSION_CODE; - return ops->vidioc_querycap(file, fh, cap); + + ret = ops->vidioc_querycap(file, fh, cap); + + cap->capabilities |= V4L2_CAP_EXT_PIX_FORMAT; + + return ret; } static int v4l_s_input(const struct v4l2_ioctl_ops *ops, @@ -1103,12 +1138,17 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops, bool is_sdr = vfd->vfl_type == VFL_TYPE_SDR; bool is_rx = vfd->vfl_dir != VFL_DIR_TX; bool is_tx = vfd->vfl_dir != VFL_DIR_RX; + int ret; + + p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC; switch (p->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: if (unlikely(!is_rx || !is_vid || !ops->vidioc_g_fmt_vid_cap)) break; - return ops->vidioc_g_fmt_vid_cap(file, fh, arg); + ret = ops->vidioc_g_fmt_vid_cap(file, fh, arg); + p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC; + return ret; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: if (unlikely(!is_rx || !is_vid || !ops->vidioc_g_fmt_vid_cap_mplane)) break; @@ -1128,7 +1168,9 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops, case V4L2_BUF_TYPE_VIDEO_OUTPUT: if (unlikely(!is_tx || !is_vid || !ops->vidioc_g_fmt_vid_out)) break; - return ops->vidioc_g_fmt_vid_out(file, fh, arg); + ret = ops->vidioc_g_fmt_vid_out(file, fh, arg); + p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC; + return ret; case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: if (unlikely(!is_tx || !is_vid || !ops->vidioc_g_fmt_vid_out_mplane)) break; @@ -1163,6 +1205,8 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops, bool is_rx = vfd->vfl_dir != VFL_DIR_TX; bool is_tx = vfd->vfl_dir != VFL_DIR_RX; + v4l_sanitize_format(p); + switch (p->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: if (unlikely(!is_rx || !is_vid || !ops->vidioc_s_fmt_vid_cap)) @@ -1233,6 +1277,8 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops, bool is_rx = vfd->vfl_dir != VFL_DIR_TX; bool is_tx = vfd->vfl_dir != VFL_DIR_RX; + v4l_sanitize_format(p); + switch (p->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: if (unlikely(!is_rx || !is_vid || !ops->vidioc_try_fmt_vid_cap)) @@ -1516,7 +1562,18 @@ static int v4l_create_bufs(const struct v4l2_ioctl_ops *ops, struct v4l2_create_buffers *create = arg; int ret = check_fmt(file, create->format.type); - return ret ? ret : ops->vidioc_create_bufs(file, fh, create); + if (ret) + return ret; + + v4l_sanitize_format(&create->format); + + ret = ops->vidioc_create_bufs(file, fh, create); + + if (create->format.type == V4L2_BUF_TYPE_VIDEO_CAPTURE || + create->format.type == V4L2_BUF_TYPE_VIDEO_OUTPUT) + create->format.fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC; + + return ret; } static int v4l_prepare_buf(const struct v4l2_ioctl_ops *ops, -- cgit v1.1 From c96fd46afb34a554406bce9784126b96ad09091e Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 27 May 2014 10:12:43 -0300 Subject: [media] v4l: Add premultiplied alpha flag for pixel formats When set, the new V4L2_PIX_FMT_FLAG_PREMUL_ALPHA flag indicates that the pixel values are premultiplied by the alpha channel value. Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ioctl.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 2e63000..e0bafda 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -256,7 +256,8 @@ static void v4l_print_format(const void *arg, bool write_only) pix = &p->fmt.pix; pr_cont(", width=%u, height=%u, " "pixelformat=%c%c%c%c, field=%s, " - "bytesperline=%u, sizeimage=%u, colorspace=%d\n", + "bytesperline=%u, sizeimage=%u, colorspace=%d, " + "flags %u\n", pix->width, pix->height, (pix->pixelformat & 0xff), (pix->pixelformat >> 8) & 0xff, @@ -264,7 +265,7 @@ static void v4l_print_format(const void *arg, bool write_only) (pix->pixelformat >> 24) & 0xff, prt_names(pix->field, v4l2_field_names), pix->bytesperline, pix->sizeimage, - pix->colorspace); + pix->colorspace, pix->flags); break; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: -- cgit v1.1 From bd994ddb2a12a3ff48cd549ec82cdceaea9614df Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Mon, 23 Jun 2014 18:00:22 -0300 Subject: [media] v4l: vb2: Fix stream start and buffer completion race videobuf2 stores the driver streaming state internally in the queue in the start_streaming_called variable. The state is set right after the driver start_stream operation returns, and checked in the vb2_buffer_done() function, typically called from the frame completion interrupt handler. A race condition exists if the hardware finishes processing the first frame before the start_stream operation returns. Fix this by setting start_streaming_called to 1 before calling the start_stream operation, and resetting it to 0 if the operation fails. Cc: stable@vger.kernel.org # for v3.15 and up Signed-off-by: Laurent Pinchart Reviewed-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/videobuf2-core.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index 7c4489c..1d67e95 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -1750,12 +1750,14 @@ static int vb2_start_streaming(struct vb2_queue *q) __enqueue_in_driver(vb); /* Tell the driver to start streaming */ + q->start_streaming_called = 1; ret = call_qop(q, start_streaming, q, atomic_read(&q->owned_by_drv_count)); - q->start_streaming_called = ret == 0; if (!ret) return 0; + q->start_streaming_called = 0; + dprintk(1, "driver refused to start streaming\n"); if (WARN_ON(atomic_read(&q->owned_by_drv_count))) { unsigned i; -- cgit v1.1 From 9241650d62f79a3da01f1d5e8ebd195083330b75 Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 3 Jun 2014 17:41:06 -0300 Subject: [media] v4l: vb2: Don't return POLLERR during transient buffer underruns The V4L2 specification states that "When the application did not call VIDIOC_QBUF or VIDIOC_STREAMON yet the poll() function succeeds, but sets the POLLERR flag in the revents field." The vb2_poll() function sets POLLERR when the queued buffers list is empty, regardless of whether this is caused by the stream not being active yet, or by a transient buffer underrun. Bring the implementation in line with the specification by returning POLLERR if no buffer has been queued only when the queue is not streaming. Buffer underruns during streaming are not treated specially anymore and just result in poll() blocking until the next event. Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Acked-by: Pawel Osciak Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/videobuf2-core.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index 1d67e95..1fc85fb 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -2559,9 +2559,10 @@ unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait) } /* - * There is nothing to wait for if no buffers have already been queued. + * There is nothing to wait for if no buffer has been queued and the + * queue isn't streaming. */ - if (list_empty(&q->queued_list)) + if (list_empty(&q->queued_list) && !vb2_is_streaming(q)) return res | POLLERR; if (list_empty(&q->done_list)) -- cgit v1.1 From 4bb7267dc41247810815e8b15f0e9fb1456c8d8c Mon Sep 17 00:00:00 2001 From: Laurent Pinchart Date: Tue, 3 Jun 2014 18:53:25 -0300 Subject: [media] v4l: vb2: Add fatal error condition flag When a fatal error occurs that render the device unusable, the only options for a driver to signal the error condition to userspace is to set the V4L2_BUF_FLAG_ERROR flag when dequeuing buffers and to return an error from the buffer prepare handler when queuing buffers. The buffer error flag indicates a transient error and can't be used by applications to detect fatal errors. Returning an error from vb2_qbuf() is thus the only real indication that a fatal error occurred. However, this is difficult to handle for multithreaded applications that requeue buffers from a thread other than the control thread. In particular the poll() call in the control thread will not notify userspace of the error. This patch adds an explicit mechanism to report fatal errors to userspace. Drivers can call the vb2_queue_error() function to signal a fatal error. From this moment on, buffer preparation will return -EIO to userspace, and vb2_poll() will set the POLLERR flag and return immediately. The error flag is cleared when cancelling the queue, either at stream off time (through vb2_streamoff) or when releasing the queue with vb2_queue_release(). Signed-off-by: Laurent Pinchart Acked-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/videobuf2-core.c | 39 +++++++++++++++++++++++++++++--- 1 file changed, 36 insertions(+), 3 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index 1fc85fb..a5c41cc 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -1606,6 +1606,11 @@ static int __buf_prepare(struct vb2_buffer *vb, const struct v4l2_buffer *b) return -EINVAL; } + if (q->error) { + dprintk(1, "fatal error occurred on queue\n"); + return -EIO; + } + vb->state = VB2_BUF_STATE_PREPARING; vb->v4l2_buf.timestamp.tv_sec = 0; vb->v4l2_buf.timestamp.tv_usec = 0; @@ -1903,6 +1908,11 @@ static int __vb2_wait_for_done_vb(struct vb2_queue *q, int nonblocking) return -EINVAL; } + if (q->error) { + dprintk(1, "Queue in error state, will not wait for buffers\n"); + return -EIO; + } + if (!list_empty(&q->done_list)) { /* * Found a buffer that we were waiting for. @@ -1928,7 +1938,8 @@ static int __vb2_wait_for_done_vb(struct vb2_queue *q, int nonblocking) */ dprintk(3, "will sleep waiting for buffers\n"); ret = wait_event_interruptible(q->done_wq, - !list_empty(&q->done_list) || !q->streaming); + !list_empty(&q->done_list) || !q->streaming || + q->error); /* * We need to reevaluate both conditions again after reacquiring @@ -2125,6 +2136,7 @@ static void __vb2_queue_cancel(struct vb2_queue *q) q->streaming = 0; q->start_streaming_called = 0; q->queued_count = 0; + q->error = 0; /* * Remove all buffers from videobuf's list... @@ -2202,6 +2214,27 @@ static int vb2_internal_streamon(struct vb2_queue *q, enum v4l2_buf_type type) } /** + * vb2_queue_error() - signal a fatal error on the queue + * @q: videobuf2 queue + * + * Flag that a fatal unrecoverable error has occurred and wake up all processes + * waiting on the queue. Polling will now set POLLERR and queuing and dequeuing + * buffers will return -EIO. + * + * The error flag will be cleared when cancelling the queue, either from + * vb2_streamoff or vb2_queue_release. Drivers should thus not call this + * function before starting the stream, otherwise the error flag will remain set + * until the queue is released when closing the device node. + */ +void vb2_queue_error(struct vb2_queue *q) +{ + q->error = 1; + + wake_up_all(&q->done_wq); +} +EXPORT_SYMBOL_GPL(vb2_queue_error); + +/** * vb2_streamon - start streaming * @q: videobuf2 queue * @type: type argument passed from userspace to vidioc_streamon handler @@ -2560,9 +2593,9 @@ unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait) /* * There is nothing to wait for if no buffer has been queued and the - * queue isn't streaming. + * queue isn't streaming, or if the error flag is set. */ - if (list_empty(&q->queued_list) && !vb2_is_streaming(q)) + if ((list_empty(&q->queued_list) && !vb2_is_streaming(q)) || q->error) return res | POLLERR; if (list_empty(&q->done_list)) -- cgit v1.1 From ce71bbc91e2440a399f2f96b96cfbf263a6629c1 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 12 Jul 2014 07:54:26 -0300 Subject: [media] v4l2-ioctl.c: check vfl_type in ENUM_FMT The other format ioctls (g/s/try_fmt) all check if the passed buffer type makes sense for the device node's vfl_type. E.g. it makes no sense for a VBI buffer type to be passed through a video node instead of a vbi node. But this check was missing in ENUM_FMT which can cause a problem if you have both video and sdr device nodes. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ioctl.c | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index e0bafda..cd9e94c 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -1098,32 +1098,34 @@ static int v4l_enum_fmt(const struct v4l2_ioctl_ops *ops, { struct v4l2_fmtdesc *p = arg; struct video_device *vfd = video_devdata(file); + bool is_vid = vfd->vfl_type == VFL_TYPE_GRABBER; + bool is_sdr = vfd->vfl_type == VFL_TYPE_SDR; bool is_rx = vfd->vfl_dir != VFL_DIR_TX; bool is_tx = vfd->vfl_dir != VFL_DIR_RX; switch (p->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: - if (unlikely(!is_rx || !ops->vidioc_enum_fmt_vid_cap)) + if (unlikely(!is_rx || !is_vid || !ops->vidioc_enum_fmt_vid_cap)) break; return ops->vidioc_enum_fmt_vid_cap(file, fh, arg); case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - if (unlikely(!is_rx || !ops->vidioc_enum_fmt_vid_cap_mplane)) + if (unlikely(!is_rx || !is_vid || !ops->vidioc_enum_fmt_vid_cap_mplane)) break; return ops->vidioc_enum_fmt_vid_cap_mplane(file, fh, arg); case V4L2_BUF_TYPE_VIDEO_OVERLAY: - if (unlikely(!is_rx || !ops->vidioc_enum_fmt_vid_overlay)) + if (unlikely(!is_rx || !is_vid || !ops->vidioc_enum_fmt_vid_overlay)) break; return ops->vidioc_enum_fmt_vid_overlay(file, fh, arg); case V4L2_BUF_TYPE_VIDEO_OUTPUT: - if (unlikely(!is_tx || !ops->vidioc_enum_fmt_vid_out)) + if (unlikely(!is_tx || !is_vid || !ops->vidioc_enum_fmt_vid_out)) break; return ops->vidioc_enum_fmt_vid_out(file, fh, arg); case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: - if (unlikely(!is_tx || !ops->vidioc_enum_fmt_vid_out_mplane)) + if (unlikely(!is_tx || !is_vid || !ops->vidioc_enum_fmt_vid_out_mplane)) break; return ops->vidioc_enum_fmt_vid_out_mplane(file, fh, arg); case V4L2_BUF_TYPE_SDR_CAPTURE: - if (unlikely(!is_rx || !ops->vidioc_enum_fmt_sdr_cap)) + if (unlikely(!is_rx || !is_sdr || !ops->vidioc_enum_fmt_sdr_cap)) break; return ops->vidioc_enum_fmt_sdr_cap(file, fh, arg); } -- cgit v1.1 From a7f404af8a435b940055a04be19a3304da93a76d Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 11 Jul 2014 07:01:39 -0300 Subject: [media] v4l2-ioctl.c: fix enum_freq_bands handling If the driver supports enum_freq_bands, but only for certain device nodes, then it may return -ENOTTY. But in that case the code should fall into the fall-back case where the current tuner/modulator range is returned. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ioctl.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index cd9e94c..aef588c 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -2042,8 +2042,11 @@ static int v4l_enum_freq_bands(const struct v4l2_ioctl_ops *ops, if (type != p->type) return -EINVAL; } - if (ops->vidioc_enum_freq_bands) - return ops->vidioc_enum_freq_bands(file, fh, p); + if (ops->vidioc_enum_freq_bands) { + err = ops->vidioc_enum_freq_bands(file, fh, p); + if (err != -ENOTTY) + return err; + } if (is_valid_ioctl(vfd, VIDIOC_G_TUNER)) { struct v4l2_tuner t = { .index = p->tuner, -- cgit v1.1 From 72be89c84c42b54f4478a5fc3048cfbae314c4d3 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 10 Jul 2014 19:51:26 -0300 Subject: [media] v4l2-dev: streamon/off is only a valid ioctl for video, vbi and sdr The VIDIOC_STREAMON/OFF ioctls are not valid for radio devices, just like the other streaming I/O ioctls. Add the streamon/off ioctls to the other streaming I/O ioctls. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-dev.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c index 1cfdbdd..fbe45b8 100644 --- a/drivers/media/v4l2-core/v4l2-dev.c +++ b/drivers/media/v4l2-core/v4l2-dev.c @@ -567,8 +567,6 @@ static void determine_valid_ioctls(struct video_device *vdev) set_bit(_IOC_NR(VIDIOC_G_PRIORITY), valid_ioctls); if (ops->vidioc_s_priority) set_bit(_IOC_NR(VIDIOC_S_PRIORITY), valid_ioctls); - SET_VALID_IOCTL(ops, VIDIOC_STREAMON, vidioc_streamon); - SET_VALID_IOCTL(ops, VIDIOC_STREAMOFF, vidioc_streamoff); /* Note: the control handler can also be passed through the filehandle, and that can't be tested here. If the bit for these control ioctls is set, then the ioctl is valid. But if it is 0, then it can still @@ -684,6 +682,8 @@ static void determine_valid_ioctls(struct video_device *vdev) SET_VALID_IOCTL(ops, VIDIOC_DQBUF, vidioc_dqbuf); SET_VALID_IOCTL(ops, VIDIOC_CREATE_BUFS, vidioc_create_bufs); SET_VALID_IOCTL(ops, VIDIOC_PREPARE_BUF, vidioc_prepare_buf); + SET_VALID_IOCTL(ops, VIDIOC_STREAMON, vidioc_streamon); + SET_VALID_IOCTL(ops, VIDIOC_STREAMOFF, vidioc_streamoff); } if (is_vid || is_vbi) { -- cgit v1.1 From 57fc8eb19adc794e0d267b241561c8211a50d2a8 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 3 Mar 2014 06:47:38 -0300 Subject: [media] v4l2-dev: don't debug poll unless the debug level > 2 Some applications poll a lot, so prevent the poll message from flooding the log. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-dev.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-dev.c b/drivers/media/v4l2-core/v4l2-dev.c index fbe45b8..33617c3 100644 --- a/drivers/media/v4l2-core/v4l2-dev.c +++ b/drivers/media/v4l2-core/v4l2-dev.c @@ -335,7 +335,7 @@ static unsigned int v4l2_poll(struct file *filp, struct poll_table_struct *poll) return DEFAULT_POLLMASK; if (video_is_registered(vdev)) res = vdev->fops->poll(filp, poll); - if (vdev->debug) + if (vdev->debug > 2) printk(KERN_DEBUG "%s: poll: %08x\n", video_device_node_name(vdev), res); return res; -- cgit v1.1 From f9402a94e7ca0df34e7dc17d763bb49afbd89c3a Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 14 Mar 2014 11:52:38 -0300 Subject: [media] v4l2-ioctl: remove pointless INFO_FL_CLEAR The edid field is the last field of the struct, so there is nothing to clear. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ioctl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index aef588c..e8296ba 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -2164,8 +2164,8 @@ static struct v4l2_ioctl_info v4l2_ioctls[] = { IOCTL_INFO_FNC(VIDIOC_QUERYMENU, v4l_querymenu, v4l_print_querymenu, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_querymenu, index)), IOCTL_INFO_STD(VIDIOC_G_INPUT, vidioc_g_input, v4l_print_u32, 0), IOCTL_INFO_FNC(VIDIOC_S_INPUT, v4l_s_input, v4l_print_u32, INFO_FL_PRIO), - IOCTL_INFO_STD(VIDIOC_G_EDID, vidioc_g_edid, v4l_print_edid, INFO_FL_CLEAR(v4l2_edid, edid)), - IOCTL_INFO_STD(VIDIOC_S_EDID, vidioc_s_edid, v4l_print_edid, INFO_FL_PRIO | INFO_FL_CLEAR(v4l2_edid, edid)), + IOCTL_INFO_STD(VIDIOC_G_EDID, vidioc_g_edid, v4l_print_edid, 0), + IOCTL_INFO_STD(VIDIOC_S_EDID, vidioc_s_edid, v4l_print_edid, INFO_FL_PRIO), IOCTL_INFO_STD(VIDIOC_G_OUTPUT, vidioc_g_output, v4l_print_u32, 0), IOCTL_INFO_FNC(VIDIOC_S_OUTPUT, v4l_s_output, v4l_print_u32, INFO_FL_PRIO), IOCTL_INFO_FNC(VIDIOC_ENUMOUTPUT, v4l_enumoutput, v4l_print_enumoutput, INFO_FL_CLEAR(v4l2_output, index)), -- cgit v1.1 From 865c4642e3750a257e4e199c2eb60495fd66f530 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 24 Mar 2014 09:51:34 -0300 Subject: [media] v4l2-ioctl: clear reserved field of G/S_SELECTION Be sure that the reserved fields are cleared. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ioctl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index e8296ba..dca8ca9 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -2178,8 +2178,8 @@ static struct v4l2_ioctl_info v4l2_ioctls[] = { IOCTL_INFO_FNC(VIDIOC_CROPCAP, v4l_cropcap, v4l_print_cropcap, INFO_FL_CLEAR(v4l2_cropcap, type)), IOCTL_INFO_FNC(VIDIOC_G_CROP, v4l_g_crop, v4l_print_crop, INFO_FL_CLEAR(v4l2_crop, type)), IOCTL_INFO_FNC(VIDIOC_S_CROP, v4l_s_crop, v4l_print_crop, INFO_FL_PRIO), - IOCTL_INFO_STD(VIDIOC_G_SELECTION, vidioc_g_selection, v4l_print_selection, 0), - IOCTL_INFO_STD(VIDIOC_S_SELECTION, vidioc_s_selection, v4l_print_selection, INFO_FL_PRIO), + IOCTL_INFO_STD(VIDIOC_G_SELECTION, vidioc_g_selection, v4l_print_selection, INFO_FL_CLEAR(v4l2_selection, r)), + IOCTL_INFO_STD(VIDIOC_S_SELECTION, vidioc_s_selection, v4l_print_selection, INFO_FL_PRIO | INFO_FL_CLEAR(v4l2_selection, r)), IOCTL_INFO_STD(VIDIOC_G_JPEGCOMP, vidioc_g_jpegcomp, v4l_print_jpegcompression, 0), IOCTL_INFO_STD(VIDIOC_S_JPEGCOMP, vidioc_s_jpegcomp, v4l_print_jpegcompression, INFO_FL_PRIO), IOCTL_INFO_FNC(VIDIOC_QUERYSTD, v4l_querystd, v4l_print_std, 0), -- cgit v1.1 From 9409945c7ff7ba39727df8ede2551bd22e76b58b Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 13 Jun 2014 04:31:06 -0300 Subject: [media] v4l2-ioctl: call g_selection before calling cropcap If the vidioc_cropcap op is implemented by the driver then the v4l2 core will call that directly. If g_selection is available, then the core cropcap implementation uses g_selection to fill in the bounds and defrect and it sets the pixelaspect to 1x1. But if both are available, then I would like to use g_selection to fill in defrect and bounds before calling cropcap. That way the driver's cropcap implementation doesn't have to set defrect or bounds. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ioctl.c | 48 +++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 22 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index dca8ca9..46f45f0 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -1842,37 +1842,41 @@ static int v4l_cropcap(const struct v4l2_ioctl_ops *ops, struct file *file, void *fh, void *arg) { struct v4l2_cropcap *p = arg; - struct v4l2_selection s = { .type = p->type }; - int ret; - if (ops->vidioc_cropcap) - return ops->vidioc_cropcap(file, fh, p); + if (ops->vidioc_g_selection) { + struct v4l2_selection s = { .type = p->type }; + int ret; - /* obtaining bounds */ - if (V4L2_TYPE_IS_OUTPUT(p->type)) - s.target = V4L2_SEL_TGT_COMPOSE_BOUNDS; - else - s.target = V4L2_SEL_TGT_CROP_BOUNDS; + /* obtaining bounds */ + if (V4L2_TYPE_IS_OUTPUT(p->type)) + s.target = V4L2_SEL_TGT_COMPOSE_BOUNDS; + else + s.target = V4L2_SEL_TGT_CROP_BOUNDS; - ret = ops->vidioc_g_selection(file, fh, &s); - if (ret) - return ret; - p->bounds = s.r; + ret = ops->vidioc_g_selection(file, fh, &s); + if (ret) + return ret; + p->bounds = s.r; - /* obtaining defrect */ - if (V4L2_TYPE_IS_OUTPUT(p->type)) - s.target = V4L2_SEL_TGT_COMPOSE_DEFAULT; - else - s.target = V4L2_SEL_TGT_CROP_DEFAULT; + /* obtaining defrect */ + if (V4L2_TYPE_IS_OUTPUT(p->type)) + s.target = V4L2_SEL_TGT_COMPOSE_DEFAULT; + else + s.target = V4L2_SEL_TGT_CROP_DEFAULT; - ret = ops->vidioc_g_selection(file, fh, &s); - if (ret) - return ret; - p->defrect = s.r; + ret = ops->vidioc_g_selection(file, fh, &s); + if (ret) + return ret; + p->defrect = s.r; + } /* setting trivial pixelaspect */ p->pixelaspect.numerator = 1; p->pixelaspect.denominator = 1; + + if (ops->vidioc_cropcap) + return ops->vidioc_cropcap(file, fh, p); + return 0; } -- cgit v1.1 From e5ce558a6157e49374ba762471b77ce89ada4df0 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 17 Jul 2014 18:45:45 -0300 Subject: [media] v4l2-ioctl: clips, clipcount and bitmap should not be zeroed Otherwise you cannot get the current clip and bitmap information from an overlay. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ioctl.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 46f45f0..e620387 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -1145,6 +1145,30 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops, p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC; + /* + * fmt can't be cleared for these overlay types due to the 'clips' + * 'clipcount' and 'bitmap' pointers in struct v4l2_window. + * Those are provided by the user. So handle these two overlay types + * first, and then just do a simple memset for the other types. + */ + switch (p->type) { + case V4L2_BUF_TYPE_VIDEO_OVERLAY: + case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY: { + struct v4l2_clip *clips = p->fmt.win.clips; + u32 clipcount = p->fmt.win.clipcount; + void *bitmap = p->fmt.win.bitmap; + + memset(&p->fmt, 0, sizeof(p->fmt)); + p->fmt.win.clips = clips; + p->fmt.win.clipcount = clipcount; + p->fmt.win.bitmap = bitmap; + break; + } + default: + memset(&p->fmt, 0, sizeof(p->fmt)); + break; + } + switch (p->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: if (unlikely(!is_rx || !is_vid || !ops->vidioc_g_fmt_vid_cap)) @@ -2140,7 +2164,7 @@ struct v4l2_ioctl_info { static struct v4l2_ioctl_info v4l2_ioctls[] = { IOCTL_INFO_FNC(VIDIOC_QUERYCAP, v4l_querycap, v4l_print_querycap, 0), IOCTL_INFO_FNC(VIDIOC_ENUM_FMT, v4l_enum_fmt, v4l_print_fmtdesc, INFO_FL_CLEAR(v4l2_fmtdesc, type)), - IOCTL_INFO_FNC(VIDIOC_G_FMT, v4l_g_fmt, v4l_print_format, INFO_FL_CLEAR(v4l2_format, type)), + IOCTL_INFO_FNC(VIDIOC_G_FMT, v4l_g_fmt, v4l_print_format, 0), IOCTL_INFO_FNC(VIDIOC_S_FMT, v4l_s_fmt, v4l_print_format, INFO_FL_PRIO), IOCTL_INFO_FNC(VIDIOC_REQBUFS, v4l_reqbufs, v4l_print_requestbuffers, INFO_FL_PRIO | INFO_FL_QUEUE), IOCTL_INFO_FNC(VIDIOC_QUERYBUF, v4l_querybuf, v4l_print_buffer, INFO_FL_QUEUE | INFO_FL_CLEAR(v4l2_buffer, length)), -- cgit v1.1 From 5a573925159aeec1dd159627d849dc6c66000faf Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 12 Jun 2014 13:09:40 -0300 Subject: [media] v4l: ctrls: Provide an unlocked variant of v4l2_ctrl_modify_range() Drivers may use the v4l2_ctrl_modify_range() internally as part of other operations that need to be both serialised using a driver's lock which can also be used to serialise access to the control handler. Provide an unlocked version of the function, __v4l2_ctrl_modify_range() which then may be used by drivers for the purpose. Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 8552c83..9cd9783 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -3213,12 +3213,14 @@ void v4l2_ctrl_notify(struct v4l2_ctrl *ctrl, v4l2_ctrl_notify_fnc notify, void } EXPORT_SYMBOL(v4l2_ctrl_notify); -int v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl, +int __v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl, s64 min, s64 max, u64 step, s64 def) { int ret; struct v4l2_ext_control c; + lockdep_assert_held(ctrl->handler->lock); + switch (ctrl->type) { case V4L2_CTRL_TYPE_INTEGER: case V4L2_CTRL_TYPE_INTEGER64: @@ -3237,7 +3239,6 @@ int v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl, default: return -EINVAL; } - v4l2_ctrl_lock(ctrl); ctrl->minimum = min; ctrl->maximum = max; ctrl->step = step; @@ -3249,10 +3250,9 @@ int v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl, ret = set_ctrl(NULL, ctrl, &c, V4L2_EVENT_CTRL_CH_RANGE); else send_event(NULL, ctrl, V4L2_EVENT_CTRL_CH_RANGE); - v4l2_ctrl_unlock(ctrl); return ret; } -EXPORT_SYMBOL(v4l2_ctrl_modify_range); +EXPORT_SYMBOL(__v4l2_ctrl_modify_range); static int v4l2_ctrl_add_event(struct v4l2_subscribed_event *sev, unsigned elems) { -- cgit v1.1 From 0c4348ada001181637b8f73482242166ba2fb56e Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Thu, 12 Jun 2014 13:09:42 -0300 Subject: [media] v4l: ctrls: Unlocked variants of v4l2_ctrl_s_ctrl{,_int64}() Implement unlocked variants of v4l2_ctrl_s_ctrl() and v4l2_ctrl_s_ctrl_int64(). As drivers need to set controls as they access driver internal state elsewhere than in the control framework unlocked variants of these functions become handy. Signed-off-by: Sakari Ailus Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 26 ++++++++++++++++++++------ 1 file changed, 20 insertions(+), 6 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 9cd9783..1acc7aa 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -3175,27 +3175,41 @@ int v4l2_subdev_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *control) } EXPORT_SYMBOL(v4l2_subdev_s_ctrl); -int v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val) +int __v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val) { struct v4l2_ext_control c; + int rval; + + lockdep_assert_held(ctrl->handler->lock); /* It's a driver bug if this happens. */ WARN_ON(!ctrl->is_int); c.value = val; - return set_ctrl_lock(NULL, ctrl, &c); + rval = set_ctrl(NULL, ctrl, &c, 0); + if (!rval) + cur_to_user(&c, ctrl); + + return rval; } -EXPORT_SYMBOL(v4l2_ctrl_s_ctrl); +EXPORT_SYMBOL(__v4l2_ctrl_s_ctrl); -int v4l2_ctrl_s_ctrl_int64(struct v4l2_ctrl *ctrl, s64 val) +int __v4l2_ctrl_s_ctrl_int64(struct v4l2_ctrl *ctrl, s64 val) { struct v4l2_ext_control c; + int rval; + + lockdep_assert_held(ctrl->handler->lock); /* It's a driver bug if this happens. */ WARN_ON(ctrl->is_ptr || ctrl->type != V4L2_CTRL_TYPE_INTEGER64); c.value64 = val; - return set_ctrl_lock(NULL, ctrl, &c); + rval = set_ctrl(NULL, ctrl, &c, 0); + if (!rval) + cur_to_user(&c, ctrl); + + return rval; } -EXPORT_SYMBOL(v4l2_ctrl_s_ctrl_int64); +EXPORT_SYMBOL(__v4l2_ctrl_s_ctrl_int64); void v4l2_ctrl_notify(struct v4l2_ctrl *ctrl, v4l2_ctrl_notify_fnc notify, void *priv) { -- cgit v1.1 From 8a75ffb81b1c1b6949d191fbef3eaa03fd648852 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 17 Jul 2014 06:53:08 -0300 Subject: [media] vb2: fix bytesused == 0 handling The original report from Nikhil was that if data_offset > 0 and bytesused == 0, then the check in __verify_length() would fail, even though the spec says that if bytes_used == 0, then it will be replaced by the actual length of the buffer. After digging into it a bit more I realized that there were several other things wrong: - in __verify_length() it would use the application-provided length value for USERPTR and the vb2 core length for other memory models, but it should have used the application-provided length as well for DMABUF. - in __fill_vb2_buffer() on the other hand it would replace bytesused == 0 by the application-provided length, even for MMAP buffers where the length is determined by the vb2 core. - in __fill_vb2_buffer() it tries to figure out if all the planes have bytesused == 0 before it will decide to replace bytesused by length. However, the spec makes no such provision, and it makes for convoluted code. So just replace any bytesused == 0 by the proper length. The idea behind this was that you could use bytesused to signal empty planes, something that is currently not supported. But that is better done in the future by using one of the reserved fields in strucy v4l2_plane. This patch fixes all these issues. Regards, Hans Signed-off-by: Hans Verkuil Reported-by: Nikhil Devshatwar Cc: Nikhil Devshatwar Acked-by: Marek Szyprowski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/videobuf2-core.c | 78 ++++++++++++++++---------------- 1 file changed, 38 insertions(+), 40 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index a5c41cc..f33508f 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -576,6 +576,7 @@ static int __verify_planes_array(struct vb2_buffer *vb, const struct v4l2_buffer static int __verify_length(struct vb2_buffer *vb, const struct v4l2_buffer *b) { unsigned int length; + unsigned int bytesused; unsigned int plane; if (!V4L2_TYPE_IS_OUTPUT(b->type)) @@ -583,21 +584,24 @@ static int __verify_length(struct vb2_buffer *vb, const struct v4l2_buffer *b) if (V4L2_TYPE_IS_MULTIPLANAR(b->type)) { for (plane = 0; plane < vb->num_planes; ++plane) { - length = (b->memory == V4L2_MEMORY_USERPTR) + length = (b->memory == V4L2_MEMORY_USERPTR || + b->memory == V4L2_MEMORY_DMABUF) ? b->m.planes[plane].length : vb->v4l2_planes[plane].length; + bytesused = b->m.planes[plane].bytesused + ? b->m.planes[plane].bytesused : length; if (b->m.planes[plane].bytesused > length) return -EINVAL; if (b->m.planes[plane].data_offset > 0 && - b->m.planes[plane].data_offset >= - b->m.planes[plane].bytesused) + b->m.planes[plane].data_offset >= bytesused) return -EINVAL; } } else { length = (b->memory == V4L2_MEMORY_USERPTR) ? b->length : vb->v4l2_planes[0].length; + bytesused = b->bytesused ? b->bytesused : length; if (b->bytesused > length) return -EINVAL; @@ -1234,35 +1238,6 @@ static void __fill_vb2_buffer(struct vb2_buffer *vb, const struct v4l2_buffer *b unsigned int plane; if (V4L2_TYPE_IS_MULTIPLANAR(b->type)) { - /* Fill in driver-provided information for OUTPUT types */ - if (V4L2_TYPE_IS_OUTPUT(b->type)) { - bool bytesused_is_used; - - /* Check if bytesused == 0 for all planes */ - for (plane = 0; plane < vb->num_planes; ++plane) - if (b->m.planes[plane].bytesused) - break; - bytesused_is_used = plane < vb->num_planes; - - /* - * Will have to go up to b->length when API starts - * accepting variable number of planes. - * - * If bytesused_is_used is false, then fall back to the - * full buffer size. In that case userspace clearly - * never bothered to set it and it's a safe assumption - * that they really meant to use the full plane sizes. - */ - for (plane = 0; plane < vb->num_planes; ++plane) { - struct v4l2_plane *pdst = &v4l2_planes[plane]; - struct v4l2_plane *psrc = &b->m.planes[plane]; - - pdst->bytesused = bytesused_is_used ? - psrc->bytesused : psrc->length; - pdst->data_offset = psrc->data_offset; - } - } - if (b->memory == V4L2_MEMORY_USERPTR) { for (plane = 0; plane < vb->num_planes; ++plane) { v4l2_planes[plane].m.userptr = @@ -1279,6 +1254,28 @@ static void __fill_vb2_buffer(struct vb2_buffer *vb, const struct v4l2_buffer *b b->m.planes[plane].length; } } + + /* Fill in driver-provided information for OUTPUT types */ + if (V4L2_TYPE_IS_OUTPUT(b->type)) { + /* + * Will have to go up to b->length when API starts + * accepting variable number of planes. + * + * If bytesused == 0 for the output buffer, then fall + * back to the full buffer size. In that case + * userspace clearly never bothered to set it and + * it's a safe assumption that they really meant to + * use the full plane sizes. + */ + for (plane = 0; plane < vb->num_planes; ++plane) { + struct v4l2_plane *pdst = &v4l2_planes[plane]; + struct v4l2_plane *psrc = &b->m.planes[plane]; + + pdst->bytesused = psrc->bytesused ? + psrc->bytesused : pdst->length; + pdst->data_offset = psrc->data_offset; + } + } } else { /* * Single-planar buffers do not use planes array, @@ -1286,15 +1283,9 @@ static void __fill_vb2_buffer(struct vb2_buffer *vb, const struct v4l2_buffer *b * In videobuf we use our internal V4l2_planes struct for * single-planar buffers as well, for simplicity. * - * If bytesused == 0, then fall back to the full buffer size - * as that's a sensible default. + * If bytesused == 0 for the output buffer, then fall back + * to the full buffer size as that's a sensible default. */ - if (V4L2_TYPE_IS_OUTPUT(b->type)) - v4l2_planes[0].bytesused = - b->bytesused ? b->bytesused : b->length; - else - v4l2_planes[0].bytesused = 0; - if (b->memory == V4L2_MEMORY_USERPTR) { v4l2_planes[0].m.userptr = b->m.userptr; v4l2_planes[0].length = b->length; @@ -1304,6 +1295,13 @@ static void __fill_vb2_buffer(struct vb2_buffer *vb, const struct v4l2_buffer *b v4l2_planes[0].m.fd = b->m.fd; v4l2_planes[0].length = b->length; } + + if (V4L2_TYPE_IS_OUTPUT(b->type)) + v4l2_planes[0].bytesused = b->bytesused ? + b->bytesused : v4l2_planes[0].length; + else + v4l2_planes[0].bytesused = 0; + } /* Zero flags that the vb2 core handles */ -- cgit v1.1 From 958c7c7e65999c61af7da0812d2de12daa8fc29e Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Fri, 18 Jul 2014 05:15:21 -0300 Subject: [media] v4l2-ctrls: fix corner case in round-to-range code If you have a maximum that is at the limit of what the type supports, and the step is > 1, then you can get wrap-around errors since the code assumes that the maximum that the type supports is ctrl->maximum + ctrl->step / 2. In practice this is always fine, but in artificially crafted ranges you will hit this bug. Since this is core code it should just work. This bug has always been there but since it doesn't cause problems in practice it was never noticed. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 1acc7aa..004e7e8 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -1295,11 +1295,19 @@ static void std_log(const struct v4l2_ctrl *ctrl) } } -/* Round towards the closest legal value */ +/* + * Round towards the closest legal value. Be careful when we are + * close to the maximum range of the control type to prevent + * wrap-arounds. + */ #define ROUND_TO_RANGE(val, offset_type, ctrl) \ ({ \ offset_type offset; \ - val += (ctrl)->step / 2; \ + if ((ctrl)->maximum >= 0 && \ + val >= (ctrl)->maximum - ((ctrl)->step / 2)) \ + val = (ctrl)->maximum; \ + else \ + val += (ctrl)->step / 2; \ val = clamp_t(typeof(val), val, \ (ctrl)->minimum, (ctrl)->maximum); \ offset = (val) - (ctrl)->minimum; \ @@ -1325,7 +1333,10 @@ static int std_validate(const struct v4l2_ctrl *ctrl, u32 idx, * the u64 divide that needs special care. */ val = ptr.p_s64[idx]; - val += ctrl->step / 2; + if (ctrl->maximum >= 0 && val >= ctrl->maximum - ctrl->step / 2) + val = ctrl->maximum; + else + val += ctrl->step / 2; val = clamp_t(s64, val, ctrl->minimum, ctrl->maximum); offset = val - ctrl->minimum; do_div(offset, ctrl->step); -- cgit v1.1 From 796a2bd25b4fc3c4a1d6f04373ef2af1e12489b5 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 21 Jul 2014 04:14:46 -0300 Subject: [media] v4l2-ioctl: set V4L2_CAP_EXT_PIX_FORMAT for device_caps V4L2_CAP_EXT_PIX_FORMAT is set for capabilities, but it needs to be set for device_caps as well: device_caps should report all caps relevant to the device node, and this is one of them. Signed-off-by: Hans Verkuil Acked-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ioctl.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index e620387..00ceedf 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -1014,6 +1014,7 @@ static int v4l_querycap(const struct v4l2_ioctl_ops *ops, ret = ops->vidioc_querycap(file, fh, cap); cap->capabilities |= V4L2_CAP_EXT_PIX_FORMAT; + cap->device_caps |= V4L2_CAP_EXT_PIX_FORMAT; return ret; } -- cgit v1.1 From 48f2650a87ae462598cf0a3b4b34ee5f52c34869 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 21 Jul 2014 07:50:39 -0300 Subject: [media] v4l2-ioctl: don't set PRIV_MAGIC unconditionally in g_fmt() Regression fix: V4L2_PIX_FMT_PRIV_MAGIC should only be set for the VIDEO_CAPTURE and VIDEO_OUTPUT buffer types, and not for any others. In the case of the win format this overwrote a pointer value that is passed in from userspace. Just set it for V4L2_BUF_TYPE_VIDEO_CAPTURE and OUTPUT only. Set it before the callback is called, just as is done for try/s_fmt, and again afterwards in case the driver zeroed it. The latter was missing in try/s_fmt, so add it there as well. Currently it is quite likely that drivers clear priv (that was needed for a long time), so it makes sense to set it twice. Signed-off-by: Hans Verkuil Acked-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ioctl.c | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index 00ceedf..d15e1673 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -1144,8 +1144,6 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops, bool is_tx = vfd->vfl_dir != VFL_DIR_RX; int ret; - p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC; - /* * fmt can't be cleared for these overlay types due to the 'clips' * 'clipcount' and 'bitmap' pointers in struct v4l2_window. @@ -1174,7 +1172,9 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops, case V4L2_BUF_TYPE_VIDEO_CAPTURE: if (unlikely(!is_rx || !is_vid || !ops->vidioc_g_fmt_vid_cap)) break; + p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC; ret = ops->vidioc_g_fmt_vid_cap(file, fh, arg); + /* just in case the driver zeroed it again */ p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC; return ret; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: @@ -1196,7 +1196,9 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops, case V4L2_BUF_TYPE_VIDEO_OUTPUT: if (unlikely(!is_tx || !is_vid || !ops->vidioc_g_fmt_vid_out)) break; + p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC; ret = ops->vidioc_g_fmt_vid_out(file, fh, arg); + /* just in case the driver zeroed it again */ p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC; return ret; case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: @@ -1232,6 +1234,7 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops, bool is_sdr = vfd->vfl_type == VFL_TYPE_SDR; bool is_rx = vfd->vfl_dir != VFL_DIR_TX; bool is_tx = vfd->vfl_dir != VFL_DIR_RX; + int ret; v4l_sanitize_format(p); @@ -1240,7 +1243,10 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops, if (unlikely(!is_rx || !is_vid || !ops->vidioc_s_fmt_vid_cap)) break; CLEAR_AFTER_FIELD(p, fmt.pix); - return ops->vidioc_s_fmt_vid_cap(file, fh, arg); + ret = ops->vidioc_s_fmt_vid_cap(file, fh, arg); + /* just in case the driver zeroed it again */ + p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC; + return ret; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: if (unlikely(!is_rx || !is_vid || !ops->vidioc_s_fmt_vid_cap_mplane)) break; @@ -1265,7 +1271,10 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops, if (unlikely(!is_tx || !is_vid || !ops->vidioc_s_fmt_vid_out)) break; CLEAR_AFTER_FIELD(p, fmt.pix); - return ops->vidioc_s_fmt_vid_out(file, fh, arg); + ret = ops->vidioc_s_fmt_vid_out(file, fh, arg); + /* just in case the driver zeroed it again */ + p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC; + return ret; case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: if (unlikely(!is_tx || !is_vid || !ops->vidioc_s_fmt_vid_out_mplane)) break; @@ -1304,6 +1313,7 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops, bool is_sdr = vfd->vfl_type == VFL_TYPE_SDR; bool is_rx = vfd->vfl_dir != VFL_DIR_TX; bool is_tx = vfd->vfl_dir != VFL_DIR_RX; + int ret; v4l_sanitize_format(p); @@ -1312,7 +1322,10 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops, if (unlikely(!is_rx || !is_vid || !ops->vidioc_try_fmt_vid_cap)) break; CLEAR_AFTER_FIELD(p, fmt.pix); - return ops->vidioc_try_fmt_vid_cap(file, fh, arg); + ret = ops->vidioc_try_fmt_vid_cap(file, fh, arg); + /* just in case the driver zeroed it again */ + p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC; + return ret; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: if (unlikely(!is_rx || !is_vid || !ops->vidioc_try_fmt_vid_cap_mplane)) break; @@ -1337,7 +1350,10 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops, if (unlikely(!is_tx || !is_vid || !ops->vidioc_try_fmt_vid_out)) break; CLEAR_AFTER_FIELD(p, fmt.pix); - return ops->vidioc_try_fmt_vid_out(file, fh, arg); + ret = ops->vidioc_try_fmt_vid_out(file, fh, arg); + /* just in case the driver zeroed it again */ + p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC; + return ret; case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE: if (unlikely(!is_tx || !is_vid || !ops->vidioc_try_fmt_vid_out_mplane)) break; -- cgit v1.1 From b225e398f622c5f5579fb4fd14f8bcb0f953e650 Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Wed, 4 Jun 2014 08:22:03 -0300 Subject: [media] v4l: subdev: Unify argument validation across IOCTLs Separate validation of different argument types. There's no reason to do this separately for every IOCTL. Signed-off-by: Sakari Ailus Signed-off-by: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-subdev.c | 120 +++++++++++++++++++++------------- 1 file changed, 74 insertions(+), 46 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index b984f33d..b92cf69 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -126,6 +126,55 @@ static int subdev_close(struct file *file) return 0; } +static int check_format(struct v4l2_subdev *sd, + struct v4l2_subdev_format *format) +{ + if (format->which != V4L2_SUBDEV_FORMAT_TRY && + format->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + + if (format->pad >= sd->entity.num_pads) + return -EINVAL; + + return 0; +} + +static int check_crop(struct v4l2_subdev *sd, struct v4l2_subdev_crop *crop) +{ + if (crop->which != V4L2_SUBDEV_FORMAT_TRY && + crop->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + + if (crop->pad >= sd->entity.num_pads) + return -EINVAL; + + return 0; +} + +static int check_selection(struct v4l2_subdev *sd, + struct v4l2_subdev_selection *sel) +{ + if (sel->which != V4L2_SUBDEV_FORMAT_TRY && + sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) + return -EINVAL; + + if (sel->pad >= sd->entity.num_pads) + return -EINVAL; + + return 0; +} + +static int check_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edid) +{ + if (edid->pad >= sd->entity.num_pads) + return -EINVAL; + + if (edid->blocks && edid->edid == NULL) + return -EINVAL; + + return 0; +} + static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) { struct video_device *vdev = video_devdata(file); @@ -134,6 +183,7 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) struct v4l2_subdev_fh *subdev_fh = to_v4l2_subdev_fh(vfh); #endif + int rval; switch (cmd) { case VIDIOC_QUERYCTRL: @@ -206,12 +256,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) case VIDIOC_SUBDEV_G_FMT: { struct v4l2_subdev_format *format = arg; - if (format->which != V4L2_SUBDEV_FORMAT_TRY && - format->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - - if (format->pad >= sd->entity.num_pads) - return -EINVAL; + rval = check_format(sd, format); + if (rval) + return rval; return v4l2_subdev_call(sd, pad, get_fmt, subdev_fh, format); } @@ -219,12 +266,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) case VIDIOC_SUBDEV_S_FMT: { struct v4l2_subdev_format *format = arg; - if (format->which != V4L2_SUBDEV_FORMAT_TRY && - format->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - - if (format->pad >= sd->entity.num_pads) - return -EINVAL; + rval = check_format(sd, format); + if (rval) + return rval; return v4l2_subdev_call(sd, pad, set_fmt, subdev_fh, format); } @@ -232,14 +276,10 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) case VIDIOC_SUBDEV_G_CROP: { struct v4l2_subdev_crop *crop = arg; struct v4l2_subdev_selection sel; - int rval; - - if (crop->which != V4L2_SUBDEV_FORMAT_TRY && - crop->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - if (crop->pad >= sd->entity.num_pads) - return -EINVAL; + rval = check_crop(sd, crop); + if (rval) + return rval; rval = v4l2_subdev_call(sd, pad, get_crop, subdev_fh, crop); if (rval != -ENOIOCTLCMD) @@ -261,14 +301,10 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) case VIDIOC_SUBDEV_S_CROP: { struct v4l2_subdev_crop *crop = arg; struct v4l2_subdev_selection sel; - int rval; - - if (crop->which != V4L2_SUBDEV_FORMAT_TRY && - crop->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - if (crop->pad >= sd->entity.num_pads) - return -EINVAL; + rval = check_crop(sd, crop); + if (rval) + return rval; rval = v4l2_subdev_call(sd, pad, set_crop, subdev_fh, crop); if (rval != -ENOIOCTLCMD) @@ -339,12 +375,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) case VIDIOC_SUBDEV_G_SELECTION: { struct v4l2_subdev_selection *sel = arg; - if (sel->which != V4L2_SUBDEV_FORMAT_TRY && - sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - - if (sel->pad >= sd->entity.num_pads) - return -EINVAL; + rval = check_selection(sd, sel); + if (rval) + return rval; return v4l2_subdev_call( sd, pad, get_selection, subdev_fh, sel); @@ -353,12 +386,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) case VIDIOC_SUBDEV_S_SELECTION: { struct v4l2_subdev_selection *sel = arg; - if (sel->which != V4L2_SUBDEV_FORMAT_TRY && - sel->which != V4L2_SUBDEV_FORMAT_ACTIVE) - return -EINVAL; - - if (sel->pad >= sd->entity.num_pads) - return -EINVAL; + rval = check_selection(sd, sel); + if (rval) + return rval; return v4l2_subdev_call( sd, pad, set_selection, subdev_fh, sel); @@ -367,10 +397,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) case VIDIOC_G_EDID: { struct v4l2_subdev_edid *edid = arg; - if (edid->pad >= sd->entity.num_pads) - return -EINVAL; - if (edid->blocks && edid->edid == NULL) - return -EINVAL; + rval = check_edid(sd, edid); + if (rval) + return rval; return v4l2_subdev_call(sd, pad, get_edid, edid); } @@ -378,10 +407,9 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) case VIDIOC_S_EDID: { struct v4l2_subdev_edid *edid = arg; - if (edid->pad >= sd->entity.num_pads) - return -EINVAL; - if (edid->blocks && edid->edid == NULL) - return -EINVAL; + rval = check_edid(sd, edid); + if (rval) + return rval; return v4l2_subdev_call(sd, pad, set_edid, edid); } -- cgit v1.1 From d352bcc99067d8e5caf2c8794d0dd9e63f835e1a Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 22 Jul 2014 01:40:00 -0300 Subject: [media] v4l2-subdev: Fix compilation when !VIDEO_V4L2_SUBDEV_API As reported by Kbuildtest: drivers/media/v4l2-core/v4l2-subdev.c: In function 'check_format': drivers/media/v4l2-core/v4l2-subdev.c:136:23: error: 'struct v4l2_subdev' has no member named 'entity' if (format->pad >= sd->entity.num_pads) ^ drivers/media/v4l2-core/v4l2-subdev.c: In function 'check_crop': drivers/media/v4l2-core/v4l2-subdev.c:148:21: error: 'struct v4l2_subdev' has no member named 'entity' if (crop->pad >= sd->entity.num_pads) ^ drivers/media/v4l2-core/v4l2-subdev.c: In function 'check_selection': drivers/media/v4l2-core/v4l2-subdev.c:161:20: error: 'struct v4l2_subdev' has no member named 'entity' if (sel->pad >= sd->entity.num_pads) ^ drivers/media/v4l2-core/v4l2-subdev.c: In function 'check_edid': drivers/media/v4l2-core/v4l2-subdev.c:169:21: error: 'struct v4l2_subdev' has no member named 'entity' if (edid->pad >= sd->entity.num_pads) ^ drivers/media/v4l2-core/v4l2-subdev.c: In function 'subdev_do_ioctl': >> drivers/media/v4l2-core/v4l2-subdev.c:186:6: warning: unused variable 'rval' [-Wunused-variable] int rval; ^ drivers/media/v4l2-core/v4l2-subdev.c: At top level: drivers/media/v4l2-core/v4l2-subdev.c:129:12: warning: 'check_format' defined but not used [-Wunused-function] static int check_format(struct v4l2_subdev *sd, ^ drivers/media/v4l2-core/v4l2-subdev.c:142:12: warning: 'check_crop' defined but not used [-Wunused-function] static int check_crop(struct v4l2_subdev *sd, struct v4l2_subdev_crop *crop) ^ drivers/media/v4l2-core/v4l2-subdev.c:154:12: warning: 'check_selection' defined but not used [-Wunused-function] static int check_selection(struct v4l2_subdev *sd, ^ drivers/media/v4l2-core/v4l2-subdev.c:167:12: warning: 'check_edid' defined but not used [-Wunused-function] static int check_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edid) The above warnins happen because those functions are used only when the V4L2 subdev API is enabled. Reported-by: kbuild test robot Cc: Laurent Pinchart Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-subdev.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-subdev.c b/drivers/media/v4l2-core/v4l2-subdev.c index b92cf69..b4d235c 100644 --- a/drivers/media/v4l2-core/v4l2-subdev.c +++ b/drivers/media/v4l2-core/v4l2-subdev.c @@ -126,6 +126,7 @@ static int subdev_close(struct file *file) return 0; } +#if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) static int check_format(struct v4l2_subdev *sd, struct v4l2_subdev_format *format) { @@ -174,6 +175,7 @@ static int check_edid(struct v4l2_subdev *sd, struct v4l2_subdev_edid *edid) return 0; } +#endif static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) { @@ -182,8 +184,8 @@ static long subdev_do_ioctl(struct file *file, unsigned int cmd, void *arg) struct v4l2_fh *vfh = file->private_data; #if defined(CONFIG_VIDEO_V4L2_SUBDEV_API) struct v4l2_subdev_fh *subdev_fh = to_v4l2_subdev_fh(vfh); -#endif int rval; +#endif switch (cmd) { case VIDIOC_QUERYCTRL: -- cgit v1.1 From 1190a419e0f89254b831fb2928e1d5d6efe6abe4 Mon Sep 17 00:00:00 2001 From: Michael Olbrich Date: Tue, 22 Jul 2014 09:36:04 -0300 Subject: [media] v4l2-mem2mem: export v4l2_m2m_try_schedule Some drivers might allow to decode remaining frames from an internal ringbuffer after a decoder stop command. Allow those to call v4l2_m2m_try_schedule directly. Signed-off-by: Michael Olbrich Signed-off-by: Philipp Zabel Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-mem2mem.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-mem2mem.c b/drivers/media/v4l2-core/v4l2-mem2mem.c index 97defed..80c588f 100644 --- a/drivers/media/v4l2-core/v4l2-mem2mem.c +++ b/drivers/media/v4l2-core/v4l2-mem2mem.c @@ -208,7 +208,7 @@ static void v4l2_m2m_try_run(struct v4l2_m2m_dev *m2m_dev) * An example of the above could be an instance that requires more than one * src/dst buffer per transaction. */ -static void v4l2_m2m_try_schedule(struct v4l2_m2m_ctx *m2m_ctx) +void v4l2_m2m_try_schedule(struct v4l2_m2m_ctx *m2m_ctx) { struct v4l2_m2m_dev *m2m_dev; unsigned long flags_job, flags_out, flags_cap; @@ -274,6 +274,7 @@ static void v4l2_m2m_try_schedule(struct v4l2_m2m_ctx *m2m_ctx) v4l2_m2m_try_run(m2m_dev); } +EXPORT_SYMBOL_GPL(v4l2_m2m_try_schedule); /** * v4l2_m2m_cancel_job() - cancel pending jobs for the context -- cgit v1.1 From 5d0360a4f027576e5419d4a7c711c9ca0f1be8ca Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 21 Jul 2014 10:45:42 -0300 Subject: [media] v4l2-ctrls: add support for setting string controls Rather than always having to use a v4l2_ext_control struct to set a control value from within a driver, switch to just setting the new value. This is faster and it makes it possible to set more complex types such as a string control as is added by this patch. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 47 +++++++++++++++--------------------- 1 file changed, 20 insertions(+), 27 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 004e7e8..5db0385 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -3121,26 +3121,22 @@ static int set_ctrl(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, struct v4l2_ctrl *master = ctrl->cluster[0]; int i; - /* Compound controls are not supported. The user_to_new() and - * cur_to_user() calls below would need to be modified not to access - * userspace memory when called from set_ctrl(). - */ - if (ctrl->is_ptr) - return -EINVAL; - /* Reset the 'is_new' flags of the cluster */ for (i = 0; i < master->ncontrols; i++) if (master->cluster[i]) master->cluster[i]->is_new = 0; + if (c) + user_to_new(c, ctrl); + /* For autoclusters with volatiles that are switched from auto to manual mode we have to update the current volatile values since those will become the initial manual values after such a switch. */ if (master->is_auto && master->has_volatiles && ctrl == master && - !is_cur_manual(master) && c->value == master->manual_mode_value) + !is_cur_manual(master) && ctrl->val == master->manual_mode_value) update_from_auto_cluster(master); - user_to_new(c, ctrl); + ctrl->is_new = 1; return try_or_set_cluster(fh, master, true, ch_flags); } @@ -3188,40 +3184,37 @@ EXPORT_SYMBOL(v4l2_subdev_s_ctrl); int __v4l2_ctrl_s_ctrl(struct v4l2_ctrl *ctrl, s32 val) { - struct v4l2_ext_control c; - int rval; - lockdep_assert_held(ctrl->handler->lock); /* It's a driver bug if this happens. */ WARN_ON(!ctrl->is_int); - c.value = val; - rval = set_ctrl(NULL, ctrl, &c, 0); - if (!rval) - cur_to_user(&c, ctrl); - - return rval; + ctrl->val = val; + return set_ctrl(NULL, ctrl, NULL, 0); } EXPORT_SYMBOL(__v4l2_ctrl_s_ctrl); int __v4l2_ctrl_s_ctrl_int64(struct v4l2_ctrl *ctrl, s64 val) { - struct v4l2_ext_control c; - int rval; - lockdep_assert_held(ctrl->handler->lock); /* It's a driver bug if this happens. */ WARN_ON(ctrl->is_ptr || ctrl->type != V4L2_CTRL_TYPE_INTEGER64); - c.value64 = val; - rval = set_ctrl(NULL, ctrl, &c, 0); - if (!rval) - cur_to_user(&c, ctrl); - - return rval; + *ctrl->p_new.p_s64 = val; + return set_ctrl(NULL, ctrl, NULL, 0); } EXPORT_SYMBOL(__v4l2_ctrl_s_ctrl_int64); +int __v4l2_ctrl_s_ctrl_string(struct v4l2_ctrl *ctrl, const char *s) +{ + lockdep_assert_held(ctrl->handler->lock); + + /* It's a driver bug if this happens. */ + WARN_ON(ctrl->type != V4L2_CTRL_TYPE_STRING); + strlcpy(ctrl->p_new.p_char, s, ctrl->maximum + 1); + return set_ctrl(NULL, ctrl, NULL, 0); +} +EXPORT_SYMBOL(__v4l2_ctrl_s_ctrl_string); + void v4l2_ctrl_notify(struct v4l2_ctrl *ctrl, v4l2_ctrl_notify_fnc notify, void *priv) { if (ctrl == NULL) -- cgit v1.1 From 1612f20ea025f55741b2d6e8b9c91047a7324a76 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Thu, 24 Jul 2014 09:19:37 -0300 Subject: [media] vb2: fix vb2_poll for output streams vb2_poll should always return POLLOUT | POLLWRNORM as long as there are fewer buffers queued than there are buffers available. Poll for an output stream should only wait if all buffers are queued and nobody is dequeuing them. Signed-off-by: Hans Verkuil Acked-by: Marek Szyprowski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/videobuf2-core.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index f33508f..c359006 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -2596,6 +2596,13 @@ unsigned int vb2_poll(struct vb2_queue *q, struct file *file, poll_table *wait) if ((list_empty(&q->queued_list) && !vb2_is_streaming(q)) || q->error) return res | POLLERR; + /* + * For output streams you can write as long as there are fewer buffers + * queued than there are buffers available. + */ + if (V4L2_TYPE_IS_OUTPUT(q->type) && q->queued_count < q->num_buffers) + return res | POLLOUT | POLLWRNORM; + if (list_empty(&q->done_list)) poll_wait(file, &q->done_wq, wait); -- cgit v1.1 From 811c508104d092683ea5fe86cdcfd23dded22934 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 21 Jul 2014 10:45:37 -0300 Subject: [media] v4l2-ctrls: add new RDS TX controls The si4713 supports several RDS features not yet implemented in the driver. This patch adds the missing RDS functionality to the list of RDS controls. The ALT_FREQS control is a compound control containing an array of up to 25 (the maximum according to the RDS standard) frequencies. To support that the V4L2_CTRL_TYPE_U32 was added. Signed-off-by: Hans Verkuil Cc: Eduardo Valentin Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 5db0385..4863cf0 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -805,6 +805,15 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_RDS_TX_PTY: return "RDS Program Type"; case V4L2_CID_RDS_TX_PS_NAME: return "RDS PS Name"; case V4L2_CID_RDS_TX_RADIO_TEXT: return "RDS Radio Text"; + case V4L2_CID_RDS_TX_MONO_STEREO: return "RDS Stereo"; + case V4L2_CID_RDS_TX_ARTIFICIAL_HEAD: return "RDS Artificial Head"; + case V4L2_CID_RDS_TX_COMPRESSED: return "RDS Compressed"; + case V4L2_CID_RDS_TX_DYNAMIC_PTY: return "RDS Dynamic PTY"; + case V4L2_CID_RDS_TX_TRAFFIC_ANNOUNCEMENT: return "RDS Traffic Announcement"; + case V4L2_CID_RDS_TX_TRAFFIC_PROGRAM: return "RDS Traffic Program"; + case V4L2_CID_RDS_TX_MUSIC_SPEECH: return "RDS Music"; + case V4L2_CID_RDS_TX_ALT_FREQS_ENABLE: return "RDS Enable Alt Frequencies"; + case V4L2_CID_RDS_TX_ALT_FREQS: return "RDS Alternate Frequencies"; case V4L2_CID_AUDIO_LIMITER_ENABLED: return "Audio Limiter Feature Enabled"; case V4L2_CID_AUDIO_LIMITER_RELEASE_TIME: return "Audio Limiter Release Time"; case V4L2_CID_AUDIO_LIMITER_DEVIATION: return "Audio Limiter Deviation"; @@ -946,6 +955,14 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_RF_TUNER_IF_GAIN_AUTO: case V4L2_CID_RF_TUNER_BANDWIDTH_AUTO: case V4L2_CID_RF_TUNER_PLL_LOCK: + case V4L2_CID_RDS_TX_MONO_STEREO: + case V4L2_CID_RDS_TX_ARTIFICIAL_HEAD: + case V4L2_CID_RDS_TX_COMPRESSED: + case V4L2_CID_RDS_TX_DYNAMIC_PTY: + case V4L2_CID_RDS_TX_TRAFFIC_ANNOUNCEMENT: + case V4L2_CID_RDS_TX_TRAFFIC_PROGRAM: + case V4L2_CID_RDS_TX_MUSIC_SPEECH: + case V4L2_CID_RDS_TX_ALT_FREQS_ENABLE: *type = V4L2_CTRL_TYPE_BOOLEAN; *min = 0; *max = *step = 1; @@ -1089,6 +1106,9 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_DETECT_MD_THRESHOLD_GRID: *type = V4L2_CTRL_TYPE_U16; break; + case V4L2_CID_RDS_TX_ALT_FREQS: + *type = V4L2_CTRL_TYPE_U32; + break; default: *type = V4L2_CTRL_TYPE_INTEGER; break; @@ -1209,6 +1229,8 @@ static bool std_equal(const struct v4l2_ctrl *ctrl, u32 idx, return ptr1.p_u8[idx] == ptr2.p_u8[idx]; case V4L2_CTRL_TYPE_U16: return ptr1.p_u16[idx] == ptr2.p_u16[idx]; + case V4L2_CTRL_TYPE_U32: + return ptr1.p_u32[idx] == ptr2.p_u32[idx]; default: if (ctrl->is_int) return ptr1.p_s32[idx] == ptr2.p_s32[idx]; @@ -1242,6 +1264,9 @@ static void std_init(const struct v4l2_ctrl *ctrl, u32 idx, case V4L2_CTRL_TYPE_U16: ptr.p_u16[idx] = ctrl->default_value; break; + case V4L2_CTRL_TYPE_U32: + ptr.p_u32[idx] = ctrl->default_value; + break; default: idx *= ctrl->elem_size; memset(ptr.p + idx, 0, ctrl->elem_size); @@ -1289,6 +1314,9 @@ static void std_log(const struct v4l2_ctrl *ctrl) case V4L2_CTRL_TYPE_U16: pr_cont("%u", (unsigned)*ptr.p_u16); break; + case V4L2_CTRL_TYPE_U32: + pr_cont("%u", (unsigned)*ptr.p_u32); + break; default: pr_cont("unknown type %d", ctrl->type); break; @@ -1346,6 +1374,8 @@ static int std_validate(const struct v4l2_ctrl *ctrl, u32 idx, return ROUND_TO_RANGE(ptr.p_u8[idx], u8, ctrl); case V4L2_CTRL_TYPE_U16: return ROUND_TO_RANGE(ptr.p_u16[idx], u16, ctrl); + case V4L2_CTRL_TYPE_U32: + return ROUND_TO_RANGE(ptr.p_u32[idx], u32, ctrl); case V4L2_CTRL_TYPE_BOOLEAN: ptr.p_s32[idx] = !!ptr.p_s32[idx]; @@ -1578,6 +1608,7 @@ static int check_range(enum v4l2_ctrl_type type, /* fall through */ case V4L2_CTRL_TYPE_U8: case V4L2_CTRL_TYPE_U16: + case V4L2_CTRL_TYPE_U32: case V4L2_CTRL_TYPE_INTEGER: case V4L2_CTRL_TYPE_INTEGER64: if (step == 0 || min > max || def < min || def > max) @@ -1893,6 +1924,9 @@ static struct v4l2_ctrl *v4l2_ctrl_new(struct v4l2_ctrl_handler *hdl, case V4L2_CTRL_TYPE_U16: elem_size = sizeof(u16); break; + case V4L2_CTRL_TYPE_U32: + elem_size = sizeof(u32); + break; default: if (type < V4L2_CTRL_COMPOUND_TYPES) elem_size = sizeof(s32); @@ -3248,6 +3282,7 @@ int __v4l2_ctrl_modify_range(struct v4l2_ctrl *ctrl, case V4L2_CTRL_TYPE_BITMASK: case V4L2_CTRL_TYPE_U8: case V4L2_CTRL_TYPE_U16: + case V4L2_CTRL_TYPE_U32: if (ctrl->is_array) return -EINVAL; ret = check_range(ctrl->type, min, max, step, def); -- cgit v1.1 From 9570a14847a9d334a0baf5a5921da2dbc6b9f6d2 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Mon, 21 Jul 2014 10:45:40 -0300 Subject: [media] v4l2-ctrls: add RX RDS controls The radio-miropcm20 driver has firmware that decodes the RDS signals. So in that case the RDS data becomes available in the form of controls. Add support for these controls to the control framework, allowing the miro driver to use them. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 4863cf0..2d8ced8 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -881,7 +881,6 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_FM_RX_CLASS: return "FM Radio Receiver Controls"; case V4L2_CID_TUNE_DEEMPHASIS: return "De-Emphasis"; case V4L2_CID_RDS_RECEPTION: return "RDS Reception"; - case V4L2_CID_RF_TUNER_CLASS: return "RF Tuner Controls"; case V4L2_CID_RF_TUNER_LNA_GAIN_AUTO: return "LNA Gain, Auto"; case V4L2_CID_RF_TUNER_LNA_GAIN: return "LNA Gain"; @@ -892,6 +891,12 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_RF_TUNER_BANDWIDTH_AUTO: return "Bandwidth, Auto"; case V4L2_CID_RF_TUNER_BANDWIDTH: return "Bandwidth"; case V4L2_CID_RF_TUNER_PLL_LOCK: return "PLL Lock"; + case V4L2_CID_RDS_RX_PTY: return "RDS Program Type"; + case V4L2_CID_RDS_RX_PS_NAME: return "RDS PS Name"; + case V4L2_CID_RDS_RX_RADIO_TEXT: return "RDS Radio Text"; + case V4L2_CID_RDS_RX_TRAFFIC_ANNOUNCEMENT: return "RDS Traffic Announcement"; + case V4L2_CID_RDS_RX_TRAFFIC_PROGRAM: return "RDS Traffic Program"; + case V4L2_CID_RDS_RX_MUSIC_SPEECH: return "RDS Music"; /* Detection controls */ /* Keep the order of the 'case's the same as in v4l2-controls.h! */ @@ -900,7 +905,6 @@ const char *v4l2_ctrl_get_name(u32 id) case V4L2_CID_DETECT_MD_GLOBAL_THRESHOLD: return "MD Global Threshold"; case V4L2_CID_DETECT_MD_THRESHOLD_GRID: return "MD Threshold Grid"; case V4L2_CID_DETECT_MD_REGION_GRID: return "MD Region Grid"; - default: return NULL; } @@ -963,6 +967,9 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_RDS_TX_TRAFFIC_PROGRAM: case V4L2_CID_RDS_TX_MUSIC_SPEECH: case V4L2_CID_RDS_TX_ALT_FREQS_ENABLE: + case V4L2_CID_RDS_RX_TRAFFIC_ANNOUNCEMENT: + case V4L2_CID_RDS_RX_TRAFFIC_PROGRAM: + case V4L2_CID_RDS_RX_MUSIC_SPEECH: *type = V4L2_CTRL_TYPE_BOOLEAN; *min = 0; *max = *step = 1; @@ -1035,6 +1042,8 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, break; case V4L2_CID_RDS_TX_PS_NAME: case V4L2_CID_RDS_TX_RADIO_TEXT: + case V4L2_CID_RDS_RX_PS_NAME: + case V4L2_CID_RDS_RX_RADIO_TEXT: *type = V4L2_CTRL_TYPE_STRING; break; case V4L2_CID_ISO_SENSITIVITY: @@ -1166,6 +1175,12 @@ void v4l2_ctrl_fill(u32 id, const char **name, enum v4l2_ctrl_type *type, case V4L2_CID_DV_TX_RXSENSE: case V4L2_CID_DV_TX_EDID_PRESENT: case V4L2_CID_DV_RX_POWER_PRESENT: + case V4L2_CID_RDS_RX_PTY: + case V4L2_CID_RDS_RX_PS_NAME: + case V4L2_CID_RDS_RX_RADIO_TEXT: + case V4L2_CID_RDS_RX_TRAFFIC_ANNOUNCEMENT: + case V4L2_CID_RDS_RX_TRAFFIC_PROGRAM: + case V4L2_CID_RDS_RX_MUSIC_SPEECH: *flags |= V4L2_CTRL_FLAG_READ_ONLY; break; case V4L2_CID_RF_TUNER_PLL_LOCK: -- cgit v1.1 From 7b4eeed174b71c325705ff8c53f333bc79d0ee7a Mon Sep 17 00:00:00 2001 From: James Harper Date: Thu, 12 Jun 2014 06:53:38 -0300 Subject: [media] vmalloc_sg: make sure all pages in vmalloc area are really DMA-ready Patch originally written by Konrad. Rebased on current linux media tree. Under Xen, vmalloc_32() isn't guaranteed to return pages which are really under 4G in machine physical addresses (only in virtual pseudo-physical addresses). To work around this, implement a vmalloc variant which allocates each page with dma_alloc_coherent() to guarantee that each page is suitable for the device in question. Signed-off-by: Konrad Rzeszutek Wilk Signed-off-by: James Harper Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/videobuf-dma-sg.c | 62 +++++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 4 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/videobuf-dma-sg.c b/drivers/media/v4l2-core/videobuf-dma-sg.c index 828e7c1..3c8cc02 100644 --- a/drivers/media/v4l2-core/videobuf-dma-sg.c +++ b/drivers/media/v4l2-core/videobuf-dma-sg.c @@ -211,13 +211,36 @@ EXPORT_SYMBOL_GPL(videobuf_dma_init_user); int videobuf_dma_init_kernel(struct videobuf_dmabuf *dma, int direction, int nr_pages) { + int i; + dprintk(1, "init kernel [%d pages]\n", nr_pages); dma->direction = direction; - dma->vaddr = vmalloc_32(nr_pages << PAGE_SHIFT); + dma->vaddr_pages = kcalloc(nr_pages, sizeof(*dma->vaddr_pages), + GFP_KERNEL); + if (!dma->vaddr_pages) + return -ENOMEM; + + dma->dma_addr = kcalloc(nr_pages, sizeof(*dma->dma_addr), GFP_KERNEL); + if (!dma->dma_addr) { + kfree(dma->vaddr_pages); + return -ENOMEM; + } + for (i = 0; i < nr_pages; i++) { + void *addr; + + addr = dma_alloc_coherent(dma->dev, PAGE_SIZE, + &(dma->dma_addr[i]), GFP_KERNEL); + if (addr == NULL) + goto out_free_pages; + + dma->vaddr_pages[i] = virt_to_page(addr); + } + dma->vaddr = vmap(dma->vaddr_pages, nr_pages, VM_MAP | VM_IOREMAP, + PAGE_KERNEL); if (NULL == dma->vaddr) { dprintk(1, "vmalloc_32(%d pages) failed\n", nr_pages); - return -ENOMEM; + goto out_free_pages; } dprintk(1, "vmalloc is at addr 0x%08lx, size=%d\n", @@ -228,6 +251,19 @@ int videobuf_dma_init_kernel(struct videobuf_dmabuf *dma, int direction, dma->nr_pages = nr_pages; return 0; +out_free_pages: + while (i > 0) { + void *addr = page_address(dma->vaddr_pages[i]); + dma_free_coherent(dma->dev, PAGE_SIZE, addr, dma->dma_addr[i]); + i--; + } + kfree(dma->dma_addr); + dma->dma_addr = NULL; + kfree(dma->vaddr_pages); + dma->vaddr_pages = NULL; + + return -ENOMEM; + } EXPORT_SYMBOL_GPL(videobuf_dma_init_kernel); @@ -322,8 +358,21 @@ int videobuf_dma_free(struct videobuf_dmabuf *dma) dma->pages = NULL; } - vfree(dma->vaddr); - dma->vaddr = NULL; + if (dma->dma_addr) { + for (i = 0; i < dma->nr_pages; i++) { + void *addr; + + addr = page_address(dma->vaddr_pages[i]); + dma_free_coherent(dma->dev, PAGE_SIZE, addr, + dma->dma_addr[i]); + } + kfree(dma->dma_addr); + dma->dma_addr = NULL; + kfree(dma->vaddr_pages); + dma->vaddr_pages = NULL; + vunmap(dma->vaddr); + dma->vaddr = NULL; + } if (dma->bus_addr) dma->bus_addr = 0; @@ -461,6 +510,11 @@ static int __videobuf_iolock(struct videobuf_queue *q, MAGIC_CHECK(mem->magic, MAGIC_SG_MEM); + if (!mem->dma.dev) + mem->dma.dev = q->dev; + else + WARN_ON(mem->dma.dev != q->dev); + switch (vb->memory) { case V4L2_MEMORY_MMAP: case V4L2_MEMORY_USERPTR: -- cgit v1.1 From 9c9cb1fad865b3d9e9c4d7bbfbd20ca04cdc79b3 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 26 Jul 2014 13:02:59 -0300 Subject: [media] v4l2-ctrls: fix rounding calculation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 958c7c7e65 ("[media] v4l2-ctrls: fix corner case in round-to-range code") broke controls that use a negative range. The cause was a s32/u32 mixup: ctrl->step is unsigned while all others are signed. So the result type of the expression '(ctrl)->maximum - ((ctrl)->step / 2)' became unsigned, making 'val >= (ctrl)->maximum - ((ctrl)->step / 2)' true, since '((u32)-128) > 128' (if val = -128, maximum = 128 and step = 1). So carefully cast (step / 2) to s32. There was one cast of step to s32 where it should have been u32 because both offset and step are unsigned, so casting to signed makes no sense there. You do need a cast to u32 there, because otherwise architectures that have no 64-bit division start complaining (step is a u64). Signed-off-by: Hans Verkuil Reported-by: Frank Schäfer Signed-off-by: Mauro Carvalho Chehab --- drivers/media/v4l2-core/v4l2-ctrls.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers/media/v4l2-core') diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c index 2d8ced8..f030d6a 100644 --- a/drivers/media/v4l2-core/v4l2-ctrls.c +++ b/drivers/media/v4l2-core/v4l2-ctrls.c @@ -1347,14 +1347,14 @@ static void std_log(const struct v4l2_ctrl *ctrl) ({ \ offset_type offset; \ if ((ctrl)->maximum >= 0 && \ - val >= (ctrl)->maximum - ((ctrl)->step / 2)) \ + val >= (ctrl)->maximum - (s32)((ctrl)->step / 2)) \ val = (ctrl)->maximum; \ else \ - val += (ctrl)->step / 2; \ + val += (s32)((ctrl)->step / 2); \ val = clamp_t(typeof(val), val, \ (ctrl)->minimum, (ctrl)->maximum); \ offset = (val) - (ctrl)->minimum; \ - offset = (ctrl)->step * (offset / (s32)(ctrl)->step); \ + offset = (ctrl)->step * (offset / (u32)(ctrl)->step); \ val = (ctrl)->minimum + offset; \ 0; \ }) @@ -1376,10 +1376,10 @@ static int std_validate(const struct v4l2_ctrl *ctrl, u32 idx, * the u64 divide that needs special care. */ val = ptr.p_s64[idx]; - if (ctrl->maximum >= 0 && val >= ctrl->maximum - ctrl->step / 2) + if (ctrl->maximum >= 0 && val >= ctrl->maximum - (s64)(ctrl->step / 2)) val = ctrl->maximum; else - val += ctrl->step / 2; + val += (s64)(ctrl->step / 2); val = clamp_t(s64, val, ctrl->minimum, ctrl->maximum); offset = val - ctrl->minimum; do_div(offset, ctrl->step); -- cgit v1.1