Merge tag 'v3.6-rc3' into staging/for_v3.7
authorMauro Carvalho Chehab <mchehab@redhat.com>
Fri, 24 Aug 2012 14:25:10 +0000 (11:25 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Fri, 24 Aug 2012 14:25:10 +0000 (11:25 -0300)
Linux 3.6-rc3

* tag 'v3.6-rc3': (764 commits)
  Linux 3.6-rc3
  task_work: add a scheduling point in task_work_run()
  fs: fix fs/namei.c kernel-doc warnings
  eventpoll: use-after-possible-free in epoll_create1()
  vfio: grab vfio_device reference *before* exposing the sucker via fd_install()
  vfio: get rid of vfio_device_put()/vfio_group_get_device* races
  vfio: get rid of open-coding kref_put_mutex
  introduce kref_put_mutex()
  vfio: don't dereference after kfree...
  fbcon: fix race condition between console lock and cursor timer (v1.1)
  mm: compaction: Abort async compaction if locks are contended or taking too long
  mm: have order > 0 compaction start near a pageblock with free pages
  rapidio/tsi721: fix unused variable compiler warning
  rapidio/tsi721: fix inbound doorbell interrupt handling
  drivers/rtc/rtc-rs5c348.c: fix hour decoding in 12-hour mode
  mm: correct page->pfmemalloc to fix deactivate_slab regression
  drivers/rtc/rtc-pcf2123.c: initialize dynamic sysfs attributes
  mm/compaction.c: fix deferring compaction mistake
  drivers/misc/sgi-xp/xpc_uv.c: SGI XPC fails to load when cpu 0 is out of IRQ resources
  string: do not export memweight() to userspace
  ...

1  2 
MAINTAINERS
arch/arm/mach-imx/clk-imx27.c
drivers/media/platform/mem2mem_testdev.c
drivers/media/radio/radio-shark2.c
drivers/media/rc/Kconfig
drivers/media/usb/gspca/jl2005bcd.c
drivers/media/usb/gspca/spca506.c
drivers/media/usb/siano/smsusb.c
drivers/media/usb/uvc/uvc_queue.c
drivers/media/v4l2-core/v4l2-ioctl.c

diff --cc MAINTAINERS
Simple merge
Simple merge
index 51b6dd4ddbf7df369d66275bb20e68d969a84125,0000000000000000000000000000000000000000..0b496f39a65f66cf467f0fa961620af018224b0c
mode 100644,000000..100644
--- /dev/null
@@@ -1,1130 -1,0 +1,1130 @@@
-       cap->capabilities = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING;
 +/*
 + * A virtual v4l2-mem2mem example device.
 + *
 + * This is a virtual device driver for testing mem-to-mem videobuf framework.
 + * It simulates a device that uses memory buffers for both source and
 + * destination, processes the data and issues an "irq" (simulated by a timer).
 + * The device is capable of multi-instance, multi-buffer-per-transaction
 + * operation (via the mem2mem framework).
 + *
 + * Copyright (c) 2009-2010 Samsung Electronics Co., Ltd.
 + * Pawel Osciak, <pawel@osciak.com>
 + * Marek Szyprowski, <m.szyprowski@samsung.com>
 + *
 + * This program is free software; you can redistribute it and/or modify
 + * it under the terms of the GNU General Public License as published by the
 + * Free Software Foundation; either version 2 of the
 + * License, or (at your option) any later version
 + */
 +#include <linux/module.h>
 +#include <linux/delay.h>
 +#include <linux/fs.h>
 +#include <linux/timer.h>
 +#include <linux/sched.h>
 +#include <linux/slab.h>
 +
 +#include <linux/platform_device.h>
 +#include <media/v4l2-mem2mem.h>
 +#include <media/v4l2-device.h>
 +#include <media/v4l2-ioctl.h>
 +#include <media/v4l2-ctrls.h>
 +#include <media/v4l2-event.h>
 +#include <media/videobuf2-vmalloc.h>
 +
 +#define MEM2MEM_TEST_MODULE_NAME "mem2mem-testdev"
 +
 +MODULE_DESCRIPTION("Virtual device for mem2mem framework testing");
 +MODULE_AUTHOR("Pawel Osciak, <pawel@osciak.com>");
 +MODULE_LICENSE("GPL");
 +MODULE_VERSION("0.1.1");
 +
 +#define MIN_W 32
 +#define MIN_H 32
 +#define MAX_W 640
 +#define MAX_H 480
 +#define DIM_ALIGN_MASK 7 /* 8-byte alignment for line length */
 +
 +/* Flags that indicate a format can be used for capture/output */
 +#define MEM2MEM_CAPTURE       (1 << 0)
 +#define MEM2MEM_OUTPUT        (1 << 1)
 +
 +#define MEM2MEM_NAME          "m2m-testdev"
 +
 +/* Per queue */
 +#define MEM2MEM_DEF_NUM_BUFS  VIDEO_MAX_FRAME
 +/* In bytes, per queue */
 +#define MEM2MEM_VID_MEM_LIMIT (16 * 1024 * 1024)
 +
 +/* Default transaction time in msec */
 +#define MEM2MEM_DEF_TRANSTIME 1000
 +/* Default number of buffers per transaction */
 +#define MEM2MEM_DEF_TRANSLEN  1
 +#define MEM2MEM_COLOR_STEP    (0xff >> 4)
 +#define MEM2MEM_NUM_TILES     8
 +
 +/* Flags that indicate processing mode */
 +#define MEM2MEM_HFLIP (1 << 0)
 +#define MEM2MEM_VFLIP (1 << 1)
 +
 +#define dprintk(dev, fmt, arg...) \
 +      v4l2_dbg(1, 1, &dev->v4l2_dev, "%s: " fmt, __func__, ## arg)
 +
 +
 +void m2mtest_dev_release(struct device *dev)
 +{}
 +
 +static struct platform_device m2mtest_pdev = {
 +      .name           = MEM2MEM_NAME,
 +      .dev.release    = m2mtest_dev_release,
 +};
 +
 +struct m2mtest_fmt {
 +      char    *name;
 +      u32     fourcc;
 +      int     depth;
 +      /* Types the format can be used for */
 +      u32     types;
 +};
 +
 +static struct m2mtest_fmt formats[] = {
 +      {
 +              .name   = "RGB565 (BE)",
 +              .fourcc = V4L2_PIX_FMT_RGB565X, /* rrrrrggg gggbbbbb */
 +              .depth  = 16,
 +              /* Both capture and output format */
 +              .types  = MEM2MEM_CAPTURE | MEM2MEM_OUTPUT,
 +      },
 +      {
 +              .name   = "4:2:2, packed, YUYV",
 +              .fourcc = V4L2_PIX_FMT_YUYV,
 +              .depth  = 16,
 +              /* Output-only format */
 +              .types  = MEM2MEM_OUTPUT,
 +      },
 +};
 +
 +#define NUM_FORMATS ARRAY_SIZE(formats)
 +
 +/* Per-queue, driver-specific private data */
 +struct m2mtest_q_data {
 +      unsigned int            width;
 +      unsigned int            height;
 +      unsigned int            sizeimage;
 +      struct m2mtest_fmt      *fmt;
 +};
 +
 +enum {
 +      V4L2_M2M_SRC = 0,
 +      V4L2_M2M_DST = 1,
 +};
 +
 +#define V4L2_CID_TRANS_TIME_MSEC      (V4L2_CID_USER_BASE + 0x1000)
 +#define V4L2_CID_TRANS_NUM_BUFS               (V4L2_CID_USER_BASE + 0x1001)
 +
 +static struct m2mtest_fmt *find_format(struct v4l2_format *f)
 +{
 +      struct m2mtest_fmt *fmt;
 +      unsigned int k;
 +
 +      for (k = 0; k < NUM_FORMATS; k++) {
 +              fmt = &formats[k];
 +              if (fmt->fourcc == f->fmt.pix.pixelformat)
 +                      break;
 +      }
 +
 +      if (k == NUM_FORMATS)
 +              return NULL;
 +
 +      return &formats[k];
 +}
 +
 +struct m2mtest_dev {
 +      struct v4l2_device      v4l2_dev;
 +      struct video_device     *vfd;
 +
 +      atomic_t                num_inst;
 +      struct mutex            dev_mutex;
 +      spinlock_t              irqlock;
 +
 +      struct timer_list       timer;
 +
 +      struct v4l2_m2m_dev     *m2m_dev;
 +};
 +
 +struct m2mtest_ctx {
 +      struct v4l2_fh          fh;
 +      struct m2mtest_dev      *dev;
 +
 +      struct v4l2_ctrl_handler hdl;
 +
 +      /* Processed buffers in this transaction */
 +      u8                      num_processed;
 +
 +      /* Transaction length (i.e. how many buffers per transaction) */
 +      u32                     translen;
 +      /* Transaction time (i.e. simulated processing time) in milliseconds */
 +      u32                     transtime;
 +
 +      /* Abort requested by m2m */
 +      int                     aborting;
 +
 +      /* Processing mode */
 +      int                     mode;
 +
 +      enum v4l2_colorspace    colorspace;
 +
 +      struct v4l2_m2m_ctx     *m2m_ctx;
 +
 +      /* Source and destination queue data */
 +      struct m2mtest_q_data   q_data[2];
 +};
 +
 +static inline struct m2mtest_ctx *file2ctx(struct file *file)
 +{
 +      return container_of(file->private_data, struct m2mtest_ctx, fh);
 +}
 +
 +static struct m2mtest_q_data *get_q_data(struct m2mtest_ctx *ctx,
 +                                       enum v4l2_buf_type type)
 +{
 +      switch (type) {
 +      case V4L2_BUF_TYPE_VIDEO_OUTPUT:
 +              return &ctx->q_data[V4L2_M2M_SRC];
 +      case V4L2_BUF_TYPE_VIDEO_CAPTURE:
 +              return &ctx->q_data[V4L2_M2M_DST];
 +      default:
 +              BUG();
 +      }
 +      return NULL;
 +}
 +
 +
 +static int device_process(struct m2mtest_ctx *ctx,
 +                        struct vb2_buffer *in_vb,
 +                        struct vb2_buffer *out_vb)
 +{
 +      struct m2mtest_dev *dev = ctx->dev;
 +      struct m2mtest_q_data *q_data;
 +      u8 *p_in, *p_out;
 +      int x, y, t, w;
 +      int tile_w, bytes_left;
 +      int width, height, bytesperline;
 +
 +      q_data = get_q_data(ctx, V4L2_BUF_TYPE_VIDEO_OUTPUT);
 +
 +      width   = q_data->width;
 +      height  = q_data->height;
 +      bytesperline    = (q_data->width * q_data->fmt->depth) >> 3;
 +
 +      p_in = vb2_plane_vaddr(in_vb, 0);
 +      p_out = vb2_plane_vaddr(out_vb, 0);
 +      if (!p_in || !p_out) {
 +              v4l2_err(&dev->v4l2_dev,
 +                       "Acquiring kernel pointers to buffers failed\n");
 +              return -EFAULT;
 +      }
 +
 +      if (vb2_plane_size(in_vb, 0) > vb2_plane_size(out_vb, 0)) {
 +              v4l2_err(&dev->v4l2_dev, "Output buffer is too small\n");
 +              return -EINVAL;
 +      }
 +
 +      tile_w = (width * (q_data[V4L2_M2M_DST].fmt->depth >> 3))
 +              / MEM2MEM_NUM_TILES;
 +      bytes_left = bytesperline - tile_w * MEM2MEM_NUM_TILES;
 +      w = 0;
 +
 +      switch (ctx->mode) {
 +      case MEM2MEM_HFLIP | MEM2MEM_VFLIP:
 +              p_out += bytesperline * height - bytes_left;
 +              for (y = 0; y < height; ++y) {
 +                      for (t = 0; t < MEM2MEM_NUM_TILES; ++t) {
 +                              if (w & 0x1) {
 +                                      for (x = 0; x < tile_w; ++x)
 +                                              *--p_out = *p_in++ +
 +                                                      MEM2MEM_COLOR_STEP;
 +                              } else {
 +                                      for (x = 0; x < tile_w; ++x)
 +                                              *--p_out = *p_in++ -
 +                                                      MEM2MEM_COLOR_STEP;
 +                              }
 +                              ++w;
 +                      }
 +                      p_in += bytes_left;
 +                      p_out -= bytes_left;
 +              }
 +              break;
 +
 +      case MEM2MEM_HFLIP:
 +              for (y = 0; y < height; ++y) {
 +                      p_out += MEM2MEM_NUM_TILES * tile_w;
 +                      for (t = 0; t < MEM2MEM_NUM_TILES; ++t) {
 +                              if (w & 0x01) {
 +                                      for (x = 0; x < tile_w; ++x)
 +                                              *--p_out = *p_in++ +
 +                                                      MEM2MEM_COLOR_STEP;
 +                              } else {
 +                                      for (x = 0; x < tile_w; ++x)
 +                                              *--p_out = *p_in++ -
 +                                                      MEM2MEM_COLOR_STEP;
 +                              }
 +                              ++w;
 +                      }
 +                      p_in += bytes_left;
 +                      p_out += bytesperline;
 +              }
 +              break;
 +
 +      case MEM2MEM_VFLIP:
 +              p_out += bytesperline * (height - 1);
 +              for (y = 0; y < height; ++y) {
 +                      for (t = 0; t < MEM2MEM_NUM_TILES; ++t) {
 +                              if (w & 0x1) {
 +                                      for (x = 0; x < tile_w; ++x)
 +                                              *p_out++ = *p_in++ +
 +                                                      MEM2MEM_COLOR_STEP;
 +                              } else {
 +                                      for (x = 0; x < tile_w; ++x)
 +                                              *p_out++ = *p_in++ -
 +                                                      MEM2MEM_COLOR_STEP;
 +                              }
 +                              ++w;
 +                      }
 +                      p_in += bytes_left;
 +                      p_out += bytes_left - 2 * bytesperline;
 +              }
 +              break;
 +
 +      default:
 +              for (y = 0; y < height; ++y) {
 +                      for (t = 0; t < MEM2MEM_NUM_TILES; ++t) {
 +                              if (w & 0x1) {
 +                                      for (x = 0; x < tile_w; ++x)
 +                                              *p_out++ = *p_in++ +
 +                                                      MEM2MEM_COLOR_STEP;
 +                              } else {
 +                                      for (x = 0; x < tile_w; ++x)
 +                                              *p_out++ = *p_in++ -
 +                                                      MEM2MEM_COLOR_STEP;
 +                              }
 +                              ++w;
 +                      }
 +                      p_in += bytes_left;
 +                      p_out += bytes_left;
 +              }
 +      }
 +
 +      return 0;
 +}
 +
 +static void schedule_irq(struct m2mtest_dev *dev, int msec_timeout)
 +{
 +      dprintk(dev, "Scheduling a simulated irq\n");
 +      mod_timer(&dev->timer, jiffies + msecs_to_jiffies(msec_timeout));
 +}
 +
 +/*
 + * mem2mem callbacks
 + */
 +
 +/**
 + * job_ready() - check whether an instance is ready to be scheduled to run
 + */
 +static int job_ready(void *priv)
 +{
 +      struct m2mtest_ctx *ctx = priv;
 +
 +      if (v4l2_m2m_num_src_bufs_ready(ctx->m2m_ctx) < ctx->translen
 +          || v4l2_m2m_num_dst_bufs_ready(ctx->m2m_ctx) < ctx->translen) {
 +              dprintk(ctx->dev, "Not enough buffers available\n");
 +              return 0;
 +      }
 +
 +      return 1;
 +}
 +
 +static void job_abort(void *priv)
 +{
 +      struct m2mtest_ctx *ctx = priv;
 +
 +      /* Will cancel the transaction in the next interrupt handler */
 +      ctx->aborting = 1;
 +}
 +
 +static void m2mtest_lock(void *priv)
 +{
 +      struct m2mtest_ctx *ctx = priv;
 +      struct m2mtest_dev *dev = ctx->dev;
 +      mutex_lock(&dev->dev_mutex);
 +}
 +
 +static void m2mtest_unlock(void *priv)
 +{
 +      struct m2mtest_ctx *ctx = priv;
 +      struct m2mtest_dev *dev = ctx->dev;
 +      mutex_unlock(&dev->dev_mutex);
 +}
 +
 +
 +/* device_run() - prepares and starts the device
 + *
 + * This simulates all the immediate preparations required before starting
 + * a device. This will be called by the framework when it decides to schedule
 + * a particular instance.
 + */
 +static void device_run(void *priv)
 +{
 +      struct m2mtest_ctx *ctx = priv;
 +      struct m2mtest_dev *dev = ctx->dev;
 +      struct vb2_buffer *src_buf, *dst_buf;
 +
 +      src_buf = v4l2_m2m_next_src_buf(ctx->m2m_ctx);
 +      dst_buf = v4l2_m2m_next_dst_buf(ctx->m2m_ctx);
 +
 +      device_process(ctx, src_buf, dst_buf);
 +
 +      /* Run a timer, which simulates a hardware irq  */
 +      schedule_irq(dev, ctx->transtime);
 +}
 +
 +static void device_isr(unsigned long priv)
 +{
 +      struct m2mtest_dev *m2mtest_dev = (struct m2mtest_dev *)priv;
 +      struct m2mtest_ctx *curr_ctx;
 +      struct vb2_buffer *src_vb, *dst_vb;
 +      unsigned long flags;
 +
 +      curr_ctx = v4l2_m2m_get_curr_priv(m2mtest_dev->m2m_dev);
 +
 +      if (NULL == curr_ctx) {
 +              printk(KERN_ERR
 +                      "Instance released before the end of transaction\n");
 +              return;
 +      }
 +
 +      src_vb = v4l2_m2m_src_buf_remove(curr_ctx->m2m_ctx);
 +      dst_vb = v4l2_m2m_dst_buf_remove(curr_ctx->m2m_ctx);
 +
 +      curr_ctx->num_processed++;
 +
 +      spin_lock_irqsave(&m2mtest_dev->irqlock, flags);
 +      v4l2_m2m_buf_done(src_vb, VB2_BUF_STATE_DONE);
 +      v4l2_m2m_buf_done(dst_vb, VB2_BUF_STATE_DONE);
 +      spin_unlock_irqrestore(&m2mtest_dev->irqlock, flags);
 +
 +      if (curr_ctx->num_processed == curr_ctx->translen
 +          || curr_ctx->aborting) {
 +              dprintk(curr_ctx->dev, "Finishing transaction\n");
 +              curr_ctx->num_processed = 0;
 +              v4l2_m2m_job_finish(m2mtest_dev->m2m_dev, curr_ctx->m2m_ctx);
 +      } else {
 +              device_run(curr_ctx);
 +      }
 +}
 +
 +/*
 + * video ioctls
 + */
 +static int vidioc_querycap(struct file *file, void *priv,
 +                         struct v4l2_capability *cap)
 +{
 +      strncpy(cap->driver, MEM2MEM_NAME, sizeof(cap->driver) - 1);
 +      strncpy(cap->card, MEM2MEM_NAME, sizeof(cap->card) - 1);
 +      strlcpy(cap->bus_info, MEM2MEM_NAME, sizeof(cap->bus_info));
++      cap->device_caps = V4L2_CAP_VIDEO_M2M | V4L2_CAP_STREAMING;
 +      cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
 +      return 0;
 +}
 +
 +static int enum_fmt(struct v4l2_fmtdesc *f, u32 type)
 +{
 +      int i, num;
 +      struct m2mtest_fmt *fmt;
 +
 +      num = 0;
 +
 +      for (i = 0; i < NUM_FORMATS; ++i) {
 +              if (formats[i].types & type) {
 +                      /* index-th format of type type found ? */
 +                      if (num == f->index)
 +                              break;
 +                      /* Correct type but haven't reached our index yet,
 +                       * just increment per-type index */
 +                      ++num;
 +              }
 +      }
 +
 +      if (i < NUM_FORMATS) {
 +              /* Format found */
 +              fmt = &formats[i];
 +              strncpy(f->description, fmt->name, sizeof(f->description) - 1);
 +              f->pixelformat = fmt->fourcc;
 +              return 0;
 +      }
 +
 +      /* Format not found */
 +      return -EINVAL;
 +}
 +
 +static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
 +                                 struct v4l2_fmtdesc *f)
 +{
 +      return enum_fmt(f, MEM2MEM_CAPTURE);
 +}
 +
 +static int vidioc_enum_fmt_vid_out(struct file *file, void *priv,
 +                                 struct v4l2_fmtdesc *f)
 +{
 +      return enum_fmt(f, MEM2MEM_OUTPUT);
 +}
 +
 +static int vidioc_g_fmt(struct m2mtest_ctx *ctx, struct v4l2_format *f)
 +{
 +      struct vb2_queue *vq;
 +      struct m2mtest_q_data *q_data;
 +
 +      vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
 +      if (!vq)
 +              return -EINVAL;
 +
 +      q_data = get_q_data(ctx, f->type);
 +
 +      f->fmt.pix.width        = q_data->width;
 +      f->fmt.pix.height       = q_data->height;
 +      f->fmt.pix.field        = V4L2_FIELD_NONE;
 +      f->fmt.pix.pixelformat  = q_data->fmt->fourcc;
 +      f->fmt.pix.bytesperline = (q_data->width * q_data->fmt->depth) >> 3;
 +      f->fmt.pix.sizeimage    = q_data->sizeimage;
 +      f->fmt.pix.colorspace   = ctx->colorspace;
 +
 +      return 0;
 +}
 +
 +static int vidioc_g_fmt_vid_out(struct file *file, void *priv,
 +                              struct v4l2_format *f)
 +{
 +      return vidioc_g_fmt(file2ctx(file), f);
 +}
 +
 +static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
 +                              struct v4l2_format *f)
 +{
 +      return vidioc_g_fmt(file2ctx(file), f);
 +}
 +
 +static int vidioc_try_fmt(struct v4l2_format *f, struct m2mtest_fmt *fmt)
 +{
 +      enum v4l2_field field;
 +
 +      field = f->fmt.pix.field;
 +
 +      if (field == V4L2_FIELD_ANY)
 +              field = V4L2_FIELD_NONE;
 +      else if (V4L2_FIELD_NONE != field)
 +              return -EINVAL;
 +
 +      /* V4L2 specification suggests the driver corrects the format struct
 +       * if any of the dimensions is unsupported */
 +      f->fmt.pix.field = field;
 +
 +      if (f->fmt.pix.height < MIN_H)
 +              f->fmt.pix.height = MIN_H;
 +      else if (f->fmt.pix.height > MAX_H)
 +              f->fmt.pix.height = MAX_H;
 +
 +      if (f->fmt.pix.width < MIN_W)
 +              f->fmt.pix.width = MIN_W;
 +      else if (f->fmt.pix.width > MAX_W)
 +              f->fmt.pix.width = MAX_W;
 +
 +      f->fmt.pix.width &= ~DIM_ALIGN_MASK;
 +      f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3;
 +      f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
 +
 +      return 0;
 +}
 +
 +static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
 +                                struct v4l2_format *f)
 +{
 +      struct m2mtest_fmt *fmt;
 +      struct m2mtest_ctx *ctx = file2ctx(file);
 +
 +      fmt = find_format(f);
 +      if (!fmt || !(fmt->types & MEM2MEM_CAPTURE)) {
 +              v4l2_err(&ctx->dev->v4l2_dev,
 +                       "Fourcc format (0x%08x) invalid.\n",
 +                       f->fmt.pix.pixelformat);
 +              return -EINVAL;
 +      }
 +      f->fmt.pix.colorspace = ctx->colorspace;
 +
 +      return vidioc_try_fmt(f, fmt);
 +}
 +
 +static int vidioc_try_fmt_vid_out(struct file *file, void *priv,
 +                                struct v4l2_format *f)
 +{
 +      struct m2mtest_fmt *fmt;
 +      struct m2mtest_ctx *ctx = file2ctx(file);
 +
 +      fmt = find_format(f);
 +      if (!fmt || !(fmt->types & MEM2MEM_OUTPUT)) {
 +              v4l2_err(&ctx->dev->v4l2_dev,
 +                       "Fourcc format (0x%08x) invalid.\n",
 +                       f->fmt.pix.pixelformat);
 +              return -EINVAL;
 +      }
 +      if (!f->fmt.pix.colorspace)
 +              f->fmt.pix.colorspace = V4L2_COLORSPACE_REC709;
 +
 +      return vidioc_try_fmt(f, fmt);
 +}
 +
 +static int vidioc_s_fmt(struct m2mtest_ctx *ctx, struct v4l2_format *f)
 +{
 +      struct m2mtest_q_data *q_data;
 +      struct vb2_queue *vq;
 +
 +      vq = v4l2_m2m_get_vq(ctx->m2m_ctx, f->type);
 +      if (!vq)
 +              return -EINVAL;
 +
 +      q_data = get_q_data(ctx, f->type);
 +      if (!q_data)
 +              return -EINVAL;
 +
 +      if (vb2_is_busy(vq)) {
 +              v4l2_err(&ctx->dev->v4l2_dev, "%s queue busy\n", __func__);
 +              return -EBUSY;
 +      }
 +
 +      q_data->fmt             = find_format(f);
 +      q_data->width           = f->fmt.pix.width;
 +      q_data->height          = f->fmt.pix.height;
 +      q_data->sizeimage       = q_data->width * q_data->height
 +                              * q_data->fmt->depth >> 3;
 +
 +      dprintk(ctx->dev,
 +              "Setting format for type %d, wxh: %dx%d, fmt: %d\n",
 +              f->type, q_data->width, q_data->height, q_data->fmt->fourcc);
 +
 +      return 0;
 +}
 +
 +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
 +                              struct v4l2_format *f)
 +{
 +      int ret;
 +
 +      ret = vidioc_try_fmt_vid_cap(file, priv, f);
 +      if (ret)
 +              return ret;
 +
 +      return vidioc_s_fmt(file2ctx(file), f);
 +}
 +
 +static int vidioc_s_fmt_vid_out(struct file *file, void *priv,
 +                              struct v4l2_format *f)
 +{
 +      struct m2mtest_ctx *ctx = file2ctx(file);
 +      int ret;
 +
 +      ret = vidioc_try_fmt_vid_out(file, priv, f);
 +      if (ret)
 +              return ret;
 +
 +      ret = vidioc_s_fmt(file2ctx(file), f);
 +      if (!ret)
 +              ctx->colorspace = f->fmt.pix.colorspace;
 +      return ret;
 +}
 +
 +static int vidioc_reqbufs(struct file *file, void *priv,
 +                        struct v4l2_requestbuffers *reqbufs)
 +{
 +      struct m2mtest_ctx *ctx = file2ctx(file);
 +
 +      return v4l2_m2m_reqbufs(file, ctx->m2m_ctx, reqbufs);
 +}
 +
 +static int vidioc_querybuf(struct file *file, void *priv,
 +                         struct v4l2_buffer *buf)
 +{
 +      struct m2mtest_ctx *ctx = file2ctx(file);
 +
 +      return v4l2_m2m_querybuf(file, ctx->m2m_ctx, buf);
 +}
 +
 +static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
 +{
 +      struct m2mtest_ctx *ctx = file2ctx(file);
 +
 +      return v4l2_m2m_qbuf(file, ctx->m2m_ctx, buf);
 +}
 +
 +static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *buf)
 +{
 +      struct m2mtest_ctx *ctx = file2ctx(file);
 +
 +      return v4l2_m2m_dqbuf(file, ctx->m2m_ctx, buf);
 +}
 +
 +static int vidioc_streamon(struct file *file, void *priv,
 +                         enum v4l2_buf_type type)
 +{
 +      struct m2mtest_ctx *ctx = file2ctx(file);
 +
 +      return v4l2_m2m_streamon(file, ctx->m2m_ctx, type);
 +}
 +
 +static int vidioc_streamoff(struct file *file, void *priv,
 +                          enum v4l2_buf_type type)
 +{
 +      struct m2mtest_ctx *ctx = file2ctx(file);
 +
 +      return v4l2_m2m_streamoff(file, ctx->m2m_ctx, type);
 +}
 +
 +static int m2mtest_s_ctrl(struct v4l2_ctrl *ctrl)
 +{
 +      struct m2mtest_ctx *ctx =
 +              container_of(ctrl->handler, struct m2mtest_ctx, hdl);
 +
 +      switch (ctrl->id) {
 +      case V4L2_CID_HFLIP:
 +              if (ctrl->val)
 +                      ctx->mode |= MEM2MEM_HFLIP;
 +              else
 +                      ctx->mode &= ~MEM2MEM_HFLIP;
 +              break;
 +
 +      case V4L2_CID_VFLIP:
 +              if (ctrl->val)
 +                      ctx->mode |= MEM2MEM_VFLIP;
 +              else
 +                      ctx->mode &= ~MEM2MEM_VFLIP;
 +              break;
 +
 +      case V4L2_CID_TRANS_TIME_MSEC:
 +              ctx->transtime = ctrl->val;
 +              break;
 +
 +      case V4L2_CID_TRANS_NUM_BUFS:
 +              ctx->translen = ctrl->val;
 +              break;
 +
 +      default:
 +              v4l2_err(&ctx->dev->v4l2_dev, "Invalid control\n");
 +              return -EINVAL;
 +      }
 +
 +      return 0;
 +}
 +
 +static const struct v4l2_ctrl_ops m2mtest_ctrl_ops = {
 +      .s_ctrl = m2mtest_s_ctrl,
 +};
 +
 +
 +static const struct v4l2_ioctl_ops m2mtest_ioctl_ops = {
 +      .vidioc_querycap        = vidioc_querycap,
 +
 +      .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
 +      .vidioc_g_fmt_vid_cap   = vidioc_g_fmt_vid_cap,
 +      .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
 +      .vidioc_s_fmt_vid_cap   = vidioc_s_fmt_vid_cap,
 +
 +      .vidioc_enum_fmt_vid_out = vidioc_enum_fmt_vid_out,
 +      .vidioc_g_fmt_vid_out   = vidioc_g_fmt_vid_out,
 +      .vidioc_try_fmt_vid_out = vidioc_try_fmt_vid_out,
 +      .vidioc_s_fmt_vid_out   = vidioc_s_fmt_vid_out,
 +
 +      .vidioc_reqbufs         = vidioc_reqbufs,
 +      .vidioc_querybuf        = vidioc_querybuf,
 +
 +      .vidioc_qbuf            = vidioc_qbuf,
 +      .vidioc_dqbuf           = vidioc_dqbuf,
 +
 +      .vidioc_streamon        = vidioc_streamon,
 +      .vidioc_streamoff       = vidioc_streamoff,
 +      .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
 +      .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
 +};
 +
 +
 +/*
 + * Queue operations
 + */
 +
 +static int m2mtest_queue_setup(struct vb2_queue *vq,
 +                              const struct v4l2_format *fmt,
 +                              unsigned int *nbuffers, unsigned int *nplanes,
 +                              unsigned int sizes[], void *alloc_ctxs[])
 +{
 +      struct m2mtest_ctx *ctx = vb2_get_drv_priv(vq);
 +      struct m2mtest_q_data *q_data;
 +      unsigned int size, count = *nbuffers;
 +
 +      q_data = get_q_data(ctx, vq->type);
 +
 +      size = q_data->width * q_data->height * q_data->fmt->depth >> 3;
 +
 +      while (size * count > MEM2MEM_VID_MEM_LIMIT)
 +              (count)--;
 +
 +      *nplanes = 1;
 +      *nbuffers = count;
 +      sizes[0] = size;
 +
 +      /*
 +       * videobuf2-vmalloc allocator is context-less so no need to set
 +       * alloc_ctxs array.
 +       */
 +
 +      dprintk(ctx->dev, "get %d buffer(s) of size %d each.\n", count, size);
 +
 +      return 0;
 +}
 +
 +static int m2mtest_buf_prepare(struct vb2_buffer *vb)
 +{
 +      struct m2mtest_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
 +      struct m2mtest_q_data *q_data;
 +
 +      dprintk(ctx->dev, "type: %d\n", vb->vb2_queue->type);
 +
 +      q_data = get_q_data(ctx, vb->vb2_queue->type);
 +
 +      if (vb2_plane_size(vb, 0) < q_data->sizeimage) {
 +              dprintk(ctx->dev, "%s data will not fit into plane (%lu < %lu)\n",
 +                              __func__, vb2_plane_size(vb, 0), (long)q_data->sizeimage);
 +              return -EINVAL;
 +      }
 +
 +      vb2_set_plane_payload(vb, 0, q_data->sizeimage);
 +
 +      return 0;
 +}
 +
 +static void m2mtest_buf_queue(struct vb2_buffer *vb)
 +{
 +      struct m2mtest_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
 +      v4l2_m2m_buf_queue(ctx->m2m_ctx, vb);
 +}
 +
 +static void m2mtest_wait_prepare(struct vb2_queue *q)
 +{
 +      struct m2mtest_ctx *ctx = vb2_get_drv_priv(q);
 +      m2mtest_unlock(ctx);
 +}
 +
 +static void m2mtest_wait_finish(struct vb2_queue *q)
 +{
 +      struct m2mtest_ctx *ctx = vb2_get_drv_priv(q);
 +      m2mtest_lock(ctx);
 +}
 +
 +static struct vb2_ops m2mtest_qops = {
 +      .queue_setup     = m2mtest_queue_setup,
 +      .buf_prepare     = m2mtest_buf_prepare,
 +      .buf_queue       = m2mtest_buf_queue,
 +      .wait_prepare    = m2mtest_wait_prepare,
 +      .wait_finish     = m2mtest_wait_finish,
 +};
 +
 +static int queue_init(void *priv, struct vb2_queue *src_vq, struct vb2_queue *dst_vq)
 +{
 +      struct m2mtest_ctx *ctx = priv;
 +      int ret;
 +
 +      memset(src_vq, 0, sizeof(*src_vq));
 +      src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
 +      src_vq->io_modes = VB2_MMAP;
 +      src_vq->drv_priv = ctx;
 +      src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
 +      src_vq->ops = &m2mtest_qops;
 +      src_vq->mem_ops = &vb2_vmalloc_memops;
 +
 +      ret = vb2_queue_init(src_vq);
 +      if (ret)
 +              return ret;
 +
 +      memset(dst_vq, 0, sizeof(*dst_vq));
 +      dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 +      dst_vq->io_modes = VB2_MMAP;
 +      dst_vq->drv_priv = ctx;
 +      dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
 +      dst_vq->ops = &m2mtest_qops;
 +      dst_vq->mem_ops = &vb2_vmalloc_memops;
 +
 +      return vb2_queue_init(dst_vq);
 +}
 +
 +static const struct v4l2_ctrl_config m2mtest_ctrl_trans_time_msec = {
 +      .ops = &m2mtest_ctrl_ops,
 +      .id = V4L2_CID_TRANS_TIME_MSEC,
 +      .name = "Transaction Time (msec)",
 +      .type = V4L2_CTRL_TYPE_INTEGER,
 +      .def = 1001,
 +      .min = 1,
 +      .max = 10001,
 +      .step = 100,
 +};
 +
 +static const struct v4l2_ctrl_config m2mtest_ctrl_trans_num_bufs = {
 +      .ops = &m2mtest_ctrl_ops,
 +      .id = V4L2_CID_TRANS_NUM_BUFS,
 +      .name = "Buffers Per Transaction",
 +      .type = V4L2_CTRL_TYPE_INTEGER,
 +      .def = 1,
 +      .min = 1,
 +      .max = MEM2MEM_DEF_NUM_BUFS,
 +      .step = 1,
 +};
 +
 +/*
 + * File operations
 + */
 +static int m2mtest_open(struct file *file)
 +{
 +      struct m2mtest_dev *dev = video_drvdata(file);
 +      struct m2mtest_ctx *ctx = NULL;
 +      struct v4l2_ctrl_handler *hdl;
 +      int rc = 0;
 +
 +      if (mutex_lock_interruptible(&dev->dev_mutex))
 +              return -ERESTARTSYS;
 +      ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
 +      if (!ctx) {
 +              rc = -ENOMEM;
 +              goto open_unlock;
 +      }
 +
 +      v4l2_fh_init(&ctx->fh, video_devdata(file));
 +      file->private_data = &ctx->fh;
 +      ctx->dev = dev;
 +      hdl = &ctx->hdl;
 +      v4l2_ctrl_handler_init(hdl, 4);
 +      v4l2_ctrl_new_std(hdl, &m2mtest_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0);
 +      v4l2_ctrl_new_std(hdl, &m2mtest_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0);
 +      v4l2_ctrl_new_custom(hdl, &m2mtest_ctrl_trans_time_msec, NULL);
 +      v4l2_ctrl_new_custom(hdl, &m2mtest_ctrl_trans_num_bufs, NULL);
 +      if (hdl->error) {
 +              rc = hdl->error;
 +              v4l2_ctrl_handler_free(hdl);
 +              goto open_unlock;
 +      }
 +      ctx->fh.ctrl_handler = hdl;
 +      v4l2_ctrl_handler_setup(hdl);
 +
 +      ctx->q_data[V4L2_M2M_SRC].fmt = &formats[0];
 +      ctx->q_data[V4L2_M2M_SRC].width = 640;
 +      ctx->q_data[V4L2_M2M_SRC].height = 480;
 +      ctx->q_data[V4L2_M2M_SRC].sizeimage =
 +              ctx->q_data[V4L2_M2M_SRC].width *
 +              ctx->q_data[V4L2_M2M_SRC].height *
 +              (ctx->q_data[V4L2_M2M_SRC].fmt->depth >> 3);
 +      ctx->q_data[V4L2_M2M_DST] = ctx->q_data[V4L2_M2M_SRC];
 +      ctx->colorspace = V4L2_COLORSPACE_REC709;
 +
 +      ctx->m2m_ctx = v4l2_m2m_ctx_init(dev->m2m_dev, ctx, &queue_init);
 +
 +      if (IS_ERR(ctx->m2m_ctx)) {
 +              rc = PTR_ERR(ctx->m2m_ctx);
 +
 +              v4l2_ctrl_handler_free(hdl);
 +              kfree(ctx);
 +              goto open_unlock;
 +      }
 +
 +      v4l2_fh_add(&ctx->fh);
 +      atomic_inc(&dev->num_inst);
 +
 +      dprintk(dev, "Created instance %p, m2m_ctx: %p\n", ctx, ctx->m2m_ctx);
 +
 +open_unlock:
 +      mutex_unlock(&dev->dev_mutex);
 +      return rc;
 +}
 +
 +static int m2mtest_release(struct file *file)
 +{
 +      struct m2mtest_dev *dev = video_drvdata(file);
 +      struct m2mtest_ctx *ctx = file2ctx(file);
 +
 +      dprintk(dev, "Releasing instance %p\n", ctx);
 +
 +      v4l2_fh_del(&ctx->fh);
 +      v4l2_fh_exit(&ctx->fh);
 +      v4l2_ctrl_handler_free(&ctx->hdl);
 +      mutex_lock(&dev->dev_mutex);
 +      v4l2_m2m_ctx_release(ctx->m2m_ctx);
 +      mutex_unlock(&dev->dev_mutex);
 +      kfree(ctx);
 +
 +      atomic_dec(&dev->num_inst);
 +
 +      return 0;
 +}
 +
 +static unsigned int m2mtest_poll(struct file *file,
 +                               struct poll_table_struct *wait)
 +{
 +      struct m2mtest_ctx *ctx = file2ctx(file);
 +
 +      return v4l2_m2m_poll(file, ctx->m2m_ctx, wait);
 +}
 +
 +static int m2mtest_mmap(struct file *file, struct vm_area_struct *vma)
 +{
 +      struct m2mtest_dev *dev = video_drvdata(file);
 +      struct m2mtest_ctx *ctx = file2ctx(file);
 +      int res;
 +
 +      if (mutex_lock_interruptible(&dev->dev_mutex))
 +              return -ERESTARTSYS;
 +      res = v4l2_m2m_mmap(file, ctx->m2m_ctx, vma);
 +      mutex_unlock(&dev->dev_mutex);
 +      return res;
 +}
 +
 +static const struct v4l2_file_operations m2mtest_fops = {
 +      .owner          = THIS_MODULE,
 +      .open           = m2mtest_open,
 +      .release        = m2mtest_release,
 +      .poll           = m2mtest_poll,
 +      .unlocked_ioctl = video_ioctl2,
 +      .mmap           = m2mtest_mmap,
 +};
 +
 +static struct video_device m2mtest_videodev = {
 +      .name           = MEM2MEM_NAME,
 +      .fops           = &m2mtest_fops,
 +      .ioctl_ops      = &m2mtest_ioctl_ops,
 +      .minor          = -1,
 +      .release        = video_device_release,
 +};
 +
 +static struct v4l2_m2m_ops m2m_ops = {
 +      .device_run     = device_run,
 +      .job_ready      = job_ready,
 +      .job_abort      = job_abort,
 +      .lock           = m2mtest_lock,
 +      .unlock         = m2mtest_unlock,
 +};
 +
 +static int m2mtest_probe(struct platform_device *pdev)
 +{
 +      struct m2mtest_dev *dev;
 +      struct video_device *vfd;
 +      int ret;
 +
 +      dev = kzalloc(sizeof *dev, GFP_KERNEL);
 +      if (!dev)
 +              return -ENOMEM;
 +
 +      spin_lock_init(&dev->irqlock);
 +
 +      ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev);
 +      if (ret)
 +              goto free_dev;
 +
 +      atomic_set(&dev->num_inst, 0);
 +      mutex_init(&dev->dev_mutex);
 +
 +      vfd = video_device_alloc();
 +      if (!vfd) {
 +              v4l2_err(&dev->v4l2_dev, "Failed to allocate video device\n");
 +              ret = -ENOMEM;
 +              goto unreg_dev;
 +      }
 +
 +      *vfd = m2mtest_videodev;
 +      vfd->lock = &dev->dev_mutex;
 +
 +      ret = video_register_device(vfd, VFL_TYPE_GRABBER, 0);
 +      if (ret) {
 +              v4l2_err(&dev->v4l2_dev, "Failed to register video device\n");
 +              goto rel_vdev;
 +      }
 +
 +      video_set_drvdata(vfd, dev);
 +      snprintf(vfd->name, sizeof(vfd->name), "%s", m2mtest_videodev.name);
 +      dev->vfd = vfd;
 +      v4l2_info(&dev->v4l2_dev, MEM2MEM_TEST_MODULE_NAME
 +                      "Device registered as /dev/video%d\n", vfd->num);
 +
 +      setup_timer(&dev->timer, device_isr, (long)dev);
 +      platform_set_drvdata(pdev, dev);
 +
 +      dev->m2m_dev = v4l2_m2m_init(&m2m_ops);
 +      if (IS_ERR(dev->m2m_dev)) {
 +              v4l2_err(&dev->v4l2_dev, "Failed to init mem2mem device\n");
 +              ret = PTR_ERR(dev->m2m_dev);
 +              goto err_m2m;
 +      }
 +
 +      return 0;
 +
 +      v4l2_m2m_release(dev->m2m_dev);
 +err_m2m:
 +      video_unregister_device(dev->vfd);
 +rel_vdev:
 +      video_device_release(vfd);
 +unreg_dev:
 +      v4l2_device_unregister(&dev->v4l2_dev);
 +free_dev:
 +      kfree(dev);
 +
 +      return ret;
 +}
 +
 +static int m2mtest_remove(struct platform_device *pdev)
 +{
 +      struct m2mtest_dev *dev =
 +              (struct m2mtest_dev *)platform_get_drvdata(pdev);
 +
 +      v4l2_info(&dev->v4l2_dev, "Removing " MEM2MEM_TEST_MODULE_NAME);
 +      v4l2_m2m_release(dev->m2m_dev);
 +      del_timer_sync(&dev->timer);
 +      video_unregister_device(dev->vfd);
 +      v4l2_device_unregister(&dev->v4l2_dev);
 +      kfree(dev);
 +
 +      return 0;
 +}
 +
 +static struct platform_driver m2mtest_pdrv = {
 +      .probe          = m2mtest_probe,
 +      .remove         = m2mtest_remove,
 +      .driver         = {
 +              .name   = MEM2MEM_NAME,
 +              .owner  = THIS_MODULE,
 +      },
 +};
 +
 +static void __exit m2mtest_exit(void)
 +{
 +      platform_driver_unregister(&m2mtest_pdrv);
 +      platform_device_unregister(&m2mtest_pdev);
 +}
 +
 +static int __init m2mtest_init(void)
 +{
 +      int ret;
 +
 +      ret = platform_device_register(&m2mtest_pdev);
 +      if (ret)
 +              return ret;
 +
 +      ret = platform_driver_register(&m2mtest_pdrv);
 +      if (ret)
 +              platform_device_unregister(&m2mtest_pdev);
 +
 +      return 0;
 +}
 +
 +module_init(m2mtest_init);
 +module_exit(m2mtest_exit);
 +
Simple merge
Simple merge
index cf9d9fca5b84005cd37e24cba853c9d132e504be,0000000000000000000000000000000000000000..234777116e5fc5e8cc25a6f4e1940feca8a31412
mode 100644,000000..100644
--- /dev/null
@@@ -1,557 -1,0 +1,557 @@@
- static const __devinitdata struct usb_device_id device_table[] = {
 +/*
 + * Jeilin JL2005B/C/D library
 + *
 + * Copyright (C) 2011 Theodore Kilgore <kilgota@auburn.edu>
 + *
 + * This program is free software; you can redistribute it and/or modify
 + * it under the terms of the GNU General Public License as published by
 + * the Free Software Foundation; either version 2 of the License, or
 + * any later version.
 + *
 + * This program is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 + * GNU General Public License for more details.
 + *
 + * You should have received a copy of the GNU General Public License
 + * along with this program; if not, write to the Free Software
 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 + */
 +
 +#define MODULE_NAME "jl2005bcd"
 +
 +#include <linux/workqueue.h>
 +#include <linux/slab.h>
 +#include "gspca.h"
 +
 +
 +MODULE_AUTHOR("Theodore Kilgore <kilgota@auburn.edu>");
 +MODULE_DESCRIPTION("JL2005B/C/D USB Camera Driver");
 +MODULE_LICENSE("GPL");
 +
 +/* Default timeouts, in ms */
 +#define JL2005C_CMD_TIMEOUT 500
 +#define JL2005C_DATA_TIMEOUT 1000
 +
 +/* Maximum transfer size to use. */
 +#define JL2005C_MAX_TRANSFER 0x200
 +#define FRAME_HEADER_LEN 16
 +
 +
 +/* specific webcam descriptor */
 +struct sd {
 +      struct gspca_dev gspca_dev;  /* !! must be the first item */
 +      unsigned char firmware_id[6];
 +      const struct v4l2_pix_format *cap_mode;
 +      /* Driver stuff */
 +      struct work_struct work_struct;
 +      struct workqueue_struct *work_thread;
 +      u8 frame_brightness;
 +      int block_size; /* block size of camera */
 +      int vga;        /* 1 if vga cam, 0 if cif cam */
 +};
 +
 +
 +/* Camera has two resolution settings. What they are depends on model. */
 +static const struct v4l2_pix_format cif_mode[] = {
 +      {176, 144, V4L2_PIX_FMT_JL2005BCD, V4L2_FIELD_NONE,
 +              .bytesperline = 176,
 +              .sizeimage = 176 * 144,
 +              .colorspace = V4L2_COLORSPACE_SRGB,
 +              .priv = 0},
 +      {352, 288, V4L2_PIX_FMT_JL2005BCD, V4L2_FIELD_NONE,
 +              .bytesperline = 352,
 +              .sizeimage = 352 * 288,
 +              .colorspace = V4L2_COLORSPACE_SRGB,
 +              .priv = 0},
 +};
 +
 +static const struct v4l2_pix_format vga_mode[] = {
 +      {320, 240, V4L2_PIX_FMT_JL2005BCD, V4L2_FIELD_NONE,
 +              .bytesperline = 320,
 +              .sizeimage = 320 * 240,
 +              .colorspace = V4L2_COLORSPACE_SRGB,
 +              .priv = 0},
 +      {640, 480, V4L2_PIX_FMT_JL2005BCD, V4L2_FIELD_NONE,
 +              .bytesperline = 640,
 +              .sizeimage = 640 * 480,
 +              .colorspace = V4L2_COLORSPACE_SRGB,
 +              .priv = 0},
 +};
 +
 +/*
 + * cam uses endpoint 0x03 to send commands, 0x84 for read commands,
 + * and 0x82 for bulk data transfer.
 + */
 +
 +/* All commands are two bytes only */
 +static int jl2005c_write2(struct gspca_dev *gspca_dev, unsigned char *command)
 +{
 +      int retval;
 +
 +      memcpy(gspca_dev->usb_buf, command, 2);
 +      retval = usb_bulk_msg(gspca_dev->dev,
 +                      usb_sndbulkpipe(gspca_dev->dev, 3),
 +                      gspca_dev->usb_buf, 2, NULL, 500);
 +      if (retval < 0)
 +              pr_err("command write [%02x] error %d\n",
 +                     gspca_dev->usb_buf[0], retval);
 +      return retval;
 +}
 +
 +/* Response to a command is one byte in usb_buf[0], only if requested. */
 +static int jl2005c_read1(struct gspca_dev *gspca_dev)
 +{
 +      int retval;
 +
 +      retval = usb_bulk_msg(gspca_dev->dev,
 +                              usb_rcvbulkpipe(gspca_dev->dev, 0x84),
 +                              gspca_dev->usb_buf, 1, NULL, 500);
 +      if (retval < 0)
 +              pr_err("read command [0x%02x] error %d\n",
 +                     gspca_dev->usb_buf[0], retval);
 +      return retval;
 +}
 +
 +/* Response appears in gspca_dev->usb_buf[0] */
 +static int jl2005c_read_reg(struct gspca_dev *gspca_dev, unsigned char reg)
 +{
 +      int retval;
 +
 +      static u8 instruction[2] = {0x95, 0x00};
 +      /* put register to read in byte 1 */
 +      instruction[1] = reg;
 +      /* Send the read request */
 +      retval = jl2005c_write2(gspca_dev, instruction);
 +      if (retval < 0)
 +              return retval;
 +      retval = jl2005c_read1(gspca_dev);
 +
 +      return retval;
 +}
 +
 +static int jl2005c_start_new_frame(struct gspca_dev *gspca_dev)
 +{
 +      int i;
 +      int retval;
 +      int frame_brightness = 0;
 +
 +      static u8 instruction[2] = {0x7f, 0x01};
 +
 +      retval = jl2005c_write2(gspca_dev, instruction);
 +      if (retval < 0)
 +              return retval;
 +
 +      i = 0;
 +      while (i < 20 && !frame_brightness) {
 +              /* If we tried 20 times, give up. */
 +              retval = jl2005c_read_reg(gspca_dev, 0x7e);
 +              if (retval < 0)
 +                      return retval;
 +              frame_brightness = gspca_dev->usb_buf[0];
 +              retval = jl2005c_read_reg(gspca_dev, 0x7d);
 +              if (retval < 0)
 +                      return retval;
 +              i++;
 +      }
 +      PDEBUG(D_FRAM, "frame_brightness is 0x%02x", gspca_dev->usb_buf[0]);
 +      return retval;
 +}
 +
 +static int jl2005c_write_reg(struct gspca_dev *gspca_dev, unsigned char reg,
 +                                                  unsigned char value)
 +{
 +      int retval;
 +      u8 instruction[2];
 +
 +      instruction[0] = reg;
 +      instruction[1] = value;
 +
 +      retval = jl2005c_write2(gspca_dev, instruction);
 +      if (retval < 0)
 +                      return retval;
 +
 +      return retval;
 +}
 +
 +static int jl2005c_get_firmware_id(struct gspca_dev *gspca_dev)
 +{
 +      struct sd *sd = (struct sd *)gspca_dev;
 +      int i = 0;
 +      int retval = -1;
 +      unsigned char regs_to_read[] = {0x57, 0x02, 0x03, 0x5d, 0x5e, 0x5f};
 +
 +      PDEBUG(D_PROBE, "Running jl2005c_get_firmware_id");
 +      /* Read the first ID byte once for warmup */
 +      retval = jl2005c_read_reg(gspca_dev, regs_to_read[0]);
 +      PDEBUG(D_PROBE, "response is %02x", gspca_dev->usb_buf[0]);
 +      if (retval < 0)
 +              return retval;
 +      /* Now actually get the ID string */
 +      for (i = 0; i < 6; i++) {
 +              retval = jl2005c_read_reg(gspca_dev, regs_to_read[i]);
 +              if (retval < 0)
 +                      return retval;
 +              sd->firmware_id[i] = gspca_dev->usb_buf[0];
 +      }
 +      PDEBUG(D_PROBE, "firmware ID is %02x%02x%02x%02x%02x%02x",
 +                                              sd->firmware_id[0],
 +                                              sd->firmware_id[1],
 +                                              sd->firmware_id[2],
 +                                              sd->firmware_id[3],
 +                                              sd->firmware_id[4],
 +                                              sd->firmware_id[5]);
 +      return 0;
 +}
 +
 +static int jl2005c_stream_start_vga_lg
 +                  (struct gspca_dev *gspca_dev)
 +{
 +      int i;
 +      int retval = -1;
 +      static u8 instruction[][2] = {
 +              {0x05, 0x00},
 +              {0x7c, 0x00},
 +              {0x7d, 0x18},
 +              {0x02, 0x00},
 +              {0x01, 0x00},
 +              {0x04, 0x52},
 +      };
 +
 +      for (i = 0; i < ARRAY_SIZE(instruction); i++) {
 +              msleep(60);
 +              retval = jl2005c_write2(gspca_dev, instruction[i]);
 +              if (retval < 0)
 +                      return retval;
 +      }
 +      msleep(60);
 +      return retval;
 +}
 +
 +static int jl2005c_stream_start_vga_small(struct gspca_dev *gspca_dev)
 +{
 +      int i;
 +      int retval = -1;
 +      static u8 instruction[][2] = {
 +              {0x06, 0x00},
 +              {0x7c, 0x00},
 +              {0x7d, 0x1a},
 +              {0x02, 0x00},
 +              {0x01, 0x00},
 +              {0x04, 0x52},
 +      };
 +
 +      for (i = 0; i < ARRAY_SIZE(instruction); i++) {
 +              msleep(60);
 +              retval = jl2005c_write2(gspca_dev, instruction[i]);
 +              if (retval < 0)
 +                      return retval;
 +      }
 +      msleep(60);
 +      return retval;
 +}
 +
 +static int jl2005c_stream_start_cif_lg(struct gspca_dev *gspca_dev)
 +{
 +      int i;
 +      int retval = -1;
 +      static u8 instruction[][2] = {
 +              {0x05, 0x00},
 +              {0x7c, 0x00},
 +              {0x7d, 0x30},
 +              {0x02, 0x00},
 +              {0x01, 0x00},
 +              {0x04, 0x42},
 +      };
 +
 +      for (i = 0; i < ARRAY_SIZE(instruction); i++) {
 +              msleep(60);
 +              retval = jl2005c_write2(gspca_dev, instruction[i]);
 +              if (retval < 0)
 +                      return retval;
 +      }
 +      msleep(60);
 +      return retval;
 +}
 +
 +static int jl2005c_stream_start_cif_small(struct gspca_dev *gspca_dev)
 +{
 +      int i;
 +      int retval = -1;
 +      static u8 instruction[][2] = {
 +              {0x06, 0x00},
 +              {0x7c, 0x00},
 +              {0x7d, 0x32},
 +              {0x02, 0x00},
 +              {0x01, 0x00},
 +              {0x04, 0x42},
 +      };
 +
 +      for (i = 0; i < ARRAY_SIZE(instruction); i++) {
 +              msleep(60);
 +              retval = jl2005c_write2(gspca_dev, instruction[i]);
 +              if (retval < 0)
 +                      return retval;
 +      }
 +      msleep(60);
 +      return retval;
 +}
 +
 +
 +static int jl2005c_stop(struct gspca_dev *gspca_dev)
 +{
 +      int retval;
 +
 +      retval = jl2005c_write_reg(gspca_dev, 0x07, 0x00);
 +      return retval;
 +}
 +
 +/* This function is called as a workqueue function and runs whenever the camera
 + * is streaming data. Because it is a workqueue function it is allowed to sleep
 + * so we can use synchronous USB calls. To avoid possible collisions with other
 + * threads attempting to use the camera's USB interface the gspca usb_lock is
 + * used when performing the one USB control operation inside the workqueue,
 + * which tells the camera to close the stream. In practice the only thing
 + * which needs to be protected against is the usb_set_interface call that
 + * gspca makes during stream_off. Otherwise the camera doesn't provide any
 + * controls that the user could try to change.
 + */
 +static void jl2005c_dostream(struct work_struct *work)
 +{
 +      struct sd *dev = container_of(work, struct sd, work_struct);
 +      struct gspca_dev *gspca_dev = &dev->gspca_dev;
 +      int bytes_left = 0; /* bytes remaining in current frame. */
 +      int data_len;   /* size to use for the next read. */
 +      int header_read = 0;
 +      unsigned char header_sig[2] = {0x4a, 0x4c};
 +      int act_len;
 +      int packet_type;
 +      int ret;
 +      u8 *buffer;
 +
 +      buffer = kmalloc(JL2005C_MAX_TRANSFER, GFP_KERNEL | GFP_DMA);
 +      if (!buffer) {
 +              pr_err("Couldn't allocate USB buffer\n");
 +              goto quit_stream;
 +      }
 +
 +      while (gspca_dev->dev && gspca_dev->streaming) {
 +#ifdef CONFIG_PM
 +              if (gspca_dev->frozen)
 +                      break;
 +#endif
 +              /* Check if this is a new frame. If so, start the frame first */
 +              if (!header_read) {
 +                      mutex_lock(&gspca_dev->usb_lock);
 +                      ret = jl2005c_start_new_frame(gspca_dev);
 +                      mutex_unlock(&gspca_dev->usb_lock);
 +                      if (ret < 0)
 +                              goto quit_stream;
 +                      ret = usb_bulk_msg(gspca_dev->dev,
 +                              usb_rcvbulkpipe(gspca_dev->dev, 0x82),
 +                              buffer, JL2005C_MAX_TRANSFER, &act_len,
 +                              JL2005C_DATA_TIMEOUT);
 +                      PDEBUG(D_PACK,
 +                              "Got %d bytes out of %d for header",
 +                                      act_len, JL2005C_MAX_TRANSFER);
 +                      if (ret < 0 || act_len < JL2005C_MAX_TRANSFER)
 +                              goto quit_stream;
 +                      /* Check whether we actually got the first blodk */
 +                      if (memcmp(header_sig, buffer, 2) != 0) {
 +                              pr_err("First block is not the first block\n");
 +                              goto quit_stream;
 +                      }
 +                      /* total size to fetch is byte 7, times blocksize
 +                       * of which we already got act_len */
 +                      bytes_left = buffer[0x07] * dev->block_size - act_len;
 +                      PDEBUG(D_PACK, "bytes_left = 0x%x", bytes_left);
 +                      /* We keep the header. It has other information, too.*/
 +                      packet_type = FIRST_PACKET;
 +                      gspca_frame_add(gspca_dev, packet_type,
 +                                      buffer, act_len);
 +                      header_read = 1;
 +              }
 +              while (bytes_left > 0 && gspca_dev->dev) {
 +                      data_len = bytes_left > JL2005C_MAX_TRANSFER ?
 +                              JL2005C_MAX_TRANSFER : bytes_left;
 +                      ret = usb_bulk_msg(gspca_dev->dev,
 +                              usb_rcvbulkpipe(gspca_dev->dev, 0x82),
 +                              buffer, data_len, &act_len,
 +                              JL2005C_DATA_TIMEOUT);
 +                      if (ret < 0 || act_len < data_len)
 +                              goto quit_stream;
 +                      PDEBUG(D_PACK,
 +                              "Got %d bytes out of %d for frame",
 +                                              data_len, bytes_left);
 +                      bytes_left -= data_len;
 +                      if (bytes_left == 0) {
 +                              packet_type = LAST_PACKET;
 +                              header_read = 0;
 +                      } else
 +                              packet_type = INTER_PACKET;
 +                      gspca_frame_add(gspca_dev, packet_type,
 +                                      buffer, data_len);
 +              }
 +      }
 +quit_stream:
 +      if (gspca_dev->dev) {
 +              mutex_lock(&gspca_dev->usb_lock);
 +              jl2005c_stop(gspca_dev);
 +              mutex_unlock(&gspca_dev->usb_lock);
 +      }
 +      kfree(buffer);
 +}
 +
 +
 +
 +
 +/* This function is called at probe time */
 +static int sd_config(struct gspca_dev *gspca_dev,
 +                      const struct usb_device_id *id)
 +{
 +      struct cam *cam;
 +      struct sd *sd = (struct sd *) gspca_dev;
 +
 +      cam = &gspca_dev->cam;
 +      /* We don't use the buffer gspca allocates so make it small. */
 +      cam->bulk_size = 64;
 +      cam->bulk = 1;
 +      /* For the rest, the camera needs to be detected */
 +      jl2005c_get_firmware_id(gspca_dev);
 +      /* Here are some known firmware IDs
 +       * First some JL2005B cameras
 +       * {0x41, 0x07, 0x04, 0x2c, 0xe8, 0xf2} Sakar KidzCam
 +       * {0x45, 0x02, 0x08, 0xb9, 0x00, 0xd2} No-name JL2005B
 +       * JL2005C cameras
 +       * {0x01, 0x0c, 0x16, 0x10, 0xf8, 0xc8} Argus DC-1512
 +       * {0x12, 0x04, 0x03, 0xc0, 0x00, 0xd8} ICarly
 +       * {0x86, 0x08, 0x05, 0x02, 0x00, 0xd4} Jazz
 +       *
 +       * Based upon this scanty evidence, we can detect a CIF camera by
 +       * testing byte 0 for 0x4x.
 +       */
 +      if ((sd->firmware_id[0] & 0xf0) == 0x40) {
 +              cam->cam_mode   = cif_mode;
 +              cam->nmodes     = ARRAY_SIZE(cif_mode);
 +              sd->block_size  = 0x80;
 +      } else {
 +              cam->cam_mode   = vga_mode;
 +              cam->nmodes     = ARRAY_SIZE(vga_mode);
 +              sd->block_size  = 0x200;
 +      }
 +
 +      INIT_WORK(&sd->work_struct, jl2005c_dostream);
 +
 +      return 0;
 +}
 +
 +/* this function is called at probe and resume time */
 +static int sd_init(struct gspca_dev *gspca_dev)
 +{
 +      return 0;
 +}
 +
 +static int sd_start(struct gspca_dev *gspca_dev)
 +{
 +
 +      struct sd *sd = (struct sd *) gspca_dev;
 +      sd->cap_mode = gspca_dev->cam.cam_mode;
 +
 +      switch (gspca_dev->width) {
 +      case 640:
 +              PDEBUG(D_STREAM, "Start streaming at vga resolution");
 +              jl2005c_stream_start_vga_lg(gspca_dev);
 +              break;
 +      case 320:
 +              PDEBUG(D_STREAM, "Start streaming at qvga resolution");
 +              jl2005c_stream_start_vga_small(gspca_dev);
 +              break;
 +      case 352:
 +              PDEBUG(D_STREAM, "Start streaming at cif resolution");
 +              jl2005c_stream_start_cif_lg(gspca_dev);
 +              break;
 +      case 176:
 +              PDEBUG(D_STREAM, "Start streaming at qcif resolution");
 +              jl2005c_stream_start_cif_small(gspca_dev);
 +              break;
 +      default:
 +              pr_err("Unknown resolution specified\n");
 +              return -1;
 +      }
 +
 +      /* Start the workqueue function to do the streaming */
 +      sd->work_thread = create_singlethread_workqueue(MODULE_NAME);
 +      queue_work(sd->work_thread, &sd->work_struct);
 +
 +      return 0;
 +}
 +
 +/* called on streamoff with alt==0 and on disconnect */
 +/* the usb_lock is held at entry - restore on exit */
 +static void sd_stop0(struct gspca_dev *gspca_dev)
 +{
 +      struct sd *dev = (struct sd *) gspca_dev;
 +
 +      /* wait for the work queue to terminate */
 +      mutex_unlock(&gspca_dev->usb_lock);
 +      /* This waits for sq905c_dostream to finish */
 +      destroy_workqueue(dev->work_thread);
 +      dev->work_thread = NULL;
 +      mutex_lock(&gspca_dev->usb_lock);
 +}
 +
 +
 +
 +/* sub-driver description */
 +static const struct sd_desc sd_desc = {
 +      .name = MODULE_NAME,
 +      .config = sd_config,
 +      .init = sd_init,
 +      .start = sd_start,
 +      .stop0 = sd_stop0,
 +};
 +
 +/* -- module initialisation -- */
++static const struct usb_device_id device_table[] = {
 +      {USB_DEVICE(0x0979, 0x0227)},
 +      {}
 +};
 +MODULE_DEVICE_TABLE(usb, device_table);
 +
 +/* -- device connect -- */
 +static int sd_probe(struct usb_interface *intf,
 +                              const struct usb_device_id *id)
 +{
 +      return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
 +                              THIS_MODULE);
 +}
 +
 +static struct usb_driver sd_driver = {
 +      .name = MODULE_NAME,
 +      .id_table = device_table,
 +      .probe = sd_probe,
 +      .disconnect = gspca_disconnect,
 +#ifdef CONFIG_PM
 +      .suspend = gspca_suspend,
 +      .resume = gspca_resume,
 +      .reset_resume = gspca_resume,
 +#endif
 +};
 +
 +/* -- module insert / remove -- */
 +static int __init sd_mod_init(void)
 +{
 +      int ret;
 +
 +      ret = usb_register(&sd_driver);
 +      if (ret < 0)
 +              return ret;
 +      return 0;
 +}
 +static void __exit sd_mod_exit(void)
 +{
 +      usb_deregister(&sd_driver);
 +}
 +
 +module_init(sd_mod_init);
 +module_exit(sd_mod_exit);
index 969bb5a4cd9307812cc45f2224f3be5bd1fa5617,0000000000000000000000000000000000000000..bab01c86c3154933c819d941bbcfe3c6ffea60ef
mode 100644,000000..100644
--- /dev/null
@@@ -1,612 -1,0 +1,612 @@@
- static const struct usb_device_id device_table[] __devinitconst = {
 +/*
 + * SPCA506 chip based cameras function
 + * M Xhaard 15/04/2004 based on different work Mark Taylor and others
 + * and my own snoopy file on a pv-321c donate by a german compagny
 + *                "Firma Frank Gmbh" from  Saarbruecken
 + *
 + * V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
 + *
 + * This program is free software; you can redistribute it and/or modify
 + * it under the terms of the GNU General Public License as published by
 + * the Free Software Foundation; either version 2 of the License, or
 + * any later version.
 + *
 + * This program is distributed in the hope that it will be useful,
 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 + * GNU General Public License for more details.
 + *
 + * You should have received a copy of the GNU General Public License
 + * along with this program; if not, write to the Free Software
 + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 + */
 +
 +#define MODULE_NAME "spca506"
 +
 +#include "gspca.h"
 +
 +MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>");
 +MODULE_DESCRIPTION("GSPCA/SPCA506 USB Camera Driver");
 +MODULE_LICENSE("GPL");
 +
 +/* specific webcam descriptor */
 +struct sd {
 +      struct gspca_dev gspca_dev;     /* !! must be the first item */
 +
 +      char norme;
 +      char channel;
 +};
 +
 +static const struct v4l2_pix_format vga_mode[] = {
 +      {160, 120, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE,
 +              .bytesperline = 160,
 +              .sizeimage = 160 * 120 * 3 / 2,
 +              .colorspace = V4L2_COLORSPACE_SRGB,
 +              .priv = 5},
 +      {176, 144, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE,
 +              .bytesperline = 176,
 +              .sizeimage = 176 * 144 * 3 / 2,
 +              .colorspace = V4L2_COLORSPACE_SRGB,
 +              .priv = 4},
 +      {320, 240, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE,
 +              .bytesperline = 320,
 +              .sizeimage = 320 * 240 * 3 / 2,
 +              .colorspace = V4L2_COLORSPACE_SRGB,
 +              .priv = 2},
 +      {352, 288, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE,
 +              .bytesperline = 352,
 +              .sizeimage = 352 * 288 * 3 / 2,
 +              .colorspace = V4L2_COLORSPACE_SRGB,
 +              .priv = 1},
 +      {640, 480, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE,
 +              .bytesperline = 640,
 +              .sizeimage = 640 * 480 * 3 / 2,
 +              .colorspace = V4L2_COLORSPACE_SRGB,
 +              .priv = 0},
 +};
 +
 +#define SPCA50X_OFFSET_DATA 10
 +
 +#define SAA7113_bright 0x0a   /* defaults 0x80 */
 +#define SAA7113_contrast 0x0b /* defaults 0x47 */
 +#define SAA7113_saturation 0x0c       /* defaults 0x40 */
 +#define SAA7113_hue 0x0d      /* defaults 0x00 */
 +#define SAA7113_I2C_BASE_WRITE 0x4a
 +
 +/* read 'len' bytes to gspca_dev->usb_buf */
 +static void reg_r(struct gspca_dev *gspca_dev,
 +                __u16 req,
 +                __u16 index,
 +                __u16 length)
 +{
 +      usb_control_msg(gspca_dev->dev,
 +                      usb_rcvctrlpipe(gspca_dev->dev, 0),
 +                      req,
 +                      USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
 +                      0,              /* value */
 +                      index, gspca_dev->usb_buf, length,
 +                      500);
 +}
 +
 +static void reg_w(struct usb_device *dev,
 +                __u16 req,
 +                __u16 value,
 +                __u16 index)
 +{
 +      usb_control_msg(dev,
 +                      usb_sndctrlpipe(dev, 0),
 +                      req,
 +                      USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
 +                      value, index,
 +                      NULL, 0, 500);
 +}
 +
 +static void spca506_Initi2c(struct gspca_dev *gspca_dev)
 +{
 +      reg_w(gspca_dev->dev, 0x07, SAA7113_I2C_BASE_WRITE, 0x0004);
 +}
 +
 +static void spca506_WriteI2c(struct gspca_dev *gspca_dev, __u16 valeur,
 +                           __u16 reg)
 +{
 +      int retry = 60;
 +
 +      reg_w(gspca_dev->dev, 0x07, reg, 0x0001);
 +      reg_w(gspca_dev->dev, 0x07, valeur, 0x0000);
 +      while (retry--) {
 +              reg_r(gspca_dev, 0x07, 0x0003, 2);
 +              if ((gspca_dev->usb_buf[0] | gspca_dev->usb_buf[1]) == 0x00)
 +                      break;
 +      }
 +}
 +
 +static void spca506_SetNormeInput(struct gspca_dev *gspca_dev,
 +                               __u16 norme,
 +                               __u16 channel)
 +{
 +      struct sd *sd = (struct sd *) gspca_dev;
 +/* fixme: check if channel == 0..3 and 6..9 (8 values) */
 +      __u8 setbit0 = 0x00;
 +      __u8 setbit1 = 0x00;
 +      __u8 videomask = 0x00;
 +
 +      PDEBUG(D_STREAM, "** Open Set Norme **");
 +      spca506_Initi2c(gspca_dev);
 +      /* NTSC bit0 -> 1(525 l) PAL SECAM bit0 -> 0 (625 l) */
 +      /* Composite channel bit1 -> 1 S-video bit 1 -> 0 */
 +      /* and exclude SAA7113 reserved channel set default 0 otherwise */
 +      if (norme & V4L2_STD_NTSC)
 +              setbit0 = 0x01;
 +      if (channel == 4 || channel == 5 || channel > 9)
 +              channel = 0;
 +      if (channel < 4)
 +              setbit1 = 0x02;
 +      videomask = (0x48 | setbit0 | setbit1);
 +      reg_w(gspca_dev->dev, 0x08, videomask, 0x0000);
 +      spca506_WriteI2c(gspca_dev, (0xc0 | (channel & 0x0F)), 0x02);
 +
 +      if (norme & V4L2_STD_NTSC)
 +              spca506_WriteI2c(gspca_dev, 0x33, 0x0e);
 +                                      /* Chrominance Control NTSC N */
 +      else if (norme & V4L2_STD_SECAM)
 +              spca506_WriteI2c(gspca_dev, 0x53, 0x0e);
 +                                      /* Chrominance Control SECAM */
 +      else
 +              spca506_WriteI2c(gspca_dev, 0x03, 0x0e);
 +                                      /* Chrominance Control PAL BGHIV */
 +
 +      sd->norme = norme;
 +      sd->channel = channel;
 +      PDEBUG(D_STREAM, "Set Video Byte to 0x%2x", videomask);
 +      PDEBUG(D_STREAM, "Set Norme: %08x Channel %d", norme, channel);
 +}
 +
 +static void spca506_GetNormeInput(struct gspca_dev *gspca_dev,
 +                                __u16 *norme, __u16 *channel)
 +{
 +      struct sd *sd = (struct sd *) gspca_dev;
 +
 +      /* Read the register is not so good value change so
 +         we use your own copy in spca50x struct */
 +      *norme = sd->norme;
 +      *channel = sd->channel;
 +      PDEBUG(D_STREAM, "Get Norme: %d Channel %d", *norme, *channel);
 +}
 +
 +static void spca506_Setsize(struct gspca_dev *gspca_dev, __u16 code,
 +                          __u16 xmult, __u16 ymult)
 +{
 +      struct usb_device *dev = gspca_dev->dev;
 +
 +      PDEBUG(D_STREAM, "** SetSize **");
 +      reg_w(dev, 0x04, (0x18 | (code & 0x07)), 0x0000);
 +      /* Soft snap 0x40 Hard 0x41 */
 +      reg_w(dev, 0x04, 0x41, 0x0001);
 +      reg_w(dev, 0x04, 0x00, 0x0002);
 +      /* reserved */
 +      reg_w(dev, 0x04, 0x00, 0x0003);
 +
 +      /* reserved */
 +      reg_w(dev, 0x04, 0x00, 0x0004);
 +      /* reserved */
 +      reg_w(dev, 0x04, 0x01, 0x0005);
 +      /* reserced */
 +      reg_w(dev, 0x04, xmult, 0x0006);
 +      /* reserved */
 +      reg_w(dev, 0x04, ymult, 0x0007);
 +      /* compression 1 */
 +      reg_w(dev, 0x04, 0x00, 0x0008);
 +      /* T=64 -> 2 */
 +      reg_w(dev, 0x04, 0x00, 0x0009);
 +      /* threshold2D */
 +      reg_w(dev, 0x04, 0x21, 0x000a);
 +      /* quantization */
 +      reg_w(dev, 0x04, 0x00, 0x000b);
 +}
 +
 +/* this function is called at probe time */
 +static int sd_config(struct gspca_dev *gspca_dev,
 +                      const struct usb_device_id *id)
 +{
 +      struct cam *cam;
 +
 +      cam = &gspca_dev->cam;
 +      cam->cam_mode = vga_mode;
 +      cam->nmodes = ARRAY_SIZE(vga_mode);
 +      return 0;
 +}
 +
 +/* this function is called at probe and resume time */
 +static int sd_init(struct gspca_dev *gspca_dev)
 +{
 +      struct usb_device *dev = gspca_dev->dev;
 +
 +      reg_w(dev, 0x03, 0x00, 0x0004);
 +      reg_w(dev, 0x03, 0xFF, 0x0003);
 +      reg_w(dev, 0x03, 0x00, 0x0000);
 +      reg_w(dev, 0x03, 0x1c, 0x0001);
 +      reg_w(dev, 0x03, 0x18, 0x0001);
 +      /* Init on PAL and composite input0 */
 +      spca506_SetNormeInput(gspca_dev, 0, 0);
 +      reg_w(dev, 0x03, 0x1c, 0x0001);
 +      reg_w(dev, 0x03, 0x18, 0x0001);
 +      reg_w(dev, 0x05, 0x00, 0x0000);
 +      reg_w(dev, 0x05, 0xef, 0x0001);
 +      reg_w(dev, 0x05, 0x00, 0x00c1);
 +      reg_w(dev, 0x05, 0x00, 0x00c2);
 +      reg_w(dev, 0x06, 0x18, 0x0002);
 +      reg_w(dev, 0x06, 0xf5, 0x0011);
 +      reg_w(dev, 0x06, 0x02, 0x0012);
 +      reg_w(dev, 0x06, 0xfb, 0x0013);
 +      reg_w(dev, 0x06, 0x00, 0x0014);
 +      reg_w(dev, 0x06, 0xa4, 0x0051);
 +      reg_w(dev, 0x06, 0x40, 0x0052);
 +      reg_w(dev, 0x06, 0x71, 0x0053);
 +      reg_w(dev, 0x06, 0x40, 0x0054);
 +      /************************************************/
 +      reg_w(dev, 0x03, 0x00, 0x0004);
 +      reg_w(dev, 0x03, 0x00, 0x0003);
 +      reg_w(dev, 0x03, 0x00, 0x0004);
 +      reg_w(dev, 0x03, 0xFF, 0x0003);
 +      reg_w(dev, 0x02, 0x00, 0x0000);
 +      reg_w(dev, 0x03, 0x60, 0x0000);
 +      reg_w(dev, 0x03, 0x18, 0x0001);
 +      /* for a better reading mx :)     */
 +      /*sdca506_WriteI2c(value,register) */
 +      spca506_Initi2c(gspca_dev);
 +      spca506_WriteI2c(gspca_dev, 0x08, 0x01);
 +      spca506_WriteI2c(gspca_dev, 0xc0, 0x02);
 +                                              /* input composite video */
 +      spca506_WriteI2c(gspca_dev, 0x33, 0x03);
 +      spca506_WriteI2c(gspca_dev, 0x00, 0x04);
 +      spca506_WriteI2c(gspca_dev, 0x00, 0x05);
 +      spca506_WriteI2c(gspca_dev, 0x0d, 0x06);
 +      spca506_WriteI2c(gspca_dev, 0xf0, 0x07);
 +      spca506_WriteI2c(gspca_dev, 0x98, 0x08);
 +      spca506_WriteI2c(gspca_dev, 0x03, 0x09);
 +      spca506_WriteI2c(gspca_dev, 0x80, 0x0a);
 +      spca506_WriteI2c(gspca_dev, 0x47, 0x0b);
 +      spca506_WriteI2c(gspca_dev, 0x48, 0x0c);
 +      spca506_WriteI2c(gspca_dev, 0x00, 0x0d);
 +      spca506_WriteI2c(gspca_dev, 0x03, 0x0e);        /* Chroma Pal adjust */
 +      spca506_WriteI2c(gspca_dev, 0x2a, 0x0f);
 +      spca506_WriteI2c(gspca_dev, 0x00, 0x10);
 +      spca506_WriteI2c(gspca_dev, 0x0c, 0x11);
 +      spca506_WriteI2c(gspca_dev, 0xb8, 0x12);
 +      spca506_WriteI2c(gspca_dev, 0x01, 0x13);
 +      spca506_WriteI2c(gspca_dev, 0x00, 0x14);
 +      spca506_WriteI2c(gspca_dev, 0x00, 0x15);
 +      spca506_WriteI2c(gspca_dev, 0x00, 0x16);
 +      spca506_WriteI2c(gspca_dev, 0x00, 0x17);
 +      spca506_WriteI2c(gspca_dev, 0x00, 0x18);
 +      spca506_WriteI2c(gspca_dev, 0x00, 0x19);
 +      spca506_WriteI2c(gspca_dev, 0x00, 0x1a);
 +      spca506_WriteI2c(gspca_dev, 0x00, 0x1b);
 +      spca506_WriteI2c(gspca_dev, 0x00, 0x1c);
 +      spca506_WriteI2c(gspca_dev, 0x00, 0x1d);
 +      spca506_WriteI2c(gspca_dev, 0x00, 0x1e);
 +      spca506_WriteI2c(gspca_dev, 0xa1, 0x1f);
 +      spca506_WriteI2c(gspca_dev, 0x02, 0x40);
 +      spca506_WriteI2c(gspca_dev, 0xff, 0x41);
 +      spca506_WriteI2c(gspca_dev, 0xff, 0x42);
 +      spca506_WriteI2c(gspca_dev, 0xff, 0x43);
 +      spca506_WriteI2c(gspca_dev, 0xff, 0x44);
 +      spca506_WriteI2c(gspca_dev, 0xff, 0x45);
 +      spca506_WriteI2c(gspca_dev, 0xff, 0x46);
 +      spca506_WriteI2c(gspca_dev, 0xff, 0x47);
 +      spca506_WriteI2c(gspca_dev, 0xff, 0x48);
 +      spca506_WriteI2c(gspca_dev, 0xff, 0x49);
 +      spca506_WriteI2c(gspca_dev, 0xff, 0x4a);
 +      spca506_WriteI2c(gspca_dev, 0xff, 0x4b);
 +      spca506_WriteI2c(gspca_dev, 0xff, 0x4c);
 +      spca506_WriteI2c(gspca_dev, 0xff, 0x4d);
 +      spca506_WriteI2c(gspca_dev, 0xff, 0x4e);
 +      spca506_WriteI2c(gspca_dev, 0xff, 0x4f);
 +      spca506_WriteI2c(gspca_dev, 0xff, 0x50);
 +      spca506_WriteI2c(gspca_dev, 0xff, 0x51);
 +      spca506_WriteI2c(gspca_dev, 0xff, 0x52);
 +      spca506_WriteI2c(gspca_dev, 0xff, 0x53);
 +      spca506_WriteI2c(gspca_dev, 0xff, 0x54);
 +      spca506_WriteI2c(gspca_dev, 0xff, 0x55);
 +      spca506_WriteI2c(gspca_dev, 0xff, 0x56);
 +      spca506_WriteI2c(gspca_dev, 0xff, 0x57);
 +      spca506_WriteI2c(gspca_dev, 0x00, 0x58);
 +      spca506_WriteI2c(gspca_dev, 0x54, 0x59);
 +      spca506_WriteI2c(gspca_dev, 0x07, 0x5a);
 +      spca506_WriteI2c(gspca_dev, 0x83, 0x5b);
 +      spca506_WriteI2c(gspca_dev, 0x00, 0x5c);
 +      spca506_WriteI2c(gspca_dev, 0x00, 0x5d);
 +      spca506_WriteI2c(gspca_dev, 0x00, 0x5e);
 +      spca506_WriteI2c(gspca_dev, 0x00, 0x5f);
 +      spca506_WriteI2c(gspca_dev, 0x00, 0x60);
 +      spca506_WriteI2c(gspca_dev, 0x05, 0x61);
 +      spca506_WriteI2c(gspca_dev, 0x9f, 0x62);
 +      PDEBUG(D_STREAM, "** Close Init *");
 +      return 0;
 +}
 +
 +static int sd_start(struct gspca_dev *gspca_dev)
 +{
 +      struct usb_device *dev = gspca_dev->dev;
 +      __u16 norme;
 +      __u16 channel;
 +
 +      /**************************************/
 +      reg_w(dev, 0x03, 0x00, 0x0004);
 +      reg_w(dev, 0x03, 0x00, 0x0003);
 +      reg_w(dev, 0x03, 0x00, 0x0004);
 +      reg_w(dev, 0x03, 0xFF, 0x0003);
 +      reg_w(dev, 0x02, 0x00, 0x0000);
 +      reg_w(dev, 0x03, 0x60, 0x0000);
 +      reg_w(dev, 0x03, 0x18, 0x0001);
 +
 +      /*sdca506_WriteI2c(value,register) */
 +      spca506_Initi2c(gspca_dev);
 +      spca506_WriteI2c(gspca_dev, 0x08, 0x01);        /* Increment Delay */
 +/*    spca506_WriteI2c(gspca_dev, 0xc0, 0x02); * Analog Input Control 1 */
 +      spca506_WriteI2c(gspca_dev, 0x33, 0x03);
 +                                              /* Analog Input Control 2 */
 +      spca506_WriteI2c(gspca_dev, 0x00, 0x04);
 +                                              /* Analog Input Control 3 */
 +      spca506_WriteI2c(gspca_dev, 0x00, 0x05);
 +                                              /* Analog Input Control 4 */
 +      spca506_WriteI2c(gspca_dev, 0x0d, 0x06);
 +                                      /* Horizontal Sync Start 0xe9-0x0d */
 +      spca506_WriteI2c(gspca_dev, 0xf0, 0x07);
 +                                      /* Horizontal Sync Stop  0x0d-0xf0 */
 +
 +      spca506_WriteI2c(gspca_dev, 0x98, 0x08);        /* Sync Control */
 +/*            Defaults value                  */
 +      spca506_WriteI2c(gspca_dev, 0x03, 0x09);        /* Luminance Control */
 +      spca506_WriteI2c(gspca_dev, 0x80, 0x0a);
 +                                              /* Luminance Brightness */
 +      spca506_WriteI2c(gspca_dev, 0x47, 0x0b);        /* Luminance Contrast */
 +      spca506_WriteI2c(gspca_dev, 0x48, 0x0c);
 +                                              /* Chrominance Saturation */
 +      spca506_WriteI2c(gspca_dev, 0x00, 0x0d);
 +                                              /* Chrominance Hue Control */
 +      spca506_WriteI2c(gspca_dev, 0x2a, 0x0f);
 +                                              /* Chrominance Gain Control */
 +      /**************************************/
 +      spca506_WriteI2c(gspca_dev, 0x00, 0x10);
 +                                              /* Format/Delay Control */
 +      spca506_WriteI2c(gspca_dev, 0x0c, 0x11);        /* Output Control 1 */
 +      spca506_WriteI2c(gspca_dev, 0xb8, 0x12);        /* Output Control 2 */
 +      spca506_WriteI2c(gspca_dev, 0x01, 0x13);        /* Output Control 3 */
 +      spca506_WriteI2c(gspca_dev, 0x00, 0x14);        /* reserved */
 +      spca506_WriteI2c(gspca_dev, 0x00, 0x15);        /* VGATE START */
 +      spca506_WriteI2c(gspca_dev, 0x00, 0x16);        /* VGATE STOP */
 +      spca506_WriteI2c(gspca_dev, 0x00, 0x17);    /* VGATE Control (MSB) */
 +      spca506_WriteI2c(gspca_dev, 0x00, 0x18);
 +      spca506_WriteI2c(gspca_dev, 0x00, 0x19);
 +      spca506_WriteI2c(gspca_dev, 0x00, 0x1a);
 +      spca506_WriteI2c(gspca_dev, 0x00, 0x1b);
 +      spca506_WriteI2c(gspca_dev, 0x00, 0x1c);
 +      spca506_WriteI2c(gspca_dev, 0x00, 0x1d);
 +      spca506_WriteI2c(gspca_dev, 0x00, 0x1e);
 +      spca506_WriteI2c(gspca_dev, 0xa1, 0x1f);
 +      spca506_WriteI2c(gspca_dev, 0x02, 0x40);
 +      spca506_WriteI2c(gspca_dev, 0xff, 0x41);
 +      spca506_WriteI2c(gspca_dev, 0xff, 0x42);
 +      spca506_WriteI2c(gspca_dev, 0xff, 0x43);
 +      spca506_WriteI2c(gspca_dev, 0xff, 0x44);
 +      spca506_WriteI2c(gspca_dev, 0xff, 0x45);
 +      spca506_WriteI2c(gspca_dev, 0xff, 0x46);
 +      spca506_WriteI2c(gspca_dev, 0xff, 0x47);
 +      spca506_WriteI2c(gspca_dev, 0xff, 0x48);
 +      spca506_WriteI2c(gspca_dev, 0xff, 0x49);
 +      spca506_WriteI2c(gspca_dev, 0xff, 0x4a);
 +      spca506_WriteI2c(gspca_dev, 0xff, 0x4b);
 +      spca506_WriteI2c(gspca_dev, 0xff, 0x4c);
 +      spca506_WriteI2c(gspca_dev, 0xff, 0x4d);
 +      spca506_WriteI2c(gspca_dev, 0xff, 0x4e);
 +      spca506_WriteI2c(gspca_dev, 0xff, 0x4f);
 +      spca506_WriteI2c(gspca_dev, 0xff, 0x50);
 +      spca506_WriteI2c(gspca_dev, 0xff, 0x51);
 +      spca506_WriteI2c(gspca_dev, 0xff, 0x52);
 +      spca506_WriteI2c(gspca_dev, 0xff, 0x53);
 +      spca506_WriteI2c(gspca_dev, 0xff, 0x54);
 +      spca506_WriteI2c(gspca_dev, 0xff, 0x55);
 +      spca506_WriteI2c(gspca_dev, 0xff, 0x56);
 +      spca506_WriteI2c(gspca_dev, 0xff, 0x57);
 +      spca506_WriteI2c(gspca_dev, 0x00, 0x58);
 +      spca506_WriteI2c(gspca_dev, 0x54, 0x59);
 +      spca506_WriteI2c(gspca_dev, 0x07, 0x5a);
 +      spca506_WriteI2c(gspca_dev, 0x83, 0x5b);
 +      spca506_WriteI2c(gspca_dev, 0x00, 0x5c);
 +      spca506_WriteI2c(gspca_dev, 0x00, 0x5d);
 +      spca506_WriteI2c(gspca_dev, 0x00, 0x5e);
 +      spca506_WriteI2c(gspca_dev, 0x00, 0x5f);
 +      spca506_WriteI2c(gspca_dev, 0x00, 0x60);
 +      spca506_WriteI2c(gspca_dev, 0x05, 0x61);
 +      spca506_WriteI2c(gspca_dev, 0x9f, 0x62);
 +      /**************************************/
 +      reg_w(dev, 0x05, 0x00, 0x0003);
 +      reg_w(dev, 0x05, 0x00, 0x0004);
 +      reg_w(dev, 0x03, 0x10, 0x0001);
 +      reg_w(dev, 0x03, 0x78, 0x0000);
 +      switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) {
 +      case 0:
 +              spca506_Setsize(gspca_dev, 0, 0x10, 0x10);
 +              break;
 +      case 1:
 +              spca506_Setsize(gspca_dev, 1, 0x1a, 0x1a);
 +              break;
 +      case 2:
 +              spca506_Setsize(gspca_dev, 2, 0x1c, 0x1c);
 +              break;
 +      case 4:
 +              spca506_Setsize(gspca_dev, 4, 0x34, 0x34);
 +              break;
 +      default:
 +/*    case 5: */
 +              spca506_Setsize(gspca_dev, 5, 0x40, 0x40);
 +              break;
 +      }
 +
 +      /* compress setting and size */
 +      /* set i2c luma */
 +      reg_w(dev, 0x02, 0x01, 0x0000);
 +      reg_w(dev, 0x03, 0x12, 0x0000);
 +      reg_r(gspca_dev, 0x04, 0x0001, 2);
 +      PDEBUG(D_STREAM, "webcam started");
 +      spca506_GetNormeInput(gspca_dev, &norme, &channel);
 +      spca506_SetNormeInput(gspca_dev, norme, channel);
 +      return 0;
 +}
 +
 +static void sd_stopN(struct gspca_dev *gspca_dev)
 +{
 +      struct usb_device *dev = gspca_dev->dev;
 +
 +      reg_w(dev, 0x02, 0x00, 0x0000);
 +      reg_w(dev, 0x03, 0x00, 0x0004);
 +      reg_w(dev, 0x03, 0x00, 0x0003);
 +}
 +
 +static void sd_pkt_scan(struct gspca_dev *gspca_dev,
 +                      u8 *data,                       /* isoc packet */
 +                      int len)                        /* iso packet length */
 +{
 +      switch (data[0]) {
 +      case 0:                         /* start of frame */
 +              gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
 +              data += SPCA50X_OFFSET_DATA;
 +              len -= SPCA50X_OFFSET_DATA;
 +              gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
 +              break;
 +      case 0xff:                      /* drop */
 +/*            gspca_dev->last_packet_type = DISCARD_PACKET; */
 +              break;
 +      default:
 +              data += 1;
 +              len -= 1;
 +              gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
 +              break;
 +      }
 +}
 +
 +static void setbrightness(struct gspca_dev *gspca_dev, s32 val)
 +{
 +      spca506_Initi2c(gspca_dev);
 +      spca506_WriteI2c(gspca_dev, val, SAA7113_bright);
 +      spca506_WriteI2c(gspca_dev, 0x01, 0x09);
 +}
 +
 +static void setcontrast(struct gspca_dev *gspca_dev, s32 val)
 +{
 +      spca506_Initi2c(gspca_dev);
 +      spca506_WriteI2c(gspca_dev, val, SAA7113_contrast);
 +      spca506_WriteI2c(gspca_dev, 0x01, 0x09);
 +}
 +
 +static void setcolors(struct gspca_dev *gspca_dev, s32 val)
 +{
 +      spca506_Initi2c(gspca_dev);
 +      spca506_WriteI2c(gspca_dev, val, SAA7113_saturation);
 +      spca506_WriteI2c(gspca_dev, 0x01, 0x09);
 +}
 +
 +static void sethue(struct gspca_dev *gspca_dev, s32 val)
 +{
 +      spca506_Initi2c(gspca_dev);
 +      spca506_WriteI2c(gspca_dev, val, SAA7113_hue);
 +      spca506_WriteI2c(gspca_dev, 0x01, 0x09);
 +}
 +
 +static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
 +{
 +      struct gspca_dev *gspca_dev =
 +              container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
 +
 +      gspca_dev->usb_err = 0;
 +
 +      if (!gspca_dev->streaming)
 +              return 0;
 +
 +      switch (ctrl->id) {
 +      case V4L2_CID_BRIGHTNESS:
 +              setbrightness(gspca_dev, ctrl->val);
 +              break;
 +      case V4L2_CID_CONTRAST:
 +              setcontrast(gspca_dev, ctrl->val);
 +              break;
 +      case V4L2_CID_SATURATION:
 +              setcolors(gspca_dev, ctrl->val);
 +              break;
 +      case V4L2_CID_HUE:
 +              sethue(gspca_dev, ctrl->val);
 +              break;
 +      }
 +      return gspca_dev->usb_err;
 +}
 +
 +static const struct v4l2_ctrl_ops sd_ctrl_ops = {
 +      .s_ctrl = sd_s_ctrl,
 +};
 +
 +static int sd_init_controls(struct gspca_dev *gspca_dev)
 +{
 +      struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
 +
 +      gspca_dev->vdev.ctrl_handler = hdl;
 +      v4l2_ctrl_handler_init(hdl, 4);
 +      v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
 +                      V4L2_CID_BRIGHTNESS, 0, 255, 1, 128);
 +      v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
 +                      V4L2_CID_CONTRAST, 0, 255, 1, 0x47);
 +      v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
 +                      V4L2_CID_SATURATION, 0, 255, 1, 0x40);
 +      v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
 +                      V4L2_CID_HUE, 0, 255, 1, 0);
 +
 +      if (hdl->error) {
 +              pr_err("Could not initialize controls\n");
 +              return hdl->error;
 +      }
 +      return 0;
 +}
 +
 +/* sub-driver description */
 +static const struct sd_desc sd_desc = {
 +      .name = MODULE_NAME,
 +      .config = sd_config,
 +      .init = sd_init,
 +      .init_controls = sd_init_controls,
 +      .start = sd_start,
 +      .stopN = sd_stopN,
 +      .pkt_scan = sd_pkt_scan,
 +};
 +
 +/* -- module initialisation -- */
