V4L/DVB (11096): V4L2 Driver for the Hauppauge HD PVR usb capture device
authorJanne Grunau <j@jannau.net>
Wed, 18 Mar 2009 21:10:04 +0000 (18:10 -0300)
committerMauro Carvalho Chehab <mchehab@redhat.com>
Mon, 30 Mar 2009 15:43:28 +0000 (12:43 -0300)
The device encodes component video up to 1080i to a MPEG-TS stream with
H.264 video and stereo AAC audio. Newer firmwares accept also AC3
(up to 5.1) audio over optical SPDIF without reencoding.
Firmware upgrade is unimplemeted but rather unimportant since
the firmware sits on a flash chip.

The I2C adapter to drive the integrated infrared receiver/sender is
currently disabled due to a conflict with cx18-based devices.

Tested-by: Jarod Wilson <jarod@redhat.com>
Signed-off-by: Janne Grunau <j@jannau.net>
Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
drivers/media/video/Kconfig
drivers/media/video/Makefile
drivers/media/video/hdpvr/Kconfig [new file with mode: 0644]
drivers/media/video/hdpvr/Makefile [new file with mode: 0644]
drivers/media/video/hdpvr/hdpvr-control.c [new file with mode: 0644]
drivers/media/video/hdpvr/hdpvr-core.c [new file with mode: 0644]
drivers/media/video/hdpvr/hdpvr-i2c.c [new file with mode: 0644]
drivers/media/video/hdpvr/hdpvr-video.c [new file with mode: 0644]
drivers/media/video/hdpvr/hdpvr.h [new file with mode: 0644]
include/linux/i2c-id.h

index d5ddb81..3f85b9e 100644 (file)
@@ -789,6 +789,8 @@ source "drivers/media/video/gspca/Kconfig"
 
 source "drivers/media/video/pvrusb2/Kconfig"
 
+source "drivers/media/video/hdpvr/Kconfig"
+
 source "drivers/media/video/em28xx/Kconfig"
 
 source "drivers/media/video/usbvision/Kconfig"
index 08a0675..b904674 100644 (file)
@@ -119,6 +119,8 @@ obj-$(CONFIG_USB_PWC)           += pwc/
 obj-$(CONFIG_USB_ZC0301)        += zc0301/
 obj-$(CONFIG_USB_GSPCA)         += gspca/
 
+obj-$(CONFIG_VIDEO_HDPVR)      += hdpvr/
+
 obj-$(CONFIG_USB_IBMCAM)        += usbvideo/
 obj-$(CONFIG_USB_KONICAWC)      += usbvideo/
 obj-$(CONFIG_USB_VICAM)         += usbvideo/
