2008-07-20 08:43:17 -03:00
/*
* Video capture interface for Linux version 2
*
* A generic video device interface for the LINUX operating system
* using a set of device structures / vectors for low level operations .
*
* 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 ; either version
* 2 of the License , or ( at your option ) any later version .
*
* Authors : Alan Cox , < alan @ redhat . com > ( version 1 )
* Mauro Carvalho Chehab < mchehab @ infradead . org > ( version 2 )
*
* Fixes : 20000516 Claudio Matsuoka < claudio @ conectiva . com >
* - Added procfs support
*/
# include <linux/module.h>
# include <linux/types.h>
# include <linux/kernel.h>
# include <linux/mm.h>
# include <linux/string.h>
# include <linux/errno.h>
# include <linux/init.h>
# include <linux/kmod.h>
# include <linux/slab.h>
# include <linux/smp_lock.h>
# include <asm/uaccess.h>
# include <asm/system.h>
# include <media/v4l2-common.h>
# define VIDEO_NUM_DEVICES 256
# define VIDEO_NAME "video4linux"
/*
* sysfs stuff
*/
static ssize_t show_index ( struct device * cd ,
struct device_attribute * attr , char * buf )
{
2008-07-20 06:35:02 -03:00
struct video_device * vfd = container_of ( cd , struct video_device , dev ) ;
2008-08-23 07:48:38 -03:00
2008-07-20 08:43:17 -03:00
return sprintf ( buf , " %i \n " , vfd - > index ) ;
}
static ssize_t show_name ( struct device * cd ,
struct device_attribute * attr , char * buf )
{
2008-07-20 06:35:02 -03:00
struct video_device * vfd = container_of ( cd , struct video_device , dev ) ;
2008-08-23 07:48:38 -03:00
2008-07-20 08:43:17 -03:00
return sprintf ( buf , " %.*s \n " , ( int ) sizeof ( vfd - > name ) , vfd - > name ) ;
}
static struct device_attribute video_device_attrs [ ] = {
__ATTR ( name , S_IRUGO , show_name , NULL ) ,
__ATTR ( index , S_IRUGO , show_index , NULL ) ,
__ATTR_NULL
} ;
2008-08-29 17:31:35 -03:00
/*
* Active devices
*/
static struct video_device * video_device [ VIDEO_NUM_DEVICES ] ;
static DEFINE_MUTEX ( videodev_lock ) ;
2008-07-20 08:43:17 -03:00
struct video_device * video_device_alloc ( void )
{
2008-08-23 07:48:38 -03:00
return kzalloc ( sizeof ( struct video_device ) , GFP_KERNEL ) ;
2008-07-20 08:43:17 -03:00
}
EXPORT_SYMBOL ( video_device_alloc ) ;
void video_device_release ( struct video_device * vfd )
{
kfree ( vfd ) ;
}
EXPORT_SYMBOL ( video_device_release ) ;
2008-08-23 05:47:41 -03:00
void video_device_release_empty ( struct video_device * vfd )
{
/* Do nothing */
/* Only valid when the video_device struct is a static. */
}
EXPORT_SYMBOL ( video_device_release_empty ) ;
2008-08-29 17:31:35 -03:00
/* Called when the last user of the character device is gone. */
static void v4l2_chardev_release ( struct kobject * kobj )
{
struct video_device * vfd = container_of ( kobj , struct video_device , cdev . kobj ) ;
mutex_lock ( & videodev_lock ) ;
2008-08-30 09:40:47 -03:00
if ( video_device [ vfd - > minor ] ! = vfd ) {
mutex_unlock ( & videodev_lock ) ;
BUG ( ) ;
return ;
}
2008-08-29 17:31:35 -03:00
/* Free up this device for reuse */
video_device [ vfd - > minor ] = NULL ;
mutex_unlock ( & videodev_lock ) ;
/* Release the character device */
vfd - > cdev_release ( kobj ) ;
/* Release video_device and perform other
cleanups as needed . */
if ( vfd - > release )
vfd - > release ( vfd ) ;
}
/* The new kobj_type for the character device */
static struct kobj_type v4l2_ktype_cdev_default = {
. release = v4l2_chardev_release ,
} ;
2008-07-20 08:43:17 -03:00
static void video_release ( struct device * cd )
{
2008-07-20 06:35:02 -03:00
struct video_device * vfd = container_of ( cd , struct video_device , dev ) ;
2008-07-20 08:43:17 -03:00
2008-08-29 17:31:35 -03:00
/* It's now safe to delete the char device.
This will either trigger the v4l2_chardev_release immediately ( if
the refcount goes to 0 ) or later when the last user of the
character device closes it . */
cdev_del ( & vfd - > cdev ) ;
2008-07-20 08:43:17 -03:00
}
static struct class video_class = {
. name = VIDEO_NAME ,
. dev_attrs = video_device_attrs ,
. dev_release = video_release ,
} ;
struct video_device * video_devdata ( struct file * file )
{
return video_device [ iminor ( file - > f_path . dentry - > d_inode ) ] ;
}
EXPORT_SYMBOL ( video_devdata ) ;
/**
* get_index - assign stream number based on parent device
* @ vdev : video_device to assign index number to , vdev - > dev should be assigned
* @ num : - 1 if auto assign , requested number otherwise
*
*
* returns - ENFILE if num is already in use , a free index number if
* successful .
*/
static int get_index ( struct video_device * vdev , int num )
{
u32 used = 0 ;
const int max_index = sizeof ( used ) * 8 - 1 ;
int i ;
/* Currently a single v4l driver instance cannot create more than
32 devices .
Increase to u64 or an array of u32 if more are needed . */
if ( num > max_index ) {
printk ( KERN_ERR " videodev: %s num is too large \n " , __func__ ) ;
return - EINVAL ;
}
for ( i = 0 ; i < VIDEO_NUM_DEVICES ; i + + ) {
if ( video_device [ i ] ! = NULL & &
video_device [ i ] ! = vdev & &
2008-07-20 06:31:39 -03:00
video_device [ i ] - > parent = = vdev - > parent ) {
2008-07-20 08:43:17 -03:00
used | = 1 < < video_device [ i ] - > index ;
}
}
if ( num > = 0 ) {
if ( used & ( 1 < < num ) )
return - ENFILE ;
return num ;
}
i = ffz ( used ) ;
return i > max_index ? - ENFILE : i ;
}
static const struct file_operations video_fops ;
int video_register_device ( struct video_device * vfd , int type , int nr )
{
return video_register_device_index ( vfd , type , nr , - 1 ) ;
}
EXPORT_SYMBOL ( video_register_device ) ;
/**
2008-07-28 15:39:38 -03:00
* video_register_device_index - register video4linux devices
2008-07-20 08:43:17 -03:00
* @ vfd : video device structure we want to register
* @ type : type of device to register
* @ nr : which device number ( 0 = = / dev / video0 , 1 = = / dev / video1 , . . .
* - 1 = = first free )
2008-07-28 15:39:38 -03:00
* @ index : stream number based on parent device ;
* - 1 if auto assign , requested number otherwise
2008-07-20 08:43:17 -03:00
*
* The registration code assigns minor numbers based on the type
* requested . - ENFILE is returned in all the device slots for this
* category are full . If not then the minor field is set and the
* driver initialize function is called ( if non % NULL ) .
*
* Zero is returned on success .
*
* Valid types are
*
* % VFL_TYPE_GRABBER - A frame grabber
*
* % VFL_TYPE_VTX - A teletext device
*
* % VFL_TYPE_VBI - Vertical blank data ( undecoded )
*
* % VFL_TYPE_RADIO - A radio card
*/
int video_register_device_index ( struct video_device * vfd , int type , int nr ,
int index )
{
int i = 0 ;
int base ;
int end ;
int ret ;
char * name_base ;
2008-08-24 11:18:47 -03:00
void * priv = video_get_drvdata ( vfd ) ;
2008-07-20 08:43:17 -03:00
2008-08-23 06:27:59 -03:00
/* the release callback MUST be present */
BUG_ON ( ! vfd - > release ) ;
2008-08-22 16:41:03 -03:00
2008-09-03 17:11:53 -03:00
if ( vfd = = NULL )
return - EINVAL ;
2008-07-20 08:43:17 -03:00
switch ( type ) {
case VFL_TYPE_GRABBER :
base = MINOR_VFL_TYPE_GRABBER_MIN ;
end = MINOR_VFL_TYPE_GRABBER_MAX + 1 ;
name_base = " video " ;
break ;
case VFL_TYPE_VTX :
base = MINOR_VFL_TYPE_VTX_MIN ;
end = MINOR_VFL_TYPE_VTX_MAX + 1 ;
name_base = " vtx " ;
break ;
case VFL_TYPE_VBI :
base = MINOR_VFL_TYPE_VBI_MIN ;
end = MINOR_VFL_TYPE_VBI_MAX + 1 ;
name_base = " vbi " ;
break ;
case VFL_TYPE_RADIO :
base = MINOR_VFL_TYPE_RADIO_MIN ;
end = MINOR_VFL_TYPE_RADIO_MAX + 1 ;
name_base = " radio " ;
break ;
default :
printk ( KERN_ERR " %s called with unknown type: %d \n " ,
__func__ , type ) ;
2008-09-03 16:47:39 -03:00
return - EINVAL ;
2008-07-20 08:43:17 -03:00
}
2008-08-29 17:31:35 -03:00
/* Initialize the character device */
cdev_init ( & vfd - > cdev , vfd - > fops ) ;
vfd - > cdev . owner = vfd - > fops - > owner ;
2008-07-20 08:43:17 -03:00
/* pick a minor number */
mutex_lock ( & videodev_lock ) ;
2008-08-23 07:48:38 -03:00
if ( nr > = 0 & & nr < end - base ) {
2008-07-20 08:43:17 -03:00
/* use the one the driver asked for */
i = base + nr ;
if ( NULL ! = video_device [ i ] ) {
mutex_unlock ( & videodev_lock ) ;
return - ENFILE ;
}
} else {
/* use first free */
for ( i = base ; i < end ; i + + )
if ( NULL = = video_device [ i ] )
break ;
if ( i = = end ) {
mutex_unlock ( & videodev_lock ) ;
return - ENFILE ;
}
}
video_device [ i ] = vfd ;
2008-07-26 08:26:43 -03:00
vfd - > vfl_type = type ;
2008-07-20 08:43:17 -03:00
vfd - > minor = i ;
ret = get_index ( vfd , index ) ;
vfd - > index = ret ;
mutex_unlock ( & videodev_lock ) ;
if ( ret < 0 ) {
printk ( KERN_ERR " %s: get_index failed \n " , __func__ ) ;
goto fail_minor ;
}
2008-08-29 17:31:35 -03:00
ret = cdev_add ( & vfd - > cdev , MKDEV ( VIDEO_MAJOR , vfd - > minor ) , 1 ) ;
if ( ret < 0 ) {
printk ( KERN_ERR " %s: cdev_add failed \n " , __func__ ) ;
goto fail_minor ;
}
2008-07-20 08:43:17 -03:00
/* sysfs class */
2008-08-23 07:48:38 -03:00
memset ( & vfd - > dev , 0 , sizeof ( vfd - > dev ) ) ;
2008-08-24 11:18:47 -03:00
/* The memset above cleared the device's drvdata, so
put back the copy we made earlier . */
video_set_drvdata ( vfd , priv ) ;
2008-07-20 06:35:02 -03:00
vfd - > dev . class = & video_class ;
vfd - > dev . devt = MKDEV ( VIDEO_MAJOR , vfd - > minor ) ;
2008-07-20 06:31:39 -03:00
if ( vfd - > parent )
2008-07-20 06:35:02 -03:00
vfd - > dev . parent = vfd - > parent ;
sprintf ( vfd - > dev . bus_id , " %s%d " , name_base , i - base ) ;
ret = device_register ( & vfd - > dev ) ;
2008-07-20 08:43:17 -03:00
if ( ret < 0 ) {
printk ( KERN_ERR " %s: device_register failed \n " , __func__ ) ;
2008-08-29 17:31:35 -03:00
goto del_cdev ;
2008-07-20 08:43:17 -03:00
}
2008-08-29 17:31:35 -03:00
/* Remember the cdev's release function */
vfd - > cdev_release = vfd - > cdev . kobj . ktype - > release ;
/* Install our own */
vfd - > cdev . kobj . ktype = & v4l2_ktype_cdev_default ;
2008-07-20 08:43:17 -03:00
return 0 ;
2008-08-29 17:31:35 -03:00
del_cdev :
cdev_del ( & vfd - > cdev ) ;
2008-07-20 08:43:17 -03:00
fail_minor :
mutex_lock ( & videodev_lock ) ;
video_device [ vfd - > minor ] = NULL ;
mutex_unlock ( & videodev_lock ) ;
2008-08-23 07:48:38 -03:00
vfd - > minor = - 1 ;
2008-07-20 08:43:17 -03:00
return ret ;
}
EXPORT_SYMBOL ( video_register_device_index ) ;
/**
* video_unregister_device - unregister a video4linux device
* @ vfd : the device to unregister
*
* This unregisters the passed device and deassigns the minor
* number . Future open calls will be met with errors .
*/
void video_unregister_device ( struct video_device * vfd )
{
2008-07-20 06:35:02 -03:00
device_unregister ( & vfd - > dev ) ;
2008-07-20 08:43:17 -03:00
}
EXPORT_SYMBOL ( video_unregister_device ) ;
/*
* Initialise video for linux
*/
static int __init videodev_init ( void )
{
2008-08-29 17:31:35 -03:00
dev_t dev = MKDEV ( VIDEO_MAJOR , 0 ) ;
2008-07-20 08:43:17 -03:00
int ret ;
printk ( KERN_INFO " Linux video capture interface: v2.00 \n " ) ;
2008-08-29 17:31:35 -03:00
ret = register_chrdev_region ( dev , VIDEO_NUM_DEVICES , VIDEO_NAME ) ;
if ( ret < 0 ) {
printk ( KERN_WARNING " videodev: unable to get major %d \n " ,
VIDEO_MAJOR ) ;
return ret ;
2008-07-20 08:43:17 -03:00
}
ret = class_register ( & video_class ) ;
if ( ret < 0 ) {
2008-08-29 17:31:35 -03:00
unregister_chrdev_region ( dev , VIDEO_NUM_DEVICES ) ;
2008-07-20 08:43:17 -03:00
printk ( KERN_WARNING " video_dev: class_register failed \n " ) ;
return - EIO ;
}
return 0 ;
}
static void __exit videodev_exit ( void )
{
2008-08-29 17:31:35 -03:00
dev_t dev = MKDEV ( VIDEO_MAJOR , 0 ) ;
2008-07-20 08:43:17 -03:00
class_unregister ( & video_class ) ;
2008-08-29 17:31:35 -03:00
unregister_chrdev_region ( dev , VIDEO_NUM_DEVICES ) ;
2008-07-20 08:43:17 -03:00
}
module_init ( videodev_init )
module_exit ( videodev_exit )
MODULE_AUTHOR ( " Alan Cox, Mauro Carvalho Chehab <mchehab@infradead.org> " ) ;
MODULE_DESCRIPTION ( " Device registrar for Video4Linux drivers v2 " ) ;
MODULE_LICENSE ( " GPL " ) ;
/*
* Local variables :
* c - basic - offset : 8
* End :
*/