Commit ddac5c10 authored by Hans Verkuil's avatar Hans Verkuil Committed by Mauro Carvalho Chehab

[media] v4l2-ctrls: fix and improve volatile control handling

If you have a cluster of controls that is a mix of volatile and non-volatile
controls, then requesting the value of the volatile control would fail if the
master control of that cluster was non-volatile. The code assumed that the
volatile state of the master control was the same for all other controls in
the cluster.

This is now fixed.

In addition, it was clear from bugs in some drivers that it was confusing that
the ctrl->cur union had to be used in g_volatile_ctrl. Several drivers used the
'new' values instead. The framework was changed so that drivers now set the new
value instead of the current value.

This has an additional benefit as well: the volatile values are now only stored
in the 'new' value, leaving the current value alone. This is useful for
autofoo/foo control clusters where you want to have a 'foo' control act like a
volatile control if 'autofoo' is on, but as a normal control when it is off.

Since with this change the cur value is no longer overwritten when g_volatile_ctrl
is called, you can use it to remember the original 'foo' value. For example:

autofoo = 0, foo = 10 and foo is non-volatile.

Now autofoo is set to 1 and foo is marked volatile. Retrieving the foo value
will get the volatile value. Set autofoo back to 0, which marks foo as non-
volatile again, and retrieving foo will get the old current value of 10.
Signed-off-by: default avatarHans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: default avatarMauro Carvalho Chehab <mchehab@redhat.com>
parent 18d171ba
...@@ -1382,7 +1382,7 @@ static int wl1273_fm_g_volatile_ctrl(struct v4l2_ctrl *ctrl) ...@@ -1382,7 +1382,7 @@ static int wl1273_fm_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
switch (ctrl->id) { switch (ctrl->id) {
case V4L2_CID_TUNE_ANTENNA_CAPACITOR: case V4L2_CID_TUNE_ANTENNA_CAPACITOR:
ctrl->cur.val = wl1273_fm_get_tx_ctune(radio); ctrl->val = wl1273_fm_get_tx_ctune(radio);
break; break;
default: default:
......
...@@ -190,7 +190,7 @@ static int fm_g_volatile_ctrl(struct v4l2_ctrl *ctrl) ...@@ -190,7 +190,7 @@ static int fm_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
switch (ctrl->id) { switch (ctrl->id) {
case V4L2_CID_TUNE_ANTENNA_CAPACITOR: case V4L2_CID_TUNE_ANTENNA_CAPACITOR:
ctrl->cur.val = fm_tx_get_tune_cap_val(fmdev); ctrl->val = fm_tx_get_tune_cap_val(fmdev);
break; break;
default: default:
fmwarn("%s: Unknown IOCTL: %d\n", __func__, ctrl->id); fmwarn("%s: Unknown IOCTL: %d\n", __func__, ctrl->id);
......
...@@ -757,8 +757,8 @@ static int saa711x_g_volatile_ctrl(struct v4l2_ctrl *ctrl) ...@@ -757,8 +757,8 @@ static int saa711x_g_volatile_ctrl(struct v4l2_ctrl *ctrl)
switch (ctrl->id) { switch (ctrl->id) {
case V4L2_CID_CHROMA_AGC: case V4L2_CID_CHROMA_AGC:
/* chroma gain cluster */ /* chroma gain cluster */
if (state->agc->cur.val) if (state->agc->val)
state->gain->cur.val = state->gain->val =
saa711x_read(sd, R_0F_CHROMA_GAIN_CNTL) & 0x7f; saa711x_read(sd, R_0F_CHROMA_GAIN_CNTL) & 0x7f;
break; break;
} }
......
...@@ -25,8 +25,10 @@ ...@@ -25,8 +25,10 @@
#include <media/v4l2-ctrls.h> #include <media/v4l2-ctrls.h>
#include <media/v4l2-dev.h> #include <media/v4l2-dev.h>
#define has_op(master, op) \
(master->ops && master->ops->op)
#define call_op(master, op) \ #define call_op(master, op) \
((master->ops && master->ops->op) ? master->ops->op(master) : 0) (has_op(master, op) ? master->ops->op(master) : 0)
/* Internal temporary helper struct, one for each v4l2_ext_control */ /* Internal temporary helper struct, one for each v4l2_ext_control */
struct ctrl_helper { struct ctrl_helper {
...@@ -626,6 +628,20 @@ static int new_to_user(struct v4l2_ext_control *c, ...@@ -626,6 +628,20 @@ static int new_to_user(struct v4l2_ext_control *c,
return 0; return 0;
} }
static int ctrl_to_user(struct v4l2_ext_control *c,
struct v4l2_ctrl *ctrl)
{
if (ctrl->is_volatile)
return new_to_user(c, ctrl);
return cur_to_user(c, ctrl);
}
static int ctrl_is_volatile(struct v4l2_ext_control *c,
struct v4l2_ctrl *ctrl)
{
return ctrl->is_volatile;
}
/* Copy the new value to the current value. */ /* Copy the new value to the current value. */
static void new_to_cur(struct v4l2_ctrl *ctrl) static void new_to_cur(struct v4l2_ctrl *ctrl)
{ {
...@@ -1535,7 +1551,7 @@ int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs ...@@ -1535,7 +1551,7 @@ int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs
struct ctrl_helper helper[4]; struct ctrl_helper helper[4];
struct ctrl_helper *helpers = helper; struct ctrl_helper *helpers = helper;
int ret; int ret;
int i; int i, j;
cs->error_idx = cs->count; cs->error_idx = cs->count;
cs->ctrl_class = V4L2_CTRL_ID2CLASS(cs->ctrl_class); cs->ctrl_class = V4L2_CTRL_ID2CLASS(cs->ctrl_class);
...@@ -1562,19 +1578,33 @@ int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs ...@@ -1562,19 +1578,33 @@ int v4l2_g_ext_ctrls(struct v4l2_ctrl_handler *hdl, struct v4l2_ext_controls *cs
for (i = 0; !ret && i < cs->count; i++) { for (i = 0; !ret && i < cs->count; i++) {
struct v4l2_ctrl *ctrl = helpers[i].ctrl; struct v4l2_ctrl *ctrl = helpers[i].ctrl;
struct v4l2_ctrl *master = ctrl->cluster[0]; struct v4l2_ctrl *master = ctrl->cluster[0];
bool has_volatiles;
if (helpers[i].handled) if (helpers[i].handled)
continue; continue;
cs->error_idx = i; cs->error_idx = i;
/* Any volatile controls requested from this cluster? */
has_volatiles = ctrl->is_volatile;
if (!has_volatiles && has_op(master, g_volatile_ctrl) &&
master->ncontrols > 1)
has_volatiles = cluster_walk(i, cs, helpers,
ctrl_is_volatile);
v4l2_ctrl_lock(master); v4l2_ctrl_lock(master);
/* g_volatile_ctrl will update the current control values */
if (ctrl->is_volatile) /* g_volatile_ctrl will update the new control values */
if (has_volatiles) {
for (j = 0; j < master->ncontrols; j++)
cur_to_new(master->cluster[j]);
ret = call_op(master, g_volatile_ctrl); ret = call_op(master, g_volatile_ctrl);
/* If OK, then copy the current control values to the caller */ }
/* If OK, then copy the current (for non-volatile controls)
or the new (for volatile controls) control values to the
caller */
if (!ret) if (!ret)
ret = cluster_walk(i, cs, helpers, cur_to_user); ret = cluster_walk(i, cs, helpers, ctrl_to_user);
v4l2_ctrl_unlock(master); v4l2_ctrl_unlock(master);
cluster_done(i, cs, helpers); cluster_done(i, cs, helpers);
} }
...@@ -1596,15 +1626,21 @@ static int get_ctrl(struct v4l2_ctrl *ctrl, s32 *val) ...@@ -1596,15 +1626,21 @@ static int get_ctrl(struct v4l2_ctrl *ctrl, s32 *val)
{ {
struct v4l2_ctrl *master = ctrl->cluster[0]; struct v4l2_ctrl *master = ctrl->cluster[0];
int ret = 0; int ret = 0;
int i;
if (ctrl->flags & V4L2_CTRL_FLAG_WRITE_ONLY) if (ctrl->flags & V4L2_CTRL_FLAG_WRITE_ONLY)
return -EACCES; return -EACCES;
v4l2_ctrl_lock(master); v4l2_ctrl_lock(master);
/* g_volatile_ctrl will update the current control values */ /* g_volatile_ctrl will update the current control values */
if (ctrl->is_volatile) if (ctrl->is_volatile) {
for (i = 0; i < master->ncontrols; i++)
cur_to_new(master->cluster[i]);
ret = call_op(master, g_volatile_ctrl); ret = call_op(master, g_volatile_ctrl);
*val = ctrl->cur.val; *val = ctrl->val;
} else {
*val = ctrl->cur.val;
}
v4l2_ctrl_unlock(master); v4l2_ctrl_unlock(master);
return ret; return ret;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment