V4L/DVB (4189): Add videodev support for VIDIOC_S/G/TRY_EXT_CTRLS.

videodev.c copies the control list specified in struct v4l2_ext_controls
to kernel space.

Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
This commit is contained in:
Hans Verkuil 2006-06-18 13:43:28 -03:00 committed by Mauro Carvalho Chehab
parent 4f34171212
commit 0597691456
2 changed files with 109 additions and 2 deletions

View file

@ -190,10 +190,15 @@ video_usercopy(struct inode *inode, struct file *file,
void *mbuf = NULL;
void *parg = NULL;
int err = -EINVAL;
int is_ext_ctrl;
size_t ctrls_size = 0;
void __user *user_ptr = NULL;
#ifdef __OLD_VIDIOC_
cmd = video_fix_command(cmd);
#endif
is_ext_ctrl = (cmd == VIDIOC_S_EXT_CTRLS || cmd == VIDIOC_G_EXT_CTRLS ||
cmd == VIDIOC_TRY_EXT_CTRLS);
/* Copy arguments into temp kernel buffer */
switch (_IOC_DIR(cmd)) {
@ -215,19 +220,47 @@ video_usercopy(struct inode *inode, struct file *file,
err = -EFAULT;
if (_IOC_DIR(cmd) & _IOC_WRITE)
if (copy_from_user(parg, (void __user *)arg,
_IOC_SIZE(cmd)))
if (copy_from_user(parg, (void __user *)arg, _IOC_SIZE(cmd)))
goto out;
break;
}
if (is_ext_ctrl) {
struct v4l2_ext_controls *p = parg;
/* In case of an error, tell the caller that it wasn't
a specific control that caused it. */
p->error_idx = p->count;
user_ptr = (void __user *)p->controls;
if (p->count) {
ctrls_size = sizeof(struct v4l2_ext_control) * p->count;
/* Note: v4l2_ext_controls fits in sbuf[] so mbuf is still NULL. */
mbuf = kmalloc(ctrls_size, GFP_KERNEL);
err = -ENOMEM;
if (NULL == mbuf)
goto out_ext_ctrl;
err = -EFAULT;
if (copy_from_user(mbuf, user_ptr, ctrls_size))
goto out_ext_ctrl;
p->controls = mbuf;
}
}
/* call driver */
err = func(inode, file, cmd, parg);
if (err == -ENOIOCTLCMD)
err = -EINVAL;
if (is_ext_ctrl) {
struct v4l2_ext_controls *p = parg;
p->controls = (void *)user_ptr;
if (p->count && err == 0 && copy_to_user(user_ptr, mbuf, ctrls_size))
err = -EFAULT;
goto out_ext_ctrl;
}
if (err < 0)
goto out;
out_ext_ctrl:
/* Copy results into user buffer */
switch (_IOC_DIR(cmd))
{
@ -993,6 +1026,39 @@ static int __video_do_ioctl(struct inode *inode, struct file *file,
ret=vfd->vidioc_s_ctrl(file, fh, p);
break;
}
case VIDIOC_G_EXT_CTRLS:
{
struct v4l2_ext_controls *p = arg;
if (vfd->vidioc_g_ext_ctrls) {
dbgarg(cmd, "count=%d\n", p->count);
ret=vfd->vidioc_g_ext_ctrls(file, fh, p);
}
break;
}
case VIDIOC_S_EXT_CTRLS:
{
struct v4l2_ext_controls *p = arg;
if (vfd->vidioc_s_ext_ctrls) {
dbgarg(cmd, "count=%d\n", p->count);
ret=vfd->vidioc_s_ext_ctrls(file, fh, p);
}
break;
}
case VIDIOC_TRY_EXT_CTRLS:
{
struct v4l2_ext_controls *p = arg;
if (vfd->vidioc_try_ext_ctrls) {
dbgarg(cmd, "count=%d\n", p->count);
ret=vfd->vidioc_try_ext_ctrls(file, fh, p);
}
break;
}
case VIDIOC_QUERYMENU:
{
struct v4l2_querymenu *p=arg;
@ -1326,10 +1392,15 @@ int video_ioctl2 (struct inode *inode, struct file *file,
void *mbuf = NULL;
void *parg = NULL;
int err = -EINVAL;
int is_ext_ctrl;
size_t ctrls_size = 0;
void __user *user_ptr = NULL;
#ifdef __OLD_VIDIOC_
cmd = video_fix_command(cmd);
#endif
is_ext_ctrl = (cmd == VIDIOC_S_EXT_CTRLS || cmd == VIDIOC_G_EXT_CTRLS ||
cmd == VIDIOC_TRY_EXT_CTRLS);
/* Copy arguments into temp kernel buffer */
switch (_IOC_DIR(cmd)) {
@ -1356,13 +1427,43 @@ int video_ioctl2 (struct inode *inode, struct file *file,
break;
}
if (is_ext_ctrl) {
struct v4l2_ext_controls *p = parg;
/* In case of an error, tell the caller that it wasn't
a specific control that caused it. */
p->error_idx = p->count;
user_ptr = (void __user *)p->controls;
if (p->count) {
ctrls_size = sizeof(struct v4l2_ext_control) * p->count;
/* Note: v4l2_ext_controls fits in sbuf[] so mbuf is still NULL. */
mbuf = kmalloc(ctrls_size, GFP_KERNEL);
err = -ENOMEM;
if (NULL == mbuf)
goto out_ext_ctrl;
err = -EFAULT;
if (copy_from_user(mbuf, user_ptr, ctrls_size))
goto out_ext_ctrl;
p->controls = mbuf;
}
}
/* Handles IOCTL */
err = __video_do_ioctl(inode, file, cmd, parg);
if (err == -ENOIOCTLCMD)
err = -EINVAL;
if (is_ext_ctrl) {
struct v4l2_ext_controls *p = parg;
p->controls = (void *)user_ptr;
if (p->count && err == 0 && copy_to_user(user_ptr, mbuf, ctrls_size))
err = -EFAULT;
goto out_ext_ctrl;
}
if (err < 0)
goto out;
out_ext_ctrl:
/* Copy results into user buffer */
switch (_IOC_DIR(cmd))
{

View file

@ -232,6 +232,12 @@ struct video_device
struct v4l2_control *a);
int (*vidioc_s_ctrl) (struct file *file, void *fh,
struct v4l2_control *a);
int (*vidioc_g_ext_ctrls) (struct file *file, void *fh,
struct v4l2_ext_controls *a);
int (*vidioc_s_ext_ctrls) (struct file *file, void *fh,
struct v4l2_ext_controls *a);
int (*vidioc_try_ext_ctrls) (struct file *file, void *fh,
struct v4l2_ext_controls *a);
int (*vidioc_querymenu) (struct file *file, void *fh,
struct v4l2_querymenu *a);