mirror of
https://github.com/torvalds/linux.git
synced 2024-11-05 19:41:54 +00:00
f2b305cd67
Disconnecting the HDPVR caused a kernel oops if lockdep was enabled. In addition, if an app still had video0 open and attempted to call ioctl when the device was already disconnected the system would crash. Move the freeing and cleanup code to the release function: that is the right place for it since you know when you get there that nobody is using the device. Also removed usb_set_intfdata: v4l2_device_register sets this already to v4l2_dev. Signed-off-by: Hans Verkuil <hverkuil@xs4all.nl> Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
443 lines
12 KiB
C
443 lines
12 KiB
C
/*
|
|
* 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 <asm/atomic.h>
|
|
#include <linux/usb.h>
|
|
#include <linux/mutex.h>
|
|
#include <linux/i2c.h>
|
|
|
|
#include <linux/videodev2.h>
|
|
#include <media/v4l2-dev.h>
|
|
#include <media/v4l2-common.h>
|
|
|
|
#include "hdpvr.h"
|
|
|
|
static int video_nr[HDPVR_MAX] = {[0 ... (HDPVR_MAX - 1)] = UNSET};
|
|
module_param_array(video_nr, int, NULL, 0);
|
|
MODULE_PARM_DESC(video_nr, "video device number (-1=Auto)");
|
|
|
|
/* holds the number of currently registered devices */
|
|
static atomic_t dev_nr = ATOMIC_INIT(-1);
|
|
|
|
int hdpvr_debug;
|
|
module_param(hdpvr_debug, int, S_IRUGO|S_IWUSR);
|
|
MODULE_PARM_DESC(hdpvr_debug, "enable debugging output");
|
|
|
|
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 int boost_audio;
|
|
module_param(boost_audio, bool, S_IRUGO|S_IWUSR);
|
|
MODULE_PARM_DESC(boost_audio, "boost the audio signal");
|
|
|
|
|
|
/* table of devices that work with this driver */
|
|
static struct usb_device_id hdpvr_table[] = {
|
|
{ USB_DEVICE(HD_PVR_VENDOR_ID, HD_PVR_PRODUCT_ID) },
|
|
{ USB_DEVICE(HD_PVR_VENDOR_ID, HD_PVR_PRODUCT_ID1) },
|
|
{ USB_DEVICE(HD_PVR_VENDOR_ID, HD_PVR_PRODUCT_ID2) },
|
|
{ USB_DEVICE(HD_PVR_VENDOR_ID, HD_PVR_PRODUCT_ID3) },
|
|
{ } /* Terminating entry */
|
|
};
|
|
MODULE_DEVICE_TABLE(usb, hdpvr_table);
|
|
|
|
|
|
void hdpvr_delete(struct hdpvr_device *dev)
|
|
{
|
|
hdpvr_free_buffers(dev);
|
|
|
|
if (dev->video_dev)
|
|
video_device_release(dev->video_dev);
|
|
|
|
usb_put_dev(dev->udev);
|
|
}
|
|
|
|
static void challenge(u8 *bytes)
|
|
{
|
|
u64 *i64P, tmp64;
|
|
uint i, idx;
|
|
|
|
for (idx = 0; idx < 32; ++idx) {
|
|
|
|
if (idx & 0x3)
|
|
bytes[(idx >> 3) + 3] = bytes[(idx >> 2) & 0x3];
|
|
|
|
switch (idx & 0x3) {
|
|
case 0x3:
|
|
bytes[2] += bytes[3] * 4 + bytes[4] + bytes[5];
|
|
bytes[4] += bytes[(idx & 0x1) * 2] * 9 + 9;
|
|
break;
|
|
case 0x1:
|
|
bytes[0] *= 8;
|
|
bytes[0] += 7*idx + 4;
|
|
bytes[6] += bytes[3] * 3;
|
|
break;
|
|
case 0x0:
|
|
bytes[3 - (idx >> 3)] = bytes[idx >> 2];
|
|
bytes[5] += bytes[6] * 3;
|
|
for (i = 0; i < 3; i++)
|
|
bytes[3] *= bytes[3] + 1;
|
|
break;
|
|
case 0x2:
|
|
for (i = 0; i < 3; i++)
|
|
bytes[1] *= bytes[6] + 1;
|
|
for (i = 0; i < 3; i++) {
|
|
i64P = (u64 *)bytes;
|
|
tmp64 = le64_to_cpup(i64P);
|
|
tmp64 <<= bytes[7] & 0x0f;
|
|
*i64P += cpu_to_le64(tmp64);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* try to init the device like the windows driver */
|
|
static int device_authorization(struct hdpvr_device *dev)
|
|
{
|
|
|
|
int ret, retval = -ENOMEM;
|
|
char request_type = 0x38, rcv_request = 0x81;
|
|
char *response;
|
|
#ifdef HDPVR_DEBUG
|
|
size_t buf_size = 46;
|
|
char *print_buf = kzalloc(5*buf_size+1, GFP_KERNEL);
|
|
if (!print_buf) {
|
|
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
|
|
if (dev->usbc_buf[1] == HDPVR_FIRMWARE_VERSION) {
|
|
dev->flags &= ~HDPVR_FLAG_AC3_CAP;
|
|
} else if (dev->usbc_buf[1] == HDPVR_FIRMWARE_VERSION_AC3) {
|
|
dev->flags |= HDPVR_FLAG_AC3_CAP;
|
|
} else if (dev->usbc_buf[1] > HDPVR_FIRMWARE_VERSION_AC3) {
|
|
v4l2_info(&dev->v4l2_dev, "untested firmware version 0x%x, "
|
|
"the driver might not work\n", dev->usbc_buf[1]);
|
|
dev->flags |= HDPVR_FLAG_AC3_CAP;
|
|
} else {
|
|
v4l2_err(&dev->v4l2_dev, "unknown firmware version 0x%x\n",
|
|
dev->usbc_buf[1]);
|
|
ret = -EINVAL;
|
|
goto unlock;
|
|
}
|
|
|
|
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);
|
|
return retval;
|
|
}
|
|
|
|
static int hdpvr_device_init(struct hdpvr_device *dev)
|
|
{
|
|
int ret;
|
|
u8 *buf;
|
|
struct hdpvr_video_info *vidinf;
|
|
|
|
if (device_authorization(dev))
|
|
return -EACCES;
|
|
|
|
/* default options for init */
|
|
hdpvr_set_options(dev);
|
|
|
|
/* set filter options */
|
|
mutex_lock(&dev->usbc_mutex);
|
|
buf = dev->usbc_buf;
|
|
buf[0] = 0x03; buf[1] = 0x03; buf[2] = 0x00; buf[3] = 0x00;
|
|
ret = usb_control_msg(dev->udev,
|
|
usb_sndctrlpipe(dev->udev, 0),
|
|
0x01, 0x38,
|
|
CTRL_LOW_PASS_FILTER_VALUE, CTRL_DEFAULT_INDEX,
|
|
buf, 4,
|
|
1000);
|
|
v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
|
|
"control request returned %d\n", ret);
|
|
mutex_unlock(&dev->usbc_mutex);
|
|
|
|
vidinf = get_video_info(dev);
|
|
if (!vidinf)
|
|
v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
|
|
"no valid video signal or device init failed\n");
|
|
else
|
|
kfree(vidinf);
|
|
|
|
/* enable fan and bling leds */
|
|
mutex_lock(&dev->usbc_mutex);
|
|
buf[0] = 0x1;
|
|
ret = usb_control_msg(dev->udev,
|
|
usb_sndctrlpipe(dev->udev, 0),
|
|
0xd4, 0x38, 0, 0, buf, 1,
|
|
1000);
|
|
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,
|
|
.brightness = 0x86,
|
|
.contrast = 0x80,
|
|
.hue = 0x80,
|
|
.saturation = 0x80,
|
|
.sharpness = 0x80,
|
|
};
|
|
|
|
static int hdpvr_probe(struct usb_interface *interface,
|
|
const struct usb_device_id *id)
|
|
{
|
|
struct hdpvr_device *dev;
|
|
struct usb_host_interface *iface_desc;
|
|
struct usb_endpoint_descriptor *endpoint;
|
|
size_t buffer_size;
|
|
int i;
|
|
int retval = -ENOMEM;
|
|
|
|
/* allocate memory for our device state and initialize it */
|
|
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
|
|
if (!dev) {
|
|
err("Out of memory");
|
|
goto error;
|
|
}
|
|
|
|
/* register v4l2_device early so it can be used for printks */
|
|
if (v4l2_device_register(&interface->dev, &dev->v4l2_dev)) {
|
|
err("v4l2_device_register failed");
|
|
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;
|
|
|
|
/* init video transfer queues */
|
|
INIT_LIST_HEAD(&dev->free_buff_list);
|
|
INIT_LIST_HEAD(&dev->rec_buff_list);
|
|
|
|
dev->options = hdpvr_default_options;
|
|
|
|
if (default_video_input < HDPVR_VIDEO_INPUTS)
|
|
dev->options.video_input = default_video_input;
|
|
|
|
if (default_audio_input < HDPVR_AUDIO_INPUTS)
|
|
dev->options.audio_input = default_audio_input;
|
|
|
|
dev->udev = usb_get_dev(interface_to_usbdev(interface));
|
|
|
|
/* set up the endpoint information */
|
|
/* use only the first bulk-in and bulk-out endpoints */
|
|
iface_desc = interface->cur_altsetting;
|
|
for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
|
|
endpoint = &iface_desc->endpoint[i].desc;
|
|
|
|
if (!dev->bulk_in_endpointAddr &&
|
|
usb_endpoint_is_bulk_in(endpoint)) {
|
|
/* USB interface description is buggy, reported max
|
|
* packet size is 512 bytes, windows driver uses 8192 */
|
|
buffer_size = 8192;
|
|
dev->bulk_in_size = buffer_size;
|
|
dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;
|
|
}
|
|
|
|
}
|
|
if (!dev->bulk_in_endpointAddr) {
|
|
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 (hdpvr_register_videodev(dev, &interface->dev,
|
|
video_nr[atomic_inc_return(&dev_nr)])) {
|
|
v4l2_err(&dev->v4l2_dev, "registering videodev failed\n");
|
|
goto error;
|
|
}
|
|
|
|
#ifdef CONFIG_I2C
|
|
/* until i2c is working properly */
|
|
retval = 0; /* hdpvr_register_i2c_adapter(dev); */
|
|
if (retval < 0) {
|
|
v4l2_err(&dev->v4l2_dev, "registering i2c adapter failed\n");
|
|
goto error;
|
|
}
|
|
#endif /* CONFIG_I2C */
|
|
|
|
/* 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;
|
|
|
|
error:
|
|
if (dev) {
|
|
/* 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);
|
|
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,
|
|
};
|
|
|
|
static int __init hdpvr_init(void)
|
|
{
|
|
int result;
|
|
|
|
/* register this driver with the USB subsystem */
|
|
result = usb_register(&hdpvr_usb_driver);
|
|
if (result)
|
|
err("usb_register failed. Error number %d", result);
|
|
|
|
return result;
|
|
}
|
|
|
|
static void __exit hdpvr_exit(void)
|
|
{
|
|
/* deregister this driver with the USB subsystem */
|
|
usb_deregister(&hdpvr_usb_driver);
|
|
}
|
|
|
|
module_init(hdpvr_init);
|
|
module_exit(hdpvr_exit);
|
|
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_AUTHOR("Janne Grunau");
|
|
MODULE_DESCRIPTION("Hauppauge HD PVR driver");
|