summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/media/video/Kconfig2
-rw-r--r--drivers/media/video/Makefile2
-rw-r--r--drivers/media/video/hdpvr/Kconfig10
-rw-r--r--drivers/media/video/hdpvr/Makefile7
-rw-r--r--drivers/media/video/hdpvr/hdpvr-control.c201
-rw-r--r--drivers/media/video/hdpvr/hdpvr-core.c439
-rw-r--r--drivers/media/video/hdpvr/hdpvr-i2c.c145
-rw-r--r--drivers/media/video/hdpvr/hdpvr-video.c1228
-rw-r--r--drivers/media/video/hdpvr/hdpvr.h298
-rw-r--r--include/linux/i2c-id.h1
10 files changed, 2333 insertions, 0 deletions
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index d5ddb819a961..3f85b9e63754 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -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"
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index 08a0675fea34..b9046744463b 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -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
index 000000000000..de247f3c7d05
--- /dev/null
+++ b/drivers/media/video/hdpvr/Kconfig
@@ -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
index 000000000000..79ad2e16cb8f
--- /dev/null
+++ b/drivers/media/video/hdpvr/Makefile
@@ -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
index 000000000000..ecf02c621f13
--- /dev/null
+++ b/drivers/media/video/hdpvr/hdpvr-control.c
@@ -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
index 000000000000..e7300b570bb7
--- /dev/null
+++ b/drivers/media/video/hdpvr/hdpvr-core.c
@@ -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
index 000000000000..35096dec2411
--- /dev/null
+++ b/drivers/media/video/hdpvr/hdpvr-i2c.c
@@ -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
index 000000000000..ee481495e4fc
--- /dev/null
+++ b/drivers/media/video/hdpvr/hdpvr-video.c
@@ -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
index 000000000000..17db74feb884
--- /dev/null
+++ b/drivers/media/video/hdpvr/hdpvr.h
@@ -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);
diff --git a/include/linux/i2c-id.h b/include/linux/i2c-id.h
index 17d9af070f06..f27604af8378 100644
--- a/include/linux/i2c-id.h
+++ b/include/linux/i2c-id.h
@@ -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