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

[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: default avatarHans Verkuil <hans.verkuil@cisco.com>
Signed-off-by: default avatarMauro Carvalho Chehab <m.chehab@samsung.com>
parent 0176077a
...@@ -1304,48 +1304,64 @@ static const struct v4l2_ctrl_type_ops std_type_ops = { ...@@ -1304,48 +1304,64 @@ static const struct v4l2_ctrl_type_ops std_type_ops = {
.validate = std_validate, .validate = std_validate,
}; };
/* Helper function: copy the current control value back to the caller */ /* Helper function: copy the given control value back to the caller */
static int cur_to_user(struct v4l2_ext_control *c, static int ptr_to_user(struct v4l2_ext_control *c,
struct v4l2_ctrl *ctrl) struct v4l2_ctrl *ctrl,
union v4l2_ctrl_ptr ptr)
{ {
u32 len; u32 len;
if (ctrl->is_ptr && !ctrl->is_string) 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) { switch (ctrl->type) {
case V4L2_CTRL_TYPE_STRING: case V4L2_CTRL_TYPE_STRING:
len = strlen(ctrl->cur.string); len = strlen(ptr.p_char);
if (c->size < len + 1) { if (c->size < len + 1) {
c->size = len + 1; c->size = len + 1;
return -ENOSPC; return -ENOSPC;
} }
return copy_to_user(c->string, ctrl->cur.string, return copy_to_user(c->string, ptr.p_char, len + 1) ?
len + 1) ? -EFAULT : 0; -EFAULT : 0;
case V4L2_CTRL_TYPE_INTEGER64: case V4L2_CTRL_TYPE_INTEGER64:
c->value64 = ctrl->cur.val64; c->value64 = *ptr.p_s64;
break; break;
default: default:
c->value = ctrl->cur.val; c->value = *ptr.p_s32;
break; break;
} }
return 0; return 0;
} }
/* Helper function: copy the caller-provider value as the new control value */ /* Helper function: copy the current control value back to the caller */
static int user_to_new(struct v4l2_ext_control *c, 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) 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; int ret;
u32 size; u32 size;
ctrl->is_new = 1; ctrl->is_new = 1;
if (ctrl->is_ptr && !ctrl->is_string) 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) { switch (ctrl->type) {
case V4L2_CTRL_TYPE_INTEGER64: case V4L2_CTRL_TYPE_INTEGER64:
ctrl->val64 = c->value64; *ptr.p_s64 = c->value64;
break; break;
case V4L2_CTRL_TYPE_STRING: case V4L2_CTRL_TYPE_STRING:
size = c->size; size = c->size;
...@@ -1353,83 +1369,64 @@ static int user_to_new(struct v4l2_ext_control *c, ...@@ -1353,83 +1369,64 @@ static int user_to_new(struct v4l2_ext_control *c,
return -ERANGE; return -ERANGE;
if (size > ctrl->maximum + 1) if (size > ctrl->maximum + 1)
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) { 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, /* If the string was longer than ctrl->maximum,
then return an error. */ then return an error. */
if (strlen(ctrl->string) == ctrl->maximum && last) if (strlen(ptr.p_char) == ctrl->maximum && last)
return -ERANGE; return -ERANGE;
} }
return ret ? -EFAULT : 0; return ret ? -EFAULT : 0;
default: default:
ctrl->val = c->value; *ptr.p_s32 = c->value;
break; break;
} }
return 0; return 0;
} }
/* Helper function: copy the new control value back to the caller */ /* Helper function: copy the caller-provider value as the new control value */
static int new_to_user(struct v4l2_ext_control *c, static int user_to_new(struct v4l2_ext_control *c,
struct v4l2_ctrl *ctrl) struct v4l2_ctrl *ctrl)
{ {
u32 len; return user_to_ptr(c, ctrl, ctrl->p_new);
}
if (ctrl->is_ptr && !ctrl->is_string)
return copy_to_user(c->ptr, ctrl->p, ctrl->elem_size);
/* 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) { switch (ctrl->type) {
case V4L2_CTRL_TYPE_STRING: case V4L2_CTRL_TYPE_STRING:
len = strlen(ctrl->string); /* strings are always 0-terminated */
if (c->size < len + 1) { strcpy(to.p_char, from.p_char);
c->size = ctrl->maximum + 1; break;
return -ENOSPC;
}
return copy_to_user(c->string, ctrl->string,
len + 1) ? -EFAULT : 0;
case V4L2_CTRL_TYPE_INTEGER64: case V4L2_CTRL_TYPE_INTEGER64:
c->value64 = ctrl->val64; *to.p_s64 = *from.p_s64;
break; break;
default: 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; break;
} }
return 0;
} }
/* Copy the new value to the current value. */ /* Copy the new value to the current value. */
static void new_to_cur(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, u32 ch_flags) static void new_to_cur(struct v4l2_fh *fh, struct v4l2_ctrl *ctrl, u32 ch_flags)
{ {
bool changed = false; bool changed;
if (ctrl == NULL) if (ctrl == NULL)
return; 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) { if (ch_flags & V4L2_EVENT_CTRL_CH_FLAGS) {
/* Note: CH_FLAGS is only set for auto clusters. */ /* Note: CH_FLAGS is only set for auto clusters. */
ctrl->flags &= ctrl->flags &=
...@@ -1458,21 +1455,7 @@ static void cur_to_new(struct v4l2_ctrl *ctrl) ...@@ -1458,21 +1455,7 @@ static void cur_to_new(struct v4l2_ctrl *ctrl)
{ {
if (ctrl == NULL) if (ctrl == NULL)
return; return;
switch (ctrl->type) { ptr_to_ptr(ctrl, ctrl->p_cur, ctrl->p_new);
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;
}
} }
/* Return non-zero if one or more of the controls in the cluster has a new /* Return non-zero if one or more of the controls in the cluster has a new
......
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