summaryrefslogtreecommitdiffstats
path: root/drivers/media/usb/hdpvr
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/media/usb/hdpvr')
-rw-r--r--drivers/media/usb/hdpvr/Kconfig11
-rw-r--r--drivers/media/usb/hdpvr/Makefile4
-rw-r--r--drivers/media/usb/hdpvr/hdpvr-control.c177
-rw-r--r--drivers/media/usb/hdpvr/hdpvr-core.c449
-rw-r--r--drivers/media/usb/hdpvr/hdpvr-i2c.c206
-rw-r--r--drivers/media/usb/hdpvr/hdpvr-video.c1253
-rw-r--r--drivers/media/usb/hdpvr/hdpvr.h324
7 files changed, 2424 insertions, 0 deletions
diff --git a/drivers/media/usb/hdpvr/Kconfig b/drivers/media/usb/hdpvr/Kconfig
new file mode 100644
index 000000000..ee45a89aa
--- /dev/null
+++ b/drivers/media/usb/hdpvr/Kconfig
@@ -0,0 +1,11 @@
+# SPDX-License-Identifier: GPL-2.0-only
+
+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/usb/hdpvr/Makefile b/drivers/media/usb/hdpvr/Makefile
new file mode 100644
index 000000000..d1d57e3c3
--- /dev/null
+++ b/drivers/media/usb/hdpvr/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
+hdpvr-objs := hdpvr-control.o hdpvr-core.o hdpvr-video.o hdpvr-i2c.o
+
+obj-$(CONFIG_VIDEO_HDPVR) += hdpvr.o
diff --git a/drivers/media/usb/hdpvr/hdpvr-control.c b/drivers/media/usb/hdpvr/hdpvr-control.c
new file mode 100644
index 000000000..37c53ab85
--- /dev/null
+++ b/drivers/media/usb/hdpvr/hdpvr-control.c
@@ -0,0 +1,177 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Hauppauge HD PVR USB driver - video 4 linux 2 interface
+ *
+ * Copyright (C) 2008 Janne Grunau (j@jannau.net)
+ */
+
+#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;
+
+ 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);
+ v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
+ "config call request for value 0x%x returned %d\n", value,
+ ret);
+
+ return ret < 0 ? ret : 0;
+}
+
+int get_video_info(struct hdpvr_device *dev, struct hdpvr_video_info *vidinf)
+{
+ int ret;
+
+ vidinf->valid = false;
+ 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);
+
+#ifdef HDPVR_DEBUG
+ if (hdpvr_debug & MSG_INFO)
+ v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
+ "get video info returned: %d, %5ph\n", ret,
+ dev->usbc_buf);
+#endif
+ mutex_unlock(&dev->usbc_mutex);
+
+ if (ret < 0)
+ return ret;
+
+ 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];
+ vidinf->valid = vidinf->width && vidinf->height && vidinf->fps;
+
+ return 0;
+}
+
+int get_input_lines_info(struct hdpvr_device *dev)
+{
+ 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)
+ v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
+ "get input lines info returned: %d, %3ph\n", ret,
+ dev->usbc_buf);
+#else
+ (void)ret; /* suppress compiler warning */
+#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);
+ v4l2_err(&dev->v4l2_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, input);
+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/usb/hdpvr/hdpvr-core.c b/drivers/media/usb/hdpvr/hdpvr-core.c
new file mode 100644
index 000000000..52e05a69c
--- /dev/null
+++ b/drivers/media/usb/hdpvr/hdpvr-core.c
@@ -0,0 +1,449 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Hauppauge 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
+ */
+
+#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/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");
+
+static 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");
+
+static 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 bool 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 const 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) },
+ { USB_DEVICE(HD_PVR_VENDOR_ID, HD_PVR_PRODUCT_ID3) },
+ { USB_DEVICE(HD_PVR_VENDOR_ID, HD_PVR_PRODUCT_ID4) },
+ { } /* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, hdpvr_table);
+
+
+void hdpvr_delete(struct hdpvr_device *dev)
+{
+ hdpvr_free_buffers(dev);
+ usb_put_dev(dev->udev);
+}
+
+static void challenge(u8 *bytes)
+{
+ __le64 *i64P;
+ u64 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 = (__le64 *)bytes;
+ tmp64 = le64_to_cpup(i64P);
+ tmp64 = tmp64 + (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;
+
+ 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) {
+ v4l2_err(&dev->v4l2_dev,
+ "unexpected answer of status request, len %d\n", ret);
+ goto unlock;
+ }
+#ifdef HDPVR_DEBUG
+ else {
+ v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
+ "Status request returned, len %d: %46ph\n",
+ ret, dev->usbc_buf);
+ }
+#endif
+
+ dev->fw_ver = dev->usbc_buf[1];
+
+ dev->usbc_buf[46] = '\0';
+ v4l2_info(&dev->v4l2_dev, "firmware version 0x%x dated %s\n",
+ dev->fw_ver, &dev->usbc_buf[2]);
+
+ if (dev->fw_ver > 0x15) {
+ dev->options.brightness = 0x80;
+ dev->options.contrast = 0x40;
+ dev->options.hue = 0xf;
+ dev->options.saturation = 0x40;
+ dev->options.sharpness = 0x80;
+ }
+
+ switch (dev->fw_ver) {
+ case HDPVR_FIRMWARE_VERSION:
+ dev->flags &= ~HDPVR_FLAG_AC3_CAP;
+ break;
+ case HDPVR_FIRMWARE_VERSION_AC3:
+ case HDPVR_FIRMWARE_VERSION_0X12:
+ case HDPVR_FIRMWARE_VERSION_0X15:
+ case HDPVR_FIRMWARE_VERSION_0X1E:
+ dev->flags |= HDPVR_FLAG_AC3_CAP;
+ break;
+ default:
+ v4l2_info(&dev->v4l2_dev, "untested firmware, the driver might not work.\n");
+ if (dev->fw_ver >= HDPVR_FIRMWARE_VERSION_AC3)
+ dev->flags |= HDPVR_FLAG_AC3_CAP;
+ else
+ dev->flags &= ~HDPVR_FLAG_AC3_CAP;
+ }
+
+ response = dev->usbc_buf+38;
+#ifdef HDPVR_DEBUG
+ v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, "challenge: %8ph\n",
+ response);
+#endif
+ challenge(response);
+#ifdef HDPVR_DEBUG
+ v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, " response: %8ph\n",
+ response);
+#endif
+
+ msleep(100);
+ ret = usb_control_msg(dev->udev,
+ usb_sndctrlpipe(dev->udev, 0),
+ 0xd1, 0x00 | request_type,
+ 0x0000, 0x0000,
+ response, 8,
+ 10000);
+ v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
+ "magic request returned %d\n", ret);
+
+ retval = ret != 8;
+unlock:
+ mutex_unlock(&dev->usbc_mutex);
+ return retval;
+}
+
+static int hdpvr_device_init(struct hdpvr_device *dev)
+{
+ int ret;
+ u8 *buf;
+
+ 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);
+ v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
+ "control request returned %d\n", ret);
+ mutex_unlock(&dev->usbc_mutex);
+
+ /* 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);
+ v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_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);
+ v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_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,
+ /* original picture controls for firmware version <= 0x15 */
+ /* updated in device_authorization() for newer firmware */
+ .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;
+#if IS_ENABLED(CONFIG_I2C)
+ struct i2c_client *client;
+#endif
+ size_t buffer_size;
+ int i;
+ int dev_num;
+ int retval = -ENOMEM;
+
+ /* allocate memory for our device state and initialize it */
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev) {
+ dev_err(&interface->dev, "Out of memory\n");
+ goto error;
+ }
+
+ /* init video transfer queues first of all */
+ /* to prevent oops in hdpvr_delete() on error paths */
+ INIT_LIST_HEAD(&dev->free_buff_list);
+ INIT_LIST_HEAD(&dev->rec_buff_list);
+
+ /* register v4l2_device early so it can be used for printks */
+ if (v4l2_device_register(&interface->dev, &dev->v4l2_dev)) {
+ dev_err(&interface->dev, "v4l2_device_register failed\n");
+ goto error_free_dev;
+ }
+
+ 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) {
+ v4l2_err(&dev->v4l2_dev, "Out of memory\n");
+ goto error_v4l2_unregister;
+ }
+
+ init_waitqueue_head(&dev->wait_buffer);
+ init_waitqueue_head(&dev->wait_data);
+
+ 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;
+ if (default_audio_input == HDPVR_SPDIF)
+ dev->options.audio_codec =
+ V4L2_MPEG_AUDIO_ENCODING_AC3;
+ }
+
+ 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) {
+ v4l2_err(&dev->v4l2_dev, "Could not find bulk-in endpoint\n");
+ goto error_put_usb;
+ }
+
+ /* init the device */
+ if (hdpvr_device_init(dev)) {
+ v4l2_err(&dev->v4l2_dev, "device init failed\n");
+ goto error_put_usb;
+ }
+
+ mutex_lock(&dev->io_mutex);
+ if (hdpvr_alloc_buffers(dev, NUM_BUFFERS)) {
+ mutex_unlock(&dev->io_mutex);
+ v4l2_err(&dev->v4l2_dev,
+ "allocating transfer buffers failed\n");
+ goto error_put_usb;
+ }
+ mutex_unlock(&dev->io_mutex);
+
+#if IS_ENABLED(CONFIG_I2C)
+ retval = hdpvr_register_i2c_adapter(dev);
+ if (retval < 0) {
+ v4l2_err(&dev->v4l2_dev, "i2c adapter register failed\n");
+ goto error_free_buffers;
+ }
+
+ client = hdpvr_register_ir_i2c(dev);
+ if (IS_ERR(client)) {
+ v4l2_err(&dev->v4l2_dev, "i2c IR device register failed\n");
+ retval = PTR_ERR(client);
+ goto reg_fail;
+ }
+#endif
+
+ dev_num = atomic_inc_return(&dev_nr);
+ if (dev_num >= HDPVR_MAX) {
+ v4l2_err(&dev->v4l2_dev,
+ "max device number reached, device register failed\n");
+ atomic_dec(&dev_nr);
+ retval = -ENODEV;
+ goto reg_fail;
+ }
+
+ retval = hdpvr_register_videodev(dev, &interface->dev,
+ video_nr[dev_num]);
+ if (retval < 0) {
+ v4l2_err(&dev->v4l2_dev, "registering videodev failed\n");
+ goto reg_fail;
+ }
+
+ /* let the user know what node this device is now attached to */
+ v4l2_info(&dev->v4l2_dev, "device now attached to %s\n",
+ video_device_node_name(&dev->video_dev));
+ return 0;
+
+reg_fail:
+#if IS_ENABLED(CONFIG_I2C)
+ i2c_del_adapter(&dev->i2c_adapter);
+error_free_buffers:
+#endif
+ hdpvr_free_buffers(dev);
+error_put_usb:
+ usb_put_dev(dev->udev);
+ kfree(dev->usbc_buf);
+error_v4l2_unregister:
+ v4l2_device_unregister(&dev->v4l2_dev);
+error_free_dev:
+ kfree(dev);
+error:
+ return retval;
+}
+
+static void hdpvr_disconnect(struct usb_interface *interface)
+{
+ struct hdpvr_device *dev = to_hdpvr_dev(usb_get_intfdata(interface));
+
+ v4l2_info(&dev->v4l2_dev, "device %s disconnected\n",
+ video_device_node_name(&dev->video_dev));
+ /* prevent more I/O from starting and stop any ongoing */
+ mutex_lock(&dev->io_mutex);
+ dev->status = STATUS_DISCONNECTED;
+ wake_up_interruptible(&dev->wait_data);
+ wake_up_interruptible(&dev->wait_buffer);
+ mutex_unlock(&dev->io_mutex);
+ v4l2_device_disconnect(&dev->v4l2_dev);
+ msleep(100);
+ flush_work(&dev->worker);
+ mutex_lock(&dev->io_mutex);
+ hdpvr_cancel_queue(dev);
+ mutex_unlock(&dev->io_mutex);
+#if IS_ENABLED(CONFIG_I2C)
+ i2c_del_adapter(&dev->i2c_adapter);
+#endif
+ video_unregister_device(&dev->video_dev);
+ atomic_dec(&dev_nr);
+}
+
+
+static struct usb_driver hdpvr_usb_driver = {
+ .name = "hdpvr",
+ .probe = hdpvr_probe,
+ .disconnect = hdpvr_disconnect,
+ .id_table = hdpvr_table,
+};
+
+module_usb_driver(hdpvr_usb_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_VERSION("0.2.1");
+MODULE_AUTHOR("Janne Grunau");
+MODULE_DESCRIPTION("Hauppauge HD PVR driver");
diff --git a/drivers/media/usb/hdpvr/hdpvr-i2c.c b/drivers/media/usb/hdpvr/hdpvr-i2c.c
new file mode 100644
index 000000000..070559b01
--- /dev/null
+++ b/drivers/media/usb/hdpvr/hdpvr-i2c.c
@@ -0,0 +1,206 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+/*
+ * Hauppauge HD PVR USB driver
+ *
+ * Copyright (C) 2008 Janne Grunau (j@jannau.net)
+ *
+ * IR device registration code is
+ * Copyright (C) 2010 Andy Walls <awalls@md.metrocast.net>
+ */
+
+#if IS_ENABLED(CONFIG_I2C)
+
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/export.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
+
+#define Z8F0811_IR_TX_I2C_ADDR 0x70
+#define Z8F0811_IR_RX_I2C_ADDR 0x71
+
+
+struct i2c_client *hdpvr_register_ir_i2c(struct hdpvr_device *dev)
+{
+ struct IR_i2c_init_data *init_data = &dev->ir_i2c_init_data;
+ struct i2c_board_info info = {
+ I2C_BOARD_INFO("ir_z8f0811_hdpvr", Z8F0811_IR_RX_I2C_ADDR),
+ };
+
+ /* Our default information for ir-kbd-i2c.c to use */
+ init_data->ir_codes = RC_MAP_HAUPPAUGE;
+ init_data->internal_get_key_func = IR_KBD_GET_KEY_HAUP_XVR;
+ init_data->type = RC_PROTO_BIT_RC5 | RC_PROTO_BIT_RC6_MCE |
+ RC_PROTO_BIT_RC6_6A_32;
+ init_data->name = "HD-PVR";
+ init_data->polling_interval = 405; /* ms, duplicated from Windows */
+ info.platform_data = init_data;
+
+ return i2c_new_client_device(&dev->i2c_adapter, &info);
+}
+
+static int hdpvr_i2c_read(struct hdpvr_device *dev, int bus,
+ unsigned char addr, char *wdata, int wlen,
+ char *data, int len)
+{
+ int ret;
+
+ if ((len > sizeof(dev->i2c_buf)) || (wlen > sizeof(dev->i2c_buf)))
+ return -EINVAL;
+
+ if (wlen) {
+ memcpy(dev->i2c_buf, wdata, wlen);
+ ret = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0),
+ REQTYPE_I2C_WRITE, CTRL_WRITE_REQUEST,
+ (bus << 8) | addr, 0, dev->i2c_buf,
+ wlen, 1000);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0),
+ REQTYPE_I2C_READ, CTRL_READ_REQUEST,
+ (bus << 8) | addr, 0, dev->i2c_buf, len, 1000);
+
+ if (ret == len) {
+ memcpy(data, dev->i2c_buf, len);
+ ret = 0;
+ } else if (ret >= 0)
+ ret = -EIO;
+
+ return ret;
+}
+
+static int hdpvr_i2c_write(struct hdpvr_device *dev, int bus,
+ unsigned char addr, char *data, int len)
+{
+ int ret;
+
+ if (len > sizeof(dev->i2c_buf))
+ return -EINVAL;
+
+ memcpy(dev->i2c_buf, data, len);
+ ret = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0),
+ REQTYPE_I2C_WRITE, CTRL_WRITE_REQUEST,
+ (bus << 8) | addr, 0, dev->i2c_buf, len, 1000);
+
+ if (ret < 0)
+ return ret;
+
+ ret = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0),
+ REQTYPE_I2C_WRITE_STATT, CTRL_READ_REQUEST,
+ 0, 0, dev->i2c_buf, 2, 1000);
+
+ if ((ret == 2) && (dev->i2c_buf[1] == (len - 1)))
+ ret = 0;
+ else if (ret >= 0)
+ ret = -EIO;
+
+ 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, addr;
+
+ mutex_lock(&dev->i2c_mutex);
+
+ addr = msgs[0].addr << 1;
+
+ if (num == 1) {
+ if (msgs[0].flags & I2C_M_RD)
+ retval = hdpvr_i2c_read(dev, 1, addr, NULL, 0,
+ msgs[0].buf, msgs[0].len);
+ else
+ retval = hdpvr_i2c_write(dev, 1, addr, msgs[0].buf,
+ msgs[0].len);
+ } else if (num == 2) {
+ if (msgs[0].addr != msgs[1].addr) {
+ v4l2_warn(&dev->v4l2_dev, "refusing 2-phase i2c xfer with conflicting target addresses\n");
+ retval = -EINVAL;
+ goto out;
+ }
+
+ if ((msgs[0].flags & I2C_M_RD) || !(msgs[1].flags & I2C_M_RD)) {
+ v4l2_warn(&dev->v4l2_dev, "refusing complex xfer with r0=%d, r1=%d\n",
+ msgs[0].flags & I2C_M_RD,
+ msgs[1].flags & I2C_M_RD);
+ retval = -EINVAL;
+ goto out;
+ }
+
+ /*
+ * Write followed by atomic read is the only complex xfer that
+ * we actually support here.
+ */
+ retval = hdpvr_i2c_read(dev, 1, addr, msgs[0].buf, msgs[0].len,
+ msgs[1].buf, msgs[1].len);
+ } else {
+ v4l2_warn(&dev->v4l2_dev, "refusing %d-phase i2c xfer\n", num);
+ }
+
+out:
+ 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 const struct i2c_algorithm hdpvr_algo = {
+ .master_xfer = hdpvr_transfer,
+ .functionality = hdpvr_functionality,
+};
+
+static const struct i2c_adapter hdpvr_i2c_adapter_template = {
+ .name = "Hauppauge HD PVR I2C",
+ .owner = THIS_MODULE,
+ .algo = &hdpvr_algo,
+};
+
+static int hdpvr_activate_ir(struct hdpvr_device *dev)
+{
+ char buffer[2];
+
+ mutex_lock(&dev->i2c_mutex);
+
+ hdpvr_i2c_read(dev, 0, 0x54, NULL, 0, buffer, 1);
+
+ buffer[0] = 0;
+ buffer[1] = 0x8;
+ hdpvr_i2c_write(dev, 1, 0x54, buffer, 2);
+
+ buffer[1] = 0x18;
+ hdpvr_i2c_write(dev, 1, 0x54, buffer, 2);
+
+ mutex_unlock(&dev->i2c_mutex);
+
+ return 0;
+}
+
+int hdpvr_register_i2c_adapter(struct hdpvr_device *dev)
+{
+ hdpvr_activate_ir(dev);
+
+ dev->i2c_adapter = hdpvr_i2c_adapter_template;
+ dev->i2c_adapter.dev.parent = &dev->udev->dev;
+
+ i2c_set_adapdata(&dev->i2c_adapter, dev);
+
+ return i2c_add_adapter(&dev->i2c_adapter);
+}
+
+#endif
diff --git a/drivers/media/usb/hdpvr/hdpvr-video.c b/drivers/media/usb/hdpvr/hdpvr-video.c
new file mode 100644
index 000000000..fd7d2a9d0
--- /dev/null
+++ b/drivers/media/usb/hdpvr/hdpvr-video.c
@@ -0,0 +1,1253 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Hauppauge HD PVR USB driver - video 4 linux 2 interface
+ *
+ * Copyright (C) 2008 Janne Grunau (j@jannau.net)
+ */
+
+#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/workqueue.h>
+
+#include <linux/videodev2.h>
+#include <linux/v4l2-dv-timings.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-common.h>
+#include <media/v4l2-dv-timings.h>
+#include <media/v4l2-ioctl.h>
+#include <media/v4l2-event.h>
+#include "hdpvr.h"
+
+#define BULK_URB_TIMEOUT 90 /* 0.09 seconds */
+
+#define print_buffer_status() { \
+ v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev, \
+ "%s:%d buffer stat: %d free, %d proc\n", \
+ __func__, __LINE__, \
+ list_size(&dev->free_buff_list), \
+ list_size(&dev->rec_buff_list)); }
+
+static const struct v4l2_dv_timings hdpvr_dv_timings[] = {
+ V4L2_DV_BT_CEA_720X480I59_94,
+ V4L2_DV_BT_CEA_720X576I50,
+ V4L2_DV_BT_CEA_720X480P59_94,
+ V4L2_DV_BT_CEA_720X576P50,
+ V4L2_DV_BT_CEA_1280X720P50,
+ V4L2_DV_BT_CEA_1280X720P60,
+ V4L2_DV_BT_CEA_1920X1080I50,
+ V4L2_DV_BT_CEA_1920X1080I60,
+};
+
+/* Use 480i59 as the default timings */
+#define HDPVR_DEF_DV_TIMINGS_IDX (0)
+
+struct hdpvr_fh {
+ struct v4l2_fh fh;
+ bool legacy_mode;
+};
+
+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);
+}
+
+/*=========================================================================*/
+/* buffer 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_free_coherent(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->v4l2_dev,
+ "allocating %u buffers\n", count);
+
+ for (i = 0; i < count; i++) {
+
+ buf = kzalloc(sizeof(struct hdpvr_buffer), GFP_KERNEL);
+ if (!buf) {
+ v4l2_err(&dev->v4l2_dev, "cannot allocate buffer\n");
+ goto exit;
+ }
+ buf->dev = dev;
+
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb)
+ goto exit_urb;
+ buf->urb = urb;
+
+ mem = usb_alloc_coherent(dev->udev, dev->bulk_in_size, GFP_KERNEL,
+ &urb->transfer_dma);
+ if (!mem) {
+ v4l2_err(&dev->v4l2_dev,
+ "cannot allocate usb transfer buffer\n");
+ goto exit_urb_buffer;
+ }
+
+ 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->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ buf->status = BUFSTAT_AVAILABLE;
+ list_add_tail(&buf->buff_list, &dev->free_buff_list);
+ }
+ return 0;
+exit_urb_buffer:
+ usb_free_urb(urb);
+exit_urb:
+ kfree(buf);
+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) {
+ v4l2_err(&dev->v4l2_dev,
+ "buffer not marked as available\n");
+ ret = -EFAULT;
+ goto err;
+ }
+
+ urb = buf->urb;
+ urb->status = 0;
+ urb->actual_length = 0;
+ ret = usb_submit_urb(urb, GFP_KERNEL);
+ if (ret) {
+ v4l2_err(&dev->v4l2_dev,
+ "usb_submit_urb in %s returned %d\n",
+ __func__, ret);
+ if (++err_count > 2)
+ break;
+ continue;
+ }
+ buf->status = BUFSTAT_INPROGRESS;
+ list_move_tail(&buf->buff_list, &dev->rec_buff_list);
+ }
+err:
+ print_buffer_status();
+ 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->v4l2_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->v4l2_dev,
+ "transmit worker exited\n");
+ return;
+error:
+ v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_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;
+ if (dev->status != STATUS_IDLE)
+ return -EAGAIN;
+
+ ret = get_video_info(dev, &vidinf);
+ if (ret < 0)
+ return ret;
+
+ if (!vidinf.valid) {
+ msleep(250);
+ v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
+ "no video signal at input %d\n", dev->options.video_input);
+ return -EAGAIN;
+ }
+
+ v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev,
+ "video signal: %dx%d@%dhz\n", vidinf.width,
+ vidinf.height, vidinf.fps);
+
+ /* 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->v4l2_dev,
+ "encoder start control request returned %d\n", ret);
+ if (ret < 0)
+ return ret;
+
+ ret = hdpvr_config_call(dev, CTRL_START_STREAMING_VALUE, 0x00);
+ if (ret)
+ return ret;
+
+ dev->status = STATUS_STREAMING;
+
+ schedule_work(&dev->worker);
+
+ v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev,
+ "streaming started\n");
+
+ return 0;
+}
+
+
+/* function expects dev->io_mutex to be hold by caller */
+static int hdpvr_stop_streaming(struct hdpvr_device *dev)
+{
+ int actual_length;
+ uint c = 0;
+ u8 *buf;
+
+ if (dev->status == STATUS_IDLE)
+ return 0;
+ else if (dev->status != STATUS_STREAMING)
+ return -EAGAIN;
+
+ buf = kmalloc(dev->bulk_in_size, GFP_KERNEL);
+ if (!buf)
+ v4l2_err(&dev->v4l2_dev, "failed to allocate temporary buffer for emptying the internal device buffer. Next capture start will be slow\n");
+
+ dev->status = STATUS_SHUTTING_DOWN;
+ hdpvr_config_call(dev, CTRL_STOP_STREAMING_VALUE, 0x00);
+ mutex_unlock(&dev->io_mutex);
+
+ wake_up_interruptible(&dev->wait_buffer);
+ msleep(50);
+
+ flush_work(&dev->worker);
+
+ mutex_lock(&dev->io_mutex);
+ /* kill the still outstanding urbs */
+ hdpvr_cancel_queue(dev);
+
+ /* emptying the device buffer beforeshutting it down */
+ while (buf && ++c < 500 &&
+ !usb_bulk_msg(dev->udev,
+ usb_rcvbulkpipe(dev->udev,
+ dev->bulk_in_endpointAddr),
+ buf, dev->bulk_in_size, &actual_length,
+ BULK_URB_TIMEOUT)) {
+ v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev,
+ "%2d: got %d bytes\n", c, actual_length);
+ }
+ kfree(buf);
+ v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev,
+ "used %d urbs to empty device buffers\n", c-1);
+ msleep(10);
+
+ dev->status = STATUS_IDLE;
+
+ return 0;
+}
+
+
+/*=======================================================================*/
+/*
+ * video 4 linux 2 file operations
+ */
+
+static int hdpvr_open(struct file *file)
+{
+ struct hdpvr_fh *fh = kzalloc(sizeof(*fh), GFP_KERNEL);
+
+ if (fh == NULL)
+ return -ENOMEM;
+ fh->legacy_mode = true;
+ v4l2_fh_init(&fh->fh, video_devdata(file));
+ v4l2_fh_add(&fh->fh);
+ file->private_data = fh;
+ return 0;
+}
+
+static int hdpvr_release(struct file *file)
+{
+ struct hdpvr_device *dev = video_drvdata(file);
+
+ mutex_lock(&dev->io_mutex);
+ if (file->private_data == dev->owner) {
+ hdpvr_stop_streaming(dev);
+ dev->owner = NULL;
+ }
+ mutex_unlock(&dev->io_mutex);
+
+ return v4l2_fh_release(file);
+}
+
+/*
+ * 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_device *dev = video_drvdata(file);
+ struct hdpvr_buffer *buf = NULL;
+ struct urb *urb;
+ int ret = 0;
+ int rem, cnt;
+
+ if (*pos)
+ return -ESPIPE;
+
+ mutex_lock(&dev->io_mutex);
+ if (dev->status == STATUS_IDLE) {
+ if (hdpvr_start_streaming(dev)) {
+ v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
+ "start_streaming failed\n");
+ ret = -EIO;
+ msleep(200);
+ dev->status = STATUS_IDLE;
+ mutex_unlock(&dev->io_mutex);
+ goto err;
+ }
+ dev->owner = file->private_data;
+ print_buffer_status();
+ }
+ mutex_unlock(&dev->io_mutex);
+
+ /* wait for the first buffer */
+ if (!(file->f_flags & O_NONBLOCK)) {
+ if (wait_event_interruptible(dev->wait_data,
+ !list_empty_careful(&dev->rec_buff_list)))
+ return -ERESTARTSYS;
+ }
+
+ buf = hdpvr_get_next_buffer(dev);
+
+ while (count > 0 && buf) {
+
+ if (buf->status != BUFSTAT_READY &&
+ dev->status != STATUS_DISCONNECTED) {
+ int err;
+ /* return nonblocking */
+ if (file->f_flags & O_NONBLOCK) {
+ if (!ret)
+ ret = -EAGAIN;
+ goto err;
+ }
+
+ err = wait_event_interruptible_timeout(dev->wait_data,
+ buf->status == BUFSTAT_READY,
+ msecs_to_jiffies(1000));
+ if (err < 0) {
+ ret = err;
+ goto err;
+ }
+ if (!err) {
+ v4l2_info(&dev->v4l2_dev,
+ "timeout: restart streaming\n");
+ mutex_lock(&dev->io_mutex);
+ hdpvr_stop_streaming(dev);
+ mutex_unlock(&dev->io_mutex);
+ /*
+ * The FW needs about 4 seconds after streaming
+ * stopped before it is ready to restart
+ * streaming.
+ */
+ msleep(4000);
+ err = hdpvr_start_streaming(dev);
+ if (err) {
+ ret = err;
+ 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)) {
+ v4l2_err(&dev->v4l2_dev, "read: copy_to_user failed\n");
+ 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);
+
+ print_buffer_status();
+
+ 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 __poll_t hdpvr_poll(struct file *filp, poll_table *wait)
+{
+ __poll_t req_events = poll_requested_events(wait);
+ struct hdpvr_buffer *buf = NULL;
+ struct hdpvr_device *dev = video_drvdata(filp);
+ __poll_t mask = v4l2_ctrl_poll(filp, wait);
+
+ if (!(req_events & (EPOLLIN | EPOLLRDNORM)))
+ return mask;
+
+ mutex_lock(&dev->io_mutex);
+
+ if (dev->status == STATUS_IDLE) {
+ if (hdpvr_start_streaming(dev)) {
+ v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev,
+ "start_streaming failed\n");
+ dev->status = STATUS_IDLE;
+ } else {
+ dev->owner = filp->private_data;
+ }
+
+ print_buffer_status();
+ }
+ mutex_unlock(&dev->io_mutex);
+
+ buf = hdpvr_get_next_buffer(dev);
+ /* only wait if no data is available */
+ if (!buf || buf->status != BUFSTAT_READY) {
+ poll_wait(filp, &dev->wait_data, wait);
+ buf = hdpvr_get_next_buffer(dev);
+ }
+ if (buf && buf->status == BUFSTAT_READY)
+ mask |= EPOLLIN | EPOLLRDNORM;
+
+ return mask;
+}
+
+
+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 = video_ioctl2,
+};
+
+/*=======================================================================*/
+/*
+ * V4L2 ioctl handling
+ */
+
+static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+{
+ struct hdpvr_device *dev = video_drvdata(file);
+
+ strscpy(cap->driver, "hdpvr", sizeof(cap->driver));
+ strscpy(cap->card, "Hauppauge HD PVR", sizeof(cap->card));
+ usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
+ return 0;
+}
+
+static int vidioc_s_std(struct file *file, void *_fh,
+ v4l2_std_id std)
+{
+ struct hdpvr_device *dev = video_drvdata(file);
+ struct hdpvr_fh *fh = _fh;
+ u8 std_type = 1;
+
+ if (!fh->legacy_mode && dev->options.video_input == HDPVR_COMPONENT)
+ return -ENODATA;
+ if (dev->status != STATUS_IDLE)
+ return -EBUSY;
+ if (std & V4L2_STD_525_60)
+ std_type = 0;
+ dev->cur_std = std;
+ dev->width = 720;
+ dev->height = std_type ? 576 : 480;
+
+ return hdpvr_config_call(dev, CTRL_VIDEO_STD_TYPE, std_type);
+}
+
+static int vidioc_g_std(struct file *file, void *_fh,
+ v4l2_std_id *std)
+{
+ struct hdpvr_device *dev = video_drvdata(file);
+ struct hdpvr_fh *fh = _fh;
+
+ if (!fh->legacy_mode && dev->options.video_input == HDPVR_COMPONENT)
+ return -ENODATA;
+ *std = dev->cur_std;
+ return 0;
+}
+
+static int vidioc_querystd(struct file *file, void *_fh, v4l2_std_id *a)
+{
+ struct hdpvr_device *dev = video_drvdata(file);
+ struct hdpvr_video_info vid_info;
+ struct hdpvr_fh *fh = _fh;
+ int ret;
+
+ *a = V4L2_STD_UNKNOWN;
+ if (dev->options.video_input == HDPVR_COMPONENT)
+ return fh->legacy_mode ? 0 : -ENODATA;
+ ret = get_video_info(dev, &vid_info);
+ if (vid_info.valid && vid_info.width == 720 &&
+ (vid_info.height == 480 || vid_info.height == 576)) {
+ *a = (vid_info.height == 480) ?
+ V4L2_STD_525_60 : V4L2_STD_625_50;
+ }
+ return ret;
+}
+
+static int vidioc_s_dv_timings(struct file *file, void *_fh,
+ struct v4l2_dv_timings *timings)
+{
+ struct hdpvr_device *dev = video_drvdata(file);
+ struct hdpvr_fh *fh = _fh;
+ int i;
+
+ fh->legacy_mode = false;
+ if (dev->options.video_input)
+ return -ENODATA;
+ if (dev->status != STATUS_IDLE)
+ return -EBUSY;
+ for (i = 0; i < ARRAY_SIZE(hdpvr_dv_timings); i++)
+ if (v4l2_match_dv_timings(timings, hdpvr_dv_timings + i, 0, false))
+ break;
+ if (i == ARRAY_SIZE(hdpvr_dv_timings))
+ return -EINVAL;
+ dev->cur_dv_timings = hdpvr_dv_timings[i];
+ dev->width = hdpvr_dv_timings[i].bt.width;
+ dev->height = hdpvr_dv_timings[i].bt.height;
+ return 0;
+}
+
+static int vidioc_g_dv_timings(struct file *file, void *_fh,
+ struct v4l2_dv_timings *timings)
+{
+ struct hdpvr_device *dev = video_drvdata(file);
+ struct hdpvr_fh *fh = _fh;
+
+ fh->legacy_mode = false;
+ if (dev->options.video_input)
+ return -ENODATA;
+ *timings = dev->cur_dv_timings;
+ return 0;
+}
+
+static int vidioc_query_dv_timings(struct file *file, void *_fh,
+ struct v4l2_dv_timings *timings)
+{
+ struct hdpvr_device *dev = video_drvdata(file);
+ struct hdpvr_fh *fh = _fh;
+ struct hdpvr_video_info vid_info;
+ bool interlaced;
+ int ret = 0;
+ int i;
+
+ fh->legacy_mode = false;
+ if (dev->options.video_input)
+ return -ENODATA;
+ ret = get_video_info(dev, &vid_info);
+ if (ret)
+ return ret;
+ if (!vid_info.valid)
+ return -ENOLCK;
+ interlaced = vid_info.fps <= 30;
+ for (i = 0; i < ARRAY_SIZE(hdpvr_dv_timings); i++) {
+ const struct v4l2_bt_timings *bt = &hdpvr_dv_timings[i].bt;
+ unsigned hsize;
+ unsigned vsize;
+ unsigned fps;
+
+ hsize = V4L2_DV_BT_FRAME_WIDTH(bt);
+ vsize = V4L2_DV_BT_FRAME_HEIGHT(bt);
+ fps = (unsigned)bt->pixelclock / (hsize * vsize);
+ if (bt->width != vid_info.width ||
+ bt->height != vid_info.height ||
+ bt->interlaced != interlaced ||
+ (fps != vid_info.fps && fps + 1 != vid_info.fps))
+ continue;
+ *timings = hdpvr_dv_timings[i];
+ break;
+ }
+ if (i == ARRAY_SIZE(hdpvr_dv_timings))
+ ret = -ERANGE;
+
+ return ret;
+}
+
+static int vidioc_enum_dv_timings(struct file *file, void *_fh,
+ struct v4l2_enum_dv_timings *timings)
+{
+ struct hdpvr_device *dev = video_drvdata(file);
+ struct hdpvr_fh *fh = _fh;
+
+ fh->legacy_mode = false;
+ memset(timings->reserved, 0, sizeof(timings->reserved));
+ if (dev->options.video_input)
+ return -ENODATA;
+ if (timings->index >= ARRAY_SIZE(hdpvr_dv_timings))
+ return -EINVAL;
+ timings->timings = hdpvr_dv_timings[timings->index];
+ return 0;
+}
+
+static int vidioc_dv_timings_cap(struct file *file, void *_fh,
+ struct v4l2_dv_timings_cap *cap)
+{
+ struct hdpvr_device *dev = video_drvdata(file);
+ struct hdpvr_fh *fh = _fh;
+
+ fh->legacy_mode = false;
+ if (dev->options.video_input)
+ return -ENODATA;
+ cap->type = V4L2_DV_BT_656_1120;
+ cap->bt.min_width = 720;
+ cap->bt.max_width = 1920;
+ cap->bt.min_height = 480;
+ cap->bt.max_height = 1080;
+ cap->bt.min_pixelclock = 27000000;
+ cap->bt.max_pixelclock = 74250000;
+ cap->bt.standards = V4L2_DV_BT_STD_CEA861;
+ cap->bt.capabilities = V4L2_DV_BT_CAP_INTERLACED | V4L2_DV_BT_CAP_PROGRESSIVE;
+ return 0;
+}
+
+static const char *iname[] = {
+ [HDPVR_COMPONENT] = "Component",
+ [HDPVR_SVIDEO] = "S-Video",
+ [HDPVR_COMPOSITE] = "Composite",
+};
+
+static int vidioc_enum_input(struct file *file, void *_fh, struct v4l2_input *i)
+{
+ unsigned int n;
+
+ n = i->index;
+ if (n >= HDPVR_VIDEO_INPUTS)
+ return -EINVAL;
+
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+
+ strscpy(i->name, iname[n], sizeof(i->name));
+
+ i->audioset = 1<<HDPVR_RCA_FRONT | 1<<HDPVR_RCA_BACK | 1<<HDPVR_SPDIF;
+
+ i->capabilities = n ? V4L2_IN_CAP_STD : V4L2_IN_CAP_DV_TIMINGS;
+ i->std = n ? V4L2_STD_ALL : 0;
+
+ return 0;
+}
+
+static int vidioc_s_input(struct file *file, void *_fh,
+ unsigned int index)
+{
+ struct hdpvr_device *dev = video_drvdata(file);
+ int retval;
+
+ if (index >= HDPVR_VIDEO_INPUTS)
+ return -EINVAL;
+
+ if (dev->status != STATUS_IDLE)
+ return -EBUSY;
+
+ retval = hdpvr_config_call(dev, CTRL_VIDEO_INPUT_VALUE, index+1);
+ if (!retval) {
+ dev->options.video_input = index;
+ /*
+ * Unfortunately gstreamer calls ENUMSTD and bails out if it
+ * won't find any formats, even though component input is
+ * selected. This means that we have to leave tvnorms at
+ * V4L2_STD_ALL. We cannot use the 'legacy' trick since
+ * tvnorms is set at the device node level and not at the
+ * filehandle level.
+ *
+ * Comment this out for now, but if the legacy mode can be
+ * removed in the future, then this code should be enabled
+ * again.
+ dev->video_dev.tvnorms =
+ (index != HDPVR_COMPONENT) ? V4L2_STD_ALL : 0;
+ */
+ }
+
+ return retval;
+}
+
+static int vidioc_g_input(struct file *file, void *private_data,
+ unsigned int *index)
+{
+ struct hdpvr_device *dev = video_drvdata(file);
+
+ *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;
+
+ strscpy(audio->name, audio_iname[n], sizeof(audio->name));
+
+ return 0;
+}
+
+static int vidioc_s_audio(struct file *file, void *private_data,
+ const struct v4l2_audio *audio)
+{
+ struct hdpvr_device *dev = video_drvdata(file);
+ int retval;
+
+ if (audio->index >= HDPVR_AUDIO_INPUTS)
+ return -EINVAL;
+
+ if (dev->status != STATUS_IDLE)
+ return -EBUSY;
+
+ 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_device *dev = video_drvdata(file);
+
+ audio->index = dev->options.audio_input;
+ audio->capability = V4L2_AUDCAP_STEREO;
+ strscpy(audio->name, audio_iname[audio->index], sizeof(audio->name));
+ return 0;
+}
+
+static int hdpvr_try_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct hdpvr_device *dev =
+ container_of(ctrl->handler, struct hdpvr_device, hdl);
+
+ switch (ctrl->id) {
+ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+ if (ctrl->val == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR &&
+ dev->video_bitrate->val >= dev->video_bitrate_peak->val)
+ dev->video_bitrate_peak->val =
+ dev->video_bitrate->val + 100000;
+ break;
+ }
+ return 0;
+}
+
+static int hdpvr_s_ctrl(struct v4l2_ctrl *ctrl)
+{
+ struct hdpvr_device *dev =
+ container_of(ctrl->handler, struct hdpvr_device, hdl);
+ struct hdpvr_options *opt = &dev->options;
+ int ret = -EINVAL;
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ ret = hdpvr_config_call(dev, CTRL_BRIGHTNESS, ctrl->val);
+ if (ret)
+ break;
+ dev->options.brightness = ctrl->val;
+ return 0;
+ case V4L2_CID_CONTRAST:
+ ret = hdpvr_config_call(dev, CTRL_CONTRAST, ctrl->val);
+ if (ret)
+ break;
+ dev->options.contrast = ctrl->val;
+ return 0;
+ case V4L2_CID_SATURATION:
+ ret = hdpvr_config_call(dev, CTRL_SATURATION, ctrl->val);
+ if (ret)
+ break;
+ dev->options.saturation = ctrl->val;
+ return 0;
+ case V4L2_CID_HUE:
+ ret = hdpvr_config_call(dev, CTRL_HUE, ctrl->val);
+ if (ret)
+ break;
+ dev->options.hue = ctrl->val;
+ return 0;
+ case V4L2_CID_SHARPNESS:
+ ret = hdpvr_config_call(dev, CTRL_SHARPNESS, ctrl->val);
+ if (ret)
+ break;
+ dev->options.sharpness = ctrl->val;
+ return 0;
+ case V4L2_CID_MPEG_AUDIO_ENCODING:
+ if (dev->flags & HDPVR_FLAG_AC3_CAP) {
+ opt->audio_codec = ctrl->val;
+ return hdpvr_set_audio(dev, opt->audio_input + 1,
+ opt->audio_codec);
+ }
+ return 0;
+ case V4L2_CID_MPEG_VIDEO_ENCODING:
+ return 0;
+/* 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: {
+ uint peak_bitrate = dev->video_bitrate_peak->val / 100000;
+ uint bitrate = dev->video_bitrate->val / 100000;
+
+ if (ctrl->is_new) {
+ if (ctrl->val == V4L2_MPEG_VIDEO_BITRATE_MODE_CBR)
+ opt->bitrate_mode = HDPVR_CONSTANT;
+ else
+ opt->bitrate_mode = HDPVR_VARIABLE_AVERAGE;
+ hdpvr_config_call(dev, CTRL_BITRATE_MODE_VALUE,
+ opt->bitrate_mode);
+ v4l2_ctrl_activate(dev->video_bitrate_peak,
+ ctrl->val != V4L2_MPEG_VIDEO_BITRATE_MODE_CBR);
+ }
+
+ if (dev->video_bitrate_peak->is_new ||
+ dev->video_bitrate->is_new) {
+ opt->bitrate = bitrate;
+ opt->peak_bitrate = peak_bitrate;
+ hdpvr_set_bitrate(dev);
+ }
+ return 0;
+ }
+ case V4L2_CID_MPEG_STREAM_TYPE:
+ return 0;
+ default:
+ break;
+ }
+ return ret;
+}
+
+static int vidioc_enum_fmt_vid_cap(struct file *file, void *private_data,
+ struct v4l2_fmtdesc *f)
+{
+ if (f->index != 0)
+ return -EINVAL;
+
+ f->pixelformat = V4L2_PIX_FMT_MPEG;
+
+ return 0;
+}
+
+static int vidioc_g_fmt_vid_cap(struct file *file, void *_fh,
+ struct v4l2_format *f)
+{
+ struct hdpvr_device *dev = video_drvdata(file);
+ struct hdpvr_fh *fh = _fh;
+ int ret;
+
+ /*
+ * The original driver would always returns the current detected
+ * resolution as the format (and EFAULT if it couldn't be detected).
+ * With the introduction of VIDIOC_QUERY_DV_TIMINGS there is now a
+ * better way of doing this, but to stay compatible with existing
+ * applications we assume legacy mode every time an application opens
+ * the device. Only if one of the new DV_TIMINGS ioctls is called
+ * will the filehandle go into 'normal' mode where g_fmt returns the
+ * last set format.
+ */
+ if (fh->legacy_mode) {
+ struct hdpvr_video_info vid_info;
+
+ ret = get_video_info(dev, &vid_info);
+ if (ret < 0)
+ return ret;
+ if (!vid_info.valid)
+ return -EFAULT;
+ f->fmt.pix.width = vid_info.width;
+ f->fmt.pix.height = vid_info.height;
+ } else {
+ f->fmt.pix.width = dev->width;
+ f->fmt.pix.height = dev->height;
+ }
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
+ f->fmt.pix.sizeimage = dev->bulk_in_size;
+ f->fmt.pix.bytesperline = 0;
+ if (f->fmt.pix.width == 720) {
+ /* SDTV formats */
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+ f->fmt.pix.field = V4L2_FIELD_INTERLACED;
+ } else {
+ /* HDTV formats */
+ f->fmt.pix.colorspace = V4L2_COLORSPACE_REC709;
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+ }
+ return 0;
+}
+
+static int vidioc_encoder_cmd(struct file *filp, void *priv,
+ struct v4l2_encoder_cmd *a)
+{
+ struct hdpvr_device *dev = video_drvdata(filp);
+ int res = 0;
+
+ mutex_lock(&dev->io_mutex);
+ a->flags = 0;
+
+ switch (a->cmd) {
+ case V4L2_ENC_CMD_START:
+ if (dev->owner && filp->private_data != dev->owner) {
+ res = -EBUSY;
+ break;
+ }
+ if (dev->status == STATUS_STREAMING)
+ break;
+ res = hdpvr_start_streaming(dev);
+ if (!res)
+ dev->owner = filp->private_data;
+ else
+ dev->status = STATUS_IDLE;
+ break;
+ case V4L2_ENC_CMD_STOP:
+ if (dev->owner && filp->private_data != dev->owner) {
+ res = -EBUSY;
+ break;
+ }
+ if (dev->status == STATUS_IDLE)
+ break;
+ res = hdpvr_stop_streaming(dev);
+ if (!res)
+ dev->owner = NULL;
+ break;
+ default:
+ v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
+ "Unsupported encoder cmd %d\n", a->cmd);
+ res = -EINVAL;
+ break;
+ }
+
+ mutex_unlock(&dev->io_mutex);
+ return res;
+}
+
+static int vidioc_try_encoder_cmd(struct file *filp, void *priv,
+ struct v4l2_encoder_cmd *a)
+{
+ a->flags = 0;
+ switch (a->cmd) {
+ case V4L2_ENC_CMD_START:
+ case V4L2_ENC_CMD_STOP:
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct v4l2_ioctl_ops hdpvr_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_s_std = vidioc_s_std,
+ .vidioc_g_std = vidioc_g_std,
+ .vidioc_querystd = vidioc_querystd,
+ .vidioc_s_dv_timings = vidioc_s_dv_timings,
+ .vidioc_g_dv_timings = vidioc_g_dv_timings,
+ .vidioc_query_dv_timings= vidioc_query_dv_timings,
+ .vidioc_enum_dv_timings = vidioc_enum_dv_timings,
+ .vidioc_dv_timings_cap = vidioc_dv_timings_cap,
+ .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_enum_fmt_vid_cap= vidioc_enum_fmt_vid_cap,
+ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_s_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_try_fmt_vid_cap = vidioc_g_fmt_vid_cap,
+ .vidioc_encoder_cmd = vidioc_encoder_cmd,
+ .vidioc_try_encoder_cmd = vidioc_try_encoder_cmd,
+ .vidioc_log_status = v4l2_ctrl_log_status,
+ .vidioc_subscribe_event = v4l2_ctrl_subscribe_event,
+ .vidioc_unsubscribe_event = v4l2_event_unsubscribe,
+};
+
+static void hdpvr_device_release(struct video_device *vdev)
+{
+ struct hdpvr_device *dev = video_get_drvdata(vdev);
+
+ hdpvr_delete(dev);
+ flush_work(&dev->worker);
+
+ v4l2_device_unregister(&dev->v4l2_dev);
+ v4l2_ctrl_handler_free(&dev->hdl);
+
+ /* deregister I2C adapter */
+#if IS_ENABLED(CONFIG_I2C)
+ mutex_lock(&dev->i2c_mutex);
+ i2c_del_adapter(&dev->i2c_adapter);
+ mutex_unlock(&dev->i2c_mutex);
+#endif /* CONFIG_I2C */
+
+ kfree(dev->usbc_buf);
+ kfree(dev);
+}
+
+static const struct video_device hdpvr_video_template = {
+ .fops = &hdpvr_fops,
+ .release = hdpvr_device_release,
+ .ioctl_ops = &hdpvr_ioctl_ops,
+ .tvnorms = V4L2_STD_ALL,
+ .device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_AUDIO |
+ V4L2_CAP_READWRITE,
+};
+
+static const struct v4l2_ctrl_ops hdpvr_ctrl_ops = {
+ .try_ctrl = hdpvr_try_ctrl,
+ .s_ctrl = hdpvr_s_ctrl,
+};
+
+int hdpvr_register_videodev(struct hdpvr_device *dev, struct device *parent,
+ int devnum)
+{
+ struct v4l2_ctrl_handler *hdl = &dev->hdl;
+ bool ac3 = dev->flags & HDPVR_FLAG_AC3_CAP;
+ int res;
+
+ // initialize dev->worker
+ INIT_WORK(&dev->worker, hdpvr_transmit_buffers);
+
+ dev->cur_std = V4L2_STD_525_60;
+ dev->width = 720;
+ dev->height = 480;
+ dev->cur_dv_timings = hdpvr_dv_timings[HDPVR_DEF_DV_TIMINGS_IDX];
+ v4l2_ctrl_handler_init(hdl, 11);
+ if (dev->fw_ver > 0x15) {
+ v4l2_ctrl_new_std(hdl, &hdpvr_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0x0, 0xff, 1, 0x80);
+ v4l2_ctrl_new_std(hdl, &hdpvr_ctrl_ops,
+ V4L2_CID_CONTRAST, 0x0, 0xff, 1, 0x40);
+ v4l2_ctrl_new_std(hdl, &hdpvr_ctrl_ops,
+ V4L2_CID_SATURATION, 0x0, 0xff, 1, 0x40);
+ v4l2_ctrl_new_std(hdl, &hdpvr_ctrl_ops,
+ V4L2_CID_HUE, 0x0, 0x1e, 1, 0xf);
+ v4l2_ctrl_new_std(hdl, &hdpvr_ctrl_ops,
+ V4L2_CID_SHARPNESS, 0x0, 0xff, 1, 0x80);
+ } else {
+ v4l2_ctrl_new_std(hdl, &hdpvr_ctrl_ops,
+ V4L2_CID_BRIGHTNESS, 0x0, 0xff, 1, 0x86);
+ v4l2_ctrl_new_std(hdl, &hdpvr_ctrl_ops,
+ V4L2_CID_CONTRAST, 0x0, 0xff, 1, 0x80);
+ v4l2_ctrl_new_std(hdl, &hdpvr_ctrl_ops,
+ V4L2_CID_SATURATION, 0x0, 0xff, 1, 0x80);
+ v4l2_ctrl_new_std(hdl, &hdpvr_ctrl_ops,
+ V4L2_CID_HUE, 0x0, 0xff, 1, 0x80);
+ v4l2_ctrl_new_std(hdl, &hdpvr_ctrl_ops,
+ V4L2_CID_SHARPNESS, 0x0, 0xff, 1, 0x80);
+ }
+
+ v4l2_ctrl_new_std_menu(hdl, &hdpvr_ctrl_ops,
+ V4L2_CID_MPEG_STREAM_TYPE,
+ V4L2_MPEG_STREAM_TYPE_MPEG2_TS,
+ 0x1, V4L2_MPEG_STREAM_TYPE_MPEG2_TS);
+ v4l2_ctrl_new_std_menu(hdl, &hdpvr_ctrl_ops,
+ V4L2_CID_MPEG_AUDIO_ENCODING,
+ ac3 ? V4L2_MPEG_AUDIO_ENCODING_AC3 : V4L2_MPEG_AUDIO_ENCODING_AAC,
+ 0x7, ac3 ? dev->options.audio_codec : V4L2_MPEG_AUDIO_ENCODING_AAC);
+ v4l2_ctrl_new_std_menu(hdl, &hdpvr_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_ENCODING,
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC, 0x3,
+ V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC);
+
+ dev->video_mode = v4l2_ctrl_new_std_menu(hdl, &hdpvr_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
+ V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 0,
+ V4L2_MPEG_VIDEO_BITRATE_MODE_CBR);
+
+ dev->video_bitrate = v4l2_ctrl_new_std(hdl, &hdpvr_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_BITRATE,
+ 1000000, 13500000, 100000, 6500000);
+ dev->video_bitrate_peak = v4l2_ctrl_new_std(hdl, &hdpvr_ctrl_ops,
+ V4L2_CID_MPEG_VIDEO_BITRATE_PEAK,
+ 1100000, 20200000, 100000, 9000000);
+ dev->v4l2_dev.ctrl_handler = hdl;
+ if (hdl->error) {
+ res = hdl->error;
+ v4l2_err(&dev->v4l2_dev, "Could not register controls\n");
+ goto error;
+ }
+ v4l2_ctrl_cluster(3, &dev->video_mode);
+ res = v4l2_ctrl_handler_setup(hdl);
+ if (res < 0) {
+ v4l2_err(&dev->v4l2_dev, "Could not setup controls\n");
+ goto error;
+ }
+
+ /* setup and register video device */
+ dev->video_dev = hdpvr_video_template;
+ strscpy(dev->video_dev.name, "Hauppauge HD PVR",
+ sizeof(dev->video_dev.name));
+ dev->video_dev.v4l2_dev = &dev->v4l2_dev;
+ video_set_drvdata(&dev->video_dev, dev);
+
+ res = video_register_device(&dev->video_dev, VFL_TYPE_VIDEO, devnum);
+ if (res < 0) {
+ v4l2_err(&dev->v4l2_dev, "video_device registration failed\n");
+ goto error;
+ }
+
+ return 0;
+error:
+ v4l2_ctrl_handler_free(hdl);
+ return res;
+}
diff --git a/drivers/media/usb/hdpvr/hdpvr.h b/drivers/media/usb/hdpvr/hdpvr.h
new file mode 100644
index 000000000..84519c962
--- /dev/null
+++ b/drivers/media/usb/hdpvr/hdpvr.h
@@ -0,0 +1,324 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Hauppauge HD PVR USB driver
+ *
+ * Copyright (C) 2008 Janne Grunau (j@jannau.net)
+ */
+
+#include <linux/usb.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include <linux/workqueue.h>
+#include <linux/videodev2.h>
+
+#include <media/v4l2-device.h>
+#include <media/v4l2-ctrls.h>
+#include <media/i2c/ir-kbd-i2c.h>
+
+#define HDPVR_MAX 8
+#define HDPVR_I2C_MAX_SIZE 128
+
+/* 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 HD_PVR_PRODUCT_ID4 0x4903
+#define HD_PVR_PRODUCT_ID3 0x4982
+
+#define UNSET (-1U)
+
+#define NUM_BUFFERS 64
+
+#define HDPVR_FIRMWARE_VERSION 0x08
+#define HDPVR_FIRMWARE_VERSION_AC3 0x0d
+#define HDPVR_FIRMWARE_VERSION_0X12 0x12
+#define HDPVR_FIRMWARE_VERSION_0X15 0x15
+#define HDPVR_FIRMWARE_VERSION_0X1E 0x1e
+
+/* #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 control handler for this device */
+ struct v4l2_ctrl_handler hdl;
+ /* the usb device for this device */
+ struct usb_device *udev;
+ /* v4l2-device unused */
+ struct v4l2_device v4l2_dev;
+ struct { /* video mode/bitrate control cluster */
+ struct v4l2_ctrl *video_mode;
+ struct v4l2_ctrl *video_bitrate;
+ struct v4l2_ctrl *video_bitrate_peak;
+ };
+ /* v4l2 format */
+ uint width, height;
+
+ /* 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;
+
+ /* holds the current set options */
+ struct hdpvr_options options;
+ v4l2_std_id cur_std;
+ struct v4l2_dv_timings cur_dv_timings;
+
+ 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 work_struct worker;
+ /* current stream owner */
+ struct v4l2_fh *owner;
+
+ /* I2C adapter */
+ struct i2c_adapter i2c_adapter;
+ /* I2C lock */
+ struct mutex i2c_mutex;
+ /* I2C message buffer space */
+ char i2c_buf[HDPVR_I2C_MAX_SIZE];
+
+ /* For passing data to ir-kbd-i2c */
+ struct IR_i2c_init_data ir_i2c_init_data;
+
+ /* usb control transfer buffer and lock */
+ struct mutex usbc_mutex;
+ u8 *usbc_buf;
+ u8 fw_ver;
+};
+
+static inline struct hdpvr_device *to_hdpvr_dev(struct v4l2_device *v4l2_dev)
+{
+ return container_of(v4l2_dev, struct hdpvr_device, v4l2_dev);
+}
+
+
+/* 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;
+ bool valid;
+};
+
+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 FREQUENCY 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);
+
+int get_video_info(struct hdpvr_device *dev, struct hdpvr_video_info *vid_info);
+
+/* :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, struct device *parent,
+ int devnumber);
+
+int hdpvr_cancel_queue(struct hdpvr_device *dev);
+
+/*========================================================================*/
+/* i2c adapter registration */
+int hdpvr_register_i2c_adapter(struct hdpvr_device *dev);
+
+struct i2c_client *hdpvr_register_ir_i2c(struct hdpvr_device *dev);
+
+/*========================================================================*/
+/* buffer management */
+int hdpvr_free_buffers(struct hdpvr_device *dev);
+int hdpvr_alloc_buffers(struct hdpvr_device *dev, uint count);