case VIDIOC_ENUM_FMT:
        {
                struct v4l2_fmtdesc *f = arg;
-               enum v4l2_buf_type type;
-               unsigned int index;
 
-               index = f->index;
-               type  = f->type;
-               memset(f, 0, sizeof(*f));
-               f->index = index;
-               f->type  = type;
-
-               switch (type) {
+               switch (f->type) {
                case V4L2_BUF_TYPE_VIDEO_CAPTURE:
                        if (ops->vidioc_enum_fmt_vid_cap)
                                ret = ops->vidioc_enum_fmt_vid_cap(file, fh, f);
        {
                struct v4l2_format *f = (struct v4l2_format *)arg;
 
-               memset(f->fmt.raw_data, 0, sizeof(f->fmt.raw_data));
-
                /* FIXME: Should be one dump per type */
                dbgarg(cmd, "type=%s\n", prt_names(f->type, v4l2_type_names));
 
                if (ret)
                        break;
 
-               /* Zero out all fields starting with bytesysed, which is
-                * everything but index and type.  */
-               memset(0, &p->bytesused,
-                      sizeof(*p) - offsetof(typeof(*p), bytesused));
-
                ret = ops->vidioc_querybuf(file, fh, p);
                if (!ret)
                        dbgbuf(cmd, vfd, p);
        case VIDIOC_ENUMINPUT:
        {
                struct v4l2_input *p = arg;
-               int i = p->index;
 
                if (!ops->vidioc_enum_input)
                        break;
-               memset(p, 0, sizeof(*p));
-               p->index = i;
 
                ret = ops->vidioc_enum_input(file, fh, p);
                if (!ret)
        case VIDIOC_ENUMOUTPUT:
        {
                struct v4l2_output *p = arg;
-               int i = p->index;
 
                if (!ops->vidioc_enum_output)
                        break;
-               memset(p, 0, sizeof(*p));
-               p->index = i;
 
                ret = ops->vidioc_enum_output(file, fh, p);
                if (!ret)
        case VIDIOC_G_AUDIO:
        {
                struct v4l2_audio *p = arg;
-               __u32 index = p->index;
 
                if (!ops->vidioc_g_audio)
                        break;
 
                memset(p, 0, sizeof(*p));
-               p->index = index;
                ret = ops->vidioc_g_audio(file, fh, p);
                if (!ret)
                        dbgarg(cmd, "index=%d, name=%s, capability=0x%x, "
        case VIDIOC_G_CROP:
        {
                struct v4l2_crop *p = arg;
-               __u32 type;
 
                if (!ops->vidioc_g_crop)
                        break;
 
-               type = p->type;
-               memset(p, 0, sizeof(*p));
-               p->type = type;
-
                dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names));
                ret = ops->vidioc_g_crop(file, fh, p);
                if (!ret)
        case VIDIOC_CROPCAP:
        {
                struct v4l2_cropcap *p = arg;
-               __u32 type;
 
                /*FIXME: Should also show v4l2_fract pixelaspect */
                if (!ops->vidioc_cropcap)
                        break;
 
-               type = p->type;
-               memset(p, 0, sizeof(*p));
-               p->type = type;
-
                dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names));
                ret = ops->vidioc_cropcap(file, fh, p);
                if (!ret) {
 
                if (!ops->vidioc_encoder_cmd)
                        break;
-               memset(&p->raw, 0, sizeof(p->raw));
                ret = ops->vidioc_encoder_cmd(file, fh, p);
                if (!ret)
                        dbgarg(cmd, "cmd=%d, flags=%x\n", p->cmd, p->flags);
 
                if (!ops->vidioc_try_encoder_cmd)
                        break;
-               memset(&p->raw, 0, sizeof(p->raw));
                ret = ops->vidioc_try_encoder_cmd(file, fh, p);
                if (!ret)
                        dbgarg(cmd, "cmd=%d, flags=%x\n", p->cmd, p->flags);
        case VIDIOC_G_PARM:
        {
                struct v4l2_streamparm *p = arg;
-               __u32 type = p->type;
-
-               memset(p, 0, sizeof(*p));
-               p->type = type;
 
                if (ops->vidioc_g_parm) {
                        ret = ops->vidioc_g_parm(file, fh, p);
        case VIDIOC_G_TUNER:
        {
                struct v4l2_tuner *p = arg;
-               __u32 index = p->index;
 
                if (!ops->vidioc_g_tuner)
                        break;
 
-               memset(p, 0, sizeof(*p));
-               p->index = index;
-
                ret = ops->vidioc_g_tuner(file, fh, p);
                if (!ret)
                        dbgarg(cmd, "index=%d, name=%s, type=%d, "
                if (!ops->vidioc_g_frequency)
                        break;
 
-               memset(p->reserved, 0, sizeof(p->reserved));
-
                ret = ops->vidioc_g_frequency(file, fh, p);
                if (!ret)
                        dbgarg(cmd, "tuner=%d, type=%d, frequency=%d\n",
        case VIDIOC_G_SLICED_VBI_CAP:
        {
                struct v4l2_sliced_vbi_cap *p = arg;
-               __u32 type = p->type;
 
                if (!ops->vidioc_g_sliced_vbi_cap)
                        break;
-               memset(p, 0, sizeof(*p));
-               p->type = type;
+
+               /* Clear up to type, everything after type is zerod already */
+               memset(p, 0, offsetof(struct v4l2_sliced_vbi_cap, type));
+
                dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names));
                ret = ops->vidioc_g_sliced_vbi_cap(file, fh, p);
                if (!ret)
                if (!ops->vidioc_enum_framesizes)
                        break;
 
-               memset(p, 0, sizeof(*p));
-
                ret = ops->vidioc_enum_framesizes(file, fh, p);
                dbgarg(cmd,
                        "index=%d, pixelformat=%d, type=%d ",
                if (!ops->vidioc_enum_frameintervals)
                        break;
 
-               memset(p, 0, sizeof(*p));
-
                ret = ops->vidioc_enum_frameintervals(file, fh, p);
                dbgarg(cmd,
                        "index=%d, pixelformat=%d, width=%d, height=%d, type=%d ",
        return ret;
 }
 
+/* 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.  */
+static unsigned long cmd_input_size(unsigned int cmd)
+{
+       /* Size of structure up to and including 'field' */
+#define CMDINSIZE(cmd, type, field) case _IOC_NR(VIDIOC_##cmd): return \
+               offsetof(struct v4l2_##type, field) + \
+               sizeof(((struct v4l2_##type *)0)->field);
+
+       switch (_IOC_NR(cmd)) {
+               CMDINSIZE(ENUM_FMT,             fmtdesc,        type);
+               CMDINSIZE(G_FMT,                format,         type);
+               CMDINSIZE(QUERYBUF,             buffer,         type);
+               CMDINSIZE(G_PARM,               streamparm,     type);
+               CMDINSIZE(ENUMSTD,              standard,       index);
+               CMDINSIZE(ENUMINPUT,            input,          index);
+               CMDINSIZE(G_CTRL,               control,        id);
+               CMDINSIZE(G_TUNER,              tuner,          index);
+               CMDINSIZE(QUERYCTRL,            queryctrl,      id);
+               CMDINSIZE(QUERYMENU,            querymenu,      index);
+               CMDINSIZE(ENUMOUTPUT,           output,         index);
+               CMDINSIZE(G_MODULATOR,          modulator,      index);
+               CMDINSIZE(G_FREQUENCY,          frequency,      tuner);
+               CMDINSIZE(CROPCAP,              cropcap,        type);
+               CMDINSIZE(G_CROP,               crop,           type);
+               CMDINSIZE(ENUMAUDIO,            audio,          index);
+               CMDINSIZE(ENUMAUDOUT,           audioout,       index);
+               CMDINSIZE(ENCODER_CMD,          encoder_cmd,    flags);
+               CMDINSIZE(TRY_ENCODER_CMD,      encoder_cmd,    flags);
+               CMDINSIZE(G_SLICED_VBI_CAP,     sliced_vbi_cap, type);
+               CMDINSIZE(ENUM_FRAMESIZES,      frmsizeenum,    pixel_format);
+               CMDINSIZE(ENUM_FRAMEINTERVALS,  frmivalenum,    height);
+       default:
+               return _IOC_SIZE(cmd);
+       }
+}
+
 long video_ioctl2(struct file *file,
               unsigned int cmd, unsigned long arg)
 {
                }
 
                err = -EFAULT;
-               if (_IOC_DIR(cmd) & _IOC_WRITE)
-                       if (copy_from_user(parg, (void __user *)arg, _IOC_SIZE(cmd)))
+               if (_IOC_DIR(cmd) & _IOC_WRITE) {
+                       unsigned long n = cmd_input_size(cmd);
+
+                       if (copy_from_user(parg, (void __user *)arg, n))
                                goto out;
+
+                       /* zero out anything we don't copy from userspace */
+                       if (n < _IOC_SIZE(cmd))
+                               memset((u8 *)parg + n, 0, _IOC_SIZE(cmd) - n);
+               }
                break;
        }