++static const struct usb_device_id device_table[] = {
 +      {USB_DEVICE(0x06e1, 0xa190)},
 +/*fixme: may be IntelPCCameraPro BRIDGE_SPCA505
 +      {USB_DEVICE(0x0733, 0x0430)}, */
 +      {USB_DEVICE(0x0734, 0x043b)},
 +      {USB_DEVICE(0x99fa, 0x8988)},
 +      {}
 +};
 +MODULE_DEVICE_TABLE(usb, device_table);
 +
 +/* -- device connect -- */
 +static int __devinit sd_probe(struct usb_interface *intf,
 +                      const struct usb_device_id *id)
 +{
 +      return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
 +                              THIS_MODULE);
 +}
 +
 +static struct usb_driver sd_driver = {
 +      .name = MODULE_NAME,
 +      .id_table = device_table,
 +      .probe = sd_probe,
 +      .disconnect = gspca_disconnect,
 +#ifdef CONFIG_PM
 +      .suspend = gspca_suspend,
 +      .resume = gspca_resume,
 +      .reset_resume = gspca_resume,
 +#endif
 +};
 +
 +module_usb_driver(sd_driver);
index 664e460f247b5a7d1d53629959da7705fb259aa1,0000000000000000000000000000000000000000..aac622200e99a9e244523b8ead9994d510cac891
mode 100644,000000..100644
--- /dev/null
@@@ -1,568 -1,0 +1,568 @@@
- static const struct usb_device_id smsusb_id_table[] __devinitconst = {
 +/****************************************************************
 +
 +Siano Mobile Silicon, Inc.
 +MDTV receiver kernel modules.
 +Copyright (C) 2005-2009, Uri Shkolnik, Anatoly Greenblat
 +
 +This program is free software: you can redistribute it and/or modify
 +it under the terms of the GNU General Public License as published by
 +the Free Software Foundation, either version 2 of the License, or
 +(at your option) any later version.
 +
 + This program is distributed in the hope that it will be useful,
 +but WITHOUT ANY WARRANTY; without even the implied warranty of
 +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 +GNU General Public License for more details.
 +
 +You should have received a copy of the GNU General Public License
 +along with this program.  If not, see <http://www.gnu.org/licenses/>.
 +
 +****************************************************************/
 +
 +#include <linux/kernel.h>
 +#include <linux/init.h>
 +#include <linux/usb.h>
 +#include <linux/firmware.h>
 +#include <linux/slab.h>
 +#include <linux/module.h>
 +
 +#include "smscoreapi.h"
 +#include "sms-cards.h"
 +#include "smsendian.h"
 +
 +static int sms_dbg;
 +module_param_named(debug, sms_dbg, int, 0644);
 +MODULE_PARM_DESC(debug, "set debug level (info=1, adv=2 (or-able))");
 +
 +#define USB1_BUFFER_SIZE              0x1000
 +#define USB2_BUFFER_SIZE              0x4000
 +
 +#define MAX_BUFFERS           50
 +#define MAX_URBS              10
 +
 +struct smsusb_device_t;
 +
 +struct smsusb_urb_t {
 +      struct smscore_buffer_t *cb;
 +      struct smsusb_device_t  *dev;
 +
 +      struct urb urb;
 +};
 +
 +struct smsusb_device_t {
 +      struct usb_device *udev;
 +      struct smscore_device_t *coredev;
 +
 +      struct smsusb_urb_t     surbs[MAX_URBS];
 +
 +      int             response_alignment;
 +      int             buffer_size;
 +};
 +
 +static int smsusb_submit_urb(struct smsusb_device_t *dev,
 +                           struct smsusb_urb_t *surb);
 +
 +static void smsusb_onresponse(struct urb *urb)
 +{
 +      struct smsusb_urb_t *surb = (struct smsusb_urb_t *) urb->context;
 +      struct smsusb_device_t *dev = surb->dev;
 +
 +      if (urb->status == -ESHUTDOWN) {
 +              sms_err("error, urb status %d (-ESHUTDOWN), %d bytes",
 +                      urb->status, urb->actual_length);
 +              return;
 +      }
 +
 +      if ((urb->actual_length > 0) && (urb->status == 0)) {
 +              struct SmsMsgHdr_ST *phdr = (struct SmsMsgHdr_ST *)surb->cb->p;
 +
 +              smsendian_handle_message_header(phdr);
 +              if (urb->actual_length >= phdr->msgLength) {
 +                      surb->cb->size = phdr->msgLength;
 +
 +                      if (dev->response_alignment &&
 +                          (phdr->msgFlags & MSG_HDR_FLAG_SPLIT_MSG)) {
 +
 +                              surb->cb->offset =
 +                                      dev->response_alignment +
 +                                      ((phdr->msgFlags >> 8) & 3);
 +
 +                              /* sanity check */
 +                              if (((int) phdr->msgLength +
 +                                   surb->cb->offset) > urb->actual_length) {
 +                                      sms_err("invalid response "
 +                                              "msglen %d offset %d "
 +                                              "size %d",
 +                                              phdr->msgLength,
 +                                              surb->cb->offset,
 +                                              urb->actual_length);
 +                                      goto exit_and_resubmit;
 +                              }
 +
 +                              /* move buffer pointer and
 +                               * copy header to its new location */
 +                              memcpy((char *) phdr + surb->cb->offset,
 +                                     phdr, sizeof(struct SmsMsgHdr_ST));
 +                      } else
 +                              surb->cb->offset = 0;
 +
 +                      smscore_onresponse(dev->coredev, surb->cb);
 +                      surb->cb = NULL;
 +              } else {
 +                      sms_err("invalid response "
 +                              "msglen %d actual %d",
 +                              phdr->msgLength, urb->actual_length);
 +              }
 +      } else
 +              sms_err("error, urb status %d, %d bytes",
 +                      urb->status, urb->actual_length);
 +
 +
 +exit_and_resubmit:
 +      smsusb_submit_urb(dev, surb);
 +}
 +
 +static int smsusb_submit_urb(struct smsusb_device_t *dev,
 +                           struct smsusb_urb_t *surb)
 +{
 +      if (!surb->cb) {
 +              surb->cb = smscore_getbuffer(dev->coredev);
 +              if (!surb->cb) {
 +                      sms_err("smscore_getbuffer(...) returned NULL");
 +                      return -ENOMEM;
 +              }
 +      }
 +
 +      usb_fill_bulk_urb(
 +              &surb->urb,
 +              dev->udev,
 +              usb_rcvbulkpipe(dev->udev, 0x81),
 +              surb->cb->p,
 +              dev->buffer_size,
 +              smsusb_onresponse,
 +              surb
 +      );
 +      surb->urb.transfer_dma = surb->cb->phys;
 +      surb->urb.transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
 +
 +      return usb_submit_urb(&surb->urb, GFP_ATOMIC);
 +}
 +
 +static void smsusb_stop_streaming(struct smsusb_device_t *dev)
 +{
 +      int i;
 +
 +      for (i = 0; i < MAX_URBS; i++) {
 +              usb_kill_urb(&dev->surbs[i].urb);
 +
 +              if (dev->surbs[i].cb) {
 +                      smscore_putbuffer(dev->coredev, dev->surbs[i].cb);
 +                      dev->surbs[i].cb = NULL;
 +              }
 +      }
 +}
 +
 +static int smsusb_start_streaming(struct smsusb_device_t *dev)
 +{
 +      int i, rc;
 +
 +      for (i = 0; i < MAX_URBS; i++) {
 +              rc = smsusb_submit_urb(dev, &dev->surbs[i]);
 +              if (rc < 0) {
 +                      sms_err("smsusb_submit_urb(...) failed");
 +                      smsusb_stop_streaming(dev);
 +                      break;
 +              }
 +      }
 +
 +      return rc;
 +}
 +
 +static int smsusb_sendrequest(void *context, void *buffer, size_t size)
 +{
 +      struct smsusb_device_t *dev = (struct smsusb_device_t *) context;
 +      int dummy;
 +
 +      smsendian_handle_message_header((struct SmsMsgHdr_ST *)buffer);
 +      return usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, 2),
 +                          buffer, size, &dummy, 1000);
 +}
 +
 +static char *smsusb1_fw_lkup[] = {
 +      "dvbt_stellar_usb.inp",
 +      "dvbh_stellar_usb.inp",
 +      "tdmb_stellar_usb.inp",
 +      "none",
 +      "dvbt_bda_stellar_usb.inp",
 +};
 +
 +static inline char *sms_get_fw_name(int mode, int board_id)
 +{
 +      char **fw = sms_get_board(board_id)->fw;
 +      return (fw && fw[mode]) ? fw[mode] : smsusb1_fw_lkup[mode];
 +}
 +
 +static int smsusb1_load_firmware(struct usb_device *udev, int id, int board_id)
 +{
 +      const struct firmware *fw;
 +      u8 *fw_buffer;
 +      int rc, dummy;
 +      char *fw_filename;
 +
 +      if (id < DEVICE_MODE_DVBT || id > DEVICE_MODE_DVBT_BDA) {
 +              sms_err("invalid firmware id specified %d", id);
 +              return -EINVAL;
 +      }
 +
 +      fw_filename = sms_get_fw_name(id, board_id);
 +
 +      rc = request_firmware(&fw, fw_filename, &udev->dev);
 +      if (rc < 0) {
 +              sms_warn("failed to open \"%s\" mode %d, "
 +                       "trying again with default firmware", fw_filename, id);
 +
 +              fw_filename = smsusb1_fw_lkup[id];
 +              rc = request_firmware(&fw, fw_filename, &udev->dev);
 +              if (rc < 0) {
 +                      sms_warn("failed to open \"%s\" mode %d",
 +                               fw_filename, id);
 +
 +                      return rc;
 +              }
 +      }
 +
 +      fw_buffer = kmalloc(fw->size, GFP_KERNEL);
 +      if (fw_buffer) {
 +              memcpy(fw_buffer, fw->data, fw->size);
 +
 +              rc = usb_bulk_msg(udev, usb_sndbulkpipe(udev, 2),
 +                                fw_buffer, fw->size, &dummy, 1000);
 +
 +              sms_info("sent %zd(%d) bytes, rc %d", fw->size, dummy, rc);
 +
 +              kfree(fw_buffer);
 +      } else {
 +              sms_err("failed to allocate firmware buffer");
 +              rc = -ENOMEM;
 +      }
 +      sms_info("read FW %s, size=%zd", fw_filename, fw->size);
 +
 +      release_firmware(fw);
 +
 +      return rc;
 +}
 +
 +static void smsusb1_detectmode(void *context, int *mode)
 +{
 +      char *product_string =
 +              ((struct smsusb_device_t *) context)->udev->product;
 +
 +      *mode = DEVICE_MODE_NONE;
 +
 +      if (!product_string) {
 +              product_string = "none";
 +              sms_err("product string not found");
 +      } else if (strstr(product_string, "DVBH"))
 +              *mode = 1;
 +      else if (strstr(product_string, "BDA"))
 +              *mode = 4;
 +      else if (strstr(product_string, "DVBT"))
 +              *mode = 0;
 +      else if (strstr(product_string, "TDMB"))
 +              *mode = 2;
 +
 +      sms_info("%d \"%s\"", *mode, product_string);
 +}
 +
 +static int smsusb1_setmode(void *context, int mode)
 +{
 +      struct SmsMsgHdr_ST Msg = { MSG_SW_RELOAD_REQ, 0, HIF_TASK,
 +                           sizeof(struct SmsMsgHdr_ST), 0 };
 +
 +      if (mode < DEVICE_MODE_DVBT || mode > DEVICE_MODE_DVBT_BDA) {
 +              sms_err("invalid firmware id specified %d", mode);
 +              return -EINVAL;
 +      }
 +
 +      return smsusb_sendrequest(context, &Msg, sizeof(Msg));
 +}
 +
 +static void smsusb_term_device(struct usb_interface *intf)
 +{
 +      struct smsusb_device_t *dev = usb_get_intfdata(intf);
 +
 +      if (dev) {
 +              smsusb_stop_streaming(dev);
 +
 +              /* unregister from smscore */
 +              if (dev->coredev)
 +                      smscore_unregister_device(dev->coredev);
 +
 +              sms_info("device %p destroyed", dev);
 +              kfree(dev);
 +      }
 +
 +      usb_set_intfdata(intf, NULL);
 +}
 +
 +static int smsusb_init_device(struct usb_interface *intf, int board_id)
 +{
 +      struct smsdevice_params_t params;
 +      struct smsusb_device_t *dev;
 +      int i, rc;
 +
 +      /* create device object */
 +      dev = kzalloc(sizeof(struct smsusb_device_t), GFP_KERNEL);
 +      if (!dev) {
 +              sms_err("kzalloc(sizeof(struct smsusb_device_t) failed");
 +              return -ENOMEM;
 +      }
 +
 +      memset(&params, 0, sizeof(params));
 +      usb_set_intfdata(intf, dev);
 +      dev->udev = interface_to_usbdev(intf);
 +
 +      params.device_type = sms_get_board(board_id)->type;
 +
 +      switch (params.device_type) {
 +      case SMS_STELLAR:
 +              dev->buffer_size = USB1_BUFFER_SIZE;
 +
 +              params.setmode_handler = smsusb1_setmode;
 +              params.detectmode_handler = smsusb1_detectmode;
 +              break;
 +      default:
 +              sms_err("Unspecified sms device type!");
 +              /* fall-thru */
 +      case SMS_NOVA_A0:
 +      case SMS_NOVA_B0:
 +      case SMS_VEGA:
 +              dev->buffer_size = USB2_BUFFER_SIZE;
 +              dev->response_alignment =
 +                  le16_to_cpu(dev->udev->ep_in[1]->desc.wMaxPacketSize) -
 +                  sizeof(struct SmsMsgHdr_ST);
 +
 +              params.flags |= SMS_DEVICE_FAMILY2;
 +              break;
 +      }
 +
 +      params.device = &dev->udev->dev;
 +      params.buffer_size = dev->buffer_size;
 +      params.num_buffers = MAX_BUFFERS;
 +      params.sendrequest_handler = smsusb_sendrequest;
 +      params.context = dev;
 +      usb_make_path(dev->udev, params.devpath, sizeof(params.devpath));
 +
 +      /* register in smscore */
 +      rc = smscore_register_device(&params, &dev->coredev);
 +      if (rc < 0) {
 +              sms_err("smscore_register_device(...) failed, rc %d", rc);
 +              smsusb_term_device(intf);
 +              return rc;
 +      }
 +
 +      smscore_set_board_id(dev->coredev, board_id);
 +
 +      /* initialize urbs */
 +      for (i = 0; i < MAX_URBS; i++) {
 +              dev->surbs[i].dev = dev;
 +              usb_init_urb(&dev->surbs[i].urb);
 +      }
 +
 +      sms_info("smsusb_start_streaming(...).");
 +      rc = smsusb_start_streaming(dev);
 +      if (rc < 0) {
 +              sms_err("smsusb_start_streaming(...) failed");
 +              smsusb_term_device(intf);
 +              return rc;
 +      }
 +
 +      rc = smscore_start_device(dev->coredev);
 +      if (rc < 0) {
 +              sms_err("smscore_start_device(...) failed");
 +              smsusb_term_device(intf);
 +              return rc;
 +      }
 +
 +      sms_info("device %p created", dev);
 +
 +      return rc;
 +}
 +
 +static int __devinit smsusb_probe(struct usb_interface *intf,
 +                      const struct usb_device_id *id)
 +{
 +      struct usb_device *udev = interface_to_usbdev(intf);
 +      char devpath[32];
 +      int i, rc;
 +
 +      rc = usb_clear_halt(udev, usb_rcvbulkpipe(udev, 0x81));
 +      rc = usb_clear_halt(udev, usb_rcvbulkpipe(udev, 0x02));
 +
 +      if (intf->num_altsetting > 0) {
 +              rc = usb_set_interface(
 +                      udev, intf->cur_altsetting->desc.bInterfaceNumber, 0);
 +              if (rc < 0) {
 +                      sms_err("usb_set_interface failed, rc %d", rc);
 +                      return rc;
 +              }
 +      }
 +
 +      sms_info("smsusb_probe %d",
 +             intf->cur_altsetting->desc.bInterfaceNumber);
 +      for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++)
 +              sms_info("endpoint %d %02x %02x %d", i,
 +                     intf->cur_altsetting->endpoint[i].desc.bEndpointAddress,
 +                     intf->cur_altsetting->endpoint[i].desc.bmAttributes,
 +                     intf->cur_altsetting->endpoint[i].desc.wMaxPacketSize);
 +
 +      if ((udev->actconfig->desc.bNumInterfaces == 2) &&
 +          (intf->cur_altsetting->desc.bInterfaceNumber == 0)) {
 +              sms_err("rom interface 0 is not used");
 +              return -ENODEV;
 +      }
 +
 +      if (intf->cur_altsetting->desc.bInterfaceNumber == 1) {
 +              snprintf(devpath, sizeof(devpath), "usb\\%d-%s",
 +                       udev->bus->busnum, udev->devpath);
 +              sms_info("stellar device was found.");
 +              return smsusb1_load_firmware(
 +                              udev, smscore_registry_getmode(devpath),
 +                              id->driver_info);
 +      }
 +
 +      rc = smsusb_init_device(intf, id->driver_info);
 +      sms_info("rc %d", rc);
 +      sms_board_load_modules(id->driver_info);
 +      return rc;
 +}
 +
 +static void smsusb_disconnect(struct usb_interface *intf)
 +{
 +      smsusb_term_device(intf);
 +}
 +
 +static int smsusb_suspend(struct usb_interface *intf, pm_message_t msg)
 +{
 +      struct smsusb_device_t *dev = usb_get_intfdata(intf);
 +      printk(KERN_INFO "%s: Entering status %d.\n", __func__, msg.event);
 +      smsusb_stop_streaming(dev);
 +      return 0;
 +}
 +
 +static int smsusb_resume(struct usb_interface *intf)
 +{
 +      int rc, i;
 +      struct smsusb_device_t *dev = usb_get_intfdata(intf);
 +      struct usb_device *udev = interface_to_usbdev(intf);
 +
 +      printk(KERN_INFO "%s: Entering.\n", __func__);
 +      usb_clear_halt(udev, usb_rcvbulkpipe(udev, 0x81));
 +      usb_clear_halt(udev, usb_rcvbulkpipe(udev, 0x02));
 +
 +      for (i = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++)
 +              printk(KERN_INFO "endpoint %d %02x %02x %d\n", i,
 +                     intf->cur_altsetting->endpoint[i].desc.bEndpointAddress,
 +                     intf->cur_altsetting->endpoint[i].desc.bmAttributes,
 +                     intf->cur_altsetting->endpoint[i].desc.wMaxPacketSize);
 +
 +      if (intf->num_altsetting > 0) {
 +              rc = usb_set_interface(udev,
 +                                     intf->cur_altsetting->desc.
 +                                     bInterfaceNumber, 0);
 +              if (rc < 0) {
 +                      printk(KERN_INFO "%s usb_set_interface failed, "
 +                             "rc %d\n", __func__, rc);
 +                      return rc;
 +              }
 +      }
 +
 +      smsusb_start_streaming(dev);
 +      return 0;
 +}
 +
