2009-03-18 18:10:04 -03:00
/*
2009-03-19 19:00:35 -03:00
* Hauppauge HD PVR USB driver
2009-03-18 18:10:04 -03:00
*
* Copyright ( C ) 2001 - 2004 Greg Kroah - Hartman ( greg @ kroah . com )
* Copyright ( C ) 2008 Janne Grunau ( j @ jannau . net )
* Copyright ( C ) 2008 John Poet
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation , version 2.
*
*/
# include <linux/kernel.h>
# include <linux/errno.h>
# include <linux/init.h>
# include <linux/slab.h>
# include <linux/module.h>
# include <linux/uaccess.h>
# include <asm/atomic.h>
# include <linux/usb.h>
# include <linux/mutex.h>
# include <linux/i2c.h>
# include <linux/videodev2.h>
# include <media/v4l2-dev.h>
# include <media/v4l2-common.h>
# include "hdpvr.h"
static int video_nr [ HDPVR_MAX ] = { [ 0 . . . ( HDPVR_MAX - 1 ) ] = UNSET } ;
module_param_array ( video_nr , int , NULL , 0 ) ;
MODULE_PARM_DESC ( video_nr , " video device number (-1=Auto) " ) ;
/* holds the number of currently registered devices */
static atomic_t dev_nr = ATOMIC_INIT ( - 1 ) ;
int hdpvr_debug ;
module_param ( hdpvr_debug , int , S_IRUGO | S_IWUSR ) ;
MODULE_PARM_DESC ( hdpvr_debug , " enable debugging output " ) ;
uint default_video_input = HDPVR_VIDEO_INPUTS ;
module_param ( default_video_input , uint , S_IRUGO | S_IWUSR ) ;
MODULE_PARM_DESC ( default_video_input , " default video input: 0=Component / "
" 1=S-Video / 2=Composite " ) ;
uint default_audio_input = HDPVR_AUDIO_INPUTS ;
module_param ( default_audio_input , uint , S_IRUGO | S_IWUSR ) ;
MODULE_PARM_DESC ( default_audio_input , " default audio input: 0=RCA back / "
" 1=RCA front / 2=S/PDIF " ) ;
static int boost_audio ;
module_param ( boost_audio , bool , S_IRUGO | S_IWUSR ) ;
MODULE_PARM_DESC ( boost_audio , " boost the audio signal " ) ;
/* table of devices that work with this driver */
static struct usb_device_id hdpvr_table [ ] = {
{ USB_DEVICE ( HD_PVR_VENDOR_ID , HD_PVR_PRODUCT_ID ) } ,
{ USB_DEVICE ( HD_PVR_VENDOR_ID , HD_PVR_PRODUCT_ID1 ) } ,
{ USB_DEVICE ( HD_PVR_VENDOR_ID , HD_PVR_PRODUCT_ID2 ) } ,
{ } /* Terminating entry */
} ;
MODULE_DEVICE_TABLE ( usb , hdpvr_table ) ;
void hdpvr_delete ( struct hdpvr_device * dev )
{
hdpvr_free_buffers ( dev ) ;
if ( dev - > video_dev )
video_device_release ( dev - > video_dev ) ;
usb_put_dev ( dev - > udev ) ;
}
static void challenge ( u8 * bytes )
{
u64 * i64P , tmp64 ;
uint i , idx ;
for ( idx = 0 ; idx < 32 ; + + idx ) {
if ( idx & 0x3 )
bytes [ ( idx > > 3 ) + 3 ] = bytes [ ( idx > > 2 ) & 0x3 ] ;
switch ( idx & 0x3 ) {
case 0x3 :
bytes [ 2 ] + = bytes [ 3 ] * 4 + bytes [ 4 ] + bytes [ 5 ] ;
bytes [ 4 ] + = bytes [ ( idx & 0x1 ) * 2 ] * 9 + 9 ;
break ;
case 0x1 :
bytes [ 0 ] * = 8 ;
bytes [ 0 ] + = 7 * idx + 4 ;
bytes [ 6 ] + = bytes [ 3 ] * 3 ;
break ;
case 0x0 :
bytes [ 3 - ( idx > > 3 ) ] = bytes [ idx > > 2 ] ;
bytes [ 5 ] + = bytes [ 6 ] * 3 ;
for ( i = 0 ; i < 3 ; i + + )
bytes [ 3 ] * = bytes [ 3 ] + 1 ;
break ;
case 0x2 :
for ( i = 0 ; i < 3 ; i + + )
bytes [ 1 ] * = bytes [ 6 ] + 1 ;
for ( i = 0 ; i < 3 ; i + + ) {
i64P = ( u64 * ) bytes ;
tmp64 = le64_to_cpup ( i64P ) ;
tmp64 < < = bytes [ 7 ] & 0x0f ;
* i64P + = cpu_to_le64 ( tmp64 ) ;
}
break ;
}
}
}
/* try to init the device like the windows driver */
static int device_authorization ( struct hdpvr_device * dev )
{
int ret , retval = - ENOMEM ;
char request_type = 0x38 , rcv_request = 0x81 ;
char * response ;
# ifdef HDPVR_DEBUG
size_t buf_size = 46 ;
char * print_buf = kzalloc ( 5 * buf_size + 1 , GFP_KERNEL ) ;
if ( ! print_buf ) {
2009-03-27 20:09:40 -03:00
v4l2_err ( & dev - > v4l2_dev , " Out of memory \n " ) ;
2009-07-30 20:00:44 -03:00
return retval ;
2009-03-18 18:10:04 -03:00
}
# 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 ) {
2009-03-27 20:09:40 -03:00
v4l2_err ( & dev - > v4l2_dev ,
" unexpected answer of status request, len %d \n " , ret ) ;
2009-07-30 20:00:44 -03:00
goto unlock ;
2009-03-18 18:10:04 -03:00
}
# ifdef HDPVR_DEBUG
else {
hex_dump_to_buffer ( dev - > usbc_buf , 46 , 16 , 1 , print_buf ,
sizeof ( print_buf ) , 0 ) ;
2009-03-27 20:09:40 -03:00
v4l2_dbg ( MSG_INFO , hdpvr_debug , & dev - > v4l2_dev ,
" Status request returned, len %d: %s \n " ,
ret , print_buf ) ;
2009-03-18 18:10:04 -03:00
}
# 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 ) {
2009-03-27 20:09:40 -03:00
v4l2_info ( & dev - > v4l2_dev , " untested firmware version 0x%x, "
" the driver might not work \n " , dev - > usbc_buf [ 1 ] ) ;
2009-03-18 18:10:04 -03:00
dev - > flags | = HDPVR_FLAG_AC3_CAP ;
} else {
2009-03-27 20:09:40 -03:00
v4l2_err ( & dev - > v4l2_dev , " unknown firmware version 0x%x \n " ,
2009-03-18 18:10:04 -03:00
dev - > usbc_buf [ 1 ] ) ;
ret = - EINVAL ;
2009-07-30 20:00:44 -03:00
goto unlock ;
2009-03-18 18:10:04 -03:00
}
response = dev - > usbc_buf + 38 ;
# ifdef HDPVR_DEBUG
hex_dump_to_buffer ( response , 8 , 16 , 1 , print_buf , sizeof ( print_buf ) , 0 ) ;
2009-03-27 20:09:40 -03:00
v4l2_dbg ( MSG_INFO , hdpvr_debug , & dev - > v4l2_dev , " challenge: %s \n " ,
print_buf ) ;
2009-03-18 18:10:04 -03:00
# endif
challenge ( response ) ;
# ifdef HDPVR_DEBUG
hex_dump_to_buffer ( response , 8 , 16 , 1 , print_buf , sizeof ( print_buf ) , 0 ) ;
2009-03-27 20:09:40 -03:00
v4l2_dbg ( MSG_INFO , hdpvr_debug , & dev - > v4l2_dev , " response: %s \n " ,
print_buf ) ;
2009-03-18 18:10:04 -03:00
# endif
msleep ( 100 ) ;
ret = usb_control_msg ( dev - > udev ,
usb_sndctrlpipe ( dev - > udev , 0 ) ,
0xd1 , 0x00 | request_type ,
0x0000 , 0x0000 ,
response , 8 ,
10000 ) ;
2009-03-27 20:09:40 -03:00
v4l2_dbg ( MSG_INFO , hdpvr_debug , & dev - > v4l2_dev ,
" magic request returned %d \n " , ret ) ;
2009-03-18 18:10:04 -03:00
retval = ret ! = 8 ;
2009-07-30 20:00:44 -03:00
unlock :
mutex_unlock ( & dev - > usbc_mutex ) ;
2009-03-18 18:10:04 -03:00
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 ) ;
2009-03-27 20:09:40 -03:00
v4l2_dbg ( MSG_INFO , hdpvr_debug , & dev - > v4l2_dev ,
" control request returned %d \n " , ret ) ;
2009-03-18 18:10:04 -03:00
mutex_unlock ( & dev - > usbc_mutex ) ;
vidinf = get_video_info ( dev ) ;
if ( ! vidinf )
2009-03-27 20:09:40 -03:00
v4l2_dbg ( MSG_INFO , hdpvr_debug , & dev - > v4l2_dev ,
2009-03-18 18:10:04 -03:00
" 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 ) ;
2009-03-27 20:09:40 -03:00
v4l2_dbg ( MSG_INFO , hdpvr_debug , & dev - > v4l2_dev ,
" control request returned %d \n " , ret ) ;
2009-03-18 18:10:04 -03:00
/* 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 ) ;
2009-03-27 20:09:40 -03:00
v4l2_dbg ( MSG_INFO , hdpvr_debug , & dev - > v4l2_dev ,
" control request returned %d \n " , ret ) ;
2009-03-18 18:10:04 -03:00
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 ;
}
2009-03-27 20:01:40 -03:00
/* 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 ;
}
2009-03-18 18:10:04 -03:00
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 ) {
2009-03-27 20:09:40 -03:00
v4l2_err ( & dev - > v4l2_dev , " Out of memory \n " ) ;
2009-03-18 18:10:04 -03:00
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 ) {
2009-03-27 20:09:40 -03:00
v4l2_err ( & dev - > v4l2_dev , " Could not find bulk-in endpoint \n " ) ;
2009-03-18 18:10:04 -03:00
goto error ;
}
/* init the device */
if ( hdpvr_device_init ( dev ) ) {
2009-03-27 20:09:40 -03:00
v4l2_err ( & dev - > v4l2_dev , " device init failed \n " ) ;
2009-03-18 18:10:04 -03:00
goto error ;
}
mutex_lock ( & dev - > io_mutex ) ;
if ( hdpvr_alloc_buffers ( dev , NUM_BUFFERS ) ) {
2009-07-30 20:00:44 -03:00
mutex_unlock ( & dev - > io_mutex ) ;
2009-03-27 20:09:40 -03:00
v4l2_err ( & dev - > v4l2_dev ,
" allocating transfer buffers failed \n " ) ;
2009-03-18 18:10:04 -03:00
goto error ;
}
mutex_unlock ( & dev - > io_mutex ) ;
2009-03-26 14:40:55 -03:00
if ( hdpvr_register_videodev ( dev , & interface - > dev ,
2009-03-18 18:10:04 -03:00
video_nr [ atomic_inc_return ( & dev_nr ) ] ) ) {
2009-03-27 20:09:40 -03:00
v4l2_err ( & dev - > v4l2_dev , " registering videodev failed \n " ) ;
2009-03-18 18:10:04 -03:00
goto error ;
}
2009-03-23 18:18:54 -03:00
# ifdef CONFIG_I2C
/* until i2c is working properly */
retval = 0 ; /* hdpvr_register_i2c_adapter(dev); */
if ( retval < 0 ) {
2009-03-27 20:09:40 -03:00
v4l2_err ( & dev - > v4l2_dev , " registering i2c adapter failed \n " ) ;
2009-03-23 18:18:54 -03:00
goto error ;
}
# endif /* CONFIG_I2C */
2009-03-18 18:10:04 -03:00
/* save our data pointer in this interface device */
usb_set_intfdata ( interface , dev ) ;
/* let the user know what node this device is now attached to */
2009-03-27 20:09:40 -03:00
v4l2_info ( & dev - > v4l2_dev , " device now attached to /dev/video%d \n " ,
2009-03-18 18:10:04 -03:00
dev - > video_dev - > minor ) ;
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 ;
int minor ;
dev = usb_get_intfdata ( interface ) ;
usb_set_intfdata ( interface , NULL ) ;
minor = dev - > video_dev - > minor ;
/* prevent more I/O from starting and stop any ongoing */
mutex_lock ( & dev - > io_mutex ) ;
dev - > status = STATUS_DISCONNECTED ;
2009-03-27 20:01:40 -03:00
v4l2_device_disconnect ( & dev - > v4l2_dev ) ;
2009-03-18 18:10:04 -03:00
video_unregister_device ( dev - > video_dev ) ;
wake_up_interruptible ( & dev - > wait_data ) ;
wake_up_interruptible ( & dev - > wait_buffer ) ;
2009-03-26 20:56:06 -03:00
mutex_unlock ( & dev - > io_mutex ) ;
2009-03-18 18:10:04 -03:00
msleep ( 100 ) ;
flush_workqueue ( dev - > workqueue ) ;
2009-03-26 20:56:06 -03:00
mutex_lock ( & dev - > io_mutex ) ;
2009-03-18 18:10:04 -03:00
hdpvr_cancel_queue ( dev ) ;
destroy_workqueue ( dev - > workqueue ) ;
mutex_unlock ( & dev - > io_mutex ) ;
/* deregister I2C adapter */
2009-03-23 18:18:54 -03:00
# ifdef CONFIG_I2C
2009-03-18 18:10:04 -03:00
mutex_lock ( & dev - > i2c_mutex ) ;
if ( dev - > i2c_adapter )
i2c_del_adapter ( dev - > i2c_adapter ) ;
kfree ( dev - > i2c_adapter ) ;
dev - > i2c_adapter = NULL ;
mutex_unlock ( & dev - > i2c_mutex ) ;
2009-03-23 18:18:54 -03:00
# endif /* CONFIG_I2C */
2009-03-18 18:10:04 -03:00
atomic_dec ( & dev_nr ) ;
2009-03-27 20:09:40 -03:00
v4l2_info ( & dev - > v4l2_dev , " device /dev/video%d disconnected \n " , minor ) ;
2009-03-18 18:10:04 -03:00
2009-03-27 20:01:40 -03:00
v4l2_device_unregister ( & dev - > v4l2_dev ) ;
2009-03-18 18:10:04 -03:00
kfree ( dev - > usbc_buf ) ;
kfree ( dev ) ;
}
static struct usb_driver hdpvr_usb_driver = {
. name = " hdpvr " ,
. probe = hdpvr_probe ,
. disconnect = hdpvr_disconnect ,
. id_table = hdpvr_table ,
} ;
static int __init hdpvr_init ( void )
{
int result ;
/* register this driver with the USB subsystem */
result = usb_register ( & hdpvr_usb_driver ) ;
if ( result )
err ( " usb_register failed. Error number %d " , result ) ;
return result ;
}
static void __exit hdpvr_exit ( void )
{
/* deregister this driver with the USB subsystem */
usb_deregister ( & hdpvr_usb_driver ) ;
}
module_init ( hdpvr_init ) ;
module_exit ( hdpvr_exit ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Janne Grunau " ) ;
MODULE_DESCRIPTION ( " Hauppauge HD PVR driver " ) ;