diff --git a/drivers/media/video/hdpvr/Kconfig b/drivers/media/video/hdpvr/Kconfig
new file mode 100644 (file)
index 0000000..de247f3
--- /dev/null
@@ -0,0 +1,10 @@
+
+config VIDEO_HDPVR
+       tristate "Hauppauge HD PVR support"
+       depends on VIDEO_DEV
+       ---help---
+         This is a video4linux driver for Hauppauge's HD PVR USB device.
+
+         To compile this driver as a module, choose M here: the
+         module will be called hdpvr
+
diff --git a/drivers/media/video/hdpvr/Makefile b/drivers/media/video/hdpvr/Makefile
new file mode 100644 (file)
index 0000000..79ad2e1
--- /dev/null
@@ -0,0 +1,7 @@
+hdpvr-objs     := hdpvr-control.o hdpvr-core.o hdpvr-i2c.o hdpvr-video.o
+
+obj-$(CONFIG_VIDEO_HDPVR) += hdpvr.o
+
+EXTRA_CFLAGS += -Idrivers/media/video
+
+EXTRA_CFLAGS += $(extra-cflags-y) $(extra-cflags-m)
diff --git a/drivers/media/video/hdpvr/hdpvr-control.c b/drivers/media/video/hdpvr/hdpvr-control.c
new file mode 100644 (file)
index 0000000..ecf02c6
--- /dev/null
@@ -0,0 +1,201 @@
+/*
+ * Hauppage HD PVR USB driver - video 4 linux 2 interface
+ *
+ * Copyright (C) 2008      Janne Grunau (j@jannau.net)
+ *
+ *     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, version 2.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/mutex.h>
+
+#include <linux/videodev2.h>
+
+#include <media/v4l2-common.h>
+
+#include "hdpvr.h"
+
+
+int hdpvr_config_call(struct hdpvr_device *dev, uint value, u8 valbuf)
+{
+       int ret;
+       char request_type = 0x38, snd_request = 0x01;
+
+       msleep(10);
+
+       mutex_lock(&dev->usbc_mutex);
+       dev->usbc_buf[0] = valbuf;
+       ret = usb_control_msg(dev->udev,
+                             usb_sndctrlpipe(dev->udev, 0),
+                             snd_request, 0x00 | request_type,
+                             value, CTRL_DEFAULT_INDEX,
+                             dev->usbc_buf, 1, 10000);
+
+       mutex_unlock(&dev->usbc_mutex);
+       dev_dbg(&dev->udev->dev,
+               "config call request for value 0x%x returned %d\n", value,
+               ret);
+
+       return ret < 0 ? ret : 0;
+}
+
+struct hdpvr_video_info *get_video_info(struct hdpvr_device *dev)
+{
+       struct hdpvr_video_info *vidinf = NULL;
+#ifdef HDPVR_DEBUG
+       char print_buf[15];
+#endif
+       int ret;
+
+       vidinf = kzalloc(sizeof(struct hdpvr_video_info), GFP_KERNEL);
+       if (!vidinf) {
+               dev_err(&dev->udev->dev, "out of memory");
+               goto err;
+       }
+
+       mutex_lock(&dev->usbc_mutex);
+       ret = usb_control_msg(dev->udev,
+                             usb_rcvctrlpipe(dev->udev, 0),
+                             0x81, 0x80 | 0x38,
+                             0x1400, 0x0003,
+                             dev->usbc_buf, 5,
+                             1000);
+       if (ret == 5) {
+               vidinf->width   = dev->usbc_buf[1] << 8 | dev->usbc_buf[0];
+               vidinf->height  = dev->usbc_buf[3] << 8 | dev->usbc_buf[2];
+               vidinf->fps     = dev->usbc_buf[4];
+       }
+
+#ifdef HDPVR_DEBUG
+       if (hdpvr_debug & MSG_INFO) {
+               hex_dump_to_buffer(dev->usbc_buf, 5, 16, 1, print_buf,
+                                  sizeof(print_buf), 0);
+               dev_dbg(&dev->udev->dev, "get video info returned: %d, %s\n",
+                       ret, print_buf);
+       }
+#endif
+       mutex_unlock(&dev->usbc_mutex);
+
+       if (!vidinf->width || !vidinf->height || !vidinf->fps) {
+               kfree(vidinf);
+               vidinf = NULL;
+       }
+err:
+       return vidinf;
+}
+
+int get_input_lines_info(struct hdpvr_device *dev)
+{
+#ifdef HDPVR_DEBUG
+       char print_buf[9];
+#endif
+       int ret, lines;
+
+       mutex_lock(&dev->usbc_mutex);
+       ret = usb_control_msg(dev->udev,
+                             usb_rcvctrlpipe(dev->udev, 0),
+                             0x81, 0x80 | 0x38,
+                             0x1800, 0x0003,
+                             dev->usbc_buf, 3,
+                             1000);
+
+#ifdef HDPVR_DEBUG
+       if (hdpvr_debug & MSG_INFO) {
+               hex_dump_to_buffer(dev->usbc_buf, 3, 16, 1, print_buf,
+                                  sizeof(print_buf), 0);
+               dev_dbg(&dev->udev->dev,
+                       "get input lines info returned: %d, %s\n", ret,
+                       print_buf);
+       }
+#endif
+       lines = dev->usbc_buf[1] << 8 | dev->usbc_buf[0];
+       mutex_unlock(&dev->usbc_mutex);
+       return lines;
+}
+
+
+int hdpvr_set_bitrate(struct hdpvr_device *dev)
+{
+       int ret;
+
+       mutex_lock(&dev->usbc_mutex);
+       memset(dev->usbc_buf, 0, 4);
+       dev->usbc_buf[0] = dev->options.bitrate;
+       dev->usbc_buf[2] = dev->options.peak_bitrate;
+
+       ret = usb_control_msg(dev->udev,
+                             usb_sndctrlpipe(dev->udev, 0),
+                             0x01, 0x38, CTRL_BITRATE_VALUE,
+                             CTRL_DEFAULT_INDEX, dev->usbc_buf, 4, 1000);
+       mutex_unlock(&dev->usbc_mutex);
+
+       return ret;
+}
+
+int hdpvr_set_audio(struct hdpvr_device *dev, u8 input,
+                   enum v4l2_mpeg_audio_encoding codec)
+{
+       int ret = 0;
+
+       if (dev->flags & HDPVR_FLAG_AC3_CAP) {
+               mutex_lock(&dev->usbc_mutex);
+               memset(dev->usbc_buf, 0, 2);
+               dev->usbc_buf[0] = input;
+               if (codec == V4L2_MPEG_AUDIO_ENCODING_AAC)
+                       dev->usbc_buf[1] = 0;
+               else if (codec == V4L2_MPEG_AUDIO_ENCODING_AC3)
+                       dev->usbc_buf[1] = 1;
+               else {
+                       mutex_unlock(&dev->usbc_mutex);
+                       dev_err(&dev->udev->dev, "invalid audio codec %d\n",
+                               codec);
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               ret = usb_control_msg(dev->udev,
+                                     usb_sndctrlpipe(dev->udev, 0),
+                                     0x01, 0x38, CTRL_AUDIO_INPUT_VALUE,
+                                     CTRL_DEFAULT_INDEX, dev->usbc_buf, 2,
+                                     1000);
+               mutex_unlock(&dev->usbc_mutex);
+               if (ret == 2)
+                       ret = 0;
+       } else
+               ret = hdpvr_config_call(dev, CTRL_AUDIO_INPUT_VALUE,
+                                       dev->options.audio_input+1);
+error:
+       return ret;
+}
+
+int hdpvr_set_options(struct hdpvr_device *dev)
+{
+       hdpvr_config_call(dev, CTRL_VIDEO_STD_TYPE, dev->options.video_std);
+
+       hdpvr_config_call(dev, CTRL_VIDEO_INPUT_VALUE,
+                        dev->options.video_input+1);
+
+       hdpvr_set_audio(dev, dev->options.audio_input+1,
+                      dev->options.audio_codec);
+
+       hdpvr_set_bitrate(dev);
+       hdpvr_config_call(dev, CTRL_BITRATE_MODE_VALUE,
+                        dev->options.bitrate_mode);
+       hdpvr_config_call(dev, CTRL_GOP_MODE_VALUE, dev->options.gop_mode);
+
+       hdpvr_config_call(dev, CTRL_BRIGHTNESS, dev->options.brightness);
+       hdpvr_config_call(dev, CTRL_CONTRAST,   dev->options.contrast);
+       hdpvr_config_call(dev, CTRL_HUE,        dev->options.hue);
+       hdpvr_config_call(dev, CTRL_SATURATION, dev->options.saturation);
+       hdpvr_config_call(dev, CTRL_SHARPNESS,  dev->options.sharpness);
+
+       return 0;
+}
diff --git a/drivers/media/video/hdpvr/hdpvr-core.c b/drivers/media/video/hdpvr/hdpvr-core.c
new file mode 100644 (file)
index 0000000..e7300b5
--- /dev/null
@@ -0,0 +1,439 @@
+/*
+ * Hauppage HD PVR USB driver
+ *
+ * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com)
+ * Copyright (C) 2008      Janne Grunau (j@jannau.net)
+ * Copyright (C) 2008      John Poet
+ *
+ *     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, version 2.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <asm/atomic.h>
+#include <linux/usb.h>
+#include <linux/mutex.h>
+#include <linux/i2c.h>
+
+#include <linux/videodev2.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-common.h>
+
+#include "hdpvr.h"
+
+static int video_nr[HDPVR_MAX] = {[0 ... (HDPVR_MAX - 1)] = UNSET};
+module_param_array(video_nr, int, NULL, 0);
+MODULE_PARM_DESC(video_nr, "video device number (-1=Auto)");
+
+/* holds the number of currently registered devices */
+static atomic_t dev_nr = ATOMIC_INIT(-1);
+
+int hdpvr_debug;
+module_param(hdpvr_debug, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(hdpvr_debug, "enable debugging output");
+
+uint default_video_input = HDPVR_VIDEO_INPUTS;
+module_param(default_video_input, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(default_video_input, "default video input: 0=Component / "
+                "1=S-Video / 2=Composite");
+
+uint default_audio_input = HDPVR_AUDIO_INPUTS;
+module_param(default_audio_input, uint, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(default_audio_input, "default audio input: 0=RCA back / "
+                "1=RCA front / 2=S/PDIF");
+
+static int boost_audio;
+module_param(boost_audio, bool, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(boost_audio, "boost the audio signal");
+
+
+/* table of devices that work with this driver */
+static struct usb_device_id hdpvr_table[] = {
+       { USB_DEVICE(HD_PVR_VENDOR_ID, HD_PVR_PRODUCT_ID) },
+       { USB_DEVICE(HD_PVR_VENDOR_ID, HD_PVR_PRODUCT_ID1) },
+       { USB_DEVICE(HD_PVR_VENDOR_ID, HD_PVR_PRODUCT_ID2) },
+       { }                                     /* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, hdpvr_table);
+
+
+void hdpvr_delete(struct hdpvr_device *dev)
+{
+       hdpvr_free_buffers(dev);
+
+       if (dev->video_dev)
+               video_device_release(dev->video_dev);
+
+       usb_put_dev(dev->udev);
+}
+
+static void challenge(u8 *bytes)
+{
+       u64 *i64P, tmp64;
+       uint i, idx;
+
+       for (idx = 0; idx < 32; ++idx) {
+
+               if (idx & 0x3)
+                       bytes[(idx >> 3) + 3] = bytes[(idx >> 2) & 0x3];
+
+               switch (idx & 0x3) {
+               case 0x3:
+                       bytes[2] += bytes[3] * 4 + bytes[4] + bytes[5];
+                       bytes[4] += bytes[(idx & 0x1) * 2] * 9 + 9;
+                       break;
+               case 0x1:
+                       bytes[0] *= 8;
+                       bytes[0] += 7*idx + 4;
+                       bytes[6] += bytes[3] * 3;
+                       break;
+               case 0x0:
+                       bytes[3 - (idx >> 3)] = bytes[idx >> 2];
+                       bytes[5] += bytes[6] * 3;
+                       for (i = 0; i < 3; i++)
+                               bytes[3] *= bytes[3] + 1;
+                       break;
+               case 0x2:
+                       for (i = 0; i < 3; i++)
+                               bytes[1] *= bytes[6] + 1;
+                       for (i = 0; i < 3; i++) {
+                               i64P = (u64 *)bytes;
+                               tmp64 = le64_to_cpup(i64P);
+                               tmp64 <<= bytes[7] & 0x0f;
+                               *i64P += cpu_to_le64(tmp64);
+                       }
+                       break;
+               }
+       }
+}
+
+/* try to init the device like the windows driver */
+static int device_authorization(struct hdpvr_device *dev)
+{
+
+       int ret, retval = -ENOMEM;
+       char request_type = 0x38, rcv_request = 0x81;
+       char *response;
+#ifdef HDPVR_DEBUG
+       size_t buf_size = 46;
+       char *print_buf = kzalloc(5*buf_size+1, GFP_KERNEL);
+       if (!print_buf) {
+               dev_err(&dev->udev->dev, "Out of memory");
+               goto error;
+       }
+#endif
+
+       mutex_lock(&dev->usbc_mutex);
+       ret = usb_control_msg(dev->udev,
+                             usb_rcvctrlpipe(dev->udev, 0),
+                             rcv_request, 0x80 | request_type,
+                             0x0400, 0x0003,
+                             dev->usbc_buf, 46,
+                             10000);
+       if (ret != 46) {
+               dev_err(&dev->udev->dev,
+                       "unexpected answer of status request, len %d", ret);
+               goto error;
+       }
+#ifdef HDPVR_DEBUG
+       else {
+               hex_dump_to_buffer(dev->usbc_buf, 46, 16, 1, print_buf,
+                                  sizeof(print_buf), 0);
+               dev_dbg(&dev->udev->dev,
+                       "Status request returned, len %d: %s\n",
+                       ret, print_buf);
+       }
+#endif
+       if (dev->usbc_buf[1] == HDPVR_FIRMWARE_VERSION) {
+               dev->flags &= ~HDPVR_FLAG_AC3_CAP;
+       } else if (dev->usbc_buf[1] == HDPVR_FIRMWARE_VERSION_AC3) {
+               dev->flags |= HDPVR_FLAG_AC3_CAP;
+       } else if (dev->usbc_buf[1] > HDPVR_FIRMWARE_VERSION_AC3) {
+               dev_notice(&dev->udev->dev, "untested firmware version 0x%x, "
+                          "the driver might not work\n", dev->usbc_buf[1]);
+               dev->flags |= HDPVR_FLAG_AC3_CAP;
+       } else {
+               dev_err(&dev->udev->dev, "unknown firmware version 0x%x\n",
+                       dev->usbc_buf[1]);
+               ret = -EINVAL;
+               goto error;
+       }
+
+       response = dev->usbc_buf+38;
+#ifdef HDPVR_DEBUG
+       hex_dump_to_buffer(response, 8, 16, 1, print_buf, sizeof(print_buf), 0);
+       dev_dbg(&dev->udev->dev, "challenge: %s\n", print_buf);
+#endif
+       challenge(response);
+#ifdef HDPVR_DEBUG
+       hex_dump_to_buffer(response, 8, 16, 1, print_buf, sizeof(print_buf), 0);
+       dev_dbg(&dev->udev->dev, " response: %s\n", print_buf);
+#endif
+
+       msleep(100);
+       ret = usb_control_msg(dev->udev,
+                             usb_sndctrlpipe(dev->udev, 0),
+                             0xd1, 0x00 | request_type,
+                             0x0000, 0x0000,
+                             response, 8,
+                             10000);
+       dev_dbg(&dev->udev->dev, "magic request returned %d\n", ret);
+       mutex_unlock(&dev->usbc_mutex);
+
+       retval = ret != 8;
+error:
+       return retval;
+}
+
+static int hdpvr_device_init(struct hdpvr_device *dev)
+{
+       int ret;
+       u8 *buf;
+       struct hdpvr_video_info *vidinf;
+
+       if (device_authorization(dev))
+               return -EACCES;
+
+       /* default options for init */
+       hdpvr_set_options(dev);
+
+       /* set filter options */
+       mutex_lock(&dev->usbc_mutex);
+       buf = dev->usbc_buf;
+       buf[0] = 0x03; buf[1] = 0x03; buf[2] = 0x00; buf[3] = 0x00;
+       ret = usb_control_msg(dev->udev,
+                             usb_sndctrlpipe(dev->udev, 0),
+                             0x01, 0x38,
+                             CTRL_LOW_PASS_FILTER_VALUE, CTRL_DEFAULT_INDEX,
+                             buf, 4,
+                             1000);
+       dev_dbg(&dev->udev->dev, "control request returned %d\n", ret);
+       mutex_unlock(&dev->usbc_mutex);
+
+       vidinf = get_video_info(dev);
+       if (!vidinf)
+               dev_dbg(&dev->udev->dev,
+                       "no valid video signal or device init failed\n");
+       else
+               kfree(vidinf);
+
+       /* enable fan and bling leds */
+       mutex_lock(&dev->usbc_mutex);
+       buf[0] = 0x1;
+       ret = usb_control_msg(dev->udev,
+                             usb_sndctrlpipe(dev->udev, 0),
+                             0xd4, 0x38, 0, 0, buf, 1,
+                             1000);
+       dev_dbg(&dev->udev->dev, "control request returned %d\n", ret);
+
+       /* boost analog audio */
+       buf[0] = boost_audio;
+       ret = usb_control_msg(dev->udev,
+                             usb_sndctrlpipe(dev->udev, 0),
+                             0xd5, 0x38, 0, 0, buf, 1,
+                             1000);
+       dev_dbg(&dev->udev->dev, "control request returned %d\n", ret);
+       mutex_unlock(&dev->usbc_mutex);
+
+       dev->status = STATUS_IDLE;
+       return 0;
+}
+
+static const struct hdpvr_options hdpvr_default_options = {
+       .video_std      = HDPVR_60HZ,
+       .video_input    = HDPVR_COMPONENT,
+       .audio_input    = HDPVR_RCA_BACK,
+       .bitrate        = 65, /* 6 mbps */
+       .peak_bitrate   = 90, /* 9 mbps */
+       .bitrate_mode   = HDPVR_CONSTANT,
+       .gop_mode       = HDPVR_SIMPLE_IDR_GOP,
+       .audio_codec    = V4L2_MPEG_AUDIO_ENCODING_AAC,
+       .brightness     = 0x86,
+       .contrast       = 0x80,
+       .hue            = 0x80,
+       .saturation     = 0x80,
+       .sharpness      = 0x80,
+};
+
+static int hdpvr_probe(struct usb_interface *interface,
+                      const struct usb_device_id *id)
+{
+       struct hdpvr_device *dev;
+       struct usb_host_interface *iface_desc;
+       struct usb_endpoint_descriptor *endpoint;
+       size_t buffer_size;
+       int i;
+       int retval = -ENOMEM;
+
+       /* allocate memory for our device state and initialize it */
+       dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+       if (!dev) {
+               err("Out of memory");
+               goto error;
+       }
+       mutex_init(&dev->io_mutex);
+       mutex_init(&dev->i2c_mutex);
+       mutex_init(&dev->usbc_mutex);
+       dev->usbc_buf = kmalloc(64, GFP_KERNEL);
+       if (!dev->usbc_buf) {
+               dev_err(&dev->udev->dev, "Out of memory");
+               goto error;
+       }
+
+       init_waitqueue_head(&dev->wait_buffer);
+       init_waitqueue_head(&dev->wait_data);
+
+       dev->workqueue = create_singlethread_workqueue("hdpvr_buffer");
+       if (!dev->workqueue)
+               goto error;
+
+       /* init video transfer queues */
+       INIT_LIST_HEAD(&dev->free_buff_list);
+       INIT_LIST_HEAD(&dev->rec_buff_list);
+
+       dev->options = hdpvr_default_options;
+
+       if (default_video_input < HDPVR_VIDEO_INPUTS)
+               dev->options.video_input = default_video_input;
+
+       if (default_audio_input < HDPVR_AUDIO_INPUTS)
+               dev->options.audio_input = default_audio_input;
+
+       dev->udev = usb_get_dev(interface_to_usbdev(interface));
+
+       /* set up the endpoint information */
+       /* use only the first bulk-in and bulk-out endpoints */
+       iface_desc = interface->cur_altsetting;
+       for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+               endpoint = &iface_desc->endpoint[i].desc;
+
+               if (!dev->bulk_in_endpointAddr &&
+                   usb_endpoint_is_bulk_in(endpoint)) {
+                       /* USB interface description is buggy, reported max
+                        * packet size is 512 bytes, windows driver uses 8192 */
+                       buffer_size = 8192;
+                       dev->bulk_in_size = buffer_size;
+                       dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;
+               }
+
+       }
+       if (!dev->bulk_in_endpointAddr) {
+               err("Could not find bulk-in endpoint");
+               goto error;
+       }
+
+       /* init the device */
+       if (hdpvr_device_init(dev)) {
+               err("device init failed");
+               goto error;
+       }
+
+       mutex_lock(&dev->io_mutex);
+       if (hdpvr_alloc_buffers(dev, NUM_BUFFERS)) {
+               err("allocating transfer buffers failed");
+               goto error;
+       }
+       mutex_unlock(&dev->io_mutex);
+
+       if (hdpvr_register_videodev(dev,
+                                   video_nr[atomic_inc_return(&dev_nr)])) {
+               err("registering videodev failed");
+               goto error;
+       }
+
+
+       /* save our data pointer in this interface device */
+       usb_set_intfdata(interface, dev);
+
+       /* let the user know what node this device is now attached to */
+       v4l2_info(dev->video_dev, "device now attached to /dev/video%d\n",
+                 dev->video_dev->minor);
+       return 0;
+
+error:
+       if (dev) {
+               mutex_unlock(&dev->io_mutex);
+               /* this frees allocated memory */
+               hdpvr_delete(dev);
+       }
+       return retval;
+}
+
+static void hdpvr_disconnect(struct usb_interface *interface)
+{
+       struct hdpvr_device *dev;
+       int minor;
+
+       dev = usb_get_intfdata(interface);
+       usb_set_intfdata(interface, NULL);
+
+       minor = dev->video_dev->minor;
+
+       /* prevent more I/O from starting and stop any ongoing */
+       mutex_lock(&dev->io_mutex);
+       dev->status = STATUS_DISCONNECTED;
+       video_unregister_device(dev->video_dev);
+       wake_up_interruptible(&dev->wait_data);
+       wake_up_interruptible(&dev->wait_buffer);
+       msleep(100);
+       flush_workqueue(dev->workqueue);
+       hdpvr_cancel_queue(dev);
+       destroy_workqueue(dev->workqueue);
+       mutex_unlock(&dev->io_mutex);
+
+       /* deregister I2C adapter */
+       mutex_lock(&dev->i2c_mutex);
+       if (dev->i2c_adapter)
+               i2c_del_adapter(dev->i2c_adapter);
+       kfree(dev->i2c_adapter);
+       dev->i2c_adapter = NULL;
+       mutex_unlock(&dev->i2c_mutex);
+
+       atomic_dec(&dev_nr);
+
+       printk(KERN_INFO "Hauppauge HD PVR: device /dev/video%d disconnected\n",
+              minor);
+
+       kfree(dev->usbc_buf);
+       kfree(dev);
+}
+
+
+static struct usb_driver hdpvr_usb_driver = {
+       .name =         "hdpvr",
+       .probe =        hdpvr_probe,
+       .disconnect =   hdpvr_disconnect,
+       .id_table =     hdpvr_table,
+};
+
+static int __init hdpvr_init(void)
+{
+       int result;
+
+       /* register this driver with the USB subsystem */
+       result = usb_register(&hdpvr_usb_driver);
+       if (result)
+               err("usb_register failed. Error number %d", result);
+
+       return result;
+}
+
+static void __exit hdpvr_exit(void)
+{
+       /* deregister this driver with the USB subsystem */
+       usb_deregister(&hdpvr_usb_driver);
+}
+
+module_init(hdpvr_init);
+module_exit(hdpvr_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Janne Grunau");
+MODULE_DESCRIPTION("Hauppauge HD PVR driver");
diff --git a/drivers/media/video/hdpvr/hdpvr-i2c.c b/drivers/media/video/hdpvr/hdpvr-i2c.c
new file mode 100644 (file)
index 0000000..35096de
--- /dev/null
@@ -0,0 +1,145 @@
+
+/*
+ * Hauppage HD PVR USB driver
+ *
+ * Copyright (C) 2008      Janne Grunau (j@jannau.net)
+ *
+ *     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, version 2.
+ *
+ */
+
+#include <linux/i2c.h>
+
+#include "hdpvr.h"
+
+#define CTRL_READ_REQUEST      0xb8
+#define CTRL_WRITE_REQUEST     0x38
+
+#define REQTYPE_I2C_READ       0xb1
+#define REQTYPE_I2C_WRITE      0xb0
+#define REQTYPE_I2C_WRITE_STATT        0xd0
+
+static int hdpvr_i2c_read(struct hdpvr_device *dev, unsigned char addr,
+                         char *data, int len)
+{
+       int ret;
+       char *buf = kmalloc(len, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       ret = usb_control_msg(dev->udev,
+                             usb_rcvctrlpipe(dev->udev, 0),
+                             REQTYPE_I2C_READ, CTRL_READ_REQUEST,
+                             0x100|addr, 0, buf, len, 1000);
+
+       if (ret == len) {
+               memcpy(data, buf, len);
+               ret = 0;
+       } else if (ret >= 0)
+               ret = -EIO;
+
+       kfree(buf);
+
+       return ret;
+}
+
+static int hdpvr_i2c_write(struct hdpvr_device *dev, unsigned char addr,
+                          char *data, int len)
+{
+       int ret;
+       char *buf = kmalloc(len, GFP_KERNEL);
+       if (!buf)
+               return -ENOMEM;
+
+       memcpy(buf, data, len);
+       ret = usb_control_msg(dev->udev,
+                             usb_sndctrlpipe(dev->udev, 0),
+                             REQTYPE_I2C_WRITE, CTRL_WRITE_REQUEST,
+                             0x100|addr, 0, buf, len, 1000);
+
+       if (ret < 0)
+               goto error;
+
+       ret = usb_control_msg(dev->udev,
+                             usb_rcvctrlpipe(dev->udev, 0),
+                             REQTYPE_I2C_WRITE_STATT, CTRL_READ_REQUEST,
+                             0, 0, buf, 2, 1000);
+
+       if (ret == 2)
+               ret = 0;
+       else if (ret >= 0)
+               ret = -EIO;
+
+error:
+       kfree(buf);
+       return ret;
+}
+
+static int hdpvr_transfer(struct i2c_adapter *i2c_adapter, struct i2c_msg *msgs,
+                         int num)
+{
+       struct hdpvr_device *dev = i2c_get_adapdata(i2c_adapter);
+       int retval = 0, i, addr;
+
+       if (num <= 0)
+               return 0;
+
+       mutex_lock(&dev->i2c_mutex);
+
+       for (i = 0; i < num && !retval; i++) {
+               addr = msgs[i].addr << 1;
+
+               if (msgs[i].flags & I2C_M_RD)
+                       retval = hdpvr_i2c_read(dev, addr, msgs[i].buf,
+                                               msgs[i].len);
+               else
+                       retval = hdpvr_i2c_write(dev, addr, msgs[i].buf,
+                                                msgs[i].len);
+       }
+
+       mutex_unlock(&dev->i2c_mutex);
+
+       return retval ? retval : num;
+}
+
+static u32 hdpvr_functionality(struct i2c_adapter *adapter)
+{
+       return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
+}
+
+static struct i2c_algorithm hdpvr_algo = {
+       .master_xfer   = hdpvr_transfer,
+       .functionality = hdpvr_functionality,
+};
+
+int hdpvr_register_i2c_adapter(struct hdpvr_device *dev)
+{
+       struct i2c_adapter *i2c_adap;
+       int retval = -ENOMEM;
+
+       i2c_adap = kzalloc(sizeof(struct i2c_adapter), GFP_KERNEL);
+       if (i2c_adap == NULL)
+               goto error;
+
+       strlcpy(i2c_adap->name, "Hauppauge HD PVR I2C",
+               sizeof(i2c_adap->name));
+       i2c_adap->algo  = &hdpvr_algo;
+       i2c_adap->class = I2C_CLASS_TV_ANALOG;
+       i2c_adap->id    = I2C_HW_B_HDPVR;
+       i2c_adap->owner = THIS_MODULE;
+       i2c_adap->dev.parent = &dev->udev->dev;
+
+       i2c_set_adapdata(i2c_adap, dev);
+
+       retval = i2c_add_adapter(i2c_adap);
+
+       if (!retval)
+               dev->i2c_adapter = i2c_adap;
+       else
+               kfree(i2c_adap);
+
+error:
+       return retval;
+}
diff --git a/drivers/media/video/hdpvr/hdpvr-video.c b/drivers/media/video/hdpvr/hdpvr-video.c
new file mode 100644 (file)
index 0000000..ee48149
--- /dev/null
@@ -0,0 +1,1228 @@
+/*
+ * Hauppage HD PVR USB driver - video 4 linux 2 interface
+ *
+ * Copyright (C) 2008      Janne Grunau (j@jannau.net)
+ *
+ *     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, version 2.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <linux/usb.h>
+#include <linux/mutex.h>
+#include <linux/version.h>
+#include <linux/workqueue.h>
+
+#include <linux/videodev2.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-ioctl.h>
+#include "hdpvr.h"
+
+#define BULK_URB_TIMEOUT 1250 /* 1.25 seconds */
+
+struct hdpvr_fh {
+       struct hdpvr_device     *dev;
+};
+
+static uint list_size(struct list_head *list)
+{
+       struct list_head *tmp;
+       uint count = 0;
+
+       list_for_each(tmp, list) {
+               count++;
+       }
+
+       return count;
+}
+
+/*=========================================================================*/
+/* urb callback */
+static void hdpvr_read_bulk_callback(struct urb *urb)
+{
+       struct hdpvr_buffer *buf = (struct hdpvr_buffer *)urb->context;
+       struct hdpvr_device *dev = buf->dev;
+
+       /* marking buffer as received and wake waiting */
+       buf->status = BUFSTAT_READY;
+       wake_up_interruptible(&dev->wait_data);
+}
+
+/*=========================================================================*/
+/* bufffer bits */
+
+/* function expects dev->io_mutex to be hold by caller */
+int hdpvr_cancel_queue(struct hdpvr_device *dev)
+{
+       struct hdpvr_buffer *buf;
+
+       list_for_each_entry(buf, &dev->rec_buff_list, buff_list) {
+               usb_kill_urb(buf->urb);
+               buf->status = BUFSTAT_AVAILABLE;
+       }
+
+       list_splice_init(&dev->rec_buff_list, dev->free_buff_list.prev);
+
+       return 0;
+}
+
+static int hdpvr_free_queue(struct list_head *q)
+{
+       struct list_head *tmp;
+       struct list_head *p;
+       struct hdpvr_buffer *buf;
+       struct urb *urb;
+
+       for (p = q->next; p != q;) {
+               buf = list_entry(p, struct hdpvr_buffer, buff_list);
+
+               urb = buf->urb;
+               usb_buffer_free(urb->dev, urb->transfer_buffer_length,
+                               urb->transfer_buffer, urb->transfer_dma);
+               usb_free_urb(urb);
+               tmp = p->next;
+               list_del(p);
+               kfree(buf);
+               p = tmp;
+       }
+
+       return 0;
+}
+
+/* function expects dev->io_mutex to be hold by caller */
+int hdpvr_free_buffers(struct hdpvr_device *dev)
+{
+       hdpvr_cancel_queue(dev);
+
+       hdpvr_free_queue(&dev->free_buff_list);
+       hdpvr_free_queue(&dev->rec_buff_list);
+
+       return 0;
+}
+
+/* function expects dev->io_mutex to be hold by caller */
+int hdpvr_alloc_buffers(struct hdpvr_device *dev, uint count)
+{
+       uint i;
+       int retval = -ENOMEM;
+       u8 *mem;
+       struct hdpvr_buffer *buf;
+       struct urb *urb;
+
+       v4l2_dbg(MSG_INFO, hdpvr_debug, dev->video_dev,
+                "allocating %u buffers\n", count);
+
+       for (i = 0; i < count; i++) {
+
+               buf = kzalloc(sizeof(struct hdpvr_buffer), GFP_KERNEL);
+               if (!buf) {
+                       err("cannot allocate buffer");
+                       goto exit;
+               }
+               buf->dev = dev;
+
+               urb = usb_alloc_urb(0, GFP_KERNEL);
+               if (!urb) {
+                       err("cannot allocate urb");
+                       goto exit;
+               }
+               buf->urb = urb;
+
+               mem = usb_buffer_alloc(dev->udev, dev->bulk_in_size, GFP_KERNEL,
+                                      &urb->transfer_dma);
+               if (!mem) {
+                       err("cannot allocate usb transfer buffer");
+                       goto exit;
+               }
+
+               usb_fill_bulk_urb(buf->urb, dev->udev,
+                                 usb_rcvbulkpipe(dev->udev,
+                                                 dev->bulk_in_endpointAddr),
+                                 mem, dev->bulk_in_size,
+                                 hdpvr_read_bulk_callback, buf);
+
+               buf->status = BUFSTAT_AVAILABLE;
+               list_add_tail(&buf->buff_list, &dev->free_buff_list);
+       }
+       return 0;
+exit:
+       hdpvr_free_buffers(dev);
+       return retval;
+}
+
+static int hdpvr_submit_buffers(struct hdpvr_device *dev)
+{
+       struct hdpvr_buffer *buf;
+       struct urb *urb;
+       int ret = 0, err_count = 0;
+
+       mutex_lock(&dev->io_mutex);
+
+       while (dev->status == STATUS_STREAMING &&
+              !list_empty(&dev->free_buff_list)) {
+
+               buf = list_entry(dev->free_buff_list.next, struct hdpvr_buffer,
+                                buff_list);
+               if (buf->status != BUFSTAT_AVAILABLE) {
+                       err("buffer not marked as availbale");
+                       ret = -EFAULT;
+                       goto err;
+               }
+
+               urb = buf->urb;
+               urb->status = 0;
+               urb->actual_length = 0;
+               ret = usb_submit_urb(urb, GFP_KERNEL);
+               if (ret) {
+                       err("usb_submit_urb in %s returned %d", __func__, ret);
+                       if (++err_count > 2)
+                               break;
+                       continue;
+               }
+               buf->status = BUFSTAT_INPROGRESS;
+               list_move_tail(&buf->buff_list, &dev->rec_buff_list);
+       }
+err:
+       v4l2_dbg(MSG_BUFFER, hdpvr_debug, dev->video_dev,
+                "buffer queue stat: %d free, %d proc\n",
+                list_size(&dev->free_buff_list),
+                list_size(&dev->rec_buff_list));
+       mutex_unlock(&dev->io_mutex);
+       return ret;
+}
+
+static struct hdpvr_buffer *hdpvr_get_next_buffer(struct hdpvr_device *dev)
+{
+       struct hdpvr_buffer *buf;
+
+       mutex_lock(&dev->io_mutex);
+
+       if (list_empty(&dev->rec_buff_list)) {
+               mutex_unlock(&dev->io_mutex);
+               return NULL;
+       }
+
+       buf = list_entry(dev->rec_buff_list.next, struct hdpvr_buffer,
+                        buff_list);
+       mutex_unlock(&dev->io_mutex);
+
+       return buf;
+}
+
+static void hdpvr_transmit_buffers(struct work_struct *work)
+{
+       struct hdpvr_device *dev = container_of(work, struct hdpvr_device,
+                                               worker);
+
+       while (dev->status == STATUS_STREAMING) {
+
+               if (hdpvr_submit_buffers(dev)) {
+                       v4l2_err(dev->video_dev, "couldn't submit buffers\n");
+                       goto error;
+               }
+               if (wait_event_interruptible(dev->wait_buffer,
+                               !list_empty(&dev->free_buff_list) ||
+                                            dev->status != STATUS_STREAMING))
+                       goto error;
+       }
+
+       v4l2_dbg(MSG_INFO, hdpvr_debug, dev->video_dev,
+                "transmit worker exited\n");
+       return;
+error:
+       v4l2_dbg(MSG_INFO, hdpvr_debug, dev->video_dev,
+                "transmit buffers errored\n");
+       dev->status = STATUS_ERROR;
+}
+
+/* function expects dev->io_mutex to be hold by caller */
+static int hdpvr_start_streaming(struct hdpvr_device *dev)
+{
+       int ret;
+       struct hdpvr_video_info *vidinf;
+
+       if (dev->status == STATUS_STREAMING)
+               return 0;
+       else if (dev->status != STATUS_IDLE)
+               return -EAGAIN;
+
+       vidinf = get_video_info(dev);
+
+       if (vidinf) {
+               v4l2_dbg(MSG_BUFFER, hdpvr_debug, dev->video_dev,
+                        "video signal: %dx%d@%dhz\n", vidinf->width,
+                        vidinf->height, vidinf->fps);
+               kfree(vidinf);
+
+               /* start streaming 2 request */
+               ret = usb_control_msg(dev->udev,
+                                     usb_sndctrlpipe(dev->udev, 0),
+                                     0xb8, 0x38, 0x1, 0, NULL, 0, 8000);
+               v4l2_dbg(MSG_BUFFER, hdpvr_debug, dev->video_dev,
+                        "encoder start control request returned %d\n", ret);
+
+               hdpvr_config_call(dev, CTRL_START_STREAMING_VALUE, 0x00);
+
+               INIT_WORK(&dev->worker, hdpvr_transmit_buffers);
+               queue_work(dev->workqueue, &dev->worker);
+
+               v4l2_dbg(MSG_BUFFER, hdpvr_debug, dev->video_dev,
+                        "streaming started\n");
+               dev->status = STATUS_STREAMING;
+
+               return 0;
+       }
+       msleep(250);
+       v4l2_dbg(MSG_INFO, hdpvr_debug, dev->video_dev,
+                "no video signal at input %d\n", dev->options.video_input);
+       return -EAGAIN;
+}
+
+
+/* function expects dev->io_mutex to be hold by caller */
+static int hdpvr_stop_streaming(struct hdpvr_device *dev)
+{
+       if (dev->status == STATUS_IDLE)
+               return 0;
+       else if (dev->status != STATUS_STREAMING)
+               return -EAGAIN;
+
+       dev->status = STATUS_SHUTTING_DOWN;
+       hdpvr_config_call(dev, CTRL_STOP_STREAMING_VALUE, 0x00);
+
+       wake_up_interruptible(&dev->wait_buffer);
+       msleep(50);
+
+       flush_workqueue(dev->workqueue);
+
+       /* kill the still outstanding urbs */
+       hdpvr_cancel_queue(dev);
+
+       dev->status = STATUS_IDLE;
+
+       return 0;
+}
+
+
+/*=======================================================================*/
+/*
+ * video 4 linux 2 file operations
+ */
+
+static int hdpvr_open(struct file *file)
+{
+       struct hdpvr_device *dev;
+       struct hdpvr_fh *fh;
+       int retval = -ENOMEM;
+
+       dev = (struct hdpvr_device *)video_get_drvdata(video_devdata(file));
+       if (!dev) {
+               err("open failing with with ENODEV");
+               retval = -ENODEV;
+               goto err;
+       }
+
+       fh = kzalloc(sizeof(struct hdpvr_fh), GFP_KERNEL);
+       if (!fh) {
+               err("Out of memory?");
+               goto err;
+       }
+       /* lock the device to allow correctly handling errors
+        * in resumption */
+       mutex_lock(&dev->io_mutex);
+       dev->open_count++;
+
+       fh->dev = dev;
+
+       /* save our object in the file's private structure */
+       file->private_data = fh;
+
+       retval = 0;
+err:
+       mutex_unlock(&dev->io_mutex);
+       return retval;
+}
+
+static int hdpvr_release(struct file *file)
+{
+       struct hdpvr_fh         *fh  = (struct hdpvr_fh *)file->private_data;
+       struct hdpvr_device     *dev = fh->dev;
+
+       if (!dev)
+               return -ENODEV;
+
+       mutex_lock(&dev->io_mutex);
+       if (!(--dev->open_count) && dev->status == STATUS_STREAMING)
+               hdpvr_stop_streaming(dev);
+
+       mutex_unlock(&dev->io_mutex);
+
+       return 0;
+}
+
+/*
+ * hdpvr_v4l2_read()
+ * will allocate buffers when called for the first time
+ */
+static ssize_t hdpvr_read(struct file *file, char __user *buffer, size_t count,
+                         loff_t *pos)
+{
+       struct hdpvr_fh *fh = file->private_data;
+       struct hdpvr_device *dev = fh->dev;
+       struct hdpvr_buffer *buf = NULL;
+       struct urb *urb;
+       unsigned int ret = 0;
+       int rem, cnt;
+
+       if (*pos)
+               return -ESPIPE;
+
+       if (!dev)
+               return -ENODEV;
+
+       mutex_lock(&dev->io_mutex);
+       if (dev->status == STATUS_IDLE) {
+               if (hdpvr_start_streaming(dev)) {
+                       v4l2_dbg(MSG_INFO, hdpvr_debug, dev->video_dev,
+                                "start_streaming failed");
+                       ret = -EIO;
+                       msleep(200);
+                       dev->status = STATUS_IDLE;
+                       mutex_unlock(&dev->io_mutex);
+                       goto err;
+               }
+
+               v4l2_dbg(MSG_BUFFER, hdpvr_debug, dev->video_dev,
+                        "buffer queue stat: %d free, %d proc\n",
+                        list_size(&dev->free_buff_list),
+                        list_size(&dev->rec_buff_list));
+       }
+       mutex_unlock(&dev->io_mutex);
+
+       /* wait for the first buffer */
+       if (!(file->f_flags & O_NONBLOCK)) {
+               if (wait_event_interruptible(dev->wait_data,
+                                            hdpvr_get_next_buffer(dev)))
+                       return -ERESTARTSYS;
+       }
+
+       buf = hdpvr_get_next_buffer(dev);
+
+       while (count > 0 && buf) {
+
+               if (buf->status != BUFSTAT_READY &&
+                   dev->status != STATUS_DISCONNECTED) {
+                       /* return nonblocking */
+                       if (file->f_flags & O_NONBLOCK) {
+                               if (!ret)
+                                       ret = -EAGAIN;
+                               goto err;
+                       }
+
+                       if (wait_event_interruptible(dev->wait_data,
+                                             buf->status == BUFSTAT_READY)) {
+                               ret = -ERESTARTSYS;
+                               goto err;
+                       }
+               }
+
+               if (buf->status != BUFSTAT_READY)
+                       break;
+
+               /* set remaining bytes to copy */
+               urb = buf->urb;
+               rem = urb->actual_length - buf->pos;
+               cnt = rem > count ? count : rem;
+
+               if (copy_to_user(buffer, urb->transfer_buffer + buf->pos,
+                                cnt)) {
+                       err("read: copy_to_user failed");
+                       if (!ret)
+                               ret = -EFAULT;
+                       goto err;
+               }
+
+               buf->pos += cnt;
+               count -= cnt;
+               buffer += cnt;
+               ret += cnt;
+
+               /* finished, take next buffer */
+               if (buf->pos == urb->actual_length) {
+                       mutex_lock(&dev->io_mutex);
+                       buf->pos = 0;
+                       buf->status = BUFSTAT_AVAILABLE;
+
+                       list_move_tail(&buf->buff_list, &dev->free_buff_list);
+
+                       v4l2_dbg(MSG_BUFFER, hdpvr_debug, dev->video_dev,
+                                "buffer queue stat: %d free, %d proc\n",
+                                list_size(&dev->free_buff_list),
+                                list_size(&dev->rec_buff_list));
+
+                       mutex_unlock(&dev->io_mutex);
+
+                       wake_up_interruptible(&dev->wait_buffer);
+
+                       buf = hdpvr_get_next_buffer(dev);
+               }
+       }
+err:
+       if (!ret && !buf)
+               ret = -EAGAIN;
+       return ret;
+}
+
+static unsigned int hdpvr_poll(struct file *filp, poll_table *wait)
+{
+       struct hdpvr_fh *fh = (struct hdpvr_fh *)filp->private_data;
+       struct hdpvr_device *dev = fh->dev;
+       unsigned int mask = 0;
+
+       mutex_lock(&dev->io_mutex);
+
+       if (video_is_unregistered(dev->video_dev))
+               return -EIO;
+
+       if (dev->status == STATUS_IDLE) {
+               if (hdpvr_start_streaming(dev)) {
+                       v4l2_dbg(MSG_BUFFER, hdpvr_debug, dev->video_dev,
+                                "start_streaming failed");
+                       dev->status = STATUS_IDLE;
+               }
+
+               v4l2_dbg(MSG_BUFFER, hdpvr_debug, dev->video_dev,
+                        "buffer queue stat: %d free, %d proc\n",
+                        list_size(&dev->free_buff_list),
+                        list_size(&dev->rec_buff_list));
+       }
+       mutex_unlock(&dev->io_mutex);
+
+       poll_wait(filp, &dev->wait_data, wait);
+
+       mutex_lock(&dev->io_mutex);
+       if (!list_empty(&dev->rec_buff_list)) {
+
+               struct hdpvr_buffer *buf = list_entry(dev->rec_buff_list.next,
+                                                     struct hdpvr_buffer,
+                                                     buff_list);
+
+               if (buf->status == BUFSTAT_READY)
+                       mask |= POLLIN | POLLRDNORM;
+       }
+       mutex_unlock(&dev->io_mutex);
+
+       return mask;
+}
+
+
+static long hdpvr_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+       struct hdpvr_fh *fh = (struct hdpvr_fh *)filp->private_data;
+       struct hdpvr_device *dev = fh->dev;
+       int res;
+
+       if (video_is_unregistered(dev->video_dev))
+               return -EIO;
+
+       mutex_lock(&dev->io_mutex);
+       switch (cmd) {
+       case VIDIOC_TRY_ENCODER_CMD:
+       case VIDIOC_ENCODER_CMD: {
+               struct v4l2_encoder_cmd *enc = (struct v4l2_encoder_cmd *)arg;
+               int try = cmd == VIDIOC_TRY_ENCODER_CMD;
+
+               memset(&enc->raw, 0, sizeof(enc->raw));
+               switch (enc->cmd) {
+               case V4L2_ENC_CMD_START:
+                       enc->flags = 0;
+                       if (try)
+                               return 0;
+                       res = hdpvr_start_streaming(dev);
+                       break;
+               case V4L2_ENC_CMD_STOP:
+                       if (try)
+                               return 0;
+                       res = hdpvr_stop_streaming(dev);
+                       break;
+               default:
+                       v4l2_dbg(MSG_INFO, hdpvr_debug, dev->video_dev,
+                                "Unsupported encoder cmd %d\n", enc->cmd);
+                       return -EINVAL;
+               }
+               break;
+       }
+       default:
+               res = video_ioctl2(filp, cmd, arg);
+       }
+       mutex_unlock(&dev->io_mutex);
+       return res;
+}
+
+static const struct v4l2_file_operations hdpvr_fops = {
+       .owner          = THIS_MODULE,
+       .open           = hdpvr_open,
+       .release        = hdpvr_release,
+       .read           = hdpvr_read,
+       .poll           = hdpvr_poll,
+       .unlocked_ioctl = hdpvr_ioctl,
+};
+
+/*=======================================================================*/
+/*
+ * V4L2 ioctl handling
+ */
+
+static int vidioc_querycap(struct file *file, void  *priv,
+                          struct v4l2_capability *cap)
+{
+       struct hdpvr_device *dev = video_drvdata(file);
+
+       strcpy(cap->driver, "hdpvr");
+       strcpy(cap->card, "Haupauge HD PVR");
+       usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
+       cap->version = HDPVR_VERSION;
+       cap->capabilities =     V4L2_CAP_VIDEO_CAPTURE |
+                               V4L2_CAP_AUDIO         |
+                               V4L2_CAP_READWRITE;
+       return 0;
+}
+
+static int vidioc_s_std(struct file *file, void *private_data,
+                       v4l2_std_id *std)
+{
+       struct hdpvr_fh *fh = file->private_data;
+       struct hdpvr_device *dev = fh->dev;
+       u8 std_type = 1;
+
+       if (*std & (V4L2_STD_NTSC | V4L2_STD_PAL_60))
+               std_type = 0;
+
+       return hdpvr_config_call(dev, CTRL_VIDEO_STD_TYPE, std_type);
+}
+
+static const char *iname[] = {
+       [HDPVR_COMPONENT] = "Component",
+       [HDPVR_SVIDEO]    = "S-Video",
+       [HDPVR_COMPOSITE] = "Composite",
+};
+
+static int vidioc_enum_input(struct file *file, void *priv,
+                               struct v4l2_input *i)
+{
+       struct hdpvr_fh *fh = file->private_data;
+       struct hdpvr_device *dev = fh->dev;
+       unsigned int n;
+
+       n = i->index;
+       if (n >= HDPVR_VIDEO_INPUTS)
+               return -EINVAL;
+
+       i->type = V4L2_INPUT_TYPE_CAMERA;
+
+       strncpy(i->name, iname[n], sizeof(i->name) - 1);
+       i->name[sizeof(i->name) - 1] = '\0';
+
+       i->audioset = 1<<HDPVR_RCA_FRONT | 1<<HDPVR_RCA_BACK | 1<<HDPVR_SPDIF;
+
+       i->std = dev->video_dev->tvnorms;
+
+       return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *private_data,
+                         unsigned int index)
+{
+       struct hdpvr_fh *fh = file->private_data;
+       struct hdpvr_device *dev = fh->dev;
+       int retval;
+
+       if (index >= HDPVR_VIDEO_INPUTS)
+               return -EINVAL;
+
+       if (dev->status != STATUS_IDLE)
+               return -EAGAIN;
+
+       retval = hdpvr_config_call(dev, CTRL_VIDEO_INPUT_VALUE, index+1);
+       if (!retval)
+               dev->options.video_input = index;
+
+       return retval;
+}
+
+static int vidioc_g_input(struct file *file, void *private_data,
+                         unsigned int *index)
+{
+       struct hdpvr_fh *fh = file->private_data;
+       struct hdpvr_device *dev = fh->dev;
+
+       *index = dev->options.video_input;
+       return 0;
+}
+
+
+static const char *audio_iname[] = {
+       [HDPVR_RCA_FRONT] = "RCA front",
+       [HDPVR_RCA_BACK]  = "RCA back",
+       [HDPVR_SPDIF]     = "SPDIF",
+};
+
+static int vidioc_enumaudio(struct file *file, void *priv,
+                               struct v4l2_audio *audio)
+{
+       unsigned int n;
+
+       n = audio->index;
+       if (n >= HDPVR_AUDIO_INPUTS)
+               return -EINVAL;
+
+       audio->capability = V4L2_AUDCAP_STEREO;
+
+       strncpy(audio->name, audio_iname[n], sizeof(audio->name) - 1);
+       audio->name[sizeof(audio->name) - 1] = '\0';
+
+       return 0;
+}
+
+static int vidioc_s_audio(struct file *file, void *private_data,
+                         struct v4l2_audio *audio)
+{
+       struct hdpvr_fh *fh = file->private_data;
+       struct hdpvr_device *dev = fh->dev;
+       int retval;
+
+       if (audio->index >= HDPVR_AUDIO_INPUTS)
+               return -EINVAL;
+
+       if (dev->status != STATUS_IDLE)
+               return -EAGAIN;
+
+       retval = hdpvr_set_audio(dev, audio->index+1, dev->options.audio_codec);
+       if (!retval)
+               dev->options.audio_input = audio->index;
+
+       return retval;
+}
+
+static int vidioc_g_audio(struct file *file, void *private_data,
+                         struct v4l2_audio *audio)
+{
+       struct hdpvr_fh *fh = file->private_data;
+       struct hdpvr_device *dev = fh->dev;
+
+       audio->index = dev->options.audio_input;
+       audio->capability = V4L2_AUDCAP_STEREO;
+       strncpy(audio->name, audio_iname[audio->index], sizeof(audio->name));
+       audio->name[sizeof(audio->name) - 1] = '\0';
+       return 0;
+}
+
+static const s32 supported_v4l2_ctrls[] = {
+       V4L2_CID_BRIGHTNESS,
+       V4L2_CID_CONTRAST,
+       V4L2_CID_SATURATION,
+       V4L2_CID_HUE,
+       V4L2_CID_SHARPNESS,
+       V4L2_CID_MPEG_AUDIO_ENCODING,
+       V4L2_CID_MPEG_VIDEO_ENCODING,
+       V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
+       V4L2_CID_MPEG_VIDEO_BITRATE,
+       V4L2_CID_MPEG_VIDEO_BITRATE_PEAK,
+};
+
+static int fill_queryctrl(struct hdpvr_options *opt, struct v4l2_queryctrl *qc,
+                         int ac3)
+{
+       int err;
+
+       switch (qc->id) {
+       case V4L2_CID_BRIGHTNESS:
+               return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x86);
+       case V4L2_CID_CONTRAST:
+               return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80);
+       case V4L2_CID_SATURATION:
+               return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80);
+       case V4L2_CID_HUE:
+               return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80);
+       case V4L2_CID_SHARPNESS:
+               return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80);
+       case V4L2_CID_MPEG_AUDIO_ENCODING:
+               return v4l2_ctrl_query_fill(
+                       qc, V4L2_MPEG_AUDIO_ENCODING_AAC,
+                       ac3 ? V4L2_MPEG_AUDIO_ENCODING_AC3
+                       : V4L2_MPEG_AUDIO_ENCODING_AAC,
+                       1, V4L2_MPEG_AUDIO_ENCODING_AAC);
+       case V4L2_CID_MPEG_VIDEO_ENCODING:
+               return v4l2_ctrl_query_fill(
+                       qc, V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC,
+                       V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC, 1,
+                       V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC);
+
+/*     case V4L2_CID_MPEG_VIDEO_? maybe keyframe interval: */
+/*             return v4l2_ctrl_query_fill(qc, 0, 128, 128, 0); */
+       case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+               return v4l2_ctrl_query_fill(
+                       qc, V4L2_MPEG_VIDEO_BITRATE_MODE_VBR,
+                       V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 1,
+                       V4L2_MPEG_VIDEO_BITRATE_MODE_CBR);
+
+       case V4L2_CID_MPEG_VIDEO_BITRATE:
+               return v4l2_ctrl_query_fill(qc, 1000000, 13500000, 100000,
+                                           6500000);
+       case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
+               err = v4l2_ctrl_query_fill(qc, 1100000, 20200000, 100000,
+                                          9000000);
+               if (!err && opt->bitrate_mode == HDPVR_CONSTANT)
+                       qc->flags |= V4L2_CTRL_FLAG_INACTIVE;
+               return err;
+       default:
+               return -EINVAL;
+       }
+}
+
+static int vidioc_queryctrl(struct file *file, void *private_data,
+                           struct v4l2_queryctrl *qc)
+{
+       struct hdpvr_fh *fh = file->private_data;
+       struct hdpvr_device *dev = fh->dev;
+       int i, next;
+       u32 id = qc->id;
+
+       memset(qc, 0, sizeof(*qc));
+
+       next = !!(id &  V4L2_CTRL_FLAG_NEXT_CTRL);
+       qc->id = id & ~V4L2_CTRL_FLAG_NEXT_CTRL;
+
+       for (i = 0; i < ARRAY_SIZE(supported_v4l2_ctrls); i++) {
+               if (next) {
+                       if (qc->id < supported_v4l2_ctrls[i])
+                               qc->id = supported_v4l2_ctrls[i];
+                       else
+                               continue;
+               }
+
+               if (qc->id == supported_v4l2_ctrls[i])
+                       return fill_queryctrl(&dev->options, qc,
+                                             dev->flags & HDPVR_FLAG_AC3_CAP);
+
+               if (qc->id < supported_v4l2_ctrls[i])
+                       break;
+       }
+
+       return -EINVAL;
+}
+
+static int vidioc_g_ctrl(struct file *file, void *private_data,
+                        struct v4l2_control *ctrl)
+{
+       struct hdpvr_fh *fh = file->private_data;
+       struct hdpvr_device *dev = fh->dev;
+
+       switch (ctrl->id) {
+       case V4L2_CID_BRIGHTNESS:
+               ctrl->value = dev->options.brightness;
+               break;
+       case V4L2_CID_CONTRAST:
+               ctrl->value = dev->options.contrast;
+               break;
+       case V4L2_CID_SATURATION:
+               ctrl->value = dev->options.saturation;
+               break;
+       case V4L2_CID_HUE:
+               ctrl->value = dev->options.hue;
+               break;
+       case V4L2_CID_SHARPNESS:
+               ctrl->value = dev->options.sharpness;
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int vidioc_s_ctrl(struct file *file, void *private_data,
+                        struct v4l2_control *ctrl)
+{
+       struct hdpvr_fh *fh = file->private_data;
+       struct hdpvr_device *dev = fh->dev;
+       int retval;
+
+       switch (ctrl->id) {
+       case V4L2_CID_BRIGHTNESS:
+               retval = hdpvr_config_call(dev, CTRL_BRIGHTNESS, ctrl->value);
+               if (!retval)
+                       dev->options.brightness = ctrl->value;
+               break;
+       case V4L2_CID_CONTRAST:
+               retval = hdpvr_config_call(dev, CTRL_CONTRAST, ctrl->value);
+               if (!retval)
+                       dev->options.contrast = ctrl->value;
+               break;
+       case V4L2_CID_SATURATION:
+               retval = hdpvr_config_call(dev, CTRL_SATURATION, ctrl->value);
+               if (!retval)
+                       dev->options.saturation = ctrl->value;
+               break;
+       case V4L2_CID_HUE:
+               retval = hdpvr_config_call(dev, CTRL_HUE, ctrl->value);
+               if (!retval)
+                       dev->options.hue = ctrl->value;
+               break;
+       case V4L2_CID_SHARPNESS:
+               retval = hdpvr_config_call(dev, CTRL_SHARPNESS, ctrl->value);
+               if (!retval)
+                       dev->options.sharpness = ctrl->value;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return retval;
+}
+
+
+static int hdpvr_get_ctrl(struct hdpvr_options *opt,
+                         struct v4l2_ext_control *ctrl)
+{
+       switch (ctrl->id) {
+       case V4L2_CID_MPEG_AUDIO_ENCODING:
+               ctrl->value = opt->audio_codec;
+               break;
+       case V4L2_CID_MPEG_VIDEO_ENCODING:
+               ctrl->value = V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC;
+               break;
+/*     case V4L2_CID_MPEG_VIDEO_B_FRAMES: */
+/*             ctrl->value = (opt->gop_mode & 0x2) ? 0 : 128; */
+/*             break; */
+       case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+               ctrl->value = opt->bitrate_mode == HDPVR_CONSTANT
+                       ? V4L2_MPEG_VIDEO_BITRATE_MODE_CBR
+                       : V4L2_MPEG_VIDEO_BITRATE_MODE_VBR;
+               break;
+       case V4L2_CID_MPEG_VIDEO_BITRATE:
+               ctrl->value = opt->bitrate * 100000;
+               break;
+       case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
+               ctrl->value = opt->peak_bitrate * 100000;
+               break;
+       case V4L2_CID_MPEG_STREAM_TYPE:
+               ctrl->value = V4L2_MPEG_STREAM_TYPE_MPEG2_TS;
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int vidioc_g_ext_ctrls(struct file *file, void *priv,
+                             struct v4l2_ext_controls *ctrls)
+{
+       struct hdpvr_fh *fh = file->private_data;
+       struct hdpvr_device *dev = fh->dev;
+       int i, err = 0;
+
+       if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
+               for (i = 0; i < ctrls->count; i++) {
+                       struct v4l2_ext_control *ctrl = ctrls->controls + i;
+
+                       err = hdpvr_get_ctrl(&dev->options, ctrl);
+                       if (err) {
+                               ctrls->error_idx = i;
+                               break;
+                       }
+               }
+               return err;
+
+       }
+
+       return -EINVAL;
+}
+
+
+static int hdpvr_try_ctrl(struct v4l2_ext_control *ctrl, int ac3)
+{
+       int ret = -EINVAL;
+
+       switch (ctrl->id) {
+       case V4L2_CID_MPEG_AUDIO_ENCODING:
+               if (ctrl->value == V4L2_MPEG_AUDIO_ENCODING_AAC ||
+                   (ac3 && ctrl->value == V4L2_MPEG_AUDIO_ENCODING_AC3))
+                       ret = 0;
+               break;
+       case V4L2_CID_MPEG_VIDEO_ENCODING:
+               if (ctrl->value == V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC)
+                       ret = 0;
+               break;
+/*     case V4L2_CID_MPEG_VIDEO_B_FRAMES: */
+/*             if (ctrl->value == 0 || ctrl->value == 128) */
+/*                     ret = 0; */
+/*             break; */
+       case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+               if (ctrl->value == V4L2_MPEG_VIDEO_BITRATE_MODE_CBR ||
+                   ctrl->value == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR)
+                       ret = 0;
+               break;
+       case V4L2_CID_MPEG_VIDEO_BITRATE:
+       {
+               uint bitrate = ctrl->value / 100000;
+               if (bitrate >= 10 && bitrate <= 135)
+                       ret = 0;
+               break;
+       }
+       case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
+       {
+               uint peak_bitrate = ctrl->value / 100000;
+               if (peak_bitrate >= 10 && peak_bitrate <= 202)
+                       ret = 0;
+               break;
+       }
+       case V4L2_CID_MPEG_STREAM_TYPE:
+               if (ctrl->value == V4L2_MPEG_STREAM_TYPE_MPEG2_TS)
+                       ret = 0;
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+static int vidioc_try_ext_ctrls(struct file *file, void *priv,
+                               struct v4l2_ext_controls *ctrls)
+{
+       struct hdpvr_fh *fh = file->private_data;
+       struct hdpvr_device *dev = fh->dev;
+       int i, err = 0;
+
+       if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
+               for (i = 0; i < ctrls->count; i++) {
+                       struct v4l2_ext_control *ctrl = ctrls->controls + i;
+
+                       err = hdpvr_try_ctrl(ctrl,
+                                            dev->flags & HDPVR_FLAG_AC3_CAP);
+                       if (err) {
+                               ctrls->error_idx = i;
+                               break;
+                       }
+               }
+               return err;
+       }
+
+       return -EINVAL;
+}
+
+
+static int hdpvr_set_ctrl(struct hdpvr_device *dev,
+                         struct v4l2_ext_control *ctrl)
+{
+       struct hdpvr_options *opt = &dev->options;
+       int ret = 0;
+
+       switch (ctrl->id) {
+       case V4L2_CID_MPEG_AUDIO_ENCODING:
+               if (dev->flags & HDPVR_FLAG_AC3_CAP) {
+                       opt->audio_codec = ctrl->value;
+                       ret = hdpvr_set_audio(dev, opt->audio_input,
+                                             opt->audio_codec);
+               }
+               break;
+       case V4L2_CID_MPEG_VIDEO_ENCODING:
+               break;
+/*     case V4L2_CID_MPEG_VIDEO_B_FRAMES: */
+/*             if (ctrl->value == 0 && !(opt->gop_mode & 0x2)) { */
+/*                     opt->gop_mode |= 0x2; */
+/*                     hdpvr_config_call(dev, CTRL_GOP_MODE_VALUE, */
+/*                                       opt->gop_mode); */
+/*             } */
+/*             if (ctrl->value == 128 && opt->gop_mode & 0x2) { */
+/*                     opt->gop_mode &= ~0x2; */
+/*                     hdpvr_config_call(dev, CTRL_GOP_MODE_VALUE, */
+/*                                       opt->gop_mode); */
+/*             } */
+/*             break; */
+       case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+               if (ctrl->value == V4L2_MPEG_VIDEO_BITRATE_MODE_CBR &&
+                   opt->bitrate_mode != HDPVR_CONSTANT) {
+                       opt->bitrate_mode = HDPVR_CONSTANT;
+                       hdpvr_config_call(dev, CTRL_BITRATE_MODE_VALUE,
+                                         opt->bitrate_mode);
+               }
+               if (ctrl->value == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR &&
+                   opt->bitrate_mode == HDPVR_CONSTANT) {
+                       opt->bitrate_mode = HDPVR_VARIABLE_AVERAGE;
+                       hdpvr_config_call(dev, CTRL_BITRATE_MODE_VALUE,
+                                         opt->bitrate_mode);
+               }
+               break;
+       case V4L2_CID_MPEG_VIDEO_BITRATE: {
+               uint bitrate = ctrl->value / 100000;
+
+               opt->bitrate = bitrate;
+               if (bitrate >= opt->peak_bitrate)
+                       opt->peak_bitrate = bitrate+1;
+
+               hdpvr_set_bitrate(dev);
+               break;
+       }
+       case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: {
+               uint peak_bitrate = ctrl->value / 100000;
+
+               if (opt->bitrate_mode == HDPVR_CONSTANT)
+                       break;
+
+               if (opt->bitrate < peak_bitrate) {
+                       opt->peak_bitrate = peak_bitrate;
+                       hdpvr_set_bitrate(dev);
+               } else
+                       ret = -EINVAL;
+               break;
+       }
+       case V4L2_CID_MPEG_STREAM_TYPE:
+               break;
+       default:
+               return -EINVAL;
+       }
+       return ret;
+}
+
+static int vidioc_s_ext_ctrls(struct file *file, void *priv,
+                             struct v4l2_ext_controls *ctrls)
+{
+       struct hdpvr_fh *fh = file->private_data;
+       struct hdpvr_device *dev = fh->dev;
+       int i, err = 0;
+
+       if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
+               for (i = 0; i < ctrls->count; i++) {
+                       struct v4l2_ext_control *ctrl = ctrls->controls + i;
+
+                       err = hdpvr_try_ctrl(ctrl,
+                                            dev->flags & HDPVR_FLAG_AC3_CAP);
+                       if (err) {
+                               ctrls->error_idx = i;
+                               break;
+                       }
+                       err = hdpvr_set_ctrl(dev, ctrl);
+                       if (err) {
+                               ctrls->error_idx = i;
+                               break;
+                       }
+               }
+               return err;
+
+       }
+
+       return -EINVAL;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *private_data,
+                                   struct v4l2_fmtdesc *f)
+{
+
+       if (f->index != 0 || f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+               return -EINVAL;
+
+       f->flags = V4L2_FMT_FLAG_COMPRESSED;
+       strncpy(f->description, "MPEG2-TS with AVC/AAC streams", 32);
+       f->pixelformat = V4L2_PIX_FMT_MPEG;
+
+       return 0;
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *private_data,
+                               struct v4l2_format *f)
+{
+       struct hdpvr_fh *fh = file->private_data;
+       struct hdpvr_device *dev = fh->dev;
+       struct hdpvr_video_info *vid_info;
+
+       if (!dev)
+               return -ENODEV;
+
+       vid_info = get_video_info(dev);
+       if (!vid_info)
+               return -EFAULT;
+
+       f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+       f->fmt.pix.pixelformat  = V4L2_PIX_FMT_MPEG;
+       f->fmt.pix.width        = vid_info->width;
+       f->fmt.pix.height       = vid_info->height;
+       f->fmt.pix.sizeimage    = dev->bulk_in_size;
+       f->fmt.pix.colorspace   = 0;
+       f->fmt.pix.bytesperline = 0;
+       f->fmt.pix.field        = V4L2_FIELD_ANY;
+
+       kfree(vid_info);
+       return 0;
+}
+
+
+static const struct v4l2_ioctl_ops hdpvr_ioctl_ops = {
+       .vidioc_querycap        = vidioc_querycap,
+       .vidioc_s_std           = vidioc_s_std,
+       .vidioc_enum_input      = vidioc_enum_input,
+       .vidioc_g_input         = vidioc_g_input,
+       .vidioc_s_input         = vidioc_s_input,
+       .vidioc_enumaudio       = vidioc_enumaudio,
+       .vidioc_g_audio         = vidioc_g_audio,
+       .vidioc_s_audio         = vidioc_s_audio,
+       .vidioc_queryctrl       = vidioc_queryctrl,
+       .vidioc_g_ctrl          = vidioc_g_ctrl,
+       .vidioc_s_ctrl          = vidioc_s_ctrl,
+       .vidioc_g_ext_ctrls     = vidioc_g_ext_ctrls,
+       .vidioc_s_ext_ctrls     = vidioc_s_ext_ctrls,
+       .vidioc_try_ext_ctrls   = vidioc_try_ext_ctrls,
+       .vidioc_enum_fmt_vid_cap        = vidioc_enum_fmt_vid_cap,
+       .vidioc_g_fmt_vid_cap           = vidioc_g_fmt_vid_cap,
+};
+
+static void hdpvr_device_release(struct video_device *vdev)
+{
+       struct hdpvr_device *dev = video_get_drvdata(vdev);
+
+       hdpvr_delete(dev);
+}
+
+static const struct video_device hdpvr_video_template = {
+/*     .type                   = VFL_TYPE_GRABBER, */
+/*     .type2                  = VID_TYPE_CAPTURE | VID_TYPE_MPEG_ENCODER, */
+       .fops                   = &hdpvr_fops,
+       .release                = hdpvr_device_release,
+       .ioctl_ops              = &hdpvr_ioctl_ops,
+       .tvnorms                =
+               V4L2_STD_NTSC  | V4L2_STD_SECAM | V4L2_STD_PAL_B |
+               V4L2_STD_PAL_G | V4L2_STD_PAL_H | V4L2_STD_PAL_I |
+               V4L2_STD_PAL_D | V4L2_STD_PAL_M | V4L2_STD_PAL_N |
+               V4L2_STD_PAL_60,
+};
+
+int hdpvr_register_videodev(struct hdpvr_device *dev, int devnum)
+{
+       /* setup and register video device */
+       dev->video_dev = video_device_alloc();
+       if (!dev->video_dev) {
+               err("video_device_alloc() failed");
+               goto error;
+       }
+
+       *(dev->video_dev) = hdpvr_video_template;
+       strcpy(dev->video_dev->name, "Hauppauge HD PVR");
+       dev->video_dev->parent = &dev->udev->dev;
+       video_set_drvdata(dev->video_dev, dev);
+
+       if (video_register_device(dev->video_dev, VFL_TYPE_GRABBER, devnum)) {
+               err("V4L2 device registration failed");
+               goto error;
+       }
+
+       return 0;
+error:
+       return -ENOMEM;
+}
diff --git a/drivers/media/video/hdpvr/hdpvr.h b/drivers/media/video/hdpvr/hdpvr.h
new file mode 100644 (file)
index 0000000..17db74f
--- /dev/null
@@ -0,0 +1,298 @@
+/*
+ * Hauppage HD PVR USB driver
+ *
+ * Copyright (C) 2008      Janne Grunau (j@jannau.net)
+ *
+ *     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, version 2.
+ *
+ */
+
+#include <linux/usb.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include <linux/videodev2.h>
+
+#define HDPVR_MAJOR_VERSION 0
+#define HDPVR_MINOR_VERSION 2
+#define HDPVR_RELEASE 0
+#define HDPVR_VERSION \
+       KERNEL_VERSION(HDPVR_MAJOR_VERSION, HDPVR_MINOR_VERSION, HDPVR_RELEASE)
+
+#define HDPVR_MAX 8
+
+/* Define these values to match your devices */
+#define HD_PVR_VENDOR_ID       0x2040
+#define HD_PVR_PRODUCT_ID      0x4900
+#define HD_PVR_PRODUCT_ID1     0x4901
+#define HD_PVR_PRODUCT_ID2     0x4902
+
+#define UNSET    (-1U)
+
+#define NUM_BUFFERS 64
+
+#define HDPVR_FIRMWARE_VERSION         0x8
+#define HDPVR_FIRMWARE_VERSION_AC3     0xd
+
+/* #define HDPVR_DEBUG */
+
+extern int hdpvr_debug;
+
+#define MSG_INFO       1
+#define MSG_BUFFER     2
+
+struct hdpvr_options {
+       u8      video_std;
+       u8      video_input;
+       u8      audio_input;
+       u8      bitrate;        /* in 100kbps */
+       u8      peak_bitrate;   /* in 100kbps */
+       u8      bitrate_mode;
+       u8      gop_mode;
+       enum v4l2_mpeg_audio_encoding   audio_codec;
+       u8      brightness;
+       u8      contrast;
+       u8      hue;
+       u8      saturation;
+       u8      sharpness;
+};
+
+/* Structure to hold all of our device specific stuff */
+struct hdpvr_device {
+       /* the v4l device for this device */
+       struct video_device     *video_dev;
+       /* the usb device for this device */
+       struct usb_device       *udev;
+
+       /* the max packet size of the bulk endpoint */
+       size_t                  bulk_in_size;
+       /* the address of the bulk in endpoint */
+       __u8                    bulk_in_endpointAddr;
+
+       /* holds the current device status */
+       __u8                    status;
+       /* count the number of openers */
+       uint                    open_count;
+
+       /* holds the cureent set options */
+       struct hdpvr_options    options;
+
+       uint                    flags;
+
+       /* synchronize I/O */
+       struct mutex            io_mutex;
+       /* available buffers */
+       struct list_head        free_buff_list;
+       /* in progress buffers */
+       struct list_head        rec_buff_list;
+       /* waitqueue for buffers */
+       wait_queue_head_t       wait_buffer;
+       /* waitqueue for data */
+       wait_queue_head_t       wait_data;
+       /**/
+       struct workqueue_struct *workqueue;
+       /**/
+       struct work_struct      worker;
+
+       /* I2C adapter */
+       struct i2c_adapter      *i2c_adapter;
+       /* I2C lock */
+       struct mutex            i2c_mutex;
+
+       /* usb control transfer buffer and lock */
+       struct mutex            usbc_mutex;
+       u8                      *usbc_buf;
+};
+
+
+/* buffer one bulk urb of data */
+struct hdpvr_buffer {
+       struct list_head        buff_list;
+
+       struct urb              *urb;
+
+       struct hdpvr_device     *dev;
+
+       uint                    pos;
+
+       __u8                    status;
+};
+
+/* */
+
+struct hdpvr_video_info {
+       u16     width;
+       u16     height;
+       u8      fps;
+};
+
+enum {
+       STATUS_UNINITIALIZED    = 0,
+       STATUS_IDLE,
+       STATUS_STARTING,
+       STATUS_SHUTTING_DOWN,
+       STATUS_STREAMING,
+       STATUS_ERROR,
+       STATUS_DISCONNECTED,
+};
+
+enum {
+       HDPVR_FLAG_AC3_CAP = 1,
+};
+
+enum {
+       BUFSTAT_UNINITIALIZED = 0,
+       BUFSTAT_AVAILABLE,
+       BUFSTAT_INPROGRESS,
+       BUFSTAT_READY,
+};
+
+#define CTRL_START_STREAMING_VALUE     0x0700
+#define CTRL_STOP_STREAMING_VALUE      0x0800
+#define CTRL_BITRATE_VALUE             0x1000
+#define CTRL_BITRATE_MODE_VALUE                0x1200
+#define CTRL_GOP_MODE_VALUE            0x1300
+#define CTRL_VIDEO_INPUT_VALUE         0x1500
+#define CTRL_VIDEO_STD_TYPE            0x1700
+#define CTRL_AUDIO_INPUT_VALUE         0x2500
+#define CTRL_BRIGHTNESS                        0x2900
+#define CTRL_CONTRAST                  0x2a00
+#define CTRL_HUE                       0x2b00
+#define CTRL_SATURATION                        0x2c00
+#define CTRL_SHARPNESS                 0x2d00
+#define CTRL_LOW_PASS_FILTER_VALUE     0x3100
+
+#define CTRL_DEFAULT_INDEX             0x0003
+
+
+       /* :0 s 38 01 1000 0003 0004 4 = 0a00ca00
+        * BITRATE SETTING
+        *   1st and 2nd byte (little endian): average bitrate in 100 000 bit/s
+        *                                     min: 1 mbit/s, max: 13.5 mbit/s
+        *   3rd and 4th byte (little endian): peak bitrate in 100 000 bit/s
+        *                                     min: average + 100kbit/s,
+        *                                      max: 20.2 mbit/s
+        */
+
+       /* :0 s 38 01 1200 0003 0001 1 = 02
+        * BIT RATE MODE
+        *  constant = 1, variable (peak) = 2, variable (average) = 3
+        */
+
+       /* :0 s 38 01 1300 0003 0001 1 = 03
+        * GOP MODE (2 bit)
+        *    low bit 0/1: advanced/simple GOP
+        *   high bit 0/1: IDR(4/32/128) / no IDR (4/32/0)
+        */
+
+       /* :0 s 38 01 1700 0003 0001 1 = 00
+        * VIDEO STANDARD or FREQUNCY 0 = 60hz, 1 = 50hz
+        */
+
+       /* :0 s 38 01 3100 0003 0004 4 = 03030000
+        * FILTER CONTROL
+        *   1st byte luma low pass filter strength,
+        *   2nd byte chroma low pass filter strength,
+        *   3rd byte MF enable chroma, min=0, max=1
+        *   4th byte n
+        */
+
+
+       /* :0 s 38 b9 0001 0000 0000 0 */
+
+
+
+/* :0 s 38 d3 0000 0000 0001 1 = 00 */
+/*             ret = usb_control_msg(dev->udev, */
+/*                                   usb_sndctrlpipe(dev->udev, 0), */
+/*                                   0xd3, 0x38, */
+/*                                   0, 0, */
+/*                                   "\0", 1, */
+/*                                   1000); */
+
+/*             info("control request returned %d", ret); */
+/*             msleep(5000); */
+
+
+       /* :0 s b8 81 1400 0003 0005 5 <
+        * :0 0 5 = d0024002 19
+        * QUERY FRAME SIZE AND RATE
+        *   1st and 2nd byte (little endian): horizontal resolution
+        *   3rd and 4th byte (little endian): vertical resolution
+        *   5th byte: frame rate
+        */
+
+       /* :0 s b8 81 1800 0003 0003 3 <
+        * :0 0 3 = 030104
+        * QUERY SIGNAL AND DETECTED LINES, maybe INPUT
+        */
+
+enum hdpvr_video_std {
+       HDPVR_60HZ = 0,
+       HDPVR_50HZ,
+};
+
+enum hdpvr_video_input {
+       HDPVR_COMPONENT = 0,
+       HDPVR_SVIDEO,
+       HDPVR_COMPOSITE,
+       HDPVR_VIDEO_INPUTS
+};
+
+enum hdpvr_audio_inputs {
+       HDPVR_RCA_BACK = 0,
+       HDPVR_RCA_FRONT,
+       HDPVR_SPDIF,
+       HDPVR_AUDIO_INPUTS
+};
+
+enum hdpvr_bitrate_mode {
+       HDPVR_CONSTANT = 1,
+       HDPVR_VARIABLE_PEAK,
+       HDPVR_VARIABLE_AVERAGE,
+};
+
+enum hdpvr_gop_mode {
+       HDPVR_ADVANCED_IDR_GOP = 0,
+       HDPVR_SIMPLE_IDR_GOP,
+       HDPVR_ADVANCED_NOIDR_GOP,
+       HDPVR_SIMPLE_NOIDR_GOP,
+};
+
+void hdpvr_delete(struct hdpvr_device *dev);
+
+/*========================================================================*/
+/* hardware control functions */
+int hdpvr_set_options(struct hdpvr_device *dev);
+
+int hdpvr_set_bitrate(struct hdpvr_device *dev);
+
+int hdpvr_set_audio(struct hdpvr_device *dev, u8 input,
+                   enum v4l2_mpeg_audio_encoding codec);
+
+int hdpvr_config_call(struct hdpvr_device *dev, uint value,
+                     unsigned char valbuf);
+
+struct hdpvr_video_info *get_video_info(struct hdpvr_device *dev);
+
+/* :0 s b8 81 1800 0003 0003 3 < */
+/* :0 0 3 = 0301ff */
+int get_input_lines_info(struct hdpvr_device *dev);
+
+
+/*========================================================================*/
+/* v4l2 registration */
+int hdpvr_register_videodev(struct hdpvr_device *dev, int devnumber);
+
+int hdpvr_cancel_queue(struct hdpvr_device *dev);
+
+/*========================================================================*/
+/* i2c adapter registration */
+int hdpvr_register_i2c_adapter(struct hdpvr_device *dev);
+
+/*========================================================================*/
+/* buffer management */
+int hdpvr_free_buffers(struct hdpvr_device *dev);
+int hdpvr_alloc_buffers(struct hdpvr_device *dev, uint count);
index 17d9af0..f27604a 100644 (file)
@@ -88,6 +88,7 @@
 #define I2C_HW_B_CX2341X       0x010020 /* Conexant CX2341X MPEG encoder cards */
 #define I2C_HW_B_CX23885       0x010022 /* conexant 23885 based tv cards (bus1) */
 #define I2C_HW_B_AU0828                0x010023 /* auvitek au0828 usb bridge */
+#define I2C_HW_B_HDPVR         0x010025 /* Hauppauge HD PVR */
 
 /* --- SGI adapters                                                    */
 #define I2C_HW_SGI_VINO                0x160000