[media] v4l: Add compat functions for the multi-planar API

Add multi-planar ioctl handling to the 32bit compatibility layer.

[mchehab@redhat.com: Merged with a fixup patch from Pawel]
Signed-off-by: Pawel Osciak <p.osciak@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
Signed-off-by: Marek Szyprowski <m.szyprowski@samsung.com>
Reviewed-by: Hans Verkuil <hverkuil@xs4all.nl>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
This commit is contained in:
Pawel Osciak 2010-07-29 14:56:47 -03:00 committed by Mauro Carvalho Chehab
parent d14e6d76eb
commit 52a3082fea

View File

@ -97,6 +97,14 @@ static inline int get_v4l2_pix_format(struct v4l2_pix_format *kp, struct v4l2_pi
return 0;
}
static inline int get_v4l2_pix_format_mplane(struct v4l2_pix_format_mplane *kp,
struct v4l2_pix_format_mplane __user *up)
{
if (copy_from_user(kp, up, sizeof(struct v4l2_pix_format_mplane)))
return -EFAULT;
return 0;
}
static inline int put_v4l2_pix_format(struct v4l2_pix_format *kp, struct v4l2_pix_format __user *up)
{
if (copy_to_user(up, kp, sizeof(struct v4l2_pix_format)))
@ -104,6 +112,14 @@ static inline int put_v4l2_pix_format(struct v4l2_pix_format *kp, struct v4l2_pi
return 0;
}
static inline int put_v4l2_pix_format_mplane(struct v4l2_pix_format_mplane *kp,
struct v4l2_pix_format_mplane __user *up)
{
if (copy_to_user(up, kp, sizeof(struct v4l2_pix_format_mplane)))
return -EFAULT;
return 0;
}
static inline int get_v4l2_vbi_format(struct v4l2_vbi_format *kp, struct v4l2_vbi_format __user *up)
{
if (copy_from_user(kp, up, sizeof(struct v4l2_vbi_format)))
@ -136,6 +152,7 @@ struct v4l2_format32 {
enum v4l2_buf_type type;
union {
struct v4l2_pix_format pix;
struct v4l2_pix_format_mplane pix_mp;
struct v4l2_window32 win;
struct v4l2_vbi_format vbi;
struct v4l2_sliced_vbi_format sliced;
@ -152,6 +169,10 @@ static int get_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
return get_v4l2_pix_format(&kp->fmt.pix, &up->fmt.pix);
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
return get_v4l2_pix_format_mplane(&kp->fmt.pix_mp,
&up->fmt.pix_mp);
case V4L2_BUF_TYPE_VIDEO_OVERLAY:
case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
return get_v4l2_window32(&kp->fmt.win, &up->fmt.win);
@ -181,6 +202,10 @@ static int put_v4l2_format32(struct v4l2_format *kp, struct v4l2_format32 __user
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
return put_v4l2_pix_format(&kp->fmt.pix, &up->fmt.pix);
case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
return put_v4l2_pix_format_mplane(&kp->fmt.pix_mp,
&up->fmt.pix_mp);
case V4L2_BUF_TYPE_VIDEO_OVERLAY:
case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
return put_v4l2_window32(&kp->fmt.win, &up->fmt.win);
@ -232,6 +257,17 @@ static int put_v4l2_standard32(struct v4l2_standard *kp, struct v4l2_standard32
return 0;
}
struct v4l2_plane32 {
__u32 bytesused;
__u32 length;
union {
__u32 mem_offset;
compat_long_t userptr;
} m;
__u32 data_offset;
__u32 reserved[11];
};
struct v4l2_buffer32 {
__u32 index;
enum v4l2_buf_type type;
@ -247,14 +283,64 @@ struct v4l2_buffer32 {
union {
__u32 offset;
compat_long_t userptr;
compat_caddr_t planes;
} m;
__u32 length;
__u32 input;
__u32 reserved;
};
static int get_v4l2_plane32(struct v4l2_plane *up, struct v4l2_plane32 *up32,
enum v4l2_memory memory)
{
void __user *up_pln;
compat_long_t p;
if (copy_in_user(up, up32, 2 * sizeof(__u32)) ||
copy_in_user(&up->data_offset, &up32->data_offset,
sizeof(__u32)))
return -EFAULT;
if (memory == V4L2_MEMORY_USERPTR) {
if (get_user(p, &up32->m.userptr))
return -EFAULT;
up_pln = compat_ptr(p);
if (put_user((unsigned long)up_pln, &up->m.userptr))
return -EFAULT;
} else {
if (copy_in_user(&up->m.mem_offset, &up32->m.mem_offset,
sizeof(__u32)))
return -EFAULT;
}
return 0;
}
static int put_v4l2_plane32(struct v4l2_plane *up, struct v4l2_plane32 *up32,
enum v4l2_memory memory)
{
if (copy_in_user(up32, up, 2 * sizeof(__u32)) ||
copy_in_user(&up32->data_offset, &up->data_offset,
sizeof(__u32)))
return -EFAULT;
/* For MMAP, driver might've set up the offset, so copy it back.
* USERPTR stays the same (was userspace-provided), so no copying. */
if (memory == V4L2_MEMORY_MMAP)
if (copy_in_user(&up32->m.mem_offset, &up->m.mem_offset,
sizeof(__u32)))
return -EFAULT;
return 0;
}
static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user *up)
{
struct v4l2_plane32 __user *uplane32;
struct v4l2_plane __user *uplane;
compat_caddr_t p;
int num_planes;
int ret;
if (!access_ok(VERIFY_READ, up, sizeof(struct v4l2_buffer32)) ||
get_user(kp->index, &up->index) ||
@ -263,33 +349,84 @@ static int get_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
get_user(kp->memory, &up->memory) ||
get_user(kp->input, &up->input))
return -EFAULT;
switch (kp->memory) {
case V4L2_MEMORY_MMAP:
if (get_user(kp->length, &up->length) ||
get_user(kp->m.offset, &up->m.offset))
return -EFAULT;
break;
case V4L2_MEMORY_USERPTR:
{
compat_long_t tmp;
if (get_user(kp->length, &up->length) ||
get_user(tmp, &up->m.userptr))
if (V4L2_TYPE_IS_OUTPUT(kp->type))
if (get_user(kp->bytesused, &up->bytesused) ||
get_user(kp->field, &up->field) ||
get_user(kp->timestamp.tv_sec, &up->timestamp.tv_sec) ||
get_user(kp->timestamp.tv_usec,
&up->timestamp.tv_usec))
return -EFAULT;
kp->m.userptr = (unsigned long)compat_ptr(tmp);
if (V4L2_TYPE_IS_MULTIPLANAR(kp->type)) {
if (get_user(kp->length, &up->length))
return -EFAULT;
num_planes = kp->length;
if (num_planes == 0) {
kp->m.planes = NULL;
/* num_planes == 0 is legal, e.g. when userspace doesn't
* need planes array on DQBUF*/
return 0;
}
break;
case V4L2_MEMORY_OVERLAY:
if (get_user(kp->m.offset, &up->m.offset))
if (get_user(p, &up->m.planes))
return -EFAULT;
break;
uplane32 = compat_ptr(p);
if (!access_ok(VERIFY_READ, uplane32,
num_planes * sizeof(struct v4l2_plane32)))
return -EFAULT;
/* We don't really care if userspace decides to kill itself
* by passing a very big num_planes value */
uplane = compat_alloc_user_space(num_planes *
sizeof(struct v4l2_plane));
kp->m.planes = uplane;
while (--num_planes >= 0) {
ret = get_v4l2_plane32(uplane, uplane32, kp->memory);
if (ret)
return ret;
++uplane;
++uplane32;
}
} else {
switch (kp->memory) {
case V4L2_MEMORY_MMAP:
if (get_user(kp->length, &up->length) ||
get_user(kp->m.offset, &up->m.offset))
return -EFAULT;
break;
case V4L2_MEMORY_USERPTR:
{
compat_long_t tmp;
if (get_user(kp->length, &up->length) ||
get_user(tmp, &up->m.userptr))
return -EFAULT;
kp->m.userptr = (unsigned long)compat_ptr(tmp);
}
break;
case V4L2_MEMORY_OVERLAY:
if (get_user(kp->m.offset, &up->m.offset))
return -EFAULT;
break;
}
}
return 0;
}
static int put_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user *up)
{
struct v4l2_plane32 __user *uplane32;
struct v4l2_plane __user *uplane;
compat_caddr_t p;
int num_planes;
int ret;
if (!access_ok(VERIFY_WRITE, up, sizeof(struct v4l2_buffer32)) ||
put_user(kp->index, &up->index) ||
put_user(kp->type, &up->type) ||
@ -297,22 +434,7 @@ static int put_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
put_user(kp->memory, &up->memory) ||
put_user(kp->input, &up->input))
return -EFAULT;
switch (kp->memory) {
case V4L2_MEMORY_MMAP:
if (put_user(kp->length, &up->length) ||
put_user(kp->m.offset, &up->m.offset))
return -EFAULT;
break;
case V4L2_MEMORY_USERPTR:
if (put_user(kp->length, &up->length) ||
put_user(kp->m.userptr, &up->m.userptr))
return -EFAULT;
break;
case V4L2_MEMORY_OVERLAY:
if (put_user(kp->m.offset, &up->m.offset))
return -EFAULT;
break;
}
if (put_user(kp->bytesused, &up->bytesused) ||
put_user(kp->field, &up->field) ||
put_user(kp->timestamp.tv_sec, &up->timestamp.tv_sec) ||
@ -321,6 +443,43 @@ static int put_v4l2_buffer32(struct v4l2_buffer *kp, struct v4l2_buffer32 __user
put_user(kp->sequence, &up->sequence) ||
put_user(kp->reserved, &up->reserved))
return -EFAULT;
if (V4L2_TYPE_IS_MULTIPLANAR(kp->type)) {
num_planes = kp->length;
if (num_planes == 0)
return 0;
uplane = kp->m.planes;
if (get_user(p, &up->m.planes))
return -EFAULT;
uplane32 = compat_ptr(p);
while (--num_planes >= 0) {
ret = put_v4l2_plane32(uplane, uplane32, kp->memory);
if (ret)
return ret;
++uplane;
++uplane32;
}
} else {
switch (kp->memory) {
case V4L2_MEMORY_MMAP:
if (put_user(kp->length, &up->length) ||
put_user(kp->m.offset, &up->m.offset))
return -EFAULT;
break;
case V4L2_MEMORY_USERPTR:
if (put_user(kp->length, &up->length) ||
put_user(kp->m.userptr, &up->m.userptr))
return -EFAULT;
break;
case V4L2_MEMORY_OVERLAY:
if (put_user(kp->m.offset, &up->m.offset))
return -EFAULT;
break;
}
}
return 0;
}
@ -442,12 +601,13 @@ static int get_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext
if (get_user(p, &up->controls))
return -EFAULT;
ucontrols = compat_ptr(p);
if (!access_ok(VERIFY_READ, ucontrols, n * sizeof(struct v4l2_ext_control)))
if (!access_ok(VERIFY_READ, ucontrols,
n * sizeof(struct v4l2_ext_control32)))
return -EFAULT;
kcontrols = compat_alloc_user_space(n * sizeof(struct v4l2_ext_control));
kp->controls = kcontrols;
while (--n >= 0) {
if (copy_in_user(kcontrols, ucontrols, sizeof(*kcontrols)))
if (copy_in_user(kcontrols, ucontrols, sizeof(*ucontrols)))
return -EFAULT;
if (ctrl_is_pointer(kcontrols->id)) {
void __user *s;
@ -483,7 +643,8 @@ static int put_v4l2_ext_controls32(struct v4l2_ext_controls *kp, struct v4l2_ext
if (get_user(p, &up->controls))
return -EFAULT;
ucontrols = compat_ptr(p);
if (!access_ok(VERIFY_WRITE, ucontrols, n * sizeof(struct v4l2_ext_control)))
if (!access_ok(VERIFY_WRITE, ucontrols,
n * sizeof(struct v4l2_ext_control32)))
return -EFAULT;
while (--n >= 0) {