Hi Hans, A few comments below. On Sun, Sep 21, 2014 at 04:48:25PM +0200, Hans Verkuil wrote: > From: Hans Verkuil <hans.verkuil@xxxxxxxxx> > > Sometimes you want to apply a value every time v4l2_ctrl_apply_store > is called, and sometimes you want to apply that value only once. > > This adds support for that feature. > > Signed-off-by: Hans Verkuil <hans.verkuil@xxxxxxxxx> > --- > drivers/media/v4l2-core/v4l2-ctrls.c | 75 ++++++++++++++++++++++++++++-------- > drivers/media/v4l2-core/v4l2-ioctl.c | 14 +++---- > include/media/v4l2-ctrls.h | 12 ++++++ > 3 files changed, 79 insertions(+), 22 deletions(-) > > diff --git a/drivers/media/v4l2-core/v4l2-ctrls.c b/drivers/media/v4l2-core/v4l2-ctrls.c > index e5dccf2..21560b0 100644 > --- a/drivers/media/v4l2-core/v4l2-ctrls.c > +++ b/drivers/media/v4l2-core/v4l2-ctrls.c > @@ -1475,6 +1475,7 @@ static int ptr_to_user(struct v4l2_ext_control *c, > static int cur_to_user(struct v4l2_ext_control *c, > struct v4l2_ctrl *ctrl) > { > + c->flags = 0; > return ptr_to_user(c, ctrl, ctrl->p_cur); > } > > @@ -1482,8 +1483,13 @@ static int cur_to_user(struct v4l2_ext_control *c, > static int store_to_user(struct v4l2_ext_control *c, > struct v4l2_ctrl *ctrl, unsigned store) > { > + c->flags = 0; > if (store == 0) > return ptr_to_user(c, ctrl, ctrl->p_new); > + if (test_bit(store - 1, ctrl->cluster[0]->ignore_store_after_use)) > + c->flags |= V4L2_EXT_CTRL_FL_IGN_STORE_AFTER_USE; > + if (test_bit(store - 1, ctrl->cluster[0]->ignore_store)) > + c->flags |= V4L2_EXT_CTRL_FL_IGN_STORE; > return ptr_to_user(c, ctrl, ctrl->p_stores[store - 1]); > } > > @@ -1491,6 +1497,7 @@ static int store_to_user(struct v4l2_ext_control *c, > static int new_to_user(struct v4l2_ext_control *c, > struct v4l2_ctrl *ctrl) > { > + c->flags = 0; > return ptr_to_user(c, ctrl, ctrl->p_new); > } > > @@ -1546,6 +1553,8 @@ static int user_to_ptr(struct v4l2_ext_control *c, > static int user_to_new(struct v4l2_ext_control *c, > struct v4l2_ctrl *ctrl) > { > + ctrl->cluster[0]->new_ignore_store_after_use = > + c->flags & V4L2_EXT_CTRL_FL_IGN_STORE_AFTER_USE; > return user_to_ptr(c, ctrl, ctrl->p_new); > } > > @@ -1597,8 +1606,11 @@ static void new_to_cur(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, u32 ch_flags) > /* Helper function: copy the new control value to the store */ > static void new_to_store(struct v4l2_ctrl *ctrl) > { > + if (ctrl == NULL) > + return; > + > /* has_changed is set by cluster_changed */ > - if (ctrl && ctrl->has_changed) > + if (ctrl->has_changed) > ptr_to_ptr(ctrl, ctrl->p_new, ctrl->p_stores[ctrl->store - 1]); > } > > @@ -2328,6 +2340,12 @@ void v4l2_ctrl_cluster(unsigned ncontrols, struct v4l2_ctrl **controls) > > for (i = 0; i < ncontrols; i++) { > if (controls[i]) { > + /* > + * All controls in a cluster must have the same > + * V4L2_CTRL_FLAG_CAN_STORE flag value. > + */ > + WARN_ON((controls[0]->flags & controls[i]->flags) & > + V4L2_CTRL_FLAG_CAN_STORE); > controls[i]->cluster = controls; > controls[i]->ncontrols = ncontrols; > if (controls[i]->flags & V4L2_CTRL_FLAG_VOLATILE) > @@ -2850,6 +2868,10 @@ static int extend_store(struct v4l2_ctrl *ctrl, unsigned stores) > unsigned s, idx; > union v4l2_ctrl_ptr *p; > > + /* round up to the next multiple of 4 */ > + stores = (stores + 3) & ~3; You said it, round_up(). :-) The comment becomes redundant as a result, too. The above may also overflow. > + if (stores > V4L2_CTRLS_MAX_STORES) > + return -EINVAL; > p = kcalloc(stores, sizeof(union v4l2_ctrl_ptr), GFP_KERNEL); > if (p == NULL) > return -ENOMEM; > @@ -2868,6 +2890,7 @@ static int extend_store(struct v4l2_ctrl *ctrl, unsigned stores) > memcpy(p, ctrl->p_stores, ctrl->nr_of_stores * sizeof(union v4l2_ctrl_ptr)); > kfree(ctrl->p_stores); > ctrl->p_stores = p; > + bitmap_set(ctrl->ignore_store, ctrl->nr_of_stores, stores - ctrl->nr_of_stores); > ctrl->nr_of_stores = stores; > return 0; > } > @@ -3081,21 +3104,33 @@ static int try_or_set_cluster(struct v4l2_fh *fh, struct v4l2_ctrl *master, > > ret = call_op(master, try_ctrl); > > - /* Don't set if there is no change */ > - if (ret || !set || !cluster_changed(master)) > - return ret; > - ret = call_op(master, s_ctrl); > - if (ret) > + if (ret || !set) > return ret; > > - /* If OK, then make the new values permanent. */ > - update_flag = is_cur_manual(master) != is_new_manual(master); > - for (i = 0; i < master->ncontrols; i++) { > - if (store) > - new_to_store(master->cluster[i]); > + /* Don't set if there is no change */ > + if (cluster_changed(master)) { > + ret = call_op(master, s_ctrl); > + if (ret) > + return ret; > + > + /* If OK, then make the new values permanent. */ > + update_flag = is_cur_manual(master) != is_new_manual(master); > + for (i = 0; i < master->ncontrols; i++) { > + if (store) > + new_to_store(master->cluster[i]); > + else > + new_to_cur(fh, master->cluster[i], ch_flags | > + ((update_flag && i > 0) ? > + V4L2_EVENT_CTRL_CH_FLAGS : 0)); > + } > + } > + > + if (store) { > + if (master->new_ignore_store_after_use) > + set_bit(store - 1, master->ignore_store_after_use); > else > - new_to_cur(fh, master->cluster[i], ch_flags | > - ((update_flag && i > 0) ? V4L2_EVENT_CTRL_CH_FLAGS : 0)); > + clear_bit(store - 1, master->ignore_store_after_use); > + clear_bit(store - 1, master->ignore_store); How about allowing the user to forget a control in store as well? > } > return 0; > } > @@ -3401,8 +3436,18 @@ int v4l2_ctrl_apply_store(struct v4l2_ctrl_handler *hdl, unsigned store) > continue; > if (master->handler != hdl) > v4l2_ctrl_lock(master); > - for (i = 0; i < master->ncontrols; i++) > - store_to_new(master->cluster[i], store); > + for (i = 0; i < master->ncontrols; i++) { > + struct v4l2_ctrl *ctrl = master->cluster[i]; > + > + if (!ctrl || (store && test_bit(store - 1, master->ignore_store))) > + continue; > + store_to_new(ctrl, store); > + } > + > + if (store && !test_bit(store - 1, master->ignore_store)) { > + if (test_bit(store - 1, master->ignore_store_after_use)) How about: if (store && test_bit() && test_bit()) > + set_bit(store - 1, master->ignore_store); > + } > > /* For volatile autoclusters that are currently in auto mode > we need to discover if it will be set to manual mode. > diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c > index 628852c..9d3b4f2 100644 > --- a/drivers/media/v4l2-core/v4l2-ioctl.c > +++ b/drivers/media/v4l2-core/v4l2-ioctl.c > @@ -562,12 +562,14 @@ static void v4l_print_ext_controls(const void *arg, bool write_only) > pr_cont("class=0x%x, count=%d, error_idx=%d", > p->ctrl_class, p->count, p->error_idx); > for (i = 0; i < p->count; i++) { > - if (p->controls[i].size) > - pr_cont(", id/val=0x%x/0x%x", > - p->controls[i].id, p->controls[i].value); > + if (!p->controls[i].size) > + pr_cont(", id/flags/val=0x%x/0x%x/0x%x", > + p->controls[i].id, p->controls[i].flags, > + p->controls[i].value); > else > - pr_cont(", id/size=0x%x/%u", > - p->controls[i].id, p->controls[i].size); > + pr_cont(", id/flags/size=0x%x/0x%x/%u", > + p->controls[i].id, p->controls[i].flags, > + p->controls[i].size); > } > pr_cont("\n"); > } > @@ -888,8 +890,6 @@ static int check_ext_ctrls(struct v4l2_ext_controls *c, int allow_priv) > > /* zero the reserved fields */ > c->reserved[0] = c->reserved[1] = 0; > - for (i = 0; i < c->count; i++) > - c->controls[i].reserved2[0] = 0; > > /* V4L2_CID_PRIVATE_BASE cannot be used as control class > when using extended controls. > diff --git a/include/media/v4l2-ctrls.h b/include/media/v4l2-ctrls.h > index 713980a..3005d88 100644 > --- a/include/media/v4l2-ctrls.h > +++ b/include/media/v4l2-ctrls.h > @@ -36,6 +36,9 @@ struct v4l2_subscribed_event; > struct v4l2_fh; > struct poll_table_struct; > > +/* Must be a multiple of 4 */ > +#define V4L2_CTRLS_MAX_STORES VIDEO_MAX_FRAME > + > /** union v4l2_ctrl_ptr - A pointer to a control value. > * @p_s32: Pointer to a 32-bit signed value. > * @p_s64: Pointer to a 64-bit signed value. > @@ -123,6 +126,8 @@ typedef void (*v4l2_ctrl_notify_fnc)(struct v4l2_ctrl *ctrl, void *priv); > * @call_notify: If set, then call the handler's notify function whenever the > * control's value changes. > * @can_store: If set, then this control supports configuration stores. > + * @new_ignore_store_after_use: If set, then the new control had the > + * V4L2_EXT_CTRL_FL_IGN_STORE_AFTER_USE flag set. > * @manual_mode_value: If the is_auto flag is set, then this is the value > * of the auto control that determines if that control is in > * manual mode. So if the value of the auto control equals this > @@ -143,6 +148,10 @@ typedef void (*v4l2_ctrl_notify_fnc)(struct v4l2_ctrl *ctrl, void *priv); > * @nr_of_dims:The number of dimensions in @dims. > * @nr_of_stores: The number of allocated configuration stores of this control. > * @store: The configuration store that the control op operates on. > + * @ignore_store: If the bit for the corresponding store is 1, then don't apply that > + * store's value. > + * @ignore_store_after_use: If the bit for the corresponding store is 1, then set the > + * bit in @ignore_store after the store's value has been applied. > * @menu_skip_mask: The control's skip mask for menu controls. This makes it > * easy to skip menu items that are not valid. If bit X is set, > * then menu item X is skipped. Of course, this only works for > @@ -183,6 +192,7 @@ struct v4l2_ctrl { > unsigned int has_volatiles:1; > unsigned int call_notify:1; > unsigned int can_store:1; > + unsigned int new_ignore_store_after_use:1; > unsigned int manual_mode_value:8; > > const struct v4l2_ctrl_ops *ops; > @@ -197,6 +207,8 @@ struct v4l2_ctrl { > u32 nr_of_dims; > u16 nr_of_stores; > u16 store; > + DECLARE_BITMAP(ignore_store, V4L2_CTRLS_MAX_STORES); > + DECLARE_BITMAP(ignore_store_after_use, V4L2_CTRLS_MAX_STORES); I'd store this information next to the value itself. The reason is that stores are typically accessed one at a time, and thus keeping data related to a single store in a single contiguous location reduces cache misses. > union { > u64 step; > u64 menu_skip_mask; -- Kind regards, Sakari Ailus e-mail: sakari.ailus@xxxxxx XMPP: sailus@xxxxxxxxxxxxxx -- To unsubscribe from this list: send the line "unsubscribe linux-media" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html