2005-04-16 15:20:36 -07:00
/*
* dvbdev . c
*
* Copyright ( C ) 2000 Ralph Metzler < ralph @ convergence . de >
* & Marcus Metzler < marcus @ convergence . de >
* for convergence integrated media GmbH
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU Lesser General Public License
* as published by the Free Software Foundation ; either version 2.1
* of the License , or ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place - Suite 330 , Boston , MA 02111 - 1307 , USA .
*
*/
# include <linux/types.h>
# include <linux/errno.h>
# include <linux/string.h>
# include <linux/module.h>
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/slab.h>
# include <linux/device.h>
# include <linux/fs.h>
# include <linux/cdev.h>
2006-01-15 07:52:23 -02:00
# include <linux/mutex.h>
2008-05-15 16:26:57 -06:00
# include <linux/smp_lock.h>
2005-04-16 15:20:36 -07:00
# include "dvbdev.h"
static int dvbdev_debug ;
module_param ( dvbdev_debug , int , 0644 ) ;
MODULE_PARM_DESC ( dvbdev_debug , " Turn on/off device debugging (default:off). " ) ;
# define dprintk if (dvbdev_debug) printk
static LIST_HEAD ( dvb_adapter_list ) ;
2006-01-15 07:52:23 -02:00
static DEFINE_MUTEX ( dvbdev_register_lock ) ;
2005-04-16 15:20:36 -07:00
static const char * const dnames [ ] = {
2005-12-12 00:37:27 -08:00
" video " , " audio " , " sec " , " frontend " , " demux " , " dvr " , " ca " ,
2005-04-16 15:20:36 -07:00
" net " , " osd "
} ;
2008-10-23 12:11:19 -03:00
# ifdef CONFIG_DVB_DYNAMIC_MINORS
# define MAX_DVB_MINORS 256
# define DVB_MAX_IDS MAX_DVB_MINORS
# else
2005-04-16 15:20:36 -07:00
# define DVB_MAX_IDS 4
# define nums2minor(num,type,id) ((num << 6) | (id << 4) | type)
# define MAX_DVB_MINORS (DVB_MAX_ADAPTERS*64)
2008-10-23 12:11:19 -03:00
# endif
2005-04-16 15:20:36 -07:00
2005-03-23 10:01:41 -08:00
static struct class * dvb_class ;
2005-04-16 15:20:36 -07:00
2008-10-23 12:11:19 -03:00
static struct dvb_device * dvb_minors [ MAX_DVB_MINORS ] ;
static DECLARE_RWSEM ( minor_rwsem ) ;
2005-04-16 15:20:36 -07:00
static int dvb_device_open ( struct inode * inode , struct file * file )
{
struct dvb_device * dvbdev ;
2008-05-15 16:26:57 -06:00
lock_kernel ( ) ;
2008-10-23 12:11:19 -03:00
down_read ( & minor_rwsem ) ;
dvbdev = dvb_minors [ iminor ( inode ) ] ;
2005-04-16 15:20:36 -07:00
if ( dvbdev & & dvbdev - > fops ) {
int err = 0 ;
2006-03-28 01:56:41 -08:00
const struct file_operations * old_fops ;
2005-04-16 15:20:36 -07:00
file - > private_data = dvbdev ;
old_fops = file - > f_op ;
2005-12-12 00:37:27 -08:00
file - > f_op = fops_get ( dvbdev - > fops ) ;
if ( file - > f_op - > open )
2006-01-09 15:25:34 -02:00
err = file - > f_op - > open ( inode , file ) ;
2005-12-12 00:37:27 -08:00
if ( err ) {
2006-01-09 15:25:34 -02:00
fops_put ( file - > f_op ) ;
file - > f_op = fops_get ( old_fops ) ;
2005-12-12 00:37:27 -08:00
}
fops_put ( old_fops ) ;
2008-10-23 12:11:19 -03:00
up_read ( & minor_rwsem ) ;
2008-05-15 16:26:57 -06:00
unlock_kernel ( ) ;
2005-12-12 00:37:27 -08:00
return err ;
2005-04-16 15:20:36 -07:00
}
2008-10-23 12:11:19 -03:00
up_read ( & minor_rwsem ) ;
2008-05-15 16:26:57 -06:00
unlock_kernel ( ) ;
2005-04-16 15:20:36 -07:00
return - ENODEV ;
}
2008-04-22 14:42:01 -03:00
static const struct file_operations dvb_device_fops =
2005-04-16 15:20:36 -07:00
{
. owner = THIS_MODULE ,
. open = dvb_device_open ,
} ;
2007-09-12 15:06:57 -07:00
static struct cdev dvb_device_cdev ;
2005-04-16 15:20:36 -07:00
int dvb_generic_open ( struct inode * inode , struct file * file )
{
2005-12-12 00:37:27 -08:00
struct dvb_device * dvbdev = file - > private_data ;
2005-04-16 15:20:36 -07:00
2005-12-12 00:37:27 -08:00
if ( ! dvbdev )
return - ENODEV ;
2005-04-16 15:20:36 -07:00
if ( ! dvbdev - > users )
2005-12-12 00:37:27 -08:00
return - EBUSY ;
2005-04-16 15:20:36 -07:00
if ( ( file - > f_flags & O_ACCMODE ) = = O_RDONLY ) {
2005-12-12 00:37:27 -08:00
if ( ! dvbdev - > readers )
return - EBUSY ;
2005-04-16 15:20:36 -07:00
dvbdev - > readers - - ;
} else {
2005-12-12 00:37:27 -08:00
if ( ! dvbdev - > writers )
return - EBUSY ;
2005-04-16 15:20:36 -07:00
dvbdev - > writers - - ;
}
dvbdev - > users - - ;
return 0 ;
}
EXPORT_SYMBOL ( dvb_generic_open ) ;
int dvb_generic_release ( struct inode * inode , struct file * file )
{
2005-12-12 00:37:27 -08:00
struct dvb_device * dvbdev = file - > private_data ;
2005-04-16 15:20:36 -07:00
if ( ! dvbdev )
2005-12-12 00:37:27 -08:00
return - ENODEV ;
2005-04-16 15:20:36 -07:00
if ( ( file - > f_flags & O_ACCMODE ) = = O_RDONLY ) {
dvbdev - > readers + + ;
} else {
dvbdev - > writers + + ;
}
dvbdev - > users + + ;
return 0 ;
}
EXPORT_SYMBOL ( dvb_generic_release ) ;
int dvb_generic_ioctl ( struct inode * inode , struct file * file ,
unsigned int cmd , unsigned long arg )
{
2005-12-12 00:37:27 -08:00
struct dvb_device * dvbdev = file - > private_data ;
2005-04-16 15:20:36 -07:00
2005-12-12 00:37:27 -08:00
if ( ! dvbdev )
return - ENODEV ;
2005-04-16 15:20:36 -07:00
if ( ! dvbdev - > kernel_ioctl )
return - EINVAL ;
return dvb_usercopy ( inode , file , cmd , arg , dvbdev - > kernel_ioctl ) ;
}
EXPORT_SYMBOL ( dvb_generic_ioctl ) ;
static int dvbdev_get_free_id ( struct dvb_adapter * adap , int type )
{
u32 id = 0 ;
while ( id < DVB_MAX_IDS ) {
2007-10-10 05:37:39 -03:00
struct dvb_device * dev ;
list_for_each_entry ( dev , & adap - > device_list , list_head )
2005-04-16 15:20:36 -07:00
if ( dev - > type = = type & & dev - > id = = id )
goto skip ;
return id ;
skip :
id + + ;
}
return - ENFILE ;
}
int dvb_register_device ( struct dvb_adapter * adap , struct dvb_device * * pdvbdev ,
const struct dvb_device * template , void * priv , int type )
{
struct dvb_device * dvbdev ;
2007-02-13 09:46:55 -03:00
struct file_operations * dvbdevfops ;
2007-08-15 14:00:09 -03:00
struct device * clsdev ;
2008-10-23 12:11:19 -03:00
int minor ;
2005-04-16 15:20:36 -07:00
int id ;
2007-03-10 06:21:25 -03:00
mutex_lock ( & dvbdev_register_lock ) ;
2005-04-16 15:20:36 -07:00
2007-02-13 09:46:55 -03:00
if ( ( id = dvbdev_get_free_id ( adap , type ) ) < 0 ) {
2006-01-15 07:52:23 -02:00
mutex_unlock ( & dvbdev_register_lock ) ;
2005-04-16 15:20:36 -07:00
* pdvbdev = NULL ;
2008-04-08 23:20:00 -03:00
printk ( KERN_ERR " %s: couldn't find free device id \n " , __func__ ) ;
2005-04-16 15:20:36 -07:00
return - ENFILE ;
}
* pdvbdev = dvbdev = kmalloc ( sizeof ( struct dvb_device ) , GFP_KERNEL ) ;
2007-02-13 09:46:55 -03:00
if ( ! dvbdev ) {
mutex_unlock ( & dvbdev_register_lock ) ;
return - ENOMEM ;
}
dvbdevfops = kzalloc ( sizeof ( struct file_operations ) , GFP_KERNEL ) ;
if ( ! dvbdevfops ) {
kfree ( dvbdev ) ;
2006-01-15 07:52:23 -02:00
mutex_unlock ( & dvbdev_register_lock ) ;
2005-04-16 15:20:36 -07:00
return - ENOMEM ;
}
memcpy ( dvbdev , template , sizeof ( struct dvb_device ) ) ;
dvbdev - > type = type ;
dvbdev - > id = id ;
dvbdev - > adapter = adap ;
dvbdev - > priv = priv ;
2007-02-13 09:46:55 -03:00
dvbdev - > fops = dvbdevfops ;
2007-04-14 10:18:58 -03:00
init_waitqueue_head ( & dvbdev - > wait_queue ) ;
2005-04-16 15:20:36 -07:00
2007-02-13 09:46:55 -03:00
memcpy ( dvbdev - > fops , template - > fops , sizeof ( struct file_operations ) ) ;
2005-04-16 15:20:36 -07:00
dvbdev - > fops - > owner = adap - > module ;
list_add_tail ( & dvbdev - > list_head , & adap - > device_list ) ;
2008-10-23 12:11:19 -03:00
down_write ( & minor_rwsem ) ;
# ifdef CONFIG_DVB_DYNAMIC_MINORS
for ( minor = 0 ; minor < MAX_DVB_MINORS ; minor + + )
if ( dvb_minors [ minor ] = = NULL )
break ;
if ( minor = = MAX_DVB_MINORS ) {
kfree ( dvbdevfops ) ;
kfree ( dvbdev ) ;
mutex_unlock ( & dvbdev_register_lock ) ;
return - EINVAL ;
}
# else
minor = nums2minor ( adap - > num , type , id ) ;
# endif
dvbdev - > minor = minor ;
dvb_minors [ minor ] = dvbdev ;
up_write ( & minor_rwsem ) ;
2006-04-04 09:41:47 -03:00
mutex_unlock ( & dvbdev_register_lock ) ;
2008-07-21 20:03:34 -07:00
clsdev = device_create ( dvb_class , adap - > device ,
2007-08-15 14:00:09 -03:00
MKDEV ( DVB_MAJOR , nums2minor ( adap - > num , type , id ) ) ,
2008-05-21 12:52:33 -07:00
NULL , " dvb%d.%s%d " , adap - > num , dnames [ type ] , id ) ;
2007-05-06 20:56:14 -03:00
if ( IS_ERR ( clsdev ) ) {
printk ( KERN_ERR " %s: failed to create device dvb%d.%s%d (%ld) \n " ,
2008-04-08 23:20:00 -03:00
__func__ , adap - > num , dnames [ type ] , id , PTR_ERR ( clsdev ) ) ;
2007-05-06 20:56:14 -03:00
return PTR_ERR ( clsdev ) ;
}
2005-04-16 15:20:36 -07:00
2007-05-06 21:06:32 -03:00
dprintk ( KERN_DEBUG " DVB: register adapter%d/%s%d @ minor: %i (0x%02x) \n " ,
2008-10-23 12:11:19 -03:00
adap - > num , dnames [ type ] , id , minor , minor ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
EXPORT_SYMBOL ( dvb_register_device ) ;
void dvb_unregister_device ( struct dvb_device * dvbdev )
{
if ( ! dvbdev )
return ;
2008-10-23 12:11:19 -03:00
down_write ( & minor_rwsem ) ;
dvb_minors [ dvbdev - > minor ] = NULL ;
up_write ( & minor_rwsem ) ;
device_destroy ( dvb_class , MKDEV ( DVB_MAJOR , dvbdev - > minor ) ) ;
2005-04-16 15:20:36 -07:00
list_del ( & dvbdev - > list_head ) ;
2007-02-13 09:46:55 -03:00
kfree ( dvbdev - > fops ) ;
2005-04-16 15:20:36 -07:00
kfree ( dvbdev ) ;
}
EXPORT_SYMBOL ( dvb_unregister_device ) ;
2008-04-09 19:13:13 -03:00
static int dvbdev_check_free_adapter_num ( int num )
{
struct list_head * entry ;
list_for_each ( entry , & dvb_adapter_list ) {
struct dvb_adapter * adap ;
adap = list_entry ( entry , struct dvb_adapter , list_head ) ;
if ( adap - > num = = num )
return 0 ;
}
return 1 ;
}
2005-04-16 15:20:36 -07:00
static int dvbdev_get_free_adapter_num ( void )
{
int num = 0 ;
while ( num < DVB_MAX_ADAPTERS ) {
2008-04-09 19:13:13 -03:00
if ( dvbdev_check_free_adapter_num ( num ) )
return num ;
2005-04-16 15:20:36 -07:00
num + + ;
}
return - ENFILE ;
}
2008-04-09 19:13:13 -03:00
int dvb_register_adapter ( struct dvb_adapter * adap , const char * name ,
struct module * module , struct device * device ,
short * adapter_nums )
2005-04-16 15:20:36 -07:00
{
2008-04-09 19:13:13 -03:00
int i , num ;
2005-04-16 15:20:36 -07:00
2007-03-10 06:21:25 -03:00
mutex_lock ( & dvbdev_register_lock ) ;
2005-04-16 15:20:36 -07:00
2008-04-09 19:13:13 -03:00
for ( i = 0 ; i < DVB_MAX_ADAPTERS ; + + i ) {
num = adapter_nums [ i ] ;
if ( num > = 0 & & num < DVB_MAX_ADAPTERS ) {
/* use the one the driver asked for */
if ( dvbdev_check_free_adapter_num ( num ) )
break ;
} else {
num = dvbdev_get_free_adapter_num ( ) ;
break ;
}
num = - 1 ;
}
if ( num < 0 ) {
2006-01-15 07:52:23 -02:00
mutex_unlock ( & dvbdev_register_lock ) ;
2005-04-16 15:20:36 -07:00
return - ENFILE ;
}
memset ( adap , 0 , sizeof ( struct dvb_adapter ) ) ;
INIT_LIST_HEAD ( & adap - > device_list ) ;
2007-05-06 21:06:32 -03:00
printk ( KERN_INFO " DVB: registering new adapter (%s) \n " , name ) ;
2005-04-16 15:20:36 -07:00
adap - > num = num ;
adap - > name = name ;
adap - > module = module ;
2006-04-10 09:27:37 -03:00
adap - > device = device ;
2008-10-11 11:44:05 -03:00
adap - > mfe_shared = 0 ;
adap - > mfe_dvbdev = NULL ;
mutex_init ( & adap - > mfe_lock ) ;
2005-04-16 15:20:36 -07:00
list_add_tail ( & adap - > list_head , & dvb_adapter_list ) ;
2006-01-15 07:52:23 -02:00
mutex_unlock ( & dvbdev_register_lock ) ;
2005-04-16 15:20:36 -07:00
return num ;
}
EXPORT_SYMBOL ( dvb_register_adapter ) ;
int dvb_unregister_adapter ( struct dvb_adapter * adap )
{
2007-03-10 06:21:25 -03:00
mutex_lock ( & dvbdev_register_lock ) ;
2005-04-16 15:20:36 -07:00
list_del ( & adap - > list_head ) ;
2006-01-15 07:52:23 -02:00
mutex_unlock ( & dvbdev_register_lock ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
EXPORT_SYMBOL ( dvb_unregister_adapter ) ;
/* if the miracle happens and "generic_usercopy()" is included into
the kernel , then this can vanish . please don ' t make the mistake and
define this as video_usercopy ( ) . this will introduce a dependecy
to the v4l " videodev.o " module , which is unnecessary for some
cards ( ie . the budget dvb - cards don ' t need the v4l module . . . ) */
int dvb_usercopy ( struct inode * inode , struct file * file ,
2005-12-12 00:37:27 -08:00
unsigned int cmd , unsigned long arg ,
2005-04-16 15:20:36 -07:00
int ( * func ) ( struct inode * inode , struct file * file ,
unsigned int cmd , void * arg ) )
{
2005-12-12 00:37:27 -08:00
char sbuf [ 128 ] ;
void * mbuf = NULL ;
void * parg = NULL ;
int err = - EINVAL ;
/* Copy arguments into temp kernel buffer */
switch ( _IOC_DIR ( cmd ) ) {
case _IOC_NONE :
2005-04-16 15:20:36 -07:00
/*
* For this command , the pointer is actually an integer
* argument .
*/
parg = ( void * ) arg ;
break ;
2005-12-12 00:37:27 -08:00
case _IOC_READ : /* some v4l ioctls are marked wrong ... */
case _IOC_WRITE :
case ( _IOC_WRITE | _IOC_READ ) :
if ( _IOC_SIZE ( cmd ) < = sizeof ( sbuf ) ) {
2006-01-09 15:25:34 -02:00
parg = sbuf ;
2005-12-12 00:37:27 -08:00
} else {
2006-01-09 15:25:34 -02:00
/* too big to allocate from stack */
mbuf = kmalloc ( _IOC_SIZE ( cmd ) , GFP_KERNEL ) ;
if ( NULL = = mbuf )
return - ENOMEM ;
parg = mbuf ;
2005-12-12 00:37:27 -08:00
}
err = - EFAULT ;
if ( copy_from_user ( parg , ( void __user * ) arg , _IOC_SIZE ( cmd ) ) )
2006-01-09 15:25:34 -02:00
goto out ;
2005-12-12 00:37:27 -08:00
break ;
}
/* call driver */
if ( ( err = func ( inode , file , cmd , parg ) ) = = - ENOIOCTLCMD )
err = - EINVAL ;
if ( err < 0 )
goto out ;
/* Copy results into user buffer */
switch ( _IOC_DIR ( cmd ) )
{
case _IOC_READ :
case ( _IOC_WRITE | _IOC_READ ) :
if ( copy_to_user ( ( void __user * ) arg , parg , _IOC_SIZE ( cmd ) ) )
2006-01-09 15:25:34 -02:00
err = - EFAULT ;
2005-12-12 00:37:27 -08:00
break ;
}
2005-04-16 15:20:36 -07:00
out :
2005-12-12 00:37:27 -08:00
kfree ( mbuf ) ;
return err ;
2005-04-16 15:20:36 -07:00
}
static int __init init_dvbdev ( void )
{
int retval ;
dev_t dev = MKDEV ( DVB_MAJOR , 0 ) ;
if ( ( retval = register_chrdev_region ( dev , MAX_DVB_MINORS , " DVB " ) ) ! = 0 ) {
2007-05-06 21:06:32 -03:00
printk ( KERN_ERR " dvb-core: unable to get major %d \n " , DVB_MAJOR ) ;
2005-04-16 15:20:36 -07:00
return retval ;
}
cdev_init ( & dvb_device_cdev , & dvb_device_fops ) ;
if ( ( retval = cdev_add ( & dvb_device_cdev , dev , MAX_DVB_MINORS ) ) ! = 0 ) {
2007-05-06 21:06:32 -03:00
printk ( KERN_ERR " dvb-core: unable register character device \n " ) ;
2005-04-16 15:20:36 -07:00
goto error ;
}
2005-03-23 10:01:41 -08:00
dvb_class = class_create ( THIS_MODULE , " dvb " ) ;
2005-04-16 15:20:36 -07:00
if ( IS_ERR ( dvb_class ) ) {
retval = PTR_ERR ( dvb_class ) ;
goto error ;
}
return 0 ;
error :
cdev_del ( & dvb_device_cdev ) ;
unregister_chrdev_region ( dev , MAX_DVB_MINORS ) ;
return retval ;
}
static void __exit exit_dvbdev ( void )
{
2005-03-23 10:01:41 -08:00
class_destroy ( dvb_class ) ;
2005-04-16 15:20:36 -07:00
cdev_del ( & dvb_device_cdev ) ;
2005-12-12 00:37:27 -08:00
unregister_chrdev_region ( MKDEV ( DVB_MAJOR , 0 ) , MAX_DVB_MINORS ) ;
2005-04-16 15:20:36 -07:00
}
2007-05-06 20:56:14 -03:00
subsys_initcall ( init_dvbdev ) ;
2005-04-16 15:20:36 -07:00
module_exit ( exit_dvbdev ) ;
MODULE_DESCRIPTION ( " DVB Core Driver " ) ;
MODULE_AUTHOR ( " Marcus Metzler, Ralph Metzler, Holger Waechtler " ) ;
MODULE_LICENSE ( " GPL " ) ;