++static const struct usb_device_id smsusb_id_table[] = {
 +      { USB_DEVICE(0x187f, 0x0010),
 +              .driver_info = SMS1XXX_BOARD_SIANO_STELLAR },
 +      { USB_DEVICE(0x187f, 0x0100),
 +              .driver_info = SMS1XXX_BOARD_SIANO_STELLAR },
 +      { USB_DEVICE(0x187f, 0x0200),
 +              .driver_info = SMS1XXX_BOARD_SIANO_NOVA_A },
 +      { USB_DEVICE(0x187f, 0x0201),
 +              .driver_info = SMS1XXX_BOARD_SIANO_NOVA_B },
 +      { USB_DEVICE(0x187f, 0x0300),
 +              .driver_info = SMS1XXX_BOARD_SIANO_VEGA },
 +      { USB_DEVICE(0x2040, 0x1700),
 +              .driver_info = SMS1XXX_BOARD_HAUPPAUGE_CATAMOUNT },
 +      { USB_DEVICE(0x2040, 0x1800),
 +              .driver_info = SMS1XXX_BOARD_HAUPPAUGE_OKEMO_A },
 +      { USB_DEVICE(0x2040, 0x1801),
 +              .driver_info = SMS1XXX_BOARD_HAUPPAUGE_OKEMO_B },
 +      { USB_DEVICE(0x2040, 0x2000),
 +              .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD },
 +      { USB_DEVICE(0x2040, 0x2009),
 +              .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2 },
 +      { USB_DEVICE(0x2040, 0x200a),
 +              .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD },
 +      { USB_DEVICE(0x2040, 0x2010),
 +              .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD },
 +      { USB_DEVICE(0x2040, 0x2011),
 +              .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD },
 +      { USB_DEVICE(0x2040, 0x2019),
 +              .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD },
 +      { USB_DEVICE(0x2040, 0x5500),
 +              .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
 +      { USB_DEVICE(0x2040, 0x5510),
 +              .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
 +      { USB_DEVICE(0x2040, 0x5520),
 +              .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
 +      { USB_DEVICE(0x2040, 0x5530),
 +              .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
 +      { USB_DEVICE(0x2040, 0x5580),
 +              .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
 +      { USB_DEVICE(0x2040, 0x5590),
 +              .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
 +      { USB_DEVICE(0x187f, 0x0202),
 +              .driver_info = SMS1XXX_BOARD_SIANO_NICE },
 +      { USB_DEVICE(0x187f, 0x0301),
 +              .driver_info = SMS1XXX_BOARD_SIANO_VENICE },
 +      { USB_DEVICE(0x2040, 0xb900),
 +              .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
 +      { USB_DEVICE(0x2040, 0xb910),
 +              .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
 +      { USB_DEVICE(0x2040, 0xb980),
 +              .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
 +      { USB_DEVICE(0x2040, 0xb990),
 +              .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
 +      { USB_DEVICE(0x2040, 0xc000),
 +              .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
 +      { USB_DEVICE(0x2040, 0xc010),
 +              .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
 +      { USB_DEVICE(0x2040, 0xc080),
 +              .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
 +      { USB_DEVICE(0x2040, 0xc090),
 +              .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
 +      { USB_DEVICE(0x2040, 0xc0a0),
 +              .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
 +      { USB_DEVICE(0x2040, 0xf5a0),
 +              .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
 +      { } /* Terminating entry */
 +      };
 +
 +MODULE_DEVICE_TABLE(usb, smsusb_id_table);
 +
 +static struct usb_driver smsusb_driver = {
 +      .name                   = "smsusb",
 +      .probe                  = smsusb_probe,
 +      .disconnect             = smsusb_disconnect,
 +      .id_table               = smsusb_id_table,
 +
 +      .suspend                = smsusb_suspend,
 +      .resume                 = smsusb_resume,
 +};
 +
 +module_usb_driver(smsusb_driver);
 +
 +MODULE_DESCRIPTION("Driver for the Siano SMS1xxx USB dongle");
 +MODULE_AUTHOR("Siano Mobile Silicon, INC. (uris@siano-ms.com)");
 +MODULE_LICENSE("GPL");
index 9288fbd5001b26e8bcf3210ffbfc0da7891f1182,0000000000000000000000000000000000000000..5577381b5bf057357c6c4f591e4eb09269c2eb38
mode 100644,000000..100644
--- /dev/null
@@@ -1,359 -1,0 +1,360 @@@
 +/*
 + *      uvc_queue.c  --  USB Video Class driver - Buffers management
 + *
 + *      Copyright (C) 2005-2010
 + *          Laurent Pinchart (laurent.pinchart@ideasonboard.com)
 + *
 + *      This program is free software; you can redistribute it and/or modify
 + *      it under the terms of the GNU General Public License as published by
 + *      the Free Software Foundation; either version 2 of the License, or
 + *      (at your option) any later version.
 + *
 + */
 +
 +#include <linux/atomic.h>
 +#include <linux/kernel.h>
 +#include <linux/mm.h>
 +#include <linux/list.h>
 +#include <linux/module.h>
 +#include <linux/usb.h>
 +#include <linux/videodev2.h>
 +#include <linux/vmalloc.h>
 +#include <linux/wait.h>
 +#include <media/videobuf2-vmalloc.h>
 +
 +#include "uvcvideo.h"
 +
 +/* ------------------------------------------------------------------------
 + * Video buffers queue management.
 + *
 + * Video queues is initialized by uvc_queue_init(). The function performs
 + * basic initialization of the uvc_video_queue struct and never fails.
 + *
 + * Video buffers are managed by videobuf2. The driver uses a mutex to protect
 + * the videobuf2 queue operations by serializing calls to videobuf2 and a
 + * spinlock to protect the IRQ queue that holds the buffers to be processed by
 + * the driver.
 + */
 +
 +/* -----------------------------------------------------------------------------
 + * videobuf2 queue operations
 + */
 +
 +static int uvc_queue_setup(struct vb2_queue *vq, const struct v4l2_format *fmt,
 +                         unsigned int *nbuffers, unsigned int *nplanes,
 +                         unsigned int sizes[], void *alloc_ctxs[])
 +{
 +      struct uvc_video_queue *queue = vb2_get_drv_priv(vq);
 +      struct uvc_streaming *stream =
 +                      container_of(queue, struct uvc_streaming, queue);
 +
 +      if (*nbuffers > UVC_MAX_VIDEO_BUFFERS)
 +              *nbuffers = UVC_MAX_VIDEO_BUFFERS;
 +
 +      *nplanes = 1;
 +
 +      sizes[0] = stream->ctrl.dwMaxVideoFrameSize;
 +
 +      return 0;
 +}
 +
 +static int uvc_buffer_prepare(struct vb2_buffer *vb)
 +{
 +      struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue);
 +      struct uvc_buffer *buf = container_of(vb, struct uvc_buffer, buf);
 +
 +      if (vb->v4l2_buf.type == V4L2_BUF_TYPE_VIDEO_OUTPUT &&
 +          vb2_get_plane_payload(vb, 0) > vb2_plane_size(vb, 0)) {
 +              uvc_trace(UVC_TRACE_CAPTURE, "[E] Bytes used out of bounds.\n");
 +              return -EINVAL;
 +      }
 +
 +      if (unlikely(queue->flags & UVC_QUEUE_DISCONNECTED))
 +              return -ENODEV;
 +
 +      buf->state = UVC_BUF_STATE_QUEUED;
 +      buf->error = 0;
 +      buf->mem = vb2_plane_vaddr(vb, 0);
 +      buf->length = vb2_plane_size(vb, 0);
 +      if (vb->v4l2_buf.type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
 +              buf->bytesused = 0;
 +      else
 +              buf->bytesused = vb2_get_plane_payload(vb, 0);
 +
 +      return 0;
 +}
 +
 +static void uvc_buffer_queue(struct vb2_buffer *vb)
 +{
 +      struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue);
 +      struct uvc_buffer *buf = container_of(vb, struct uvc_buffer, buf);
 +      unsigned long flags;
 +
 +      spin_lock_irqsave(&queue->irqlock, flags);
 +      if (likely(!(queue->flags & UVC_QUEUE_DISCONNECTED))) {
 +              list_add_tail(&buf->queue, &queue->irqqueue);
 +      } else {
 +              /* If the device is disconnected return the buffer to userspace
 +               * directly. The next QBUF call will fail with -ENODEV.
 +               */
 +              buf->state = UVC_BUF_STATE_ERROR;
 +              vb2_buffer_done(&buf->buf, VB2_BUF_STATE_ERROR);
 +      }
 +
 +      spin_unlock_irqrestore(&queue->irqlock, flags);
 +}
 +
 +static int uvc_buffer_finish(struct vb2_buffer *vb)
 +{
 +      struct uvc_video_queue *queue = vb2_get_drv_priv(vb->vb2_queue);
 +      struct uvc_streaming *stream =
 +                      container_of(queue, struct uvc_streaming, queue);
 +      struct uvc_buffer *buf = container_of(vb, struct uvc_buffer, buf);
 +
 +      uvc_video_clock_update(stream, &vb->v4l2_buf, buf);
 +      return 0;
 +}
 +
 +static struct vb2_ops uvc_queue_qops = {
 +      .queue_setup = uvc_queue_setup,
 +      .buf_prepare = uvc_buffer_prepare,
 +      .buf_queue = uvc_buffer_queue,
 +      .buf_finish = uvc_buffer_finish,
 +};
 +
 +void uvc_queue_init(struct uvc_video_queue *queue, enum v4l2_buf_type type,
 +                  int drop_corrupted)
 +{
 +      queue->queue.type = type;
 +      queue->queue.io_modes = VB2_MMAP | VB2_USERPTR;
 +      queue->queue.drv_priv = queue;
 +      queue->queue.buf_struct_size = sizeof(struct uvc_buffer);
 +      queue->queue.ops = &uvc_queue_qops;
 +      queue->queue.mem_ops = &vb2_vmalloc_memops;
 +      vb2_queue_init(&queue->queue);
 +
 +      mutex_init(&queue->mutex);
 +      spin_lock_init(&queue->irqlock);
 +      INIT_LIST_HEAD(&queue->irqqueue);
 +      queue->flags = drop_corrupted ? UVC_QUEUE_DROP_CORRUPTED : 0;
 +}
 +
 +/* -----------------------------------------------------------------------------
 + * V4L2 queue operations
 + */
 +
 +int uvc_alloc_buffers(struct uvc_video_queue *queue,
 +                    struct v4l2_requestbuffers *rb)
 +{
 +      int ret;
 +
 +      mutex_lock(&queue->mutex);
 +      ret = vb2_reqbufs(&queue->queue, rb);
 +      mutex_unlock(&queue->mutex);
 +
 +      return ret ? ret : rb->count;
 +}
 +
 +void uvc_free_buffers(struct uvc_video_queue *queue)
 +{
 +      mutex_lock(&queue->mutex);
 +      vb2_queue_release(&queue->queue);
 +      mutex_unlock(&queue->mutex);
 +}
 +
 +int uvc_query_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf)
 +{
 +      int ret;
 +
 +      mutex_lock(&queue->mutex);
 +      ret = vb2_querybuf(&queue->queue, buf);
 +      mutex_unlock(&queue->mutex);
 +
 +      return ret;
 +}
 +
 +int uvc_queue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf)
 +{
 +      int ret;
 +
 +      mutex_lock(&queue->mutex);
 +      ret = vb2_qbuf(&queue->queue, buf);
 +      mutex_unlock(&queue->mutex);
 +
 +      return ret;
 +}
 +
 +int uvc_dequeue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf,
 +                     int nonblocking)
 +{
 +      int ret;
 +
 +      mutex_lock(&queue->mutex);
 +      ret = vb2_dqbuf(&queue->queue, buf, nonblocking);
 +      mutex_unlock(&queue->mutex);
 +
 +      return ret;
 +}
 +
 +int uvc_queue_mmap(struct uvc_video_queue *queue, struct vm_area_struct *vma)
 +{
 +      int ret;
 +
 +      mutex_lock(&queue->mutex);
 +      ret = vb2_mmap(&queue->queue, vma);
 +      mutex_unlock(&queue->mutex);
 +
 +      return ret;
 +}
 +
 +#ifndef CONFIG_MMU
 +unsigned long uvc_queue_get_unmapped_area(struct uvc_video_queue *queue,
 +              unsigned long pgoff)
 +{
 +      unsigned long ret;
 +
 +      mutex_lock(&queue->mutex);
 +      ret = vb2_get_unmapped_area(&queue->queue, 0, 0, pgoff, 0);
 +      mutex_unlock(&queue->mutex);
 +      return ret;
 +}
 +#endif
 +
 +unsigned int uvc_queue_poll(struct uvc_video_queue *queue, struct file *file,
 +                          poll_table *wait)
 +{
 +      unsigned int ret;
 +
 +      mutex_lock(&queue->mutex);
 +      ret = vb2_poll(&queue->queue, file, wait);
 +      mutex_unlock(&queue->mutex);
 +
 +      return ret;
 +}
 +
 +/* -----------------------------------------------------------------------------
 + *
 + */
 +
 +/*
 + * Check if buffers have been allocated.
 + */
 +int uvc_queue_allocated(struct uvc_video_queue *queue)
 +{
 +      int allocated;
 +
 +      mutex_lock(&queue->mutex);
 +      allocated = vb2_is_busy(&queue->queue);
 +      mutex_unlock(&queue->mutex);
 +
 +      return allocated;
 +}
 +
 +/*
 + * Enable or disable the video buffers queue.
 + *
 + * The queue must be enabled before starting video acquisition and must be
 + * disabled after stopping it. This ensures that the video buffers queue
 + * state can be properly initialized before buffers are accessed from the
 + * interrupt handler.
 + *
 + * Enabling the video queue returns -EBUSY if the queue is already enabled.
 + *
 + * Disabling the video queue cancels the queue and removes all buffers from
 + * the main queue.
 + *
 + * This function can't be called from interrupt context. Use
 + * uvc_queue_cancel() instead.
 + */
 +int uvc_queue_enable(struct uvc_video_queue *queue, int enable)
 +{
 +      unsigned long flags;
 +      int ret;
 +
 +      mutex_lock(&queue->mutex);
 +      if (enable) {
 +              ret = vb2_streamon(&queue->queue, queue->queue.type);
 +              if (ret < 0)
 +                      goto done;
 +
 +              queue->buf_used = 0;
 +      } else {
 +              ret = vb2_streamoff(&queue->queue, queue->queue.type);
 +              if (ret < 0)
 +                      goto done;
 +
 +              spin_lock_irqsave(&queue->irqlock, flags);
 +              INIT_LIST_HEAD(&queue->irqqueue);
 +              spin_unlock_irqrestore(&queue->irqlock, flags);
 +      }
 +
 +done:
 +      mutex_unlock(&queue->mutex);
 +      return ret;
 +}
 +
 +/*
 + * Cancel the video buffers queue.
 + *
 + * Cancelling the queue marks all buffers on the irq queue as erroneous,
 + * wakes them up and removes them from the queue.
 + *
 + * If the disconnect parameter is set, further calls to uvc_queue_buffer will
 + * fail with -ENODEV.
 + *
 + * This function acquires the irq spinlock and can be called from interrupt
 + * context.
 + */
 +void uvc_queue_cancel(struct uvc_video_queue *queue, int disconnect)
 +{
 +      struct uvc_buffer *buf;
 +      unsigned long flags;
 +
 +      spin_lock_irqsave(&queue->irqlock, flags);
 +      while (!list_empty(&queue->irqqueue)) {
 +              buf = list_first_entry(&queue->irqqueue, struct uvc_buffer,
 +                                     queue);
 +              list_del(&buf->queue);
 +              buf->state = UVC_BUF_STATE_ERROR;
 +              vb2_buffer_done(&buf->buf, VB2_BUF_STATE_ERROR);
 +      }
 +      /* This must be protected by the irqlock spinlock to avoid race
 +       * conditions between uvc_buffer_queue and the disconnection event that
 +       * could result in an interruptible wait in uvc_dequeue_buffer. Do not
 +       * blindly replace this logic by checking for the UVC_QUEUE_DISCONNECTED
 +       * state outside the queue code.
 +       */
 +      if (disconnect)
 +              queue->flags |= UVC_QUEUE_DISCONNECTED;
 +      spin_unlock_irqrestore(&queue->irqlock, flags);
 +}
 +
 +struct uvc_buffer *uvc_queue_next_buffer(struct uvc_video_queue *queue,
 +              struct uvc_buffer *buf)
 +{
 +      struct uvc_buffer *nextbuf;
 +      unsigned long flags;
 +
 +      if ((queue->flags & UVC_QUEUE_DROP_CORRUPTED) && buf->error) {
 +              buf->error = 0;
 +              buf->state = UVC_BUF_STATE_QUEUED;
++              buf->bytesused = 0;
 +              vb2_set_plane_payload(&buf->buf, 0, 0);
 +              return buf;
 +      }
 +
 +      spin_lock_irqsave(&queue->irqlock, flags);
 +      list_del(&buf->queue);
 +      if (!list_empty(&queue->irqqueue))
 +              nextbuf = list_first_entry(&queue->irqqueue, struct uvc_buffer,
 +                                         queue);
 +      else
 +              nextbuf = NULL;
 +      spin_unlock_irqrestore(&queue->irqlock, flags);
 +
 +      buf->state = buf->error ? VB2_BUF_STATE_ERROR : UVC_BUF_STATE_DONE;
 +      vb2_set_plane_payload(&buf->buf, 0, buf->bytesused);
 +      vb2_buffer_done(&buf->buf, VB2_BUF_STATE_DONE);
 +
 +      return nextbuf;
 +}
index c3b7b5f59b328a8beea170977261e4d650da88b7,0000000000000000000000000000000000000000..6bc47fc82fe2f799bfff8a3f885fae27bf1a0aa0
mode 100644,000000..100644
--- /dev/null
@@@ -1,2324 -1,0 +1,2330 @@@
-       pr_cont("tuner=%u, type=%u, seek_upward=%u, wrap_around=%u, spacing=%u\n",
-               p->tuner, p->type, p->seek_upward, p->wrap_around, p->spacing);
 +/*
 + * Video capture interface for Linux version 2
 + *
 + * A generic framework to process V4L2 ioctl commands.
 + *
 + * This program is free software; you can redistribute it and/or
 + * modify it under the terms of the GNU General Public License
 + * as published by the Free Software Foundation; either version
 + * 2 of the License, or (at your option) any later version.
 + *
 + * Authors:   Alan Cox, <alan@lxorguk.ukuu.org.uk> (version 1)
 + *              Mauro Carvalho Chehab <mchehab@infradead.org> (version 2)
 + */
 +
 +#include <linux/module.h>
 +#include <linux/slab.h>
 +#include <linux/types.h>
 +#include <linux/kernel.h>
 +#include <linux/version.h>
 +
 +#include <linux/videodev2.h>
 +
 +#include <media/v4l2-common.h>
 +#include <media/v4l2-ioctl.h>
 +#include <media/v4l2-ctrls.h>
 +#include <media/v4l2-fh.h>
 +#include <media/v4l2-event.h>
 +#include <media/v4l2-device.h>
 +#include <media/v4l2-chip-ident.h>
 +#include <media/videobuf2-core.h>
 +
 +/* Zero out the end of the struct pointed to by p.  Everything after, but
 + * not including, the specified field is cleared. */
 +#define CLEAR_AFTER_FIELD(p, field) \
 +      memset((u8 *)(p) + offsetof(typeof(*(p)), field) + sizeof((p)->field), \
 +      0, sizeof(*(p)) - offsetof(typeof(*(p)), field) - sizeof((p)->field))
 +
 +struct std_descr {
 +      v4l2_std_id std;
 +      const char *descr;
 +};
 +
 +static const struct std_descr standards[] = {
 +      { V4L2_STD_NTSC,        "NTSC"      },
 +      { V4L2_STD_NTSC_M,      "NTSC-M"    },
 +      { V4L2_STD_NTSC_M_JP,   "NTSC-M-JP" },
 +      { V4L2_STD_NTSC_M_KR,   "NTSC-M-KR" },
 +      { V4L2_STD_NTSC_443,    "NTSC-443"  },
 +      { V4L2_STD_PAL,         "PAL"       },
 +      { V4L2_STD_PAL_BG,      "PAL-BG"    },
 +      { V4L2_STD_PAL_B,       "PAL-B"     },
 +      { V4L2_STD_PAL_B1,      "PAL-B1"    },
 +      { V4L2_STD_PAL_G,       "PAL-G"     },
 +      { V4L2_STD_PAL_H,       "PAL-H"     },
 +      { V4L2_STD_PAL_I,       "PAL-I"     },
 +      { V4L2_STD_PAL_DK,      "PAL-DK"    },
 +      { V4L2_STD_PAL_D,       "PAL-D"     },
 +      { V4L2_STD_PAL_D1,      "PAL-D1"    },
 +      { V4L2_STD_PAL_K,       "PAL-K"     },
 +      { V4L2_STD_PAL_M,       "PAL-M"     },
 +      { V4L2_STD_PAL_N,       "PAL-N"     },
 +      { V4L2_STD_PAL_Nc,      "PAL-Nc"    },
 +      { V4L2_STD_PAL_60,      "PAL-60"    },
 +      { V4L2_STD_SECAM,       "SECAM"     },
 +      { V4L2_STD_SECAM_B,     "SECAM-B"   },
 +      { V4L2_STD_SECAM_G,     "SECAM-G"   },
 +      { V4L2_STD_SECAM_H,     "SECAM-H"   },
 +      { V4L2_STD_SECAM_DK,    "SECAM-DK"  },
 +      { V4L2_STD_SECAM_D,     "SECAM-D"   },
 +      { V4L2_STD_SECAM_K,     "SECAM-K"   },
 +      { V4L2_STD_SECAM_K1,    "SECAM-K1"  },
 +      { V4L2_STD_SECAM_L,     "SECAM-L"   },
 +      { V4L2_STD_SECAM_LC,    "SECAM-Lc"  },
 +      { 0,                    "Unknown"   }
 +};
 +
 +/* video4linux standard ID conversion to standard name
 + */
 +const char *v4l2_norm_to_name(v4l2_std_id id)
 +{
 +      u32 myid = id;
 +      int i;
 +
 +      /* HACK: ppc32 architecture doesn't have __ucmpdi2 function to handle
 +         64 bit comparations. So, on that architecture, with some gcc
 +         variants, compilation fails. Currently, the max value is 30bit wide.
 +       */
 +      BUG_ON(myid != id);
 +
 +      for (i = 0; standards[i].std; i++)
 +              if (myid == standards[i].std)
 +                      break;
 +      return standards[i].descr;
 +}
 +EXPORT_SYMBOL(v4l2_norm_to_name);
 +
 +/* Returns frame period for the given standard */
 +void v4l2_video_std_frame_period(int id, struct v4l2_fract *frameperiod)
 +{
 +      if (id & V4L2_STD_525_60) {
 +              frameperiod->numerator = 1001;
 +              frameperiod->denominator = 30000;
 +      } else {
 +              frameperiod->numerator = 1;
 +              frameperiod->denominator = 25;
 +      }
 +}
 +EXPORT_SYMBOL(v4l2_video_std_frame_period);
 +
 +/* Fill in the fields of a v4l2_standard structure according to the
 +   'id' and 'transmission' parameters.  Returns negative on error.  */
 +int v4l2_video_std_construct(struct v4l2_standard *vs,
 +                           int id, const char *name)
 +{
 +      vs->id = id;
 +      v4l2_video_std_frame_period(id, &vs->frameperiod);
 +      vs->framelines = (id & V4L2_STD_525_60) ? 525 : 625;
 +      strlcpy(vs->name, name, sizeof(vs->name));
 +      return 0;
 +}
 +EXPORT_SYMBOL(v4l2_video_std_construct);
 +
 +/* ----------------------------------------------------------------- */
 +/* some arrays for pretty-printing debug messages of enum types      */
 +
 +const char *v4l2_field_names[] = {
 +      [V4L2_FIELD_ANY]        = "any",
 +      [V4L2_FIELD_NONE]       = "none",
 +      [V4L2_FIELD_TOP]        = "top",
 +      [V4L2_FIELD_BOTTOM]     = "bottom",
 +      [V4L2_FIELD_INTERLACED] = "interlaced",
 +      [V4L2_FIELD_SEQ_TB]     = "seq-tb",
 +      [V4L2_FIELD_SEQ_BT]     = "seq-bt",
 +      [V4L2_FIELD_ALTERNATE]  = "alternate",
 +      [V4L2_FIELD_INTERLACED_TB] = "interlaced-tb",
 +      [V4L2_FIELD_INTERLACED_BT] = "interlaced-bt",
 +};
 +EXPORT_SYMBOL(v4l2_field_names);
 +
 +const char *v4l2_type_names[] = {
 +      [V4L2_BUF_TYPE_VIDEO_CAPTURE]      = "vid-cap",
 +      [V4L2_BUF_TYPE_VIDEO_OVERLAY]      = "vid-overlay",
 +      [V4L2_BUF_TYPE_VIDEO_OUTPUT]       = "vid-out",
 +      [V4L2_BUF_TYPE_VBI_CAPTURE]        = "vbi-cap",
 +      [V4L2_BUF_TYPE_VBI_OUTPUT]         = "vbi-out",
 +      [V4L2_BUF_TYPE_SLICED_VBI_CAPTURE] = "sliced-vbi-cap",
 +      [V4L2_BUF_TYPE_SLICED_VBI_OUTPUT]  = "sliced-vbi-out",
 +      [V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY] = "vid-out-overlay",
 +      [V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE] = "vid-cap-mplane",
 +      [V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE] = "vid-out-mplane",
 +};
 +EXPORT_SYMBOL(v4l2_type_names);
 +
 +static const char *v4l2_memory_names[] = {
 +      [V4L2_MEMORY_MMAP]    = "mmap",
 +      [V4L2_MEMORY_USERPTR] = "userptr",
 +      [V4L2_MEMORY_OVERLAY] = "overlay",
 +};
 +
 +#define prt_names(a, arr) ((((a) >= 0) && ((a) < ARRAY_SIZE(arr))) ? \
 +                         arr[a] : "unknown")
 +
 +/* ------------------------------------------------------------------ */
 +/* debug help functions                                               */
 +
 +static void v4l_print_querycap(const void *arg, bool write_only)
 +{
 +      const struct v4l2_capability *p = arg;
 +
 +      pr_cont("driver=%s, card=%s, bus=%s, version=0x%08x, "
 +              "capabilities=0x%08x, device_caps=0x%08x\n",
 +              p->driver, p->card, p->bus_info,
 +              p->version, p->capabilities, p->device_caps);
 +}
 +
 +static void v4l_print_enuminput(const void *arg, bool write_only)
 +{
 +      const struct v4l2_input *p = arg;
 +
 +      pr_cont("index=%u, name=%s, type=%u, audioset=0x%x, tuner=%u, "
 +              "std=0x%08Lx, status=0x%x, capabilities=0x%x\n",
 +              p->index, p->name, p->type, p->audioset, p->tuner,
 +              (unsigned long long)p->std, p->status, p->capabilities);
 +}
 +
 +static void v4l_print_enumoutput(const void *arg, bool write_only)
 +{
 +      const struct v4l2_output *p = arg;
 +
 +      pr_cont("index=%u, name=%s, type=%u, audioset=0x%x, "
 +              "modulator=%u, std=0x%08Lx, capabilities=0x%x\n",
 +              p->index, p->name, p->type, p->audioset, p->modulator,
 +              (unsigned long long)p->std, p->capabilities);
 +}
 +
 +static void v4l_print_audio(const void *arg, bool write_only)
 +{
 +      const struct v4l2_audio *p = arg;
 +
 +      if (write_only)
 +              pr_cont("index=%u, mode=0x%x\n", p->index, p->mode);
 +      else
 +              pr_cont("index=%u, name=%s, capability=0x%x, mode=0x%x\n",
 +                      p->index, p->name, p->capability, p->mode);
 +}
 +
 +static void v4l_print_audioout(const void *arg, bool write_only)
 +{
 +      const struct v4l2_audioout *p = arg;
 +
 +      if (write_only)
 +              pr_cont("index=%u\n", p->index);
 +      else
 +              pr_cont("index=%u, name=%s, capability=0x%x, mode=0x%x\n",
 +                      p->index, p->name, p->capability, p->mode);
 +}
 +
 +static void v4l_print_fmtdesc(const void *arg, bool write_only)
 +{
 +      const struct v4l2_fmtdesc *p = arg;
 +
 +      pr_cont("index=%u, type=%s, flags=0x%x, pixelformat=%c%c%c%c, description='%s'\n",
 +              p->index, prt_names(p->type, v4l2_type_names),
 +              p->flags, (p->pixelformat & 0xff),
 +              (p->pixelformat >>  8) & 0xff,
 +              (p->pixelformat >> 16) & 0xff,
 +              (p->pixelformat >> 24) & 0xff,
 +              p->description);
 +}
 +
 +static void v4l_print_format(const void *arg, bool write_only)
 +{
 +      const struct v4l2_format *p = arg;
 +      const struct v4l2_pix_format *pix;
 +      const struct v4l2_pix_format_mplane *mp;
 +      const struct v4l2_vbi_format *vbi;
 +      const struct v4l2_sliced_vbi_format *sliced;
 +      const struct v4l2_window *win;
 +      const struct v4l2_clip *clip;
 +      unsigned i;
 +
 +      pr_cont("type=%s", prt_names(p->type, v4l2_type_names));
 +      switch (p->type) {
 +      case V4L2_BUF_TYPE_VIDEO_CAPTURE:
 +      case V4L2_BUF_TYPE_VIDEO_OUTPUT:
 +              pix = &p->fmt.pix;
 +              pr_cont(", width=%u, height=%u, "
 +                      "pixelformat=%c%c%c%c, field=%s, "
 +                      "bytesperline=%u sizeimage=%u, colorspace=%d\n",
 +                      pix->width, pix->height,
 +                      (pix->pixelformat & 0xff),
 +                      (pix->pixelformat >>  8) & 0xff,
 +                      (pix->pixelformat >> 16) & 0xff,
 +                      (pix->pixelformat >> 24) & 0xff,
 +                      prt_names(pix->field, v4l2_field_names),
 +                      pix->bytesperline, pix->sizeimage,
 +                      pix->colorspace);
 +              break;
 +      case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
 +      case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
 +              mp = &p->fmt.pix_mp;
 +              pr_cont(", width=%u, height=%u, "
 +                      "format=%c%c%c%c, field=%s, "
 +                      "colorspace=%d, num_planes=%u\n",
 +                      mp->width, mp->height,
 +                      (mp->pixelformat & 0xff),
 +                      (mp->pixelformat >>  8) & 0xff,
 +                      (mp->pixelformat >> 16) & 0xff,
 +                      (mp->pixelformat >> 24) & 0xff,
 +                      prt_names(mp->field, v4l2_field_names),
 +                      mp->colorspace, mp->num_planes);
 +              for (i = 0; i < mp->num_planes; i++)
 +                      printk(KERN_DEBUG "plane %u: bytesperline=%u sizeimage=%u\n", i,
 +                                      mp->plane_fmt[i].bytesperline,
 +                                      mp->plane_fmt[i].sizeimage);
 +              break;
 +      case V4L2_BUF_TYPE_VIDEO_OVERLAY:
 +      case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
 +              win = &p->fmt.win;
 +              pr_cont(", wxh=%dx%d, x,y=%d,%d, field=%s, "
 +                      "chromakey=0x%08x, bitmap=%p, "
 +                      "global_alpha=0x%02x\n",
 +                      win->w.width, win->w.height,
 +                      win->w.left, win->w.top,
 +                      prt_names(win->field, v4l2_field_names),
 +                      win->chromakey, win->bitmap, win->global_alpha);
 +              clip = win->clips;
 +              for (i = 0; i < win->clipcount; i++) {
 +                      printk(KERN_DEBUG "clip %u: wxh=%dx%d, x,y=%d,%d\n",
 +                                      i, clip->c.width, clip->c.height,
 +                                      clip->c.left, clip->c.top);
 +                      clip = clip->next;
 +              }
 +              break;
 +      case V4L2_BUF_TYPE_VBI_CAPTURE:
 +      case V4L2_BUF_TYPE_VBI_OUTPUT:
 +              vbi = &p->fmt.vbi;
 +              pr_cont(", sampling_rate=%u, offset=%u, samples_per_line=%u, "
 +                      "sample_format=%c%c%c%c, start=%u,%u, count=%u,%u\n",
 +                      vbi->sampling_rate, vbi->offset,
 +                      vbi->samples_per_line,
 +                      (vbi->sample_format & 0xff),
 +                      (vbi->sample_format >>  8) & 0xff,
 +                      (vbi->sample_format >> 16) & 0xff,
 +                      (vbi->sample_format >> 24) & 0xff,
 +                      vbi->start[0], vbi->start[1],
 +                      vbi->count[0], vbi->count[1]);
 +              break;
 +      case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
 +      case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
 +              sliced = &p->fmt.sliced;
 +              pr_cont(", service_set=0x%08x, io_size=%d\n",
 +                              sliced->service_set, sliced->io_size);
 +              for (i = 0; i < 24; i++)
 +                      printk(KERN_DEBUG "line[%02u]=0x%04x, 0x%04x\n", i,
 +                              sliced->service_lines[0][i],
 +                              sliced->service_lines[1][i]);
 +              break;
 +      case V4L2_BUF_TYPE_PRIVATE:
 +              pr_cont("\n");
 +              break;
 +      }
 +}
 +
 +static void v4l_print_framebuffer(const void *arg, bool write_only)
 +{
 +      const struct v4l2_framebuffer *p = arg;
 +
 +      pr_cont("capability=0x%x, flags=0x%x, base=0x%p, width=%u, "
 +              "height=%u, pixelformat=%c%c%c%c, "
 +              "bytesperline=%u sizeimage=%u, colorspace=%d\n",
 +                      p->capability, p->flags, p->base,
 +                      p->fmt.width, p->fmt.height,
 +                      (p->fmt.pixelformat & 0xff),
 +                      (p->fmt.pixelformat >>  8) & 0xff,
 +                      (p->fmt.pixelformat >> 16) & 0xff,
 +                      (p->fmt.pixelformat >> 24) & 0xff,
 +                      p->fmt.bytesperline, p->fmt.sizeimage,
 +                      p->fmt.colorspace);
 +}
 +
 +static void v4l_print_buftype(const void *arg, bool write_only)
 +{
 +      pr_cont("type=%s\n", prt_names(*(u32 *)arg, v4l2_type_names));
 +}
 +
 +static void v4l_print_modulator(const void *arg, bool write_only)
 +{
 +      const struct v4l2_modulator *p = arg;
 +
 +      if (write_only)
 +              pr_cont("index=%u, txsubchans=0x%x", p->index, p->txsubchans);
 +      else
 +              pr_cont("index=%u, name=%s, capability=0x%x, "
 +                      "rangelow=%u, rangehigh=%u, txsubchans=0x%x\n",
 +                      p->index, p->name, p->capability,
 +                      p->rangelow, p->rangehigh, p->txsubchans);
 +}
 +
 +static void v4l_print_tuner(const void *arg, bool write_only)
 +{
 +      const struct v4l2_tuner *p = arg;
 +
 +      if (write_only)
 +              pr_cont("index=%u, audmode=%u\n", p->index, p->audmode);
 +      else
 +              pr_cont("index=%u, name=%s, type=%u, capability=0x%x, "
 +                      "rangelow=%u, rangehigh=%u, signal=%u, afc=%d, "
 +                      "rxsubchans=0x%x, audmode=%u\n",
 +                      p->index, p->name, p->type,
 +                      p->capability, p->rangelow,
 +                      p->rangehigh, p->signal, p->afc,
 +                      p->rxsubchans, p->audmode);
 +}
 +
 +static void v4l_print_frequency(const void *arg, bool write_only)
 +{
 +      const struct v4l2_frequency *p = arg;
 +
 +      pr_cont("tuner=%u, type=%u, frequency=%u\n",
 +                              p->tuner, p->type, p->frequency);
 +}
 +
 +static void v4l_print_standard(const void *arg, bool write_only)
 +{
 +      const struct v4l2_standard *p = arg;
 +
 +      pr_cont("index=%u, id=0x%Lx, name=%s, fps=%u/%u, "
 +              "framelines=%u\n", p->index,
 +              (unsigned long long)p->id, p->name,
 +              p->frameperiod.numerator,
 +              p->frameperiod.denominator,
 +              p->framelines);
 +}
 +
 +static void v4l_print_std(const void *arg, bool write_only)
 +{
 +      pr_cont("std=0x%08Lx\n", *(const long long unsigned *)arg);
 +}
 +
 +static void v4l_print_hw_freq_seek(const void *arg, bool write_only)
 +{
 +      const struct v4l2_hw_freq_seek *p = arg;
 +
++      pr_cont("tuner=%u, type=%u, seek_upward=%u, wrap_around=%u, spacing=%u, "
++              "rangelow=%u, rangehigh=%u\n",
++              p->tuner, p->type, p->seek_upward, p->wrap_around, p->spacing,
++              p->rangelow, p->rangehigh);
 +}
 +
 +static void v4l_print_requestbuffers(const void *arg, bool write_only)
 +{
 +      const struct v4l2_requestbuffers *p = arg;
 +
 +      pr_cont("count=%d, type=%s, memory=%s\n",
 +              p->count,
 +              prt_names(p->type, v4l2_type_names),
 +              prt_names(p->memory, v4l2_memory_names));
 +}
 +
 +static void v4l_print_buffer(const void *arg, bool write_only)
 +{
 +      const struct v4l2_buffer *p = arg;
 +      const struct v4l2_timecode *tc = &p->timecode;
 +      const struct v4l2_plane *plane;
 +      int i;
 +
 +      pr_cont("%02ld:%02d:%02d.%08ld index=%d, type=%s, "
 +              "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),
 +                      (long)p->timestamp.tv_usec,
 +                      p->index,
 +                      prt_names(p->type, v4l2_type_names),
 +                      p->flags, prt_names(p->field, v4l2_field_names),
 +                      p->sequence, prt_names(p->memory, v4l2_memory_names));
 +
 +      if (V4L2_TYPE_IS_MULTIPLANAR(p->type) && p->m.planes) {
 +              pr_cont("\n");
 +              for (i = 0; i < p->length; ++i) {
 +                      plane = &p->m.planes[i];
 +                      printk(KERN_DEBUG
 +                              "plane %d: bytesused=%d, data_offset=0x%08x "
 +                              "offset/userptr=0x%lx, length=%d\n",
 +                              i, plane->bytesused, plane->data_offset,
 +                              plane->m.userptr, plane->length);
 +              }
 +      } else {
 +              pr_cont("bytesused=%d, offset/userptr=0x%lx, length=%d\n",
 +                      p->bytesused, p->m.userptr, p->length);
 +      }
 +
 +      printk(KERN_DEBUG "timecode=%02d:%02d:%02d type=%d, "
 +              "flags=0x%08x, frames=%d, userbits=0x%08x\n",
 +                      tc->hours, tc->minutes, tc->seconds,
 +                      tc->type, tc->flags, tc->frames, *(__u32 *)tc->userbits);
 +}
 +
 +static void v4l_print_create_buffers(const void *arg, bool write_only)
 +{
 +      const struct v4l2_create_buffers *p = arg;
 +
 +      pr_cont("index=%d, count=%d, memory=%s, ",
 +                      p->index, p->count,
 +                      prt_names(p->memory, v4l2_memory_names));
 +      v4l_print_format(&p->format, write_only);
 +}
 +
 +static void v4l_print_streamparm(const void *arg, bool write_only)
 +{
 +      const struct v4l2_streamparm *p = arg;
 +
 +      pr_cont("type=%s", prt_names(p->type, v4l2_type_names));
 +
 +      if (p->type == V4L2_BUF_TYPE_VIDEO_CAPTURE ||
 +          p->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
 +              const struct v4l2_captureparm *c = &p->parm.capture;
 +
 +              pr_cont(", capability=0x%x, capturemode=0x%x, timeperframe=%d/%d, "
 +                      "extendedmode=%d, readbuffers=%d\n",
 +                      c->capability, c->capturemode,
 +                      c->timeperframe.numerator, c->timeperframe.denominator,
 +                      c->extendedmode, c->readbuffers);
 +      } else if (p->type == V4L2_BUF_TYPE_VIDEO_OUTPUT ||
 +                 p->type == V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE) {
 +              const struct v4l2_outputparm *c = &p->parm.output;
 +
 +              pr_cont(", capability=0x%x, outputmode=0x%x, timeperframe=%d/%d, "
 +                      "extendedmode=%d, writebuffers=%d\n",
 +                      c->capability, c->outputmode,
 +                      c->timeperframe.numerator, c->timeperframe.denominator,
 +                      c->extendedmode, c->writebuffers);
 +      }
 +}
 +
 +static void v4l_print_queryctrl(const void *arg, bool write_only)
 +{
 +      const struct v4l2_queryctrl *p = arg;
 +
 +      pr_cont("id=0x%x, type=%d, name=%s, min/max=%d/%d, "
 +              "step=%d, default=%d, flags=0x%08x\n",
 +                      p->id, p->type, p->name,
 +                      p->minimum, p->maximum,
 +                      p->step, p->default_value, p->flags);
 +}
 +
 +static void v4l_print_querymenu(const void *arg, bool write_only)
 +{
 +      const struct v4l2_querymenu *p = arg;
 +
 +      pr_cont("id=0x%x, index=%d\n", p->id, p->index);
 +}
 +
 +static void v4l_print_control(const void *arg, bool write_only)
 +{
 +      const struct v4l2_control *p = arg;
 +
 +      pr_cont("id=0x%x, value=%d\n", p->id, p->value);
 +}
 +
 +static void v4l_print_ext_controls(const void *arg, bool write_only)
 +{
 +      const struct v4l2_ext_controls *p = arg;
 +      int i;
 +
 +      pr_cont("class=0x%x, count=%d, error_idx=%d",
 +                      p->ctrl_class, p->count, p->error_idx);
 +      for (i = 0; i < p->count; i++) {
 +              if (p->controls[i].size)
 +                      pr_cont(", id/val=0x%x/0x%x",
 +                              p->controls[i].id, p->controls[i].value);
 +              else
 +                      pr_cont(", id/size=0x%x/%u",
 +                              p->controls[i].id, p->controls[i].size);
 +      }
 +      pr_cont("\n");
 +}
 +
 +static void v4l_print_cropcap(const void *arg, bool write_only)
 +{
 +      const struct v4l2_cropcap *p = arg;
 +
 +      pr_cont("type=%s, bounds wxh=%dx%d, x,y=%d,%d, "
 +              "defrect wxh=%dx%d, x,y=%d,%d\n, "
 +              "pixelaspect %d/%d\n",
 +              prt_names(p->type, v4l2_type_names),
 +              p->bounds.width, p->bounds.height,
 +              p->bounds.left, p->bounds.top,
 +              p->defrect.width, p->defrect.height,
 +              p->defrect.left, p->defrect.top,
 +              p->pixelaspect.numerator, p->pixelaspect.denominator);
 +}
 +
 +static void v4l_print_crop(const void *arg, bool write_only)
 +{
 +      const struct v4l2_crop *p = arg;
 +
 +      pr_cont("type=%s, wxh=%dx%d, x,y=%d,%d\n",
 +              prt_names(p->type, v4l2_type_names),
 +              p->c.width, p->c.height,
 +              p->c.left, p->c.top);
 +}
 +
 +static void v4l_print_selection(const void *arg, bool write_only)
 +{
 +      const struct v4l2_selection *p = arg;
 +
 +      pr_cont("type=%s, target=%d, flags=0x%x, wxh=%dx%d, x,y=%d,%d\n",
 +              prt_names(p->type, v4l2_type_names),
 +              p->target, p->flags,
 +              p->r.width, p->r.height, p->r.left, p->r.top);
 +}
 +
 +static void v4l_print_jpegcompression(const void *arg, bool write_only)
 +{
 +      const struct v4l2_jpegcompression *p = arg;
 +
 +      pr_cont("quality=%d, APPn=%d, APP_len=%d, "
 +              "COM_len=%d, jpeg_markers=0x%x\n",
 +              p->quality, p->APPn, p->APP_len,
 +              p->COM_len, p->jpeg_markers);
 +}
 +
 +static void v4l_print_enc_idx(const void *arg, bool write_only)
 +{
 +      const struct v4l2_enc_idx *p = arg;
 +
 +      pr_cont("entries=%d, entries_cap=%d\n",
 +                      p->entries, p->entries_cap);
 +}
 +
 +static void v4l_print_encoder_cmd(const void *arg, bool write_only)
 +{
 +      const struct v4l2_encoder_cmd *p = arg;
 +
 +      pr_cont("cmd=%d, flags=0x%x\n",
 +                      p->cmd, p->flags);
 +}
 +
 +static void v4l_print_decoder_cmd(const void *arg, bool write_only)
 +{
 +      const struct v4l2_decoder_cmd *p = arg;
 +
 +      pr_cont("cmd=%d, flags=0x%x\n", p->cmd, p->flags);
 +
 +      if (p->cmd == V4L2_DEC_CMD_START)
 +              pr_info("speed=%d, format=%u\n",
 +                              p->start.speed, p->start.format);
 +      else if (p->cmd == V4L2_DEC_CMD_STOP)
 +              pr_info("pts=%llu\n", p->stop.pts);
 +}
 +
 +static void v4l_print_dbg_chip_ident(const void *arg, bool write_only)
 +{
 +      const struct v4l2_dbg_chip_ident *p = arg;
 +
 +      pr_cont("type=%u, ", p->match.type);
 +      if (p->match.type == V4L2_CHIP_MATCH_I2C_DRIVER)
 +              pr_cont("name=%s, ", p->match.name);
 +      else
 +              pr_cont("addr=%u, ", p->match.addr);
 +      pr_cont("chip_ident=%u, revision=0x%x\n",
 +                      p->ident, p->revision);
 +}
 +
 +static void v4l_print_dbg_register(const void *arg, bool write_only)
 +{
 +      const struct v4l2_dbg_register *p = arg;
 +
 +      pr_cont("type=%u, ", p->match.type);
 +      if (p->match.type == V4L2_CHIP_MATCH_I2C_DRIVER)
 +              pr_cont("name=%s, ", p->match.name);
 +      else
 +              pr_cont("addr=%u, ", p->match.addr);
 +      pr_cont("reg=0x%llx, val=0x%llx\n",
 +                      p->reg, p->val);
 +}
 +
 +static void v4l_print_dv_enum_presets(const void *arg, bool write_only)
 +{
 +      const struct v4l2_dv_enum_preset *p = arg;
 +
 +      pr_cont("index=%u, preset=%u, name=%s, width=%u, height=%u\n",
 +                      p->index, p->preset, p->name, p->width, p->height);
 +}
 +
 +static void v4l_print_dv_preset(const void *arg, bool write_only)
 +{
 +      const struct v4l2_dv_preset *p = arg;
 +
 +      pr_cont("preset=%u\n", p->preset);
 +}
 +
 +static void v4l_print_dv_timings(const void *arg, bool write_only)
 +{
 +      const struct v4l2_dv_timings *p = arg;
 +
 +      switch (p->type) {
 +      case V4L2_DV_BT_656_1120:
 +              pr_cont("type=bt-656/1120, interlaced=%u, "
 +                      "pixelclock=%llu, "
 +                      "width=%u, height=%u, polarities=0x%x, "
 +                      "hfrontporch=%u, hsync=%u, "
 +                      "hbackporch=%u, vfrontporch=%u, "
 +                      "vsync=%u, vbackporch=%u, "
 +                      "il_vfrontporch=%u, il_vsync=%u, "
 +                      "il_vbackporch=%u, standards=0x%x, flags=0x%x\n",
 +                              p->bt.interlaced, p->bt.pixelclock,
 +                              p->bt.width, p->bt.height,
 +                              p->bt.polarities, p->bt.hfrontporch,
 +                              p->bt.hsync, p->bt.hbackporch,
 +                              p->bt.vfrontporch, p->bt.vsync,
 +                              p->bt.vbackporch, p->bt.il_vfrontporch,
 +                              p->bt.il_vsync, p->bt.il_vbackporch,
 +                              p->bt.standards, p->bt.flags);
 +              break;
 +      default:
 +              pr_cont("type=%d\n", p->type);
 +              break;
 +      }
 +}
 +
 +static void v4l_print_enum_dv_timings(const void *arg, bool write_only)
 +{
 +      const struct v4l2_enum_dv_timings *p = arg;
 +
 +      pr_cont("index=%u, ", p->index);
 +      v4l_print_dv_timings(&p->timings, write_only);
 +}
 +
 +static void v4l_print_dv_timings_cap(const void *arg, bool write_only)
 +{
 +      const struct v4l2_dv_timings_cap *p = arg;
 +
 +      switch (p->type) {
 +      case V4L2_DV_BT_656_1120:
 +              pr_cont("type=bt-656/1120, width=%u-%u, height=%u-%u, "
 +                      "pixelclock=%llu-%llu, standards=0x%x, capabilities=0x%x\n",
 +                      p->bt.min_width, p->bt.max_width,
 +                      p->bt.min_height, p->bt.max_height,
 +                      p->bt.min_pixelclock, p->bt.max_pixelclock,
 +                      p->bt.standards, p->bt.capabilities);
 +              break;
 +      default:
 +              pr_cont("type=%u\n", p->type);
 +              break;
 +      }
 +}
 +
 +static void v4l_print_frmsizeenum(const void *arg, bool write_only)
 +{
 +      const struct v4l2_frmsizeenum *p = arg;
 +
 +      pr_cont("index=%u, pixelformat=%c%c%c%c, type=%u",
 +                      p->index,
 +                      (p->pixel_format & 0xff),
 +                      (p->pixel_format >>  8) & 0xff,
 +                      (p->pixel_format >> 16) & 0xff,
 +                      (p->pixel_format >> 24) & 0xff,
 +                      p->type);
 +      switch (p->type) {
 +      case V4L2_FRMSIZE_TYPE_DISCRETE:
 +              pr_cont(" wxh=%ux%u\n",
 +                      p->discrete.width, p->discrete.height);
 +              break;
 +      case V4L2_FRMSIZE_TYPE_STEPWISE:
 +              pr_cont(" min=%ux%u, max=%ux%u, step=%ux%u\n",
 +                              p->stepwise.min_width,  p->stepwise.min_height,
 +                              p->stepwise.step_width, p->stepwise.step_height,
 +                              p->stepwise.max_width,  p->stepwise.max_height);
 +              break;
 +      case V4L2_FRMSIZE_TYPE_CONTINUOUS:
 +              /* fall through */
 +      default:
 +              pr_cont("\n");
 +              break;
 +      }
 +}
 +
 +static void v4l_print_frmivalenum(const void *arg, bool write_only)
 +{
 +      const struct v4l2_frmivalenum *p = arg;
 +
 +      pr_cont("index=%u, pixelformat=%c%c%c%c, wxh=%ux%u, type=%u",
 +                      p->index,
 +                      (p->pixel_format & 0xff),
 +                      (p->pixel_format >>  8) & 0xff,
 +                      (p->pixel_format >> 16) & 0xff,
 +                      (p->pixel_format >> 24) & 0xff,
 +                      p->width, p->height, p->type);
 +      switch (p->type) {
 +      case V4L2_FRMIVAL_TYPE_DISCRETE:
 +              pr_cont(" fps=%d/%d\n",
 +                              p->discrete.numerator,
 +                              p->discrete.denominator);
 +              break;
 +      case V4L2_FRMIVAL_TYPE_STEPWISE:
 +              pr_cont(" min=%d/%d, max=%d/%d, step=%d/%d\n",
 +                              p->stepwise.min.numerator,
 +                              p->stepwise.min.denominator,
 +                              p->stepwise.max.numerator,
 +                              p->stepwise.max.denominator,
 +                              p->stepwise.step.numerator,
 +                              p->stepwise.step.denominator);
 +              break;
 +      case V4L2_FRMIVAL_TYPE_CONTINUOUS:
 +              /* fall through */
 +      default:
 +              pr_cont("\n");
 +              break;
 +      }
 +}
 +
 +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",
 +                      p->type, p->pending, p->sequence, p->id,
 +                      p->timestamp.tv_sec, p->timestamp.tv_nsec);
 +      switch (p->type) {
 +      case V4L2_EVENT_VSYNC:
 +              printk(KERN_DEBUG "field=%s\n",
 +                      prt_names(p->u.vsync.field, v4l2_field_names));
 +              break;
 +      case V4L2_EVENT_CTRL:
 +              c = &p->u.ctrl;
 +              printk(KERN_DEBUG "changes=0x%x, type=%u, ",
 +                      c->changes, c->type);
 +              if (c->type == V4L2_CTRL_TYPE_INTEGER64)
 +                      pr_cont("value64=%lld, ", c->value64);
 +              else
 +                      pr_cont("value=%d, ", c->value);
 +              pr_cont("flags=0x%x, minimum=%d, maximum=%d, step=%d,"
 +                              " default_value=%d\n",
 +                      c->flags, c->minimum, c->maximum,
 +                      c->step, c->default_value);
 +              break;
 +      case V4L2_EVENT_FRAME_SYNC:
 +              pr_cont("frame_sequence=%u\n",
 +                      p->u.frame_sync.frame_sequence);
 +              break;
 +      }
 +}
 +
 +static void v4l_print_event_subscription(const void *arg, bool write_only)
 +{
 +      const struct v4l2_event_subscription *p = arg;
 +
 +      pr_cont("type=0x%x, id=0x%x, flags=0x%x\n",
 +                      p->type, p->id, p->flags);
 +}
 +
 +static void v4l_print_sliced_vbi_cap(const void *arg, bool write_only)
 +{
 +      const struct v4l2_sliced_vbi_cap *p = arg;
 +      int i;
 +
 +      pr_cont("type=%s, service_set=0x%08x\n",
 +                      prt_names(p->type, v4l2_type_names), p->service_set);
 +      for (i = 0; i < 24; i++)
 +              printk(KERN_DEBUG "line[%02u]=0x%04x, 0x%04x\n", i,
 +                              p->service_lines[0][i],
 +                              p->service_lines[1][i]);
 +}
 +
 +static void v4l_print_freq_band(const void *arg, bool write_only)
 +{
 +      const struct v4l2_frequency_band *p = arg;
 +
 +      pr_cont("tuner=%u, type=%u, index=%u, capability=0x%x, "
 +                      "rangelow=%u, rangehigh=%u, modulation=0x%x\n",
 +                      p->tuner, p->type, p->index,
 +                      p->capability, p->rangelow,
 +                      p->rangehigh, p->modulation);
 +}
 +
 +static void v4l_print_u32(const void *arg, bool write_only)
 +{
 +      pr_cont("value=%u\n", *(const u32 *)arg);
 +}
 +
 +static void v4l_print_newline(const void *arg, bool write_only)
 +{
 +      pr_cont("\n");
 +}
 +
 +static void v4l_print_default(const void *arg, bool write_only)
 +{
 +      pr_cont("driver-specific ioctl\n");
 +}
 +
 +static int check_ext_ctrls(struct v4l2_ext_controls *c, int allow_priv)
 +{
 +      __u32 i;
 +
 +      /* zero the reserved fields */
 +      c->reserved[0] = c->reserved[1] = 0;
 +      for (i = 0; i < c->count; i++)
 +              c->controls[i].reserved2[0] = 0;
 +
 +      /* V4L2_CID_PRIVATE_BASE cannot be used as control class
 +         when using extended controls.
 +         Only when passed in through VIDIOC_G_CTRL and VIDIOC_S_CTRL
 +         is it allowed for backwards compatibility.
 +       */
 +      if (!allow_priv && c->ctrl_class == V4L2_CID_PRIVATE_BASE)
 +              return 0;
 +      /* Check that all controls are from the same control class. */
 +      for (i = 0; i < c->count; i++) {
 +              if (V4L2_CTRL_ID2CLASS(c->controls[i].id) != c->ctrl_class) {
 +                      c->error_idx = i;
 +                      return 0;
 +              }
 +      }
 +      return 1;
 +}
 +
 +static int check_fmt(const struct v4l2_ioctl_ops *ops, enum v4l2_buf_type type)
 +{
 +      if (ops == NULL)
 +              return -EINVAL;
 +
 +      switch (type) {
 +      case V4L2_BUF_TYPE_VIDEO_CAPTURE:
 +              if (ops->vidioc_g_fmt_vid_cap ||
 +                              ops->vidioc_g_fmt_vid_cap_mplane)
 +                      return 0;
 +              break;
 +      case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
 +              if (ops->vidioc_g_fmt_vid_cap_mplane)
 +                      return 0;
 +              break;
 +      case V4L2_BUF_TYPE_VIDEO_OVERLAY:
 +              if (ops->vidioc_g_fmt_vid_overlay)
 +                      return 0;
 +              break;
 +      case V4L2_BUF_TYPE_VIDEO_OUTPUT:
 +              if (ops->vidioc_g_fmt_vid_out ||
 +                              ops->vidioc_g_fmt_vid_out_mplane)
 +                      return 0;
 +              break;
 +      case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
 +              if (ops->vidioc_g_fmt_vid_out_mplane)
 +                      return 0;
 +              break;
 +      case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
 +              if (ops->vidioc_g_fmt_vid_out_overlay)
 +                      return 0;
 +              break;
 +      case V4L2_BUF_TYPE_VBI_CAPTURE:
 +              if (ops->vidioc_g_fmt_vbi_cap)
 +                      return 0;
 +              break;
 +      case V4L2_BUF_TYPE_VBI_OUTPUT:
 +              if (ops->vidioc_g_fmt_vbi_out)
 +                      return 0;
 +              break;
 +      case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
 +              if (ops->vidioc_g_fmt_sliced_vbi_cap)
 +                      return 0;
 +              break;
 +      case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
 +              if (ops->vidioc_g_fmt_sliced_vbi_out)
 +                      return 0;
 +              break;
 +      case V4L2_BUF_TYPE_PRIVATE:
 +              if (ops->vidioc_g_fmt_type_private)
 +                      return 0;
 +              break;
 +      }
 +      return -EINVAL;
 +}
 +
 +static int v4l_querycap(const struct v4l2_ioctl_ops *ops,
 +                              struct file *file, void *fh, void *arg)
 +{
 +      struct v4l2_capability *cap = (struct v4l2_capability *)arg;
 +
 +      cap->version = LINUX_VERSION_CODE;
 +      return ops->vidioc_querycap(file, fh, cap);
 +}
 +
 +static int v4l_s_input(const struct v4l2_ioctl_ops *ops,
 +                              struct file *file, void *fh, void *arg)
 +{
 +      return ops->vidioc_s_input(file, fh, *(unsigned int *)arg);
 +}
 +
 +static int v4l_s_output(const struct v4l2_ioctl_ops *ops,
 +                              struct file *file, void *fh, void *arg)
 +{
 +      return ops->vidioc_s_output(file, fh, *(unsigned int *)arg);
 +}
 +
 +static int v4l_g_priority(const struct v4l2_ioctl_ops *ops,
 +                              struct file *file, void *fh, void *arg)
 +{
 +      struct video_device *vfd;
 +      u32 *p = arg;
 +
 +      if (ops->vidioc_g_priority)
 +              return ops->vidioc_g_priority(file, fh, arg);
 +      vfd = video_devdata(file);
 +      *p = v4l2_prio_max(&vfd->v4l2_dev->prio);
 +      return 0;
 +}
 +
 +static int v4l_s_priority(const struct v4l2_ioctl_ops *ops,
 +                              struct file *file, void *fh, void *arg)
 +{
 +      struct video_device *vfd;
 +      struct v4l2_fh *vfh;
 +      u32 *p = arg;
 +
 +      if (ops->vidioc_s_priority)
 +              return ops->vidioc_s_priority(file, fh, *p);
 +      vfd = video_devdata(file);
 +      vfh = file->private_data;
 +      return v4l2_prio_change(&vfd->v4l2_dev->prio, &vfh->prio, *p);
 +}
 +
 +static int v4l_enuminput(const struct v4l2_ioctl_ops *ops,
 +                              struct file *file, void *fh, void *arg)
 +{
 +      struct v4l2_input *p = arg;
 +
 +      /*
 +       * We set the flags for CAP_PRESETS, CAP_CUSTOM_TIMINGS &
 +       * CAP_STD here based on ioctl handler provided by the
 +       * driver. If the driver doesn't support these
 +       * for a specific input, it must override these flags.
 +       */
 +      if (ops->vidioc_s_std)
 +              p->capabilities |= V4L2_IN_CAP_STD;
 +      if (ops->vidioc_s_dv_preset)
 +              p->capabilities |= V4L2_IN_CAP_PRESETS;
 +      if (ops->vidioc_s_dv_timings)
 +              p->capabilities |= V4L2_IN_CAP_CUSTOM_TIMINGS;
 +
 +      return ops->vidioc_enum_input(file, fh, p);
 +}
 +
 +static int v4l_enumoutput(const struct v4l2_ioctl_ops *ops,
 +                              struct file *file, void *fh, void *arg)
 +{
 +      struct v4l2_output *p = arg;
 +
 +      /*
 +       * We set the flags for CAP_PRESETS, CAP_CUSTOM_TIMINGS &
 +       * CAP_STD here based on ioctl handler provided by the
 +       * driver. If the driver doesn't support these
 +       * for a specific output, it must override these flags.
 +       */
 +      if (ops->vidioc_s_std)
 +              p->capabilities |= V4L2_OUT_CAP_STD;
 +      if (ops->vidioc_s_dv_preset)
 +              p->capabilities |= V4L2_OUT_CAP_PRESETS;
 +      if (ops->vidioc_s_dv_timings)
 +              p->capabilities |= V4L2_OUT_CAP_CUSTOM_TIMINGS;
 +
 +      return ops->vidioc_enum_output(file, fh, p);
 +}
 +
 +static int v4l_enum_fmt(const struct v4l2_ioctl_ops *ops,
 +                              struct file *file, void *fh, void *arg)
 +{
 +      struct v4l2_fmtdesc *p = arg;
 +
 +      switch (p->type) {
 +      case V4L2_BUF_TYPE_VIDEO_CAPTURE:
 +              if (unlikely(!ops->vidioc_enum_fmt_vid_cap))
 +                      break;
 +              return ops->vidioc_enum_fmt_vid_cap(file, fh, arg);
 +      case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
 +              if (unlikely(!ops->vidioc_enum_fmt_vid_cap_mplane))
 +                      break;
 +              return ops->vidioc_enum_fmt_vid_cap_mplane(file, fh, arg);
 +      case V4L2_BUF_TYPE_VIDEO_OVERLAY:
 +              if (unlikely(!ops->vidioc_enum_fmt_vid_overlay))
 +                      break;
 +              return ops->vidioc_enum_fmt_vid_overlay(file, fh, arg);
 +      case V4L2_BUF_TYPE_VIDEO_OUTPUT:
 +              if (unlikely(!ops->vidioc_enum_fmt_vid_out))
 +                      break;
 +              return ops->vidioc_enum_fmt_vid_out(file, fh, arg);
 +      case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
 +              if (unlikely(!ops->vidioc_enum_fmt_vid_out_mplane))
 +                      break;
 +              return ops->vidioc_enum_fmt_vid_out_mplane(file, fh, arg);
 +      case V4L2_BUF_TYPE_PRIVATE:
 +              if (unlikely(!ops->vidioc_enum_fmt_type_private))
 +                      break;
 +              return ops->vidioc_enum_fmt_type_private(file, fh, arg);
 +      }
 +      return -EINVAL;
 +}
 +
 +static int v4l_g_fmt(const struct v4l2_ioctl_ops *ops,
 +                              struct file *file, void *fh, void *arg)
 +{
 +      struct v4l2_format *p = arg;
 +
 +      switch (p->type) {
 +      case V4L2_BUF_TYPE_VIDEO_CAPTURE:
 +              if (unlikely(!ops->vidioc_g_fmt_vid_cap))
 +                      break;
 +              return ops->vidioc_g_fmt_vid_cap(file, fh, arg);
 +      case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
 +              if (unlikely(!ops->vidioc_g_fmt_vid_cap_mplane))
 +                      break;
 +              return ops->vidioc_g_fmt_vid_cap_mplane(file, fh, arg);
 +      case V4L2_BUF_TYPE_VIDEO_OVERLAY:
 +              if (unlikely(!ops->vidioc_g_fmt_vid_overlay))
 +                      break;
 +              return ops->vidioc_g_fmt_vid_overlay(file, fh, arg);
 +      case V4L2_BUF_TYPE_VIDEO_OUTPUT:
 +              if (unlikely(!ops->vidioc_g_fmt_vid_out))
 +                      break;
 +              return ops->vidioc_g_fmt_vid_out(file, fh, arg);
 +      case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
 +              if (unlikely(!ops->vidioc_g_fmt_vid_out_mplane))
 +                      break;
 +              return ops->vidioc_g_fmt_vid_out_mplane(file, fh, arg);
 +      case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
 +              if (unlikely(!ops->vidioc_g_fmt_vid_out_overlay))
 +                      break;
 +              return ops->vidioc_g_fmt_vid_out_overlay(file, fh, arg);
 +      case V4L2_BUF_TYPE_VBI_CAPTURE:
 +              if (unlikely(!ops->vidioc_g_fmt_vbi_cap))
 +                      break;
 +              return ops->vidioc_g_fmt_vbi_cap(file, fh, arg);
 +      case V4L2_BUF_TYPE_VBI_OUTPUT:
 +              if (unlikely(!ops->vidioc_g_fmt_vbi_out))
 +                      break;
 +              return ops->vidioc_g_fmt_vbi_out(file, fh, arg);
 +      case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
 +              if (unlikely(!ops->vidioc_g_fmt_sliced_vbi_cap))
 +                      break;
 +              return ops->vidioc_g_fmt_sliced_vbi_cap(file, fh, arg);
 +      case V4L2_BUF_TYPE_SLICED_VBI_OUTPUT:
 +              if (unlikely(!ops->vidioc_g_fmt_sliced_vbi_out))
 +                      break;
 +              return ops->vidioc_g_fmt_sliced_vbi_out(file, fh, arg);
 +      case V4L2_BUF_TYPE_PRIVATE:
 +              if (unlikely(!ops->vidioc_g_fmt_type_private))
 +                      break;
 +              return ops->vidioc_g_fmt_type_private(file, fh, arg);
 +      }
 +      return -EINVAL;
 +}
 +
 +static int v4l_s_fmt(const struct v4l2_ioctl_ops *ops,
 +                              struct file *file, void *fh, void *arg)
 +{
 +      struct v4l2_format *p = arg;
 +
 +      switch (p->type) {
 +      case V4L2_BUF_TYPE_VIDEO_CAPTURE:
 +              if (unlikely(!ops->vidioc_s_fmt_vid_cap))
 +                      break;
 +              CLEAR_AFTER_FIELD(p, fmt.pix);
 +              return ops->vidioc_s_fmt_vid_cap(file, fh, arg);
 +      case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
 +              if (unlikely(!ops->vidioc_s_fmt_vid_cap_mplane))
 +                      break;
 +              CLEAR_AFTER_FIELD(p, fmt.pix_mp);
 +              return ops->vidioc_s_fmt_vid_cap_mplane(file, fh, arg);
 +      case V4L2_BUF_TYPE_VIDEO_OVERLAY:
 +              if (unlikely(!ops->vidioc_s_fmt_vid_overlay))
 +                      break;
 +              CLEAR_AFTER_FIELD(p, fmt.win);
 +              return ops->vidioc_s_fmt_vid_overlay(file, fh, arg);
 +      case V4L2_BUF_TYPE_VIDEO_OUTPUT:
 +              if (unlikely(!ops->vidioc_s_fmt_vid_out))
 +                      break;
 +              CLEAR_AFTER_FIELD(p, fmt.pix);
 +              return ops->vidioc_s_fmt_vid_out(file, fh, arg);
 +      case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
 +              if (unlikely(!ops->vidioc_s_fmt_vid_out_mplane))
 +                      break;
 +              CLEAR_AFTER_FIELD(p, fmt.pix_mp);
 +              return ops->vidioc_s_fmt_vid_out_mplane(file, fh, arg);
 +      case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
 +              if (unlikely(!ops->vidioc_s_fmt_vid_out_overlay))
 +                      break;
 +              CLEAR_AFTER_FIELD(p, fmt.win);
 +              return ops->vidioc_s_fmt_vid_out_overlay(file, fh, arg);
 +      case V4L2_BUF_TYPE_VBI_CAPTURE:
 +              if (unlikely(!ops->vidioc_s_fmt_vbi_cap))
 +                      break;
 +              CLEAR_AFTER_FIELD(p, fmt.vbi);
 +              return ops->vidioc_s_fmt_vbi_cap(file, fh, arg);
 +      case V4L2_BUF_TYPE_VBI_OUTPUT:
 +              if (unlikely(!ops->vidioc_s_fmt_vbi_out))
 +                      break;
 +              CLEAR_AFTER_FIELD(p, fmt.vbi);
 +              return ops->vidioc_s_fmt_vbi_out(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);
 +              return ops->vidioc_s_fmt_sliced_vbi_cap(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);
 +              return ops->vidioc_s_fmt_sliced_vbi_out(file, fh, arg);
 +      case V4L2_BUF_TYPE_PRIVATE:
 +              if (unlikely(!ops->vidioc_s_fmt_type_private))
 +                      break;
 +              return ops->vidioc_s_fmt_type_private(file, fh, arg);
 +      }
 +      return -EINVAL;
 +}
 +
 +static int v4l_try_fmt(const struct v4l2_ioctl_ops *ops,
 +                              struct file *file, void *fh, void *arg)
 +{
 +      struct v4l2_format *p = arg;
 +
 +      switch (p->type) {
 +      case V4L2_BUF_TYPE_VIDEO_CAPTURE:
 +              if (unlikely(!ops->vidioc_try_fmt_vid_cap))
 +                      break;
 +              CLEAR_AFTER_FIELD(p, fmt.pix);
 +              return ops->vidioc_try_fmt_vid_cap(file, fh, arg);
 +      case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE:
 +              if (unlikely(!ops->vidioc_try_fmt_vid_cap_mplane))
 +                      break;
 +              CLEAR_AFTER_FIELD(p, fmt.pix_mp);
 +              return ops->vidioc_try_fmt_vid_cap_mplane(file, fh, arg);
 +      case V4L2_BUF_TYPE_VIDEO_OVERLAY:
 +              if (unlikely(!ops->vidioc_try_fmt_vid_overlay))
 +                      break;
 +              CLEAR_AFTER_FIELD(p, fmt.win);
 +              return ops->vidioc_try_fmt_vid_overlay(file, fh, arg);
 +      case V4L2_BUF_TYPE_VIDEO_OUTPUT:
 +              if (unlikely(!ops->vidioc_try_fmt_vid_out))
 +                      break;
 +              CLEAR_AFTER_FIELD(p, fmt.pix);
 +              return ops->vidioc_try_fmt_vid_out(file, fh, arg);
 +      case V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE:
 +              if (unlikely(!ops->vidioc_try_fmt_vid_out_mplane))
 +                      break;
 +              CLEAR_AFTER_FIELD(p, fmt.pix_mp);
 +              return ops->vidioc_try_fmt_vid_out_mplane(file, fh, arg);
 +      case V4L2_BUF_TYPE_VIDEO_OUTPUT_OVERLAY:
 +              if (unlikely(!ops->vidioc_try_fmt_vid_out_overlay))
 +                      break;
 +              CLEAR_AFTER_FIELD(p, fmt.win);
 +              return ops->vidioc_try_fmt_vid_out_overlay(file, fh, arg);
 +      case V4L2_BUF_TYPE_VBI_CAPTURE:
 +              if (unlikely(!ops->vidioc_try_fmt_vbi_cap))
 +                      break;
 +              CLEAR_AFTER_FIELD(p, fmt.vbi);
 +              return ops->vidioc_try_fmt_vbi_cap(file, fh, arg);
 +      case V4L2_BUF_TYPE_VBI_OUTPUT:
 +              if (unlikely(!ops->vidioc_try_fmt_vbi_out))
 +                      break;
 +              CLEAR_AFTER_FIELD(p, fmt.vbi);
 +              return ops->vidioc_try_fmt_vbi_out(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);
 +              return ops->vidioc_try_fmt_sliced_vbi_cap(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);
 +              return ops->vidioc_try_fmt_sliced_vbi_out(file, fh, arg);
 +      case V4L2_BUF_TYPE_PRIVATE:
 +              if (unlikely(!ops->vidioc_try_fmt_type_private))
 +                      break;
 +              return ops->vidioc_try_fmt_type_private(file, fh, arg);
 +      }
 +      return -EINVAL;
 +}
 +
 +static int v4l_streamon(const struct v4l2_ioctl_ops *ops,
 +                              struct file *file, void *fh, void *arg)
 +{
 +      return ops->vidioc_streamon(file, fh, *(unsigned int *)arg);
 +}
 +
 +static int v4l_streamoff(const struct v4l2_ioctl_ops *ops,
 +                              struct file *file, void *fh, void *arg)
 +{
 +      return ops->vidioc_streamoff(file, fh, *(unsigned int *)arg);
 +}
 +
 +static int v4l_g_tuner(const struct v4l2_ioctl_ops *ops,
 +                              struct file *file, void *fh, void *arg)
 +{
 +      struct video_device *vfd = video_devdata(file);
 +      struct v4l2_tuner *p = arg;
 +      int err;
 +
 +      p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
 +                      V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
 +      err = ops->vidioc_g_tuner(file, fh, p);
 +      if (!err)
 +              p->capability |= V4L2_TUNER_CAP_FREQ_BANDS;
 +      return err;
 +}
 +
 +static int v4l_s_tuner(const struct v4l2_ioctl_ops *ops,
 +                              struct file *file, void *fh, void *arg)
 +{
 +      struct video_device *vfd = video_devdata(file);
 +      struct v4l2_tuner *p = arg;
 +
 +      p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
 +                      V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
 +      return ops->vidioc_s_tuner(file, fh, p);
 +}
 +
 +static int v4l_g_modulator(const struct v4l2_ioctl_ops *ops,
 +                              struct file *file, void *fh, void *arg)
 +{
 +      struct v4l2_modulator *p = arg;
 +      int err;
 +
 +      err = ops->vidioc_g_modulator(file, fh, p);
 +      if (!err)
 +              p->capability |= V4L2_TUNER_CAP_FREQ_BANDS;
 +      return err;
 +}
 +
 +static int v4l_g_frequency(const struct v4l2_ioctl_ops *ops,
 +                              struct file *file, void *fh, void *arg)
 +{
 +      struct video_device *vfd = video_devdata(file);
 +      struct v4l2_frequency *p = arg;
 +
 +      p->type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
 +                      V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
 +      return ops->vidioc_g_frequency(file, fh, p);
 +}
 +
 +static int v4l_s_frequency(const struct v4l2_ioctl_ops *ops,
 +                              struct file *file, void *fh, void *arg)
 +{
 +      struct video_device *vfd = video_devdata(file);
 +      struct v4l2_frequency *p = arg;
 +      enum v4l2_tuner_type type;
 +
 +      type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
 +                      V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
 +      if (p->type != type)
 +              return -EINVAL;
 +      return ops->vidioc_s_frequency(file, fh, p);
 +}
 +
 +static int v4l_enumstd(const struct v4l2_ioctl_ops *ops,
 +                              struct file *file, void *fh, void *arg)
 +{
 +      struct video_device *vfd = video_devdata(file);
 +      struct v4l2_standard *p = arg;
 +      v4l2_std_id id = vfd->tvnorms, curr_id = 0;
 +      unsigned int index = p->index, i, j = 0;
 +      const char *descr = "";
 +
 +      /* Return norm array in a canonical way */
 +      for (i = 0; i <= index && id; i++) {
 +              /* last std value in the standards array is 0, so this
 +                 while always ends there since (id & 0) == 0. */
 +              while ((id & standards[j].std) != standards[j].std)
 +                      j++;
 +              curr_id = standards[j].std;
 +              descr = standards[j].descr;
 +              j++;
 +              if (curr_id == 0)
 +                      break;
 +              if (curr_id != V4L2_STD_PAL &&
 +                              curr_id != V4L2_STD_SECAM &&
 +                              curr_id != V4L2_STD_NTSC)
 +                      id &= ~curr_id;
 +      }
 +      if (i <= index)
 +              return -EINVAL;
 +
 +      v4l2_video_std_construct(p, curr_id, descr);
 +      return 0;
 +}
 +
 +static int v4l_g_std(const struct v4l2_ioctl_ops *ops,
 +                              struct file *file, void *fh, void *arg)
 +{
 +      struct video_device *vfd = video_devdata(file);
 +      v4l2_std_id *id = arg;
 +
 +      /* Calls the specific handler */
 +      if (ops->vidioc_g_std)
 +              return ops->vidioc_g_std(file, fh, arg);
 +      if (vfd->current_norm) {
 +              *id = vfd->current_norm;
 +              return 0;
 +      }
 +      return -ENOTTY;
 +}
 +
 +static int v4l_s_std(const struct v4l2_ioctl_ops *ops,
 +                              struct file *file, void *fh, void *arg)
 +{
 +      struct video_device *vfd = video_devdata(file);
 +      v4l2_std_id *id = arg, norm;
 +      int ret;
 +
 +      norm = (*id) & vfd->tvnorms;
 +      if (vfd->tvnorms && !norm)      /* Check if std is supported */
 +              return -EINVAL;
 +
 +      /* Calls the specific handler */
 +      ret = ops->vidioc_s_std(file, fh, &norm);
 +
 +      /* Updates standard information */
 +      if (ret >= 0)
 +              vfd->current_norm = norm;
 +      return ret;
 +}
 +
 +static int v4l_querystd(const struct v4l2_ioctl_ops *ops,
 +                              struct file *file, void *fh, void *arg)
 +{
 +      struct video_device *vfd = video_devdata(file);
 +      v4l2_std_id *p = arg;
 +
 +      /*
 +       * If nothing detected, it should return all supported
 +       * standard.
 +       * Drivers just need to mask the std argument, in order
 +       * to remove the standards that don't apply from the mask.
 +       * This means that tuners, audio and video decoders can join
 +       * their efforts to improve the standards detection.
 +       */
 +      *p = vfd->tvnorms;
 +      return ops->vidioc_querystd(file, fh, arg);
 +}
 +
 +static int v4l_s_hw_freq_seek(const struct v4l2_ioctl_ops *ops,
 +                              struct file *file, void *fh, void *arg)
 +{
 +      struct video_device *vfd = video_devdata(file);
 +      struct v4l2_hw_freq_seek *p = arg;
 +      enum v4l2_tuner_type type;
 +
 +      type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
 +              V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
 +      if (p->type != type)
 +              return -EINVAL;
 +      return ops->vidioc_s_hw_freq_seek(file, fh, p);
 +}
 +
 +static int v4l_reqbufs(const struct v4l2_ioctl_ops *ops,
 +                              struct file *file, void *fh, void *arg)
 +{
 +      struct v4l2_requestbuffers *p = arg;
 +      int ret = check_fmt(ops, p->type);
 +
 +      if (ret)
 +              return ret;
 +
 +      if (p->type < V4L2_BUF_TYPE_PRIVATE)
 +              CLEAR_AFTER_FIELD(p, memory);
 +
 +      return ops->vidioc_reqbufs(file, fh, p);
 +}
 +
 +static int v4l_querybuf(const struct v4l2_ioctl_ops *ops,
 +                              struct file *file, void *fh, void *arg)
 +{
 +      struct v4l2_buffer *p = arg;
 +      int ret = check_fmt(ops, p->type);
 +
 +      return ret ? ret : ops->vidioc_querybuf(file, fh, p);
 +}
 +
 +static int v4l_qbuf(const struct v4l2_ioctl_ops *ops,
 +                              struct file *file, void *fh, void *arg)
 +{
 +      struct v4l2_buffer *p = arg;
 +      int ret = check_fmt(ops, p->type);
 +
 +      return ret ? ret : ops->vidioc_qbuf(file, fh, p);
 +}
 +
 +static int v4l_dqbuf(const struct v4l2_ioctl_ops *ops,
 +                              struct file *file, void *fh, void *arg)
 +{
 +      struct v4l2_buffer *p = arg;
 +      int ret = check_fmt(ops, p->type);
 +
 +      return ret ? ret : ops->vidioc_dqbuf(file, fh, p);
 +}
 +
 +static int v4l_create_bufs(const struct v4l2_ioctl_ops *ops,
 +                              struct file *file, void *fh, void *arg)
 +{
 +      struct v4l2_create_buffers *create = arg;
 +      int ret = check_fmt(ops, create->format.type);
 +
 +      return ret ? ret : ops->vidioc_create_bufs(file, fh, create);
 +}
 +
 +static int v4l_prepare_buf(const struct v4l2_ioctl_ops *ops,
 +                              struct file *file, void *fh, void *arg)
 +{
 +      struct v4l2_buffer *b = arg;
 +      int ret = check_fmt(ops, b->type);
 +
 +      return ret ? ret : ops->vidioc_prepare_buf(file, fh, b);
 +}
 +
 +static int v4l_g_parm(const struct v4l2_ioctl_ops *ops,
 +                              struct file *file, void *fh, void *arg)
 +{
 +      struct video_device *vfd = video_devdata(file);
 +      struct v4l2_streamparm *p = arg;
 +      v4l2_std_id std;
 +      int ret = check_fmt(ops, p->type);
 +
 +      if (ret)
 +              return ret;
 +      if (ops->vidioc_g_parm)
 +              return ops->vidioc_g_parm(file, fh, p);
 +      std = vfd->current_norm;
 +      if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE &&
 +          p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE)
 +              return -EINVAL;
 +      p->parm.capture.readbuffers = 2;
 +      if (ops->vidioc_g_std)
 +              ret = ops->vidioc_g_std(file, fh, &std);
 +      if (ret == 0)
 +              v4l2_video_std_frame_period(std,
 +                          &p->parm.capture.timeperframe);
 +      return ret;
 +}
 +
 +static int v4l_s_parm(const struct v4l2_ioctl_ops *ops,
 +                              struct file *file, void *fh, void *arg)
 +{
 +      struct v4l2_streamparm *p = arg;
 +      int ret = check_fmt(ops, p->type);
 +
 +      return ret ? ret : ops->vidioc_s_parm(file, fh, p);
 +}
 +
 +static int v4l_queryctrl(const struct v4l2_ioctl_ops *ops,
 +                              struct file *file, void *fh, void *arg)
 +{
 +      struct video_device *vfd = video_devdata(file);
 +      struct v4l2_queryctrl *p = arg;
 +      struct v4l2_fh *vfh =
 +              test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL;
 +
 +      if (vfh && vfh->ctrl_handler)
 +              return v4l2_queryctrl(vfh->ctrl_handler, p);
 +      if (vfd->ctrl_handler)
 +              return v4l2_queryctrl(vfd->ctrl_handler, p);
 +      if (ops->vidioc_queryctrl)
 +              return ops->vidioc_queryctrl(file, fh, p);
 +      return -ENOTTY;
 +}
 +
 +static int v4l_querymenu(const struct v4l2_ioctl_ops *ops,
 +                              struct file *file, void *fh, void *arg)
 +{
 +      struct video_device *vfd = video_devdata(file);
 +      struct v4l2_querymenu *p = arg;
 +      struct v4l2_fh *vfh =
 +              test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL;
 +
 +      if (vfh && vfh->ctrl_handler)
 +              return v4l2_querymenu(vfh->ctrl_handler, p);
 +      if (vfd->ctrl_handler)
 +              return v4l2_querymenu(vfd->ctrl_handler, p);
 +      if (ops->vidioc_querymenu)
 +              return ops->vidioc_querymenu(file, fh, p);
 +      return -ENOTTY;
 +}
 +
 +static int v4l_g_ctrl(const struct v4l2_ioctl_ops *ops,
 +                              struct file *file, void *fh, void *arg)
 +{
 +      struct video_device *vfd = video_devdata(file);
 +      struct v4l2_control *p = arg;
 +      struct v4l2_fh *vfh =
 +              test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL;
 +      struct v4l2_ext_controls ctrls;
 +      struct v4l2_ext_control ctrl;
 +
 +      if (vfh && vfh->ctrl_handler)
 +              return v4l2_g_ctrl(vfh->ctrl_handler, p);
 +      if (vfd->ctrl_handler)
 +              return v4l2_g_ctrl(vfd->ctrl_handler, p);
 +      if (ops->vidioc_g_ctrl)
 +              return ops->vidioc_g_ctrl(file, fh, p);
 +      if (ops->vidioc_g_ext_ctrls == NULL)
 +              return -ENOTTY;
 +
 +      ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(p->id);
 +      ctrls.count = 1;
 +      ctrls.controls = &ctrl;
 +      ctrl.id = p->id;
 +      ctrl.value = p->value;
 +      if (check_ext_ctrls(&ctrls, 1)) {
 +              int ret = ops->vidioc_g_ext_ctrls(file, fh, &ctrls);
 +
 +              if (ret == 0)
 +                      p->value = ctrl.value;
 +              return ret;
 +      }
 +      return -EINVAL;
 +}
 +
 +static int v4l_s_ctrl(const struct v4l2_ioctl_ops *ops,
 +                              struct file *file, void *fh, void *arg)
 +{
 +      struct video_device *vfd = video_devdata(file);
 +      struct v4l2_control *p = arg;
 +      struct v4l2_fh *vfh =
 +              test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL;
 +      struct v4l2_ext_controls ctrls;
 +      struct v4l2_ext_control ctrl;
 +
 +      if (vfh && vfh->ctrl_handler)
 +              return v4l2_s_ctrl(vfh, vfh->ctrl_handler, p);
 +      if (vfd->ctrl_handler)
 +              return v4l2_s_ctrl(NULL, vfd->ctrl_handler, p);
 +      if (ops->vidioc_s_ctrl)
 +              return ops->vidioc_s_ctrl(file, fh, p);
 +      if (ops->vidioc_s_ext_ctrls == NULL)
 +              return -ENOTTY;
 +
 +      ctrls.ctrl_class = V4L2_CTRL_ID2CLASS(p->id);
 +      ctrls.count = 1;
 +      ctrls.controls = &ctrl;
 +      ctrl.id = p->id;
 +      ctrl.value = p->value;
 +      if (check_ext_ctrls(&ctrls, 1))
 +              return ops->vidioc_s_ext_ctrls(file, fh, &ctrls);
 +      return -EINVAL;
 +}
 +
 +static int v4l_g_ext_ctrls(const struct v4l2_ioctl_ops *ops,
 +                              struct file *file, void *fh, void *arg)
 +{
 +      struct video_device *vfd = video_devdata(file);
 +      struct v4l2_ext_controls *p = arg;
 +      struct v4l2_fh *vfh =
 +              test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL;
 +
 +      p->error_idx = p->count;
 +      if (vfh && vfh->ctrl_handler)
 +              return v4l2_g_ext_ctrls(vfh->ctrl_handler, p);
 +      if (vfd->ctrl_handler)
 +              return v4l2_g_ext_ctrls(vfd->ctrl_handler, p);
 +      if (ops->vidioc_g_ext_ctrls == NULL)
 +              return -ENOTTY;
 +      return check_ext_ctrls(p, 0) ? ops->vidioc_g_ext_ctrls(file, fh, p) :
 +                                      -EINVAL;
 +}
 +
 +static int v4l_s_ext_ctrls(const struct v4l2_ioctl_ops *ops,
 +                              struct file *file, void *fh, void *arg)
 +{
 +      struct video_device *vfd = video_devdata(file);
 +      struct v4l2_ext_controls *p = arg;
 +      struct v4l2_fh *vfh =
 +              test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL;
 +
 +      p->error_idx = p->count;
 +      if (vfh && vfh->ctrl_handler)
 +              return v4l2_s_ext_ctrls(vfh, vfh->ctrl_handler, p);
 +      if (vfd->ctrl_handler)
 +              return v4l2_s_ext_ctrls(NULL, vfd->ctrl_handler, p);
 +      if (ops->vidioc_s_ext_ctrls == NULL)
 +              return -ENOTTY;
 +      return check_ext_ctrls(p, 0) ? ops->vidioc_s_ext_ctrls(file, fh, p) :
 +                                      -EINVAL;
 +}
 +
 +static int v4l_try_ext_ctrls(const struct v4l2_ioctl_ops *ops,
 +                              struct file *file, void *fh, void *arg)
 +{
 +      struct video_device *vfd = video_devdata(file);
 +      struct v4l2_ext_controls *p = arg;
 +      struct v4l2_fh *vfh =
 +              test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags) ? fh : NULL;
 +
 +      p->error_idx = p->count;
 +      if (vfh && vfh->ctrl_handler)
 +              return v4l2_try_ext_ctrls(vfh->ctrl_handler, p);
 +      if (vfd->ctrl_handler)
 +              return v4l2_try_ext_ctrls(vfd->ctrl_handler, p);
 +      if (ops->vidioc_try_ext_ctrls == NULL)
 +              return -ENOTTY;
 +      return check_ext_ctrls(p, 0) ? ops->vidioc_try_ext_ctrls(file, fh, p) :
 +                                      -EINVAL;
 +}
 +
 +static int v4l_g_crop(const struct v4l2_ioctl_ops *ops,
 +                              struct file *file, void *fh, void *arg)
 +{
 +      struct v4l2_crop *p = arg;
 +      struct v4l2_selection s = {
 +              .type = p->type,
 +      };
 +      int ret;
 +
 +      if (ops->vidioc_g_crop)
 +              return ops->vidioc_g_crop(file, fh, p);
 +      /* simulate capture crop using selection api */
 +
 +      /* crop means compose for output devices */
 +      if (V4L2_TYPE_IS_OUTPUT(p->type))
 +              s.target = V4L2_SEL_TGT_COMPOSE_ACTIVE;
 +      else
 +              s.target = V4L2_SEL_TGT_CROP_ACTIVE;
 +
 +      ret = ops->vidioc_g_selection(file, fh, &s);
 +
 +      /* copying results to old structure on success */
 +      if (!ret)
 +              p->c = s.r;
 +      return ret;
 +}
 +
 +static int v4l_s_crop(const struct v4l2_ioctl_ops *ops,
 +                              struct file *file, void *fh, void *arg)
 +{
 +      struct v4l2_crop *p = arg;
 +      struct v4l2_selection s = {
 +              .type = p->type,
 +              .r = p->c,
 +      };
 +
 +      if (ops->vidioc_s_crop)
 +              return ops->vidioc_s_crop(file, fh, p);
 +      /* simulate capture crop using selection api */
 +
 +      /* crop means compose for output devices */
 +      if (V4L2_TYPE_IS_OUTPUT(p->type))
 +              s.target = V4L2_SEL_TGT_COMPOSE_ACTIVE;
 +      else
 +              s.target = V4L2_SEL_TGT_CROP_ACTIVE;
 +
 +      return ops->vidioc_s_selection(file, fh, &s);
 +}
 +
 +static int v4l_cropcap(const struct v4l2_ioctl_ops *ops,
 +                              struct file *file, void *fh, void *arg)
 +{
 +      struct v4l2_cropcap *p = arg;
 +      struct v4l2_selection s = { .type = p->type };
 +      int ret;
 +
 +      if (ops->vidioc_cropcap)
 +              return ops->vidioc_cropcap(file, fh, p);
 +
 +      /* obtaining bounds */
 +      if (V4L2_TYPE_IS_OUTPUT(p->type))
 +              s.target = V4L2_SEL_TGT_COMPOSE_BOUNDS;
 +      else
 +              s.target = V4L2_SEL_TGT_CROP_BOUNDS;
 +
 +      ret = ops->vidioc_g_selection(file, fh, &s);
 +      if (ret)
 +              return ret;
 +      p->bounds = s.r;
 +
 +      /* obtaining defrect */
 +      if (V4L2_TYPE_IS_OUTPUT(p->type))
 +              s.target = V4L2_SEL_TGT_COMPOSE_DEFAULT;
 +      else
 +              s.target = V4L2_SEL_TGT_CROP_DEFAULT;
 +
 +      ret = ops->vidioc_g_selection(file, fh, &s);
 +      if (ret)
 +              return ret;
 +      p->defrect = s.r;
 +
 +      /* setting trivial pixelaspect */
 +      p->pixelaspect.numerator = 1;
 +      p->pixelaspect.denominator = 1;
 +      return 0;
 +}
 +
 +static int v4l_log_status(const struct v4l2_ioctl_ops *ops,
 +                              struct file *file, void *fh, void *arg)
 +{
 +      struct video_device *vfd = video_devdata(file);
 +      int ret;
 +
 +      if (vfd->v4l2_dev)
 +              pr_info("%s: =================  START STATUS  =================\n",
 +                      vfd->v4l2_dev->name);
 +      ret = ops->vidioc_log_status(file, fh);
 +      if (vfd->v4l2_dev)
 +              pr_info("%s: ==================  END STATUS  ==================\n",
 +                      vfd->v4l2_dev->name);
 +      return ret;
 +}
 +
 +static int v4l_dbg_g_register(const struct v4l2_ioctl_ops *ops,
 +                              struct file *file, void *fh, void *arg)
 +{
 +#ifdef CONFIG_VIDEO_ADV_DEBUG
 +      struct v4l2_dbg_register *p = arg;
 +
 +      if (!capable(CAP_SYS_ADMIN))
 +              return -EPERM;
 +      return ops->vidioc_g_register(file, fh, p);
 +#else
 +      return -ENOTTY;
 +#endif
 +}
 +
 +static int v4l_dbg_s_register(const struct v4l2_ioctl_ops *ops,
 +                              struct file *file, void *fh, void *arg)
 +{
 +#ifdef CONFIG_VIDEO_ADV_DEBUG
 +      struct v4l2_dbg_register *p = arg;
 +
 +      if (!capable(CAP_SYS_ADMIN))
 +              return -EPERM;
 +      return ops->vidioc_s_register(file, fh, p);
 +#else
 +      return -ENOTTY;
 +#endif
 +}
 +
 +static int v4l_dbg_g_chip_ident(const struct v4l2_ioctl_ops *ops,
 +                              struct file *file, void *fh, void *arg)
 +{
 +      struct v4l2_dbg_chip_ident *p = arg;
 +
 +      p->ident = V4L2_IDENT_NONE;
 +      p->revision = 0;
 +      return ops->vidioc_g_chip_ident(file, fh, p);
 +}
 +
 +static int v4l_dqevent(const struct v4l2_ioctl_ops *ops,
 +                              struct file *file, void *fh, void *arg)
 +{
 +      return v4l2_event_dequeue(fh, arg, file->f_flags & O_NONBLOCK);
 +}
 +
 +static int v4l_subscribe_event(const struct v4l2_ioctl_ops *ops,
 +                              struct file *file, void *fh, void *arg)
 +{
 +      return ops->vidioc_subscribe_event(fh, arg);
 +}
 +
 +static int v4l_unsubscribe_event(const struct v4l2_ioctl_ops *ops,
 +                              struct file *file, void *fh, void *arg)
 +{
 +      return ops->vidioc_unsubscribe_event(fh, arg);
 +}
 +
 +static int v4l_g_sliced_vbi_cap(const struct v4l2_ioctl_ops *ops,
 +                              struct file *file, void *fh, void *arg)
 +{
 +      struct v4l2_sliced_vbi_cap *p = arg;
 +
 +      /* Clear up to type, everything after type is zeroed already */
 +      memset(p, 0, offsetof(struct v4l2_sliced_vbi_cap, type));
 +
 +      return ops->vidioc_g_sliced_vbi_cap(file, fh, p);
 +}
 +
 +static int v4l_enum_freq_bands(const struct v4l2_ioctl_ops *ops,
 +                              struct file *file, void *fh, void *arg)
 +{
 +      struct video_device *vfd = video_devdata(file);
 +      struct v4l2_frequency_band *p = arg;
 +      enum v4l2_tuner_type type;
 +      int err;
 +
 +      type = (vfd->vfl_type == VFL_TYPE_RADIO) ?
 +                      V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
 +
 +      if (type != p->type)
 +              return -EINVAL;
 +      if (ops->vidioc_enum_freq_bands)
 +              return ops->vidioc_enum_freq_bands(file, fh, p);
 +      if (ops->vidioc_g_tuner) {
 +              struct v4l2_tuner t = {
 +                      .index = p->tuner,
 +                      .type = type,
 +              };
 +
++              if (p->index)
++                      return -EINVAL;
 +              err = ops->vidioc_g_tuner(file, fh, &t);
 +              if (err)
 +                      return err;
 +              p->capability = t.capability | V4L2_TUNER_CAP_FREQ_BANDS;
 +              p->rangelow = t.rangelow;
 +              p->rangehigh = t.rangehigh;
 +              p->modulation = (type == V4L2_TUNER_RADIO) ?
 +                      V4L2_BAND_MODULATION_FM : V4L2_BAND_MODULATION_VSB;
 +              return 0;
 +      }
 +      if (ops->vidioc_g_modulator) {
 +              struct v4l2_modulator m = {
 +                      .index = p->tuner,
 +              };
 +
 +              if (type != V4L2_TUNER_RADIO)
 +                      return -EINVAL;
++              if (p->index)
++                      return -EINVAL;
 +              err = ops->vidioc_g_modulator(file, fh, &m);
 +              if (err)
 +                      return err;
 +              p->capability = m.capability | V4L2_TUNER_CAP_FREQ_BANDS;
 +              p->rangelow = m.rangelow;
 +              p->rangehigh = m.rangehigh;
 +              p->modulation = (type == V4L2_TUNER_RADIO) ?
 +                      V4L2_BAND_MODULATION_FM : V4L2_BAND_MODULATION_VSB;
 +              return 0;
 +      }
 +      return -ENOTTY;
 +}
 +
 +struct v4l2_ioctl_info {
 +      unsigned int ioctl;
 +      u32 flags;
 +      const char * const name;
 +      union {
 +              u32 offset;
 +              int (*func)(const struct v4l2_ioctl_ops *ops,
 +                              struct file *file, void *fh, void *p);
 +      } u;
 +      void (*debug)(const void *arg, bool write_only);
 +};
 +
 +/* This control needs a priority check */
 +#define INFO_FL_PRIO  (1 << 0)
 +/* This control can be valid if the filehandle passes a control handler. */
 +#define INFO_FL_CTRL  (1 << 1)
 +/* This is a standard ioctl, no need for special code */
 +#define INFO_FL_STD   (1 << 2)
 +/* This is ioctl has its own function */
 +#define INFO_FL_FUNC  (1 << 3)
 +/* Queuing ioctl */
 +#define INFO_FL_QUEUE (1 << 4)
 +/* 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)
 +#define INFO_FL_CLEAR_MASK (_IOC_SIZEMASK << 16)
 +
 +#define IOCTL_INFO_STD(_ioctl, _vidioc, _debug, _flags)                       \
 +      [_IOC_NR(_ioctl)] = {                                           \
 +              .ioctl = _ioctl,                                        \
 +              .flags = _flags | INFO_FL_STD,                          \
 +              .name = #_ioctl,                                        \
 +              .u.offset = offsetof(struct v4l2_ioctl_ops, _vidioc),   \
 +              .debug = _debug,                                        \
 +      }
 +
 +#define IOCTL_INFO_FNC(_ioctl, _func, _debug, _flags)                 \
 +      [_IOC_NR(_ioctl)] = {                                           \
 +              .ioctl = _ioctl,                                        \
 +              .flags = _flags | INFO_FL_FUNC,                         \
 +              .name = #_ioctl,                                        \
 +              .u.func = _func,                                        \
 +              .debug = _debug,                                        \
 +      }
 +
 +static struct v4l2_ioctl_info v4l2_ioctls[] = {
 +      IOCTL_INFO_FNC(VIDIOC_QUERYCAP, v4l_querycap, v4l_print_querycap, 0),
 +      IOCTL_INFO_FNC(VIDIOC_ENUM_FMT, v4l_enum_fmt, v4l_print_fmtdesc, INFO_FL_CLEAR(v4l2_fmtdesc, type)),
 +      IOCTL_INFO_FNC(VIDIOC_G_FMT, v4l_g_fmt, v4l_print_format, INFO_FL_CLEAR(v4l2_format, type)),
 +      IOCTL_INFO_FNC(VIDIOC_S_FMT, v4l_s_fmt, v4l_print_format, INFO_FL_PRIO),
 +      IOCTL_INFO_FNC(VIDIOC_REQBUFS, v4l_reqbufs, v4l_print_requestbuffers, INFO_FL_PRIO | INFO_FL_QUEUE),
 +      IOCTL_INFO_FNC(VIDIOC_QUERYBUF, v4l_querybuf, v4l_print_buffer, INFO_FL_QUEUE | INFO_FL_CLEAR(v4l2_buffer, length)),
 +      IOCTL_INFO_STD(VIDIOC_G_FBUF, vidioc_g_fbuf, v4l_print_framebuffer, 0),
 +      IOCTL_INFO_STD(VIDIOC_S_FBUF, vidioc_s_fbuf, v4l_print_framebuffer, INFO_FL_PRIO),
 +      IOCTL_INFO_STD(VIDIOC_OVERLAY, vidioc_overlay, v4l_print_u32, INFO_FL_PRIO),
 +      IOCTL_INFO_FNC(VIDIOC_QBUF, v4l_qbuf, v4l_print_buffer, INFO_FL_QUEUE),
 +      IOCTL_INFO_FNC(VIDIOC_DQBUF, v4l_dqbuf, v4l_print_buffer, INFO_FL_QUEUE),
 +      IOCTL_INFO_FNC(VIDIOC_STREAMON, v4l_streamon, v4l_print_buftype, INFO_FL_PRIO | INFO_FL_QUEUE),
 +      IOCTL_INFO_FNC(VIDIOC_STREAMOFF, v4l_streamoff, v4l_print_buftype, INFO_FL_PRIO | INFO_FL_QUEUE),
 +      IOCTL_INFO_FNC(VIDIOC_G_PARM, v4l_g_parm, v4l_print_streamparm, INFO_FL_CLEAR(v4l2_streamparm, type)),
 +      IOCTL_INFO_FNC(VIDIOC_S_PARM, v4l_s_parm, v4l_print_streamparm, INFO_FL_PRIO),
 +      IOCTL_INFO_FNC(VIDIOC_G_STD, v4l_g_std, v4l_print_std, 0),
 +      IOCTL_INFO_FNC(VIDIOC_S_STD, v4l_s_std, v4l_print_std, INFO_FL_PRIO),
 +      IOCTL_INFO_FNC(VIDIOC_ENUMSTD, v4l_enumstd, v4l_print_standard, INFO_FL_CLEAR(v4l2_standard, index)),
 +      IOCTL_INFO_FNC(VIDIOC_ENUMINPUT, v4l_enuminput, v4l_print_enuminput, INFO_FL_CLEAR(v4l2_input, index)),
 +      IOCTL_INFO_FNC(VIDIOC_G_CTRL, v4l_g_ctrl, v4l_print_control, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_control, id)),
 +      IOCTL_INFO_FNC(VIDIOC_S_CTRL, v4l_s_ctrl, v4l_print_control, INFO_FL_PRIO | INFO_FL_CTRL),
 +      IOCTL_INFO_FNC(VIDIOC_G_TUNER, v4l_g_tuner, v4l_print_tuner, INFO_FL_CLEAR(v4l2_tuner, index)),
 +      IOCTL_INFO_FNC(VIDIOC_S_TUNER, v4l_s_tuner, v4l_print_tuner, INFO_FL_PRIO),
 +      IOCTL_INFO_STD(VIDIOC_G_AUDIO, vidioc_g_audio, v4l_print_audio, 0),
 +      IOCTL_INFO_STD(VIDIOC_S_AUDIO, vidioc_s_audio, v4l_print_audio, INFO_FL_PRIO),
 +      IOCTL_INFO_FNC(VIDIOC_QUERYCTRL, v4l_queryctrl, v4l_print_queryctrl, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_queryctrl, id)),
 +      IOCTL_INFO_FNC(VIDIOC_QUERYMENU, v4l_querymenu, v4l_print_querymenu, INFO_FL_CTRL | INFO_FL_CLEAR(v4l2_querymenu, index)),
 +      IOCTL_INFO_STD(VIDIOC_G_INPUT, vidioc_g_input, v4l_print_u32, 0),
 +      IOCTL_INFO_FNC(VIDIOC_S_INPUT, v4l_s_input, v4l_print_u32, INFO_FL_PRIO),
 +      IOCTL_INFO_STD(VIDIOC_G_OUTPUT, vidioc_g_output, v4l_print_u32, 0),
 +      IOCTL_INFO_FNC(VIDIOC_S_OUTPUT, v4l_s_output, v4l_print_u32, INFO_FL_PRIO),
 +      IOCTL_INFO_FNC(VIDIOC_ENUMOUTPUT, v4l_enumoutput, v4l_print_enumoutput, INFO_FL_CLEAR(v4l2_output, index)),
 +      IOCTL_INFO_STD(VIDIOC_G_AUDOUT, vidioc_g_audout, v4l_print_audioout, 0),
 +      IOCTL_INFO_STD(VIDIOC_S_AUDOUT, vidioc_s_audout, v4l_print_audioout, INFO_FL_PRIO),
 +      IOCTL_INFO_FNC(VIDIOC_G_MODULATOR, v4l_g_modulator, v4l_print_modulator, INFO_FL_CLEAR(v4l2_modulator, index)),
 +      IOCTL_INFO_STD(VIDIOC_S_MODULATOR, vidioc_s_modulator, v4l_print_modulator, INFO_FL_PRIO),
 +      IOCTL_INFO_FNC(VIDIOC_G_FREQUENCY, v4l_g_frequency, v4l_print_frequency, INFO_FL_CLEAR(v4l2_frequency, tuner)),
 +      IOCTL_INFO_FNC(VIDIOC_S_FREQUENCY, v4l_s_frequency, v4l_print_frequency, INFO_FL_PRIO),
 +      IOCTL_INFO_FNC(VIDIOC_CROPCAP, v4l_cropcap, v4l_print_cropcap, INFO_FL_CLEAR(v4l2_cropcap, type)),
 +      IOCTL_INFO_FNC(VIDIOC_G_CROP, v4l_g_crop, v4l_print_crop, INFO_FL_CLEAR(v4l2_crop, type)),
 +      IOCTL_INFO_FNC(VIDIOC_S_CROP, v4l_s_crop, v4l_print_crop, INFO_FL_PRIO),
 +      IOCTL_INFO_STD(VIDIOC_G_SELECTION, vidioc_g_selection, v4l_print_selection, 0),
 +      IOCTL_INFO_STD(VIDIOC_S_SELECTION, vidioc_s_selection, v4l_print_selection, INFO_FL_PRIO),
 +      IOCTL_INFO_STD(VIDIOC_G_JPEGCOMP, vidioc_g_jpegcomp, v4l_print_jpegcompression, 0),
 +      IOCTL_INFO_STD(VIDIOC_S_JPEGCOMP, vidioc_s_jpegcomp, v4l_print_jpegcompression, INFO_FL_PRIO),
 +      IOCTL_INFO_FNC(VIDIOC_QUERYSTD, v4l_querystd, v4l_print_std, 0),
 +      IOCTL_INFO_FNC(VIDIOC_TRY_FMT, v4l_try_fmt, v4l_print_format, 0),
 +      IOCTL_INFO_STD(VIDIOC_ENUMAUDIO, vidioc_enumaudio, v4l_print_audio, INFO_FL_CLEAR(v4l2_audio, index)),
 +      IOCTL_INFO_STD(VIDIOC_ENUMAUDOUT, vidioc_enumaudout, v4l_print_audioout, INFO_FL_CLEAR(v4l2_audioout, index)),
 +      IOCTL_INFO_FNC(VIDIOC_G_PRIORITY, v4l_g_priority, v4l_print_u32, 0),
 +      IOCTL_INFO_FNC(VIDIOC_S_PRIORITY, v4l_s_priority, v4l_print_u32, INFO_FL_PRIO),
 +      IOCTL_INFO_FNC(VIDIOC_G_SLICED_VBI_CAP, v4l_g_sliced_vbi_cap, v4l_print_sliced_vbi_cap, INFO_FL_CLEAR(v4l2_sliced_vbi_cap, type)),
 +      IOCTL_INFO_FNC(VIDIOC_LOG_STATUS, v4l_log_status, v4l_print_newline, 0),
 +      IOCTL_INFO_FNC(VIDIOC_G_EXT_CTRLS, v4l_g_ext_ctrls, v4l_print_ext_controls, INFO_FL_CTRL),
 +      IOCTL_INFO_FNC(VIDIOC_S_EXT_CTRLS, v4l_s_ext_ctrls, v4l_print_ext_controls, INFO_FL_PRIO | INFO_FL_CTRL),
 +      IOCTL_INFO_FNC(VIDIOC_TRY_EXT_CTRLS, v4l_try_ext_ctrls, v4l_print_ext_controls, INFO_FL_CTRL),
 +      IOCTL_INFO_STD(VIDIOC_ENUM_FRAMESIZES, vidioc_enum_framesizes, v4l_print_frmsizeenum, INFO_FL_CLEAR(v4l2_frmsizeenum, pixel_format)),
 +      IOCTL_INFO_STD(VIDIOC_ENUM_FRAMEINTERVALS, vidioc_enum_frameintervals, v4l_print_frmivalenum, INFO_FL_CLEAR(v4l2_frmivalenum, height)),
 +      IOCTL_INFO_STD(VIDIOC_G_ENC_INDEX, vidioc_g_enc_index, v4l_print_enc_idx, 0),
 +      IOCTL_INFO_STD(VIDIOC_ENCODER_CMD, vidioc_encoder_cmd, v4l_print_encoder_cmd, INFO_FL_PRIO | INFO_FL_CLEAR(v4l2_encoder_cmd, flags)),
 +      IOCTL_INFO_STD(VIDIOC_TRY_ENCODER_CMD, vidioc_try_encoder_cmd, v4l_print_encoder_cmd, INFO_FL_CLEAR(v4l2_encoder_cmd, flags)),
 +      IOCTL_INFO_STD(VIDIOC_DECODER_CMD, vidioc_decoder_cmd, v4l_print_decoder_cmd, INFO_FL_PRIO),
 +      IOCTL_INFO_STD(VIDIOC_TRY_DECODER_CMD, vidioc_try_decoder_cmd, v4l_print_decoder_cmd, 0),
 +      IOCTL_INFO_FNC(VIDIOC_DBG_S_REGISTER, v4l_dbg_s_register, v4l_print_dbg_register, 0),
 +      IOCTL_INFO_FNC(VIDIOC_DBG_G_REGISTER, v4l_dbg_g_register, v4l_print_dbg_register, 0),
 +      IOCTL_INFO_FNC(VIDIOC_DBG_G_CHIP_IDENT, v4l_dbg_g_chip_ident, v4l_print_dbg_chip_ident, 0),
 +      IOCTL_INFO_FNC(VIDIOC_S_HW_FREQ_SEEK, v4l_s_hw_freq_seek, v4l_print_hw_freq_seek, INFO_FL_PRIO),
 +      IOCTL_INFO_STD(VIDIOC_ENUM_DV_PRESETS, vidioc_enum_dv_presets, v4l_print_dv_enum_presets, 0),
 +      IOCTL_INFO_STD(VIDIOC_S_DV_PRESET, vidioc_s_dv_preset, v4l_print_dv_preset, INFO_FL_PRIO),
 +      IOCTL_INFO_STD(VIDIOC_G_DV_PRESET, vidioc_g_dv_preset, v4l_print_dv_preset, 0),
 +      IOCTL_INFO_STD(VIDIOC_QUERY_DV_PRESET, vidioc_query_dv_preset, v4l_print_dv_preset, 0),
 +      IOCTL_INFO_STD(VIDIOC_S_DV_TIMINGS, vidioc_s_dv_timings, v4l_print_dv_timings, INFO_FL_PRIO),
 +      IOCTL_INFO_STD(VIDIOC_G_DV_TIMINGS, vidioc_g_dv_timings, v4l_print_dv_timings, 0),
 +      IOCTL_INFO_FNC(VIDIOC_DQEVENT, v4l_dqevent, v4l_print_event, 0),
 +      IOCTL_INFO_FNC(VIDIOC_SUBSCRIBE_EVENT, v4l_subscribe_event, v4l_print_event_subscription, 0),
 +      IOCTL_INFO_FNC(VIDIOC_UNSUBSCRIBE_EVENT, v4l_unsubscribe_event, v4l_print_event_subscription, 0),
 +      IOCTL_INFO_FNC(VIDIOC_CREATE_BUFS, v4l_create_bufs, v4l_print_create_buffers, INFO_FL_PRIO | INFO_FL_QUEUE),
 +      IOCTL_INFO_FNC(VIDIOC_PREPARE_BUF, v4l_prepare_buf, v4l_print_buffer, INFO_FL_QUEUE),
 +      IOCTL_INFO_STD(VIDIOC_ENUM_DV_TIMINGS, vidioc_enum_dv_timings, v4l_print_enum_dv_timings, 0),
 +      IOCTL_INFO_STD(VIDIOC_QUERY_DV_TIMINGS, vidioc_query_dv_timings, v4l_print_dv_timings, 0),
 +      IOCTL_INFO_STD(VIDIOC_DV_TIMINGS_CAP, vidioc_dv_timings_cap, v4l_print_dv_timings_cap, INFO_FL_CLEAR(v4l2_dv_timings_cap, type)),
 +      IOCTL_INFO_FNC(VIDIOC_ENUM_FREQ_BANDS, v4l_enum_freq_bands, v4l_print_freq_band, 0),
 +};
 +#define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls)
 +
 +bool v4l2_is_known_ioctl(unsigned int cmd)
 +{
 +      if (_IOC_NR(cmd) >= V4L2_IOCTLS)
 +              return false;
 +      return v4l2_ioctls[_IOC_NR(cmd)].ioctl == cmd;
 +}
 +
 +struct mutex *v4l2_ioctl_get_lock(struct video_device *vdev, unsigned cmd)
 +{
 +      if (_IOC_NR(cmd) >= V4L2_IOCTLS)
 +              return vdev->lock;
 +      if (test_bit(_IOC_NR(cmd), vdev->disable_locking))
 +              return NULL;
 +      if (vdev->queue && vdev->queue->lock &&
 +                      (v4l2_ioctls[_IOC_NR(cmd)].flags & INFO_FL_QUEUE))
 +              return vdev->queue->lock;
 +      return vdev->lock;
 +}
 +
 +/* Common ioctl debug function. This function can be used by
 +   external ioctl messages as well as internal V4L ioctl */
 +void v4l_printk_ioctl(const char *prefix, unsigned int cmd)
 +{
 +      const char *dir, *type;
 +
 +      if (prefix)
 +              printk(KERN_DEBUG "%s: ", prefix);
 +
 +      switch (_IOC_TYPE(cmd)) {
 +      case 'd':
 +              type = "v4l2_int";
 +              break;
 +      case 'V':
 +              if (_IOC_NR(cmd) >= V4L2_IOCTLS) {
 +                      type = "v4l2";
 +                      break;
 +              }
 +              pr_cont("%s", v4l2_ioctls[_IOC_NR(cmd)].name);
 +              return;
 +      default:
 +              type = "unknown";
 +              break;
 +      }
 +
 +      switch (_IOC_DIR(cmd)) {
 +      case _IOC_NONE:              dir = "--"; break;
 +      case _IOC_READ:              dir = "r-"; break;
 +      case _IOC_WRITE:             dir = "-w"; break;
 +      case _IOC_READ | _IOC_WRITE: dir = "rw"; break;
 +      default:                     dir = "*ERR*"; break;
 +      }
 +      pr_cont("%s ioctl '%c', dir=%s, #%d (0x%08x)",
 +              type, _IOC_TYPE(cmd), dir, _IOC_NR(cmd), cmd);
 +}
 +EXPORT_SYMBOL(v4l_printk_ioctl);
 +
 +static long __video_do_ioctl(struct file *file,
 +              unsigned int cmd, void *arg)
 +{
 +      struct video_device *vfd = video_devdata(file);
 +      const struct v4l2_ioctl_ops *ops = vfd->ioctl_ops;
 +      bool write_only = false;
 +      struct v4l2_ioctl_info default_info;
 +      const struct v4l2_ioctl_info *info;
 +      void *fh = file->private_data;
 +      struct v4l2_fh *vfh = NULL;
 +      int use_fh_prio = 0;
 +      int debug = vfd->debug;
 +      long ret = -ENOTTY;
 +
 +      if (ops == NULL) {
 +              pr_warn("%s: has no ioctl_ops.\n",
 +                              video_device_node_name(vfd));
 +              return ret;
 +      }
 +
 +      if (test_bit(V4L2_FL_USES_V4L2_FH, &vfd->flags)) {
 +              vfh = file->private_data;
 +              use_fh_prio = test_bit(V4L2_FL_USE_FH_PRIO, &vfd->flags);
 +      }
 +
 +      if (v4l2_is_known_ioctl(cmd)) {
 +              info = &v4l2_ioctls[_IOC_NR(cmd)];
 +
 +              if (!test_bit(_IOC_NR(cmd), vfd->valid_ioctls) &&
 +                  !((info->flags & INFO_FL_CTRL) && vfh && vfh->ctrl_handler))
 +                      goto done;
 +
 +              if (use_fh_prio && (info->flags & INFO_FL_PRIO)) {
 +                      ret = v4l2_prio_check(vfd->prio, vfh->prio);
 +                      if (ret)
 +                              goto done;
 +              }
 +      } else {
 +              default_info.ioctl = cmd;
 +              default_info.flags = 0;
 +              default_info.debug = v4l_print_default;
 +              info = &default_info;
 +      }
 +
 +      write_only = _IOC_DIR(cmd) == _IOC_WRITE;
 +      if (write_only && debug > V4L2_DEBUG_IOCTL) {
 +              v4l_printk_ioctl(video_device_node_name(vfd), cmd);
 +              pr_cont(": ");
 +              info->debug(arg, write_only);
 +      }
 +      if (info->flags & INFO_FL_STD) {
 +              typedef int (*vidioc_op)(struct file *file, void *fh, void *p);
 +              const void *p = vfd->ioctl_ops;
 +              const vidioc_op *vidioc = p + info->u.offset;
 +
 +              ret = (*vidioc)(file, fh, arg);
 +      } else if (info->flags & INFO_FL_FUNC) {
 +              ret = info->u.func(ops, file, fh, arg);
 +      } else if (!ops->vidioc_default) {
 +              ret = -ENOTTY;
 +      } else {
 +              ret = ops->vidioc_default(file, fh,
 +                      use_fh_prio ? v4l2_prio_check(vfd->prio, vfh->prio) >= 0 : 0,
 +                      cmd, arg);
 +      }
 +
 +done:
 +      if (debug) {
 +              if (write_only && debug > V4L2_DEBUG_IOCTL) {
 +                      if (ret < 0)
 +                              printk(KERN_DEBUG "%s: error %ld\n",
 +                                      video_device_node_name(vfd), ret);
 +                      return ret;
 +              }
 +              v4l_printk_ioctl(video_device_node_name(vfd), cmd);
 +              if (ret < 0)
 +                      pr_cont(": error %ld\n", ret);
 +              else if (debug == V4L2_DEBUG_IOCTL)
 +                      pr_cont("\n");
 +              else if (_IOC_DIR(cmd) == _IOC_NONE)
 +                      info->debug(arg, write_only);
 +              else {
 +                      pr_cont(": ");
 +                      info->debug(arg, write_only);
 +              }
 +      }
 +
 +      return ret;
 +}
 +
 +static int check_array_args(unsigned int cmd, void *parg, size_t *array_size,
 +                          void * __user *user_ptr, void ***kernel_ptr)
 +{
 +      int ret = 0;
 +
 +      switch (cmd) {
 +      case VIDIOC_QUERYBUF:
 +      case VIDIOC_QBUF:
 +      case VIDIOC_DQBUF: {
 +              struct v4l2_buffer *buf = parg;
 +
 +              if (V4L2_TYPE_IS_MULTIPLANAR(buf->type) && buf->length > 0) {
 +                      if (buf->length > VIDEO_MAX_PLANES) {
 +                              ret = -EINVAL;
 +                              break;
 +                      }
 +                      *user_ptr = (void __user *)buf->m.planes;
 +                      *kernel_ptr = (void *)&buf->m.planes;
 +                      *array_size = sizeof(struct v4l2_plane) * buf->length;
 +                      ret = 1;
 +              }
 +              break;
 +      }
 +
 +      case VIDIOC_S_EXT_CTRLS:
 +      case VIDIOC_G_EXT_CTRLS:
 +      case VIDIOC_TRY_EXT_CTRLS: {
 +              struct v4l2_ext_controls *ctrls = parg;
 +
 +              if (ctrls->count != 0) {
 +                      if (ctrls->count > V4L2_CID_MAX_CTRLS) {
 +                              ret = -EINVAL;
 +                              break;
 +                      }
 +                      *user_ptr = (void __user *)ctrls->controls;
 +                      *kernel_ptr = (void *)&ctrls->controls;
 +                      *array_size = sizeof(struct v4l2_ext_control)
 +                                  * ctrls->count;
 +                      ret = 1;
 +              }
 +              break;
 +      }
 +      }
 +
 +      return ret;
 +}
 +
 +long
 +video_usercopy(struct file *file, unsigned int cmd, unsigned long arg,
 +             v4l2_kioctl func)
 +{
 +      char    sbuf[128];
 +      void    *mbuf = NULL;
 +      void    *parg = (void *)arg;
 +      long    err  = -EINVAL;
 +      bool    has_array_args;
 +      size_t  array_size = 0;
 +      void __user *user_ptr = NULL;
 +      void    **kernel_ptr = NULL;
 +
 +      /*  Copy arguments into temp kernel buffer  */
 +      if (_IOC_DIR(cmd) != _IOC_NONE) {
 +              if (_IOC_SIZE(cmd) <= sizeof(sbuf)) {
 +                      parg = sbuf;
 +              } else {
 +                      /* too big to allocate from stack */
 +                      mbuf = kmalloc(_IOC_SIZE(cmd), GFP_KERNEL);
 +                      if (NULL == mbuf)
 +                              return -ENOMEM;
 +                      parg = mbuf;
 +              }
 +
 +              err = -EFAULT;
 +              if (_IOC_DIR(cmd) & _IOC_WRITE) {
 +                      unsigned int n = _IOC_SIZE(cmd);
 +
 +                      /*
 +                       * 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;
 +                      }
 +
 +                      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);
 +              } else {
 +                      /* read-only ioctl */
 +                      memset(parg, 0, _IOC_SIZE(cmd));
 +              }
 +      }
 +
 +      err = check_array_args(cmd, parg, &array_size, &user_ptr, &kernel_ptr);
 +      if (err < 0)
 +              goto out;
 +      has_array_args = err;
 +
 +      if (has_array_args) {
 +              /*
 +               * When adding new types of array args, make sure that the
 +               * parent argument to ioctl (which contains the pointer to the
 +               * array) fits into sbuf (so that mbuf will still remain
 +               * unused up to here).
 +               */
 +              mbuf = kmalloc(array_size, GFP_KERNEL);
 +              err = -ENOMEM;
 +              if (NULL == mbuf)
 +                      goto out_array_args;
 +              err = -EFAULT;
 +              if (copy_from_user(mbuf, user_ptr, array_size))
 +                      goto out_array_args;
 +              *kernel_ptr = mbuf;
 +      }
 +
 +      /* Handles IOCTL */
 +      err = func(file, cmd, parg);
 +      if (err == -ENOIOCTLCMD)
 +              err = -ENOTTY;
 +
 +      if (has_array_args) {
 +              *kernel_ptr = user_ptr;
 +              if (copy_to_user(user_ptr, mbuf, array_size))
 +                      err = -EFAULT;
 +              goto out_array_args;
 +      }
 +      /* VIDIOC_QUERY_DV_TIMINGS can return an error, but still have valid
 +         results that must be returned. */
 +      if (err < 0 && cmd != VIDIOC_QUERY_DV_TIMINGS)
 +              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(cmd)))
 +                      err = -EFAULT;
 +              break;
 +      }
 +
 +out:
 +      kfree(mbuf);
 +      return err;
 +}
 +EXPORT_SYMBOL(video_usercopy);
 +
 +long video_ioctl2(struct file *file,
 +             unsigned int cmd, unsigned long arg)
 +{
 +      return video_usercopy(file, cmd, arg, __video_do_ioctl);
 +}
 +EXPORT_SYMBOL(video_ioctl2);