diff options
Diffstat (limited to 'drivers/media/usb/hdpvr')
| -rw-r--r-- | drivers/media/usb/hdpvr/Kconfig | 10 | ||||
| -rw-r--r-- | drivers/media/usb/hdpvr/Makefile | 7 | ||||
| -rw-r--r-- | drivers/media/usb/hdpvr/hdpvr-control.c | 190 | ||||
| -rw-r--r-- | drivers/media/usb/hdpvr/hdpvr-core.c | 471 | ||||
| -rw-r--r-- | drivers/media/usb/hdpvr/hdpvr-i2c.c | 230 | ||||
| -rw-r--r-- | drivers/media/usb/hdpvr/hdpvr-video.c | 1255 | ||||
| -rw-r--r-- | drivers/media/usb/hdpvr/hdpvr.h | 331 | 
7 files changed, 2494 insertions, 0 deletions
diff --git a/drivers/media/usb/hdpvr/Kconfig b/drivers/media/usb/hdpvr/Kconfig new file mode 100644 index 00000000000..d73d9a1952b --- /dev/null +++ b/drivers/media/usb/hdpvr/Kconfig @@ -0,0 +1,10 @@ + +config VIDEO_HDPVR +	tristate "Hauppauge HD PVR support" +	depends on VIDEO_DEV && VIDEO_V4L2 +	---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 00000000000..9b8d1463c52 --- /dev/null +++ b/drivers/media/usb/hdpvr/Makefile @@ -0,0 +1,7 @@ +hdpvr-objs	:= hdpvr-control.o hdpvr-core.o hdpvr-video.o hdpvr-i2c.o + +obj-$(CONFIG_VIDEO_HDPVR) += hdpvr.o + +ccflags-y += -Idrivers/media/i2c + +ccflags-y += $(extra-cflags-y) $(extra-cflags-m) diff --git a/drivers/media/usb/hdpvr/hdpvr-control.c b/drivers/media/usb/hdpvr/hdpvr-control.c new file mode 100644 index 00000000000..6053661dc04 --- /dev/null +++ b/drivers/media/usb/hdpvr/hdpvr-control.c @@ -0,0 +1,190 @@ +/* + * Hauppauge 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; + +	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) { +		char print_buf[15]; +		hex_dump_to_buffer(dev->usbc_buf, 5, 16, 1, print_buf, +				   sizeof(print_buf), 0); +		v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, +			 "get video info returned: %d, %s\n", ret, print_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) +{ +#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); +		v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, +			 "get input lines info returned: %d, %s\n", ret, +			 print_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 00000000000..c5638964c3f --- /dev/null +++ b/drivers/media/usb/hdpvr/hdpvr-core.c @@ -0,0 +1,471 @@ +/* + * 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 + * + *	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/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 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); + +	if (dev->video_dev) +		video_device_release(dev->video_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; +#ifdef HDPVR_DEBUG +	size_t buf_size = 46; +	char *print_buf = kzalloc(5*buf_size+1, GFP_KERNEL); +	if (!print_buf) { +		v4l2_err(&dev->v4l2_dev, "Out of memory\n"); +		return retval; +	} +#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) { +		v4l2_err(&dev->v4l2_dev, +			 "unexpected answer of status request, len %d\n", ret); +		goto unlock; +	} +#ifdef HDPVR_DEBUG +	else { +		hex_dump_to_buffer(dev->usbc_buf, 46, 16, 1, print_buf, +				   5*buf_size+1, 0); +		v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, +			 "Status request returned, len %d: %s\n", +			 ret, print_buf); +	} +#endif + +	dev->fw_ver = dev->usbc_buf[1]; + +	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 +	hex_dump_to_buffer(response, 8, 16, 1, print_buf, 5*buf_size+1, 0); +	v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, "challenge: %s\n", +		 print_buf); +#endif +	challenge(response); +#ifdef HDPVR_DEBUG +	hex_dump_to_buffer(response, 8, 16, 1, print_buf, 5*buf_size+1, 0); +	v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_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); +	v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, +		 "magic request returned %d\n", ret); + +	retval = ret != 8; +unlock: +	mutex_unlock(&dev->usbc_mutex); +#ifdef HDPVR_DEBUG +	kfree(print_buf); +#endif +	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; +	struct i2c_client *client; +	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) { +		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; +	} + +	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; +	} + +	init_waitqueue_head(&dev->wait_buffer); +	init_waitqueue_head(&dev->wait_data); + +	dev->workqueue = create_singlethread_workqueue("hdpvr_buffer"); +	if (!dev->workqueue) +		goto error; + +	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; +	} + +	/* init the device */ +	if (hdpvr_device_init(dev)) { +		v4l2_err(&dev->v4l2_dev, "device init failed\n"); +		goto error; +	} + +	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; +	} +	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; +	} + +	client = hdpvr_register_ir_rx_i2c(dev); +	if (!client) { +		v4l2_err(&dev->v4l2_dev, "i2c IR RX device register failed\n"); +		retval = -ENODEV; +		goto reg_fail; +	} + +	client = hdpvr_register_ir_tx_i2c(dev); +	if (!client) { +		v4l2_err(&dev->v4l2_dev, "i2c IR TX device register failed\n"); +		retval = -ENODEV; +		goto reg_fail; +	} +#endif + +	retval = hdpvr_register_videodev(dev, &interface->dev, +				    video_nr[atomic_inc_return(&dev_nr)]); +	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); +#endif +error: +	if (dev) { +		/* Destroy single thread */ +		if (dev->workqueue) +			destroy_workqueue(dev->workqueue); +		/* this frees allocated memory */ +		hdpvr_delete(dev); +	} +	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_workqueue(dev->workqueue); +	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 00000000000..a38f58c4c6b --- /dev/null +++ b/drivers/media/usb/hdpvr/hdpvr-i2c.c @@ -0,0 +1,230 @@ + +/* + * 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> + * + *	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. + * + */ + +#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_tx_i2c(struct hdpvr_device *dev) +{ +	struct IR_i2c_init_data *init_data = &dev->ir_i2c_init_data; +	struct i2c_board_info hdpvr_ir_tx_i2c_board_info = { +		I2C_BOARD_INFO("ir_tx_z8f0811_hdpvr", Z8F0811_IR_TX_I2C_ADDR), +	}; + +	init_data->name = "HD-PVR"; +	hdpvr_ir_tx_i2c_board_info.platform_data = init_data; + +	return i2c_new_device(&dev->i2c_adapter, &hdpvr_ir_tx_i2c_board_info); +} + +struct i2c_client *hdpvr_register_ir_rx_i2c(struct hdpvr_device *dev) +{ +	struct IR_i2c_init_data *init_data = &dev->ir_i2c_init_data; +	struct i2c_board_info hdpvr_ir_rx_i2c_board_info = { +		I2C_BOARD_INFO("ir_rx_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_BIT_RC5; +	init_data->name = "HD-PVR"; +	init_data->polling_interval = 405; /* ms, duplicated from Windows */ +	hdpvr_ir_rx_i2c_board_info.platform_data = init_data; + +	return i2c_new_device(&dev->i2c_adapter, &hdpvr_ir_rx_i2c_board_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; + +	if (num <= 0) +		return 0; + +	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 struct i2c_algorithm hdpvr_algo = { +	.master_xfer   = hdpvr_transfer, +	.functionality = hdpvr_functionality, +}; + +static struct i2c_adapter hdpvr_i2c_adapter_template = { +	.name   = "Hauppage 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) +{ +	int retval = -ENOMEM; + +	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); + +	retval = i2c_add_adapter(&dev->i2c_adapter); + +	return retval; +} + +#endif diff --git a/drivers/media/usb/hdpvr/hdpvr-video.c b/drivers/media/usb/hdpvr/hdpvr-video.c new file mode 100644 index 00000000000..6bce01a674f --- /dev/null +++ b/drivers/media/usb/hdpvr/hdpvr-video.c @@ -0,0 +1,1255 @@ +/* + * Hauppauge 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/kconfig.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) { +			v4l2_err(&dev->v4l2_dev, "cannot allocate urb\n"); +			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; + +	INIT_WORK(&dev->worker, hdpvr_transmit_buffers); +	queue_work(dev->workqueue, &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_workqueue(dev->workqueue); + +	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; +	unsigned 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, +					     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)) { +			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 unsigned int hdpvr_poll(struct file *filp, poll_table *wait) +{ +	unsigned long req_events = poll_requested_events(wait); +	struct hdpvr_buffer *buf = NULL; +	struct hdpvr_device *dev = video_drvdata(filp); +	unsigned int mask = v4l2_ctrl_poll(filp, wait); + +	if (!(req_events & (POLLIN | POLLRDNORM))) +		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 |= POLLIN | POLLRDNORM; + +	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); + +	strcpy(cap->driver, "hdpvr"); +	strcpy(cap->card, "Hauppauge HD PVR"); +	usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); +	cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_AUDIO | +			    V4L2_CAP_READWRITE; +	cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; +	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)) +			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; + +	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->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; + +	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, +			  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; +	strncpy(audio->name, audio_iname[audio->index], sizeof(audio->name)); +	audio->name[sizeof(audio->name) - 1] = '\0'; +	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->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 *_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; +	f->fmt.pix.priv		= 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_SMPTE240M; +		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); +	mutex_lock(&dev->io_mutex); +	destroy_workqueue(dev->workqueue); +	mutex_unlock(&dev->io_mutex); + +	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, +}; + +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; + +	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 = video_device_alloc(); +	if (!dev->video_dev) { +		v4l2_err(&dev->v4l2_dev, "video_device_alloc() failed\n"); +		res = -ENOMEM; +		goto error; +	} + +	*dev->video_dev = hdpvr_video_template; +	strcpy(dev->video_dev->name, "Hauppauge HD PVR"); +	dev->video_dev->v4l2_dev = &dev->v4l2_dev; +	video_set_drvdata(dev->video_dev, dev); +	set_bit(V4L2_FL_USE_FH_PRIO, &dev->video_dev->flags); + +	res = video_register_device(dev->video_dev, VFL_TYPE_GRABBER, 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 00000000000..dc685d44cb3 --- /dev/null +++ b/drivers/media/usb/hdpvr/hdpvr.h @@ -0,0 +1,331 @@ +/* + * Hauppauge 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> + +#include <media/v4l2-device.h> +#include <media/v4l2-ctrls.h> +#include <media/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 workqueue_struct	*workqueue; +	/**/ +	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 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); + +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_rx_i2c(struct hdpvr_device *dev); +struct i2c_client *hdpvr_register_ir_tx_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);  | 
