// SPDX-License-Identifier: GPL-2.0-or-later
/*
* V4L2 controls framework uAPI implementation:
*
* Copyright (C) 2010-2021 Hans Verkuil <hverkuil-cisco@xs4all.nl>
*/
#define pr_fmt(fmt) "v4l2-ctrls: " fmt
#include <linux/export.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-dev.h>
#include <media/v4l2-device.h>
#include <media/v4l2-event.h>
#include <media/v4l2-ioctl.h>
#include "v4l2-ctrls-priv.h"
/* Internal temporary helper struct, one for each v4l2_ext_control */
struct v4l2_ctrl_helper {
/* Pointer to the control reference of the master control */
struct v4l2_ctrl_ref *mref;
/* The control ref corresponding to the v4l2_ext_control ID field. */
struct v4l2_ctrl_ref *ref;
/*
* v4l2_ext_control index of the next control belonging to the
* same cluster, or 0 if there isn't any.
*/
u32 next;
};
/*
* Helper functions to copy control payload data from kernel space to
* user space and vice versa.
*/
/* 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, ptr.p_const, c->size) ?
-EFAULT : 0;
switch (ctrl->type) {
case V4L2_CTRL_TYPE_STRING:
len = strlen(ptr.p_char);
if (c->size < len + 1) {
c->size = ctrl->elem_size;
return -ENOSPC;
}
return copy_to_user(c->string, ptr.p_char, len + 1) ?
-EFAULT : 0;
case V4L2_CTRL_TYPE_INTEGER64:
c->value64 = *ptr.p_s64;
break;
default:
c->value = *ptr.p_s32;
break;
}
return 0;
}
/* 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 request value back to the caller */
static int req_to_user(struct v4l2_ext_control *c,
struct v4l2_ctrl_ref *ref)
{
return ptr_to_user(c, ref->ctrl, ref->p_req);
}
/* Helper function: copy the initial control value back to the caller */
static int def_to_user(struct v4l2_ext_control *c, struct v4l2_ctrl *ctrl)
{
ctrl->type_ops->init(ctrl, 0, ctrl->p_new);
return ptr_to_user(c, ctrl, ctrl->p_new);
}
/* 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)
{
int ret;
u32 size;
ctrl->is_new = 0;
if (ctrl->is_