diff options
Diffstat (limited to 'drivers/media/v4l2-core/v4l2-ioctl.c')
-rw-r--r-- | drivers/media/v4l2-core/v4l2-ioctl.c | 364 |
1 files changed, 267 insertions, 97 deletions
diff --git a/drivers/media/v4l2-core/v4l2-ioctl.c b/drivers/media/v4l2-core/v4l2-ioctl.c index b1f4b991dba6..aaf83e254272 100644 --- a/drivers/media/v4l2-core/v4l2-ioctl.c +++ b/drivers/media/v4l2-core/v4l2-ioctl.c @@ -474,10 +474,10 @@ static void v4l_print_buffer(const void *arg, bool write_only) const struct v4l2_plane *plane; int i; - pr_cont("%02ld:%02d:%02d.%08ld index=%d, type=%s, request_fd=%d, flags=0x%08x, field=%s, sequence=%d, memory=%s", - p->timestamp.tv_sec / 3600, - (int)(p->timestamp.tv_sec / 60) % 60, - (int)(p->timestamp.tv_sec % 60), + pr_cont("%02d:%02d:%02d.%09ld index=%d, type=%s, request_fd=%d, flags=0x%08x, field=%s, sequence=%d, memory=%s", + (int)p->timestamp.tv_sec / 3600, + ((int)p->timestamp.tv_sec / 60) % 60, + ((int)p->timestamp.tv_sec % 60), (long)p->timestamp.tv_usec, p->index, prt_names(p->type, v4l2_type_names), p->request_fd, @@ -821,7 +821,7 @@ static void v4l_print_event(const void *arg, bool write_only) const struct v4l2_event *p = arg; const struct v4l2_event_ctrl *c; - pr_cont("type=0x%x, pending=%u, sequence=%u, id=%u, timestamp=%lu.%9.9lu\n", + pr_cont("type=0x%x, pending=%u, sequence=%u, id=%u, timestamp=%llu.%9.9llu\n", p->type, p->pending, p->sequence, p->id, p->timestamp.tv_sec, p->timestamp.tv_nsec); switch (p->type) { @@ -932,12 +932,22 @@ static int check_ext_ctrls(struct v4l2_ext_controls *c, int allow_priv) static int check_fmt(struct file *file, enum v4l2_buf_type type) { + const u32 vid_caps = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_VIDEO_CAPTURE_MPLANE | + V4L2_CAP_VIDEO_OUTPUT | + V4L2_CAP_VIDEO_OUTPUT_MPLANE | + V4L2_CAP_VIDEO_M2M | V4L2_CAP_VIDEO_M2M_MPLANE; + const u32 meta_caps = V4L2_CAP_META_CAPTURE | + V4L2_CAP_META_OUTPUT; struct video_device *vfd = video_devdata(file); const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops; - bool is_vid = vfd->vfl_type == VFL_TYPE_GRABBER; + bool is_vid = vfd->vfl_type == VFL_TYPE_GRABBER && + (vfd->device_caps & vid_caps); bool is_vbi = vfd->vfl_type == VFL_TYPE_VBI; bool is_sdr = vfd->vfl_type == VFL_TYPE_SDR; bool is_tch = vfd->vfl_type == VFL_TYPE_TOUCH; + bool is_meta = vfd->vfl_type == VFL_TYPE_GRABBER && + (vfd->device_caps & meta_caps); bool is_rx = vfd->vfl_dir != VFL_DIR_TX; bool is_tx = vfd->vfl_dir != VFL_DIR_RX; @@ -951,7 +961,7 @@ static int check_fmt(struct file *file, enum v4l2_buf_type type) return 0; break; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: - if (is_vid && is_rx && ops->vidioc_g_fmt_vid_cap_mplane) + if ((is_vid || is_tch) && is_rx && ops->vidioc_g_fmt_vid_cap_mplane) return 0; break; case V4L2_BUF_TYPE_VIDEO_OVERLAY: @@ -996,11 +1006,11 @@ static int check_fmt(struct file *file, enum v4l2_buf_type type) return 0; break; case V4L2_BUF_TYPE_META_CAPTURE: - if (is_vid && is_rx && ops->vidioc_g_fmt_meta_cap) + if (is_meta && is_rx && ops->vidioc_g_fmt_meta_cap) return 0; break; case V4L2_BUF_TYPE_META_OUTPUT: - if (is_vid && is_tx && ops->vidioc_g_fmt_meta_out) + if (is_meta && is_tx && ops->vidioc_g_fmt_meta_out) return 0; break; default: @@ -1057,14 +1067,19 @@ static int v4l_querycap(const struct v4l2_ioctl_ops *ops, ret = ops->vidioc_querycap(file, fh, cap); - cap->capabilities |= V4L2_CAP_EXT_PIX_FORMAT; /* - * Drivers MUST fill in device_caps, so check for this and - * warn if it was forgotten. + * Drivers must not change device_caps, so check for this and + * warn if this happened. + */ + WARN_ON(cap->device_caps != vfd->device_caps); + /* + * Check that capabilities is a superset of + * vfd->device_caps | V4L2_CAP_DEVICE_CAPS */ - WARN(!(cap->capabilities & V4L2_CAP_DEVICE_CAPS) || - !cap->device_caps, "Bad caps for driver %s, %x %x", - cap->driver, cap->capabilities, cap->device_caps); + WARN_ON((cap->capabilities & + (vfd->device_caps | V4L2_CAP_DEVICE_CAPS)) != + (vfd->device_caps | V4L2_CAP_DEVICE_CAPS)); + cap->capabilities |= V4L2_CAP_EXT_PIX_FORMAT; cap->device_caps |= V4L2_CAP_EXT_PIX_FORMAT; return ret; @@ -1169,9 +1184,21 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) case V4L2_PIX_FMT_RGB444: descr = "16-bit A/XRGB 4-4-4-4"; break; case V4L2_PIX_FMT_ARGB444: descr = "16-bit ARGB 4-4-4-4"; break; case V4L2_PIX_FMT_XRGB444: descr = "16-bit XRGB 4-4-4-4"; break; + case V4L2_PIX_FMT_RGBA444: descr = "16-bit RGBA 4-4-4-4"; break; + case V4L2_PIX_FMT_RGBX444: descr = "16-bit RGBX 4-4-4-4"; break; + case V4L2_PIX_FMT_ABGR444: descr = "16-bit ABGR 4-4-4-4"; break; + case V4L2_PIX_FMT_XBGR444: descr = "16-bit XBGR 4-4-4-4"; break; + case V4L2_PIX_FMT_BGRA444: descr = "16-bit BGRA 4-4-4-4"; break; + case V4L2_PIX_FMT_BGRX444: descr = "16-bit BGRX 4-4-4-4"; break; case V4L2_PIX_FMT_RGB555: descr = "16-bit A/XRGB 1-5-5-5"; break; case V4L2_PIX_FMT_ARGB555: descr = "16-bit ARGB 1-5-5-5"; break; case V4L2_PIX_FMT_XRGB555: descr = "16-bit XRGB 1-5-5-5"; break; + case V4L2_PIX_FMT_ABGR555: descr = "16-bit ABGR 1-5-5-5"; break; + case V4L2_PIX_FMT_XBGR555: descr = "16-bit XBGR 1-5-5-5"; break; + case V4L2_PIX_FMT_RGBA555: descr = "16-bit RGBA 5-5-5-1"; break; + case V4L2_PIX_FMT_RGBX555: descr = "16-bit RGBX 5-5-5-1"; break; + case V4L2_PIX_FMT_BGRA555: descr = "16-bit BGRA 5-5-5-1"; break; + case V4L2_PIX_FMT_BGRX555: descr = "16-bit BGRX 5-5-5-1"; break; case V4L2_PIX_FMT_RGB565: descr = "16-bit RGB 5-6-5"; break; case V4L2_PIX_FMT_RGB555X: descr = "16-bit A/XRGB 1-5-5-5 BE"; break; case V4L2_PIX_FMT_ARGB555X: descr = "16-bit ARGB 1-5-5-5 BE"; break; @@ -1186,6 +1213,10 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) case V4L2_PIX_FMT_RGB32: descr = "32-bit A/XRGB 8-8-8-8"; break; case V4L2_PIX_FMT_ARGB32: descr = "32-bit ARGB 8-8-8-8"; break; case V4L2_PIX_FMT_XRGB32: descr = "32-bit XRGB 8-8-8-8"; break; + case V4L2_PIX_FMT_BGRA32: descr = "32-bit ABGR 8-8-8-8"; break; + case V4L2_PIX_FMT_BGRX32: descr = "32-bit XBGR 8-8-8-8"; break; + case V4L2_PIX_FMT_RGBA32: descr = "32-bit RGBA 8-8-8-8"; break; + case V4L2_PIX_FMT_RGBX32: descr = "32-bit RGBX 8-8-8-8"; break; case V4L2_PIX_FMT_GREY: descr = "8-bit Greyscale"; break; case V4L2_PIX_FMT_Y4: descr = "4-bit Greyscale"; break; case V4L2_PIX_FMT_Y6: descr = "6-bit Greyscale"; break; @@ -1301,13 +1332,15 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) case V4L2_SDR_FMT_PCU16BE: descr = "Planar Complex U16BE"; break; case V4L2_SDR_FMT_PCU18BE: descr = "Planar Complex U18BE"; break; case V4L2_SDR_FMT_PCU20BE: descr = "Planar Complex U20BE"; break; - case V4L2_TCH_FMT_DELTA_TD16: descr = "16-bit signed deltas"; break; - case V4L2_TCH_FMT_DELTA_TD08: descr = "8-bit signed deltas"; break; - case V4L2_TCH_FMT_TU16: descr = "16-bit unsigned touch data"; break; - case V4L2_TCH_FMT_TU08: descr = "8-bit unsigned touch data"; break; + case V4L2_TCH_FMT_DELTA_TD16: descr = "16-bit Signed Deltas"; break; + case V4L2_TCH_FMT_DELTA_TD08: descr = "8-bit Signed Deltas"; break; + case V4L2_TCH_FMT_TU16: descr = "16-bit Unsigned Touch Data"; break; + case V4L2_TCH_FMT_TU08: descr = "8-bit Unsigned Touch Data"; break; case V4L2_META_FMT_VSP1_HGO: descr = "R-Car VSP1 1-D Histogram"; break; case V4L2_META_FMT_VSP1_HGT: descr = "R-Car VSP1 2-D Histogram"; break; - case V4L2_META_FMT_UVC: descr = "UVC payload header metadata"; break; + case V4L2_META_FMT_UVC: descr = "UVC Payload Header Metadata"; break; + case V4L2_META_FMT_D4XX: descr = "Intel D4xx UVC Metadata"; break; + case V4L2_META_FMT_VIVID: descr = "Vivid Metadata"; break; default: /* Compressed formats */ @@ -1321,18 +1354,20 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) case V4L2_PIX_FMT_H264: descr = "H.264"; break; case V4L2_PIX_FMT_H264_NO_SC: descr = "H.264 (No Start Codes)"; break; case V4L2_PIX_FMT_H264_MVC: descr = "H.264 MVC"; break; - case V4L2_PIX_FMT_H264_SLICE_RAW: descr = "H.264 Parsed Slice Data"; break; + case V4L2_PIX_FMT_H264_SLICE: descr = "H.264 Parsed Slice Data"; break; case V4L2_PIX_FMT_H263: descr = "H.263"; break; case V4L2_PIX_FMT_MPEG1: descr = "MPEG-1 ES"; break; case V4L2_PIX_FMT_MPEG2: descr = "MPEG-2 ES"; break; case V4L2_PIX_FMT_MPEG2_SLICE: descr = "MPEG-2 Parsed Slice Data"; break; - case V4L2_PIX_FMT_MPEG4: descr = "MPEG-4 part 2 ES"; break; + case V4L2_PIX_FMT_MPEG4: descr = "MPEG-4 Part 2 ES"; break; case V4L2_PIX_FMT_XVID: descr = "Xvid"; break; case V4L2_PIX_FMT_VC1_ANNEX_G: descr = "VC-1 (SMPTE 412M Annex G)"; break; case V4L2_PIX_FMT_VC1_ANNEX_L: descr = "VC-1 (SMPTE 412M Annex L)"; break; case V4L2_PIX_FMT_VP8: descr = "VP8"; break; + case V4L2_PIX_FMT_VP8_FRAME: descr = "VP8 Frame"; break; case V4L2_PIX_FMT_VP9: descr = "VP9"; break; case V4L2_PIX_FMT_HEVC: descr = "HEVC"; break; /* aka H.265 */ + case V4L2_PIX_FMT_HEVC_SLICE: descr = "HEVC Parsed Slice Data"; break; case V4L2_PIX_FMT_FWHT: descr = "FWHT"; break; /* used in vicodec */ case V4L2_PIX_FMT_FWHT_STATELESS: descr = "FWHT Stateless"; break; /* used in vicodec */ case V4L2_PIX_FMT_CPIA1: descr = "GSPCA CPiA YUV"; break; @@ -1365,14 +1400,14 @@ static void v4l_fill_fmtdesc(struct v4l2_fmtdesc *fmt) (char)((fmt->pixelformat >> 8) & 0x7f), (char)((fmt->pixelformat >> 16) & 0x7f), (char)((fmt->pixelformat >> 24) & 0x7f), - (fmt->pixelformat & (1 << 31)) ? "-BE" : ""); + (fmt->pixelformat & (1UL << 31)) ? "-BE" : ""); break; } } if (descr) WARN_ON(strscpy(fmt->description, descr, sz) < 0); - fmt->flags = flags; + fmt->flags |= flags; } static int v4l_enum_fmt(const struct v4l2_ioctl_ops *ops, @@ -1443,10 +1478,26 @@ static int v4l_enum_fmt(const struct v4l2_ioctl_ops *ops, return ret; } +static void v4l_pix_format_touch(struct v4l2_pix_format *p) +{ + /* + * The v4l2_pix_format structure contains fields that make no sense for + * touch. Set them to default values in this case. + */ + + p->field = V4L2_FIELD_NONE; + p->colorspace = V4L2_COLORSPACE_RAW; + p->flags = 0; + p->ycbcr_enc = 0; + p->quantization = 0; + p->xfer_func = 0; +} + static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops, struct file *file, void *fh, void *arg) { struct v4l2_format *p = arg; + struct video_device *vfd = video_devdata(file); int ret = check_fmt(file, p->type); if (ret) @@ -1484,6 +1535,8 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops, ret = ops->vidioc_g_fmt_vid_cap(file, fh, arg); /* just in case the driver zeroed it again */ p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC; + if (vfd->vfl_type == VFL_TYPE_TOUCH) + v4l_pix_format_touch(&p->fmt.pix); return ret; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: return ops->vidioc_g_fmt_vid_cap_mplane(file, fh, arg); @@ -1521,21 +1574,6 @@ static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops, return -EINVAL; } -static void v4l_pix_format_touch(struct v4l2_pix_format *p) -{ - /* - * The v4l2_pix_format structure contains fields that make no sense for - * touch. Set them to default values in this case. - */ - - p->field = V4L2_FIELD_NONE; - p->colorspace = V4L2_COLORSPACE_RAW; - p->flags = 0; - p->ycbcr_enc = 0; - p->quantization = 0; - p->xfer_func = 0; -} - static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops, struct file *file, void *fh, void *arg) { @@ -1579,12 +1617,12 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops, case V4L2_BUF_TYPE_VBI_CAPTURE: if (unlikely(!ops->vidioc_s_fmt_vbi_cap)) break; - CLEAR_AFTER_FIELD(p, fmt.vbi); + CLEAR_AFTER_FIELD(p, fmt.vbi.flags); return ops->vidioc_s_fmt_vbi_cap(file, fh, arg); case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: if (unlikely(!ops->vidioc_s_fmt_sliced_vbi_cap)) break; - CLEAR_AFTER_FIELD(p, fmt.sliced); + CLEAR_AFTER_FIELD(p, fmt.sliced.io_size); return ops->vidioc_s_fmt_sliced_vbi_cap(file, fh, arg); case V4L2_BUF_TYPE_VIDEO_OUTPUT: if (unlikely(!ops->vidioc_s_fmt_vid_out)) @@ -1610,22 +1648,22 @@ static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops, case V4L2_BUF_TYPE_VBI_OUTPUT: if (unlikely(!ops->vidioc_s_fmt_vbi_out)) break; - CLEAR_AFTER_FIELD(p, fmt.vbi); + CLEAR_AFTER_FIELD(p, fmt.vbi.flags); return ops->vidioc_s_fmt_vbi_out(file, fh, arg); case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: if (unlikely(!ops->vidioc_s_fmt_sliced_vbi_out)) break; - CLEAR_AFTER_FIELD(p, fmt.sliced); + CLEAR_AFTER_FIELD(p, fmt.sliced.io_size); return ops->vidioc_s_fmt_sliced_vbi_out(file, fh, arg); case V4L2_BUF_TYPE_SDR_CAPTURE: if (unlikely(!ops->vidioc_s_fmt_sdr_cap)) break; - CLEAR_AFTER_FIELD(p, fmt.sdr); + CLEAR_AFTER_FIELD(p, fmt.sdr.buffersize); return ops->vidioc_s_fmt_sdr_cap(file, fh, arg); case V4L2_BUF_TYPE_SDR_OUTPUT: if (unlikely(!ops->vidioc_s_fmt_sdr_out)) break; - CLEAR_AFTER_FIELD(p, fmt.sdr); + CLEAR_AFTER_FIELD(p, fmt.sdr.buffersize); return ops->vidioc_s_fmt_sdr_out(file, fh, arg); case V4L2_BUF_TYPE_META_CAPTURE: if (unlikely(!ops->vidioc_s_fmt_meta_cap)) @@ -1645,6 +1683,7 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops, struct file *file, void *fh, void *arg) { struct v4l2_format *p = arg; + struct video_device *vfd = video_devdata(file); int ret = check_fmt(file, p->type); unsigned int i; @@ -1661,6 +1700,8 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops, ret = ops->vidioc_try_fmt_vid_cap(file, fh, arg); /* just in case the driver zeroed it again */ p->fmt.pix.priv = V4L2_PIX_FMT_PRIV_MAGIC; + if (vfd->vfl_type == VFL_TYPE_TOUCH) + v4l_pix_format_touch(&p->fmt.pix); return ret; case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: if (unlikely(!ops->vidioc_try_fmt_vid_cap_mplane)) @@ -1678,12 +1719,12 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops, case V4L2_BUF_TYPE_VBI_CAPTURE: if (unlikely(!ops->vidioc_try_fmt_vbi_cap)) break; - CLEAR_AFTER_FIELD(p, fmt.vbi); + CLEAR_AFTER_FIELD(p, fmt.vbi.flags); return ops->vidioc_try_fmt_vbi_cap(file, fh, arg); case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE: if (unlikely(!ops->vidioc_try_fmt_sliced_vbi_cap)) break; - CLEAR_AFTER_FIELD(p, fmt.sliced); + CLEAR_AFTER_FIELD(p, fmt.sliced.io_size); return ops->vidioc_try_fmt_sliced_vbi_cap(file, fh, arg); case V4L2_BUF_TYPE_VIDEO_OUTPUT: if (unlikely(!ops->vidioc_try_fmt_vid_out)) @@ -1709,22 +1750,22 @@ static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops, case V4L2_BUF_TYPE_VBI_OUTPUT: if (unlikely(!ops->vidioc_try_fmt_vbi_out)) break; - CLEAR_AFTER_FIELD(p, fmt.vbi); + CLEAR_AFTER_FIELD(p, fmt.vbi.flags); return ops->vidioc_try_fmt_vbi_out(file, fh, arg); case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT: if (unlikely(!ops->vidioc_try_fmt_sliced_vbi_out)) break; - CLEAR_AFTER_FIELD(p, fmt.sliced); + CLEAR_AFTER_FIELD(p, fmt.sliced.io_size); return ops->vidioc_try_fmt_sliced_vbi_out(file, fh, arg); case V4L2_BUF_TYPE_SDR_CAPTURE: if (unlikely(!ops->vidioc_try_fmt_sdr_cap)) break; - CLEAR_AFTER_FIELD(p, fmt.sdr); + CLEAR_AFTER_FIELD(p, fmt.sdr.buffersize); return ops->vidioc_try_fmt_sdr_cap(file, fh, arg); case V4L2_BUF_TYPE_SDR_OUTPUT: if (unlikely(!ops->vidioc_try_fmt_sdr_out)) break; - CLEAR_AFTER_FIELD(p, fmt.sdr); + CLEAR_AFTER_FIELD(p, fmt.sdr.buffersize); return ops->vidioc_try_fmt_sdr_out(file, fh, arg); case V4L2_BUF_TYPE_META_CAPTURE: if (unlikely(!ops->vidioc_try_fmt_meta_cap)) @@ -2165,9 +2206,11 @@ static int v4l_g_ext_ctrls(const struct v4l2_ioctl_ops *ops, p->error_idx = p->count; if (vfh && vfh->ctrl_handler) - return v4l2_g_ext_ctrls(vfh->ctrl_handler, vfd->v4l2_dev->mdev, p); + return v4l2_g_ext_ctrls(vfh->ctrl_handler, + vfd, vfd->v4l2_dev->mdev, p); if (vfd->ctrl_handler) - return v4l2_g_ext_ctrls(vfd->ctrl_handler, vfd->v4l2_dev->mdev, p); + return v4l2_g_ext_ctrls(vfd->ctrl_handler, + vfd, vfd->v4l2_dev->mdev, p); if (ops->vidioc_g_ext_ctrls == NULL) return -ENOTTY; return check_ext_ctrls(p, 0) ? ops->vidioc_g_ext_ctrls(file, fh, p) : @@ -2184,9 +2227,11 @@ static int v4l_s_ext_ctrls(const struct v4l2_ioctl_ops *ops, p->error_idx = p->count; if (vfh && vfh->ctrl_handler) - return v4l2_s_ext_ctrls(vfh, vfh->ctrl_handler, vfd->v4l2_dev->mdev, p); + return v4l2_s_ext_ctrls(vfh, vfh->ctrl_handler, + vfd, vfd->v4l2_dev->mdev, p); if (vfd->ctrl_handler) - return v4l2_s_ext_ctrls(NULL, vfd->ctrl_handler, vfd->v4l2_dev->mdev, p); + return v4l2_s_ext_ctrls(NULL, vfd->ctrl_handler, + vfd, vfd->v4l2_dev->mdev, p); if (ops->vidioc_s_ext_ctrls == NULL) return -ENOTTY; return check_ext_ctrls(p, 0) ? ops->vidioc_s_ext_ctrls(file, fh, p) : @@ -2203,9 +2248,11 @@ static int v4l_try_ext_ctrls(const struct v4l2_ioctl_ops *ops, p->error_idx = p->count; if (vfh && vfh->ctrl_handler) - return v4l2_try_ext_ctrls(vfh->ctrl_handler, vfd->v4l2_dev->mdev, p); + return v4l2_try_ext_ctrls(vfh->ctrl_handler, + vfd, vfd->v4l2_dev->mdev, p); if (vfd->ctrl_handler) - return v4l2_try_ext_ctrls(vfd->ctrl_handler, vfd->v4l2_dev->mdev, p); + return v4l2_try_ext_ctrls(vfd->ctrl_handler, + vfd, vfd->v4l2_dev->mdev, p); if (ops->vidioc_try_ext_ctrls == NULL) return -ENOTTY; return check_ext_ctrls(p, 0) ? ops->vidioc_try_ext_ctrls(file, fh, p) : @@ -2605,7 +2652,7 @@ struct v4l2_ioctl_info { /* Zero struct from after the field to the end */ #define INFO_FL_CLEAR(v4l2_struct, field) \ ((offsetof(struct v4l2_struct, field) + \ - sizeof(((struct v4l2_struct *)0)->field)) << 16) + sizeof_field(struct v4l2_struct, field)) << 16) #define INFO_FL_CLEAR_MASK (_IOC_SIZEMASK << 16) #define DEFINE_V4L_STUB_FUNC(_vidioc) \ @@ -2976,8 +3023,162 @@ static int check_array_args(unsigned int cmd, void *parg, size_t *array_size, return ret; } +static unsigned int video_translate_cmd(unsigned int cmd) +{ + switch (cmd) { +#ifdef CONFIG_COMPAT_32BIT_TIME + case VIDIOC_DQEVENT_TIME32: + return VIDIOC_DQEVENT; + case VIDIOC_QUERYBUF_TIME32: + return VIDIOC_QUERYBUF; + case VIDIOC_QBUF_TIME32: + return VIDIOC_QBUF; + case VIDIOC_DQBUF_TIME32: + return VIDIOC_DQBUF; + case VIDIOC_PREPARE_BUF_TIME32: + return VIDIOC_PREPARE_BUF; +#endif + } + + return cmd; +} + +static int video_get_user(void __user *arg, void *parg, unsigned int cmd, + bool *always_copy) +{ + unsigned int n = _IOC_SIZE(cmd); + + if (!(_IOC_DIR(cmd) & _IOC_WRITE)) { + /* read-only ioctl */ + memset(parg, 0, n); + return 0; + } + + switch (cmd) { +#ifdef CONFIG_COMPAT_32BIT_TIME + case VIDIOC_QUERYBUF_TIME32: + case VIDIOC_QBUF_TIME32: + case VIDIOC_DQBUF_TIME32: + case VIDIOC_PREPARE_BUF_TIME32: { + struct v4l2_buffer_time32 vb32; + struct v4l2_buffer *vb = parg; + + if (copy_from_user(&vb32, arg, sizeof(vb32))) + return -EFAULT; + + *vb = (struct v4l2_buffer) { + .index = vb32.index, + .type = vb32.type, + .bytesused = vb32.bytesused, + .flags = vb32.flags, + .field = vb32.field, + .timestamp.tv_sec = vb32.timestamp.tv_sec, + .timestamp.tv_usec = vb32.timestamp.tv_usec, + .timecode = vb32.timecode, + .sequence = vb32.sequence, + .memory = vb32.memory, + .m.userptr = vb32.m.userptr, + .length = vb32.length, + .request_fd = vb32.request_fd, + }; + + if (cmd == VIDIOC_QUERYBUF_TIME32) + vb->request_fd = 0; + + break; + } +#endif + default: + /* + * In some cases, only a few fields are used as input, + * i.e. when the app sets "index" and then the driver + * fills in the rest of the structure for the thing + * with that index. We only need to copy up the first + * non-input field. + */ + if (v4l2_is_known_ioctl(cmd)) { + u32 flags = v4l2_ioctls[_IOC_NR(cmd)].flags; + + if (flags & INFO_FL_CLEAR_MASK) + n = (flags & INFO_FL_CLEAR_MASK) >> 16; + *always_copy = flags & INFO_FL_ALWAYS_COPY; + } + + if (copy_from_user(parg, (void __user *)arg, n)) + return -EFAULT; + + /* zero out anything we don't copy from userspace */ + if (n < _IOC_SIZE(cmd)) + memset((u8 *)parg + n, 0, _IOC_SIZE(cmd) - n); + break; + } + + return 0; +} + +static int video_put_user(void __user *arg, void *parg, unsigned int cmd) +{ + if (!(_IOC_DIR(cmd) & _IOC_READ)) + return 0; + + switch (cmd) { +#ifdef CONFIG_COMPAT_32BIT_TIME + case VIDIOC_DQEVENT_TIME32: { + struct v4l2_event *ev = parg; + struct v4l2_event_time32 ev32 = { + .type = ev->type, + .pending = ev->pending, + .sequence = ev->sequence, + .timestamp.tv_sec = ev->timestamp.tv_sec, + .timestamp.tv_nsec = ev->timestamp.tv_nsec, + .id = ev->id, + }; + + memcpy(&ev32.u, &ev->u, sizeof(ev->u)); + memcpy(&ev32.reserved, &ev->reserved, sizeof(ev->reserved)); + + if (copy_to_user(arg, &ev32, sizeof(ev32))) + return -EFAULT; + break; + } + case VIDIOC_QUERYBUF_TIME32: + case VIDIOC_QBUF_TIME32: + case VIDIOC_DQBUF_TIME32: + case VIDIOC_PREPARE_BUF_TIME32: { + struct v4l2_buffer *vb = parg; + struct v4l2_buffer_time32 vb32 = { + .index = vb->index, + .type = vb->type, + .bytesused = vb->bytesused, + .flags = vb->flags, + .field = vb->field, + .timestamp.tv_sec = vb->timestamp.tv_sec, + .timestamp.tv_usec = vb->timestamp.tv_usec, + .timecode = vb->timecode, + .sequence = vb->sequence, + .memory = vb->memory, + .m.userptr = vb->m.userptr, + .length = vb->length, + .request_fd = vb->request_fd, + }; + + if (copy_to_user(arg, &vb32, sizeof(vb32))) + return -EFAULT; + break; + } +#endif + default: + /* Copy results into user buffer */ + if (copy_to_user(arg, parg, _IOC_SIZE(cmd))) + return -EFAULT; + break; + } + + return 0; +} + long -video_usercopy(struct file *file, unsigned int cmd, unsigned long arg, +video_usercopy(struct file *file, unsigned int orig_cmd, unsigned long arg, v4l2_kioctl func) { char sbuf[128]; @@ -2989,6 +3190,7 @@ video_usercopy(struct file *file, unsigned int cmd, unsigned long arg, size_t array_size = 0; void __user *user_ptr = NULL; void **kernel_ptr = NULL; + unsigned int cmd = video_translate_cmd(orig_cmd); const size_t ioc_size = _IOC_SIZE(cmd); /* Copy arguments into temp kernel buffer */ @@ -3003,35 +3205,10 @@ video_usercopy(struct file *file, unsigned int cmd, unsigned long arg, parg = mbuf; } - err = -EFAULT; - if (_IOC_DIR(cmd) & _IOC_WRITE) { - unsigned int n = ioc_size; - - /* - * In some cases, only a few fields are used as input, - * i.e. when the app sets "index" and then the driver - * fills in the rest of the structure for the thing - * with that index. We only need to copy up the first - * non-input field. - */ - if (v4l2_is_known_ioctl(cmd)) { - u32 flags = v4l2_ioctls[_IOC_NR(cmd)].flags; - - if (flags & INFO_FL_CLEAR_MASK) - n = (flags & INFO_FL_CLEAR_MASK) >> 16; - always_copy = flags & INFO_FL_ALWAYS_COPY; - } - - if (copy_from_user(parg, (void __user *)arg, n)) - goto out; - - /* zero out anything we don't copy from userspace */ - if (n < ioc_size) - memset((u8 *)parg + n, 0, ioc_size - n); - } else { - /* read-only ioctl */ - memset(parg, 0, ioc_size); - } + err = video_get_user((void __user *)arg, parg, orig_cmd, + &always_copy); + if (err) + goto out; } err = check_array_args(cmd, parg, &array_size, &user_ptr, &kernel_ptr); @@ -3084,15 +3261,8 @@ video_usercopy(struct file *file, unsigned int cmd, unsigned long arg, goto out; out_array_args: - /* Copy results into user buffer */ - switch (_IOC_DIR(cmd)) { - case _IOC_READ: - case (_IOC_WRITE | _IOC_READ): - if (copy_to_user((void __user *)arg, parg, ioc_size)) - err = -EFAULT; - break; - } - + if (video_put_user((void __user *)arg, parg, orig_cmd)) + err = -EFAULT; out: kvfree(mbuf); return err; |