2008-07-20 15:43:17 +04: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 .
*
2008-10-27 21:13:47 +03:00
* Authors : Alan Cox , < alan @ lxorguk . ukuu . org . uk > ( version 1 )
2008-07-20 15:43:17 +04:00
* 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>
2008-12-23 13:35:17 +03:00
# include <media/v4l2-device.h>
2008-12-30 12:58:20 +03:00
# include <media/v4l2-ioctl.h>
2008-07-20 15:43:17 +04:00
# 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-12-20 03:28:27 +03:00
struct video_device * vdev = to_video_device ( cd ) ;
2008-08-23 14:48:38 +04:00
2008-12-20 03:28:27 +03:00
return sprintf ( buf , " %i \n " , vdev - > index ) ;
2008-07-20 15:43:17 +04:00
}
static ssize_t show_name ( struct device * cd ,
struct device_attribute * attr , char * buf )
{
2008-12-20 03:28:27 +03:00
struct video_device * vdev = to_video_device ( cd ) ;
2008-08-23 14:48:38 +04:00
2008-12-20 03:28:27 +03:00
return sprintf ( buf , " %.*s \n " , ( int ) sizeof ( vdev - > name ) , vdev - > name ) ;
2008-07-20 15:43:17 +04:00
}
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-30 00:31:35 +04:00
/*
* Active devices
*/
static struct video_device * video_device [ VIDEO_NUM_DEVICES ] ;
static DEFINE_MUTEX ( videodev_lock ) ;
2008-10-04 15:36:54 +04:00
static DECLARE_BITMAP ( video_nums [ VFL_TYPE_MAX ] , VIDEO_NUM_DEVICES ) ;
2008-08-30 00:31:35 +04:00
2008-07-20 15:43:17 +04:00
struct video_device * video_device_alloc ( void )
{
2008-08-23 14:48:38 +04:00
return kzalloc ( sizeof ( struct video_device ) , GFP_KERNEL ) ;
2008-07-20 15:43:17 +04:00
}
EXPORT_SYMBOL ( video_device_alloc ) ;
2008-12-20 03:28:27 +03:00
void video_device_release ( struct video_device * vdev )
2008-07-20 15:43:17 +04:00
{
2008-12-20 03:28:27 +03:00
kfree ( vdev ) ;
2008-07-20 15:43:17 +04:00
}
EXPORT_SYMBOL ( video_device_release ) ;
2008-12-20 03:28:27 +03:00
void video_device_release_empty ( struct video_device * vdev )
2008-08-23 12:47:41 +04:00
{
/* Do nothing */
/* Only valid when the video_device struct is a static. */
}
EXPORT_SYMBOL ( video_device_release_empty ) ;
2008-12-20 03:28:27 +03:00
static inline void video_get ( struct video_device * vdev )
2008-08-30 00:31:35 +04:00
{
2008-12-20 03:28:27 +03:00
get_device ( & vdev - > dev ) ;
}
static inline void video_put ( struct video_device * vdev )
{
put_device ( & vdev - > dev ) ;
}
/* Called when the last user of the video device exits. */
static void v4l2_device_release ( struct device * cd )
{
struct video_device * vdev = to_video_device ( cd ) ;
2008-08-30 00:31:35 +04:00
mutex_lock ( & videodev_lock ) ;
2008-12-20 03:28:27 +03:00
if ( video_device [ vdev - > minor ] ! = vdev ) {
2008-08-30 16:40:47 +04:00
mutex_unlock ( & videodev_lock ) ;
2008-12-20 03:28:27 +03:00
/* should not happen */
WARN_ON ( 1 ) ;
2008-08-30 16:40:47 +04:00
return ;
}
2008-08-30 00:31:35 +04:00
/* Free up this device for reuse */
2008-12-20 03:28:27 +03:00
video_device [ vdev - > minor ] = NULL ;
2008-08-30 00:31:35 +04:00
2008-12-20 03:28:27 +03:00
/* Delete the cdev on this minor as well */
cdev_del ( vdev - > cdev ) ;
/* Just in case some driver tries to access this from
the release ( ) callback . */
vdev - > cdev = NULL ;
2008-08-30 00:31:35 +04:00
2008-12-20 03:28:27 +03:00
/* Mark minor as free */
clear_bit ( vdev - > num , video_nums [ vdev - > vfl_type ] ) ;
2008-08-30 00:31:35 +04:00
2008-12-20 03:28:27 +03:00
mutex_unlock ( & videodev_lock ) ;
2008-07-20 15:43:17 +04:00
2008-12-20 03:28:27 +03:00
/* Release video_device and perform other
cleanups as needed . */
vdev - > release ( vdev ) ;
2008-07-20 15:43:17 +04:00
}
static struct class video_class = {
. name = VIDEO_NAME ,
. dev_attrs = video_device_attrs ,
} ;
struct video_device * video_devdata ( struct file * file )
{
return video_device [ iminor ( file - > f_path . dentry - > d_inode ) ] ;
}
EXPORT_SYMBOL ( video_devdata ) ;
2008-12-20 03:28:27 +03:00
static ssize_t v4l2_read ( struct file * filp , char __user * buf ,
size_t sz , loff_t * off )
{
struct video_device * vdev = video_devdata ( filp ) ;
if ( ! vdev - > fops - > read )
return - EINVAL ;
if ( video_is_unregistered ( vdev ) )
return - EIO ;
return vdev - > fops - > read ( filp , buf , sz , off ) ;
}
static ssize_t v4l2_write ( struct file * filp , const char __user * buf ,
size_t sz , loff_t * off )
{
struct video_device * vdev = video_devdata ( filp ) ;
if ( ! vdev - > fops - > write )
return - EINVAL ;
if ( video_is_unregistered ( vdev ) )
return - EIO ;
return vdev - > fops - > write ( filp , buf , sz , off ) ;
}
static unsigned int v4l2_poll ( struct file * filp , struct poll_table_struct * poll )
{
struct video_device * vdev = video_devdata ( filp ) ;
if ( ! vdev - > fops - > poll | | video_is_unregistered ( vdev ) )
return DEFAULT_POLLMASK ;
return vdev - > fops - > poll ( filp , poll ) ;
}
static int v4l2_ioctl ( struct inode * inode , struct file * filp ,
unsigned int cmd , unsigned long arg )
{
struct video_device * vdev = video_devdata ( filp ) ;
if ( ! vdev - > fops - > ioctl )
return - ENOTTY ;
/* Allow ioctl to continue even if the device was unregistered.
Things like dequeueing buffers might still be useful . */
2008-12-30 12:58:20 +03:00
return vdev - > fops - > ioctl ( filp , cmd , arg ) ;
2008-12-20 03:28:27 +03:00
}
static long v4l2_unlocked_ioctl ( struct file * filp ,
unsigned int cmd , unsigned long arg )
{
struct video_device * vdev = video_devdata ( filp ) ;
if ( ! vdev - > fops - > unlocked_ioctl )
return - ENOTTY ;
/* Allow ioctl to continue even if the device was unregistered.
Things like dequeueing buffers might still be useful . */
return vdev - > fops - > unlocked_ioctl ( filp , cmd , arg ) ;
}
2009-03-26 17:31:08 +03:00
# ifdef CONFIG_MMU
# define v4l2_get_unmapped_area NULL
# else
static unsigned long v4l2_get_unmapped_area ( struct file * filp ,
unsigned long addr , unsigned long len , unsigned long pgoff ,
unsigned long flags )
{
struct video_device * vdev = video_devdata ( filp ) ;
if ( ! vdev - > fops - > get_unmapped_area )
return - ENOSYS ;
if ( video_is_unregistered ( vdev ) )
return - ENODEV ;
return vdev - > fops - > get_unmapped_area ( filp , addr , len , pgoff , flags ) ;
}
# endif
2008-12-20 03:28:27 +03:00
static int v4l2_mmap ( struct file * filp , struct vm_area_struct * vm )
{
struct video_device * vdev = video_devdata ( filp ) ;
if ( ! vdev - > fops - > mmap | |
video_is_unregistered ( vdev ) )
return - ENODEV ;
return vdev - > fops - > mmap ( filp , vm ) ;
}
/* Override for the open function */
static int v4l2_open ( struct inode * inode , struct file * filp )
{
struct video_device * vdev ;
2009-03-30 03:19:38 +04:00
int ret = 0 ;
2008-12-20 03:28:27 +03:00
/* Check if the video device is available */
mutex_lock ( & videodev_lock ) ;
vdev = video_devdata ( filp ) ;
/* return ENODEV if the video device has been removed
already or if it is not registered anymore . */
if ( vdev = = NULL | | video_is_unregistered ( vdev ) ) {
mutex_unlock ( & videodev_lock ) ;
return - ENODEV ;
}
/* and increase the device refcount */
video_get ( vdev ) ;
mutex_unlock ( & videodev_lock ) ;
2009-03-30 03:19:38 +04:00
if ( vdev - > fops - > open )
ret = vdev - > fops - > open ( filp ) ;
2008-12-20 03:28:27 +03:00
/* decrease the refcount in case of an error */
if ( ret )
video_put ( vdev ) ;
return ret ;
}
/* Override for the release function */
static int v4l2_release ( struct inode * inode , struct file * filp )
{
struct video_device * vdev = video_devdata ( filp ) ;
2009-03-30 03:19:38 +04:00
int ret = 0 ;
if ( vdev - > fops - > release )
vdev - > fops - > release ( filp ) ;
2008-12-20 03:28:27 +03:00
/* decrease the refcount unconditionally since the release()
return value is ignored . */
video_put ( vdev ) ;
return ret ;
}
static const struct file_operations v4l2_unlocked_fops = {
. owner = THIS_MODULE ,
. read = v4l2_read ,
. write = v4l2_write ,
. open = v4l2_open ,
2009-03-26 17:31:08 +03:00
. get_unmapped_area = v4l2_get_unmapped_area ,
2008-12-20 03:28:27 +03:00
. mmap = v4l2_mmap ,
. unlocked_ioctl = v4l2_unlocked_ioctl ,
# ifdef CONFIG_COMPAT
2008-12-30 12:42:40 +03:00
. compat_ioctl = v4l2_compat_ioctl32 ,
2008-12-20 03:28:27 +03:00
# endif
. release = v4l2_release ,
. poll = v4l2_poll ,
. llseek = no_llseek ,
} ;
static const struct file_operations v4l2_fops = {
. owner = THIS_MODULE ,
. read = v4l2_read ,
. write = v4l2_write ,
. open = v4l2_open ,
2009-03-26 17:31:08 +03:00
. get_unmapped_area = v4l2_get_unmapped_area ,
2008-12-20 03:28:27 +03:00
. mmap = v4l2_mmap ,
. ioctl = v4l2_ioctl ,
# ifdef CONFIG_COMPAT
2008-12-30 12:42:40 +03:00
. compat_ioctl = v4l2_compat_ioctl32 ,
2008-12-20 03:28:27 +03:00
# endif
. release = v4l2_release ,
. poll = v4l2_poll ,
. llseek = no_llseek ,
} ;
2008-07-20 15:43:17 +04:00
/**
* get_index - assign stream number based on parent device
2008-12-20 03:28:27 +03:00
* @ vdev : video_device to assign index number to , vdev - > parent should be assigned
* @ num : - 1 if auto assign , requested number otherwise
2008-07-20 15:43:17 +04:00
*
2008-12-20 03:28:27 +03:00
* Note that when this is called the new device has not yet been registered
* in the video_device array .
2008-07-20 15:43:17 +04:00
*
2008-12-20 03:28:27 +03:00
* Returns - ENFILE if num is already in use , a free index number if
2008-07-20 15:43:17 +04:00
* successful .
*/
static int get_index ( struct video_device * vdev , int num )
{
2009-02-14 17:31:01 +03:00
/* This can be static since this function is called with the global
videodev_lock held . */
static DECLARE_BITMAP ( used , VIDEO_NUM_DEVICES ) ;
2008-07-20 15:43:17 +04:00
int i ;
2009-02-14 17:31:01 +03:00
if ( num > = VIDEO_NUM_DEVICES ) {
2008-07-20 15:43:17 +04:00
printk ( KERN_ERR " videodev: %s num is too large \n " , __func__ ) ;
return - EINVAL ;
}
2009-02-14 17:31:01 +03:00
/* Some drivers do not set the parent. In that case always return
num or 0. */
2008-12-19 15:10:56 +03:00
if ( vdev - > parent = = NULL )
2009-02-14 17:31:01 +03:00
return num > = 0 ? num : 0 ;
bitmap_zero ( used , VIDEO_NUM_DEVICES ) ;
2008-12-19 15:10:56 +03:00
2008-07-20 15:43:17 +04:00
for ( i = 0 ; i < VIDEO_NUM_DEVICES ; i + + ) {
if ( video_device [ i ] ! = NULL & &
2008-07-20 13:31:39 +04:00
video_device [ i ] - > parent = = vdev - > parent ) {
2009-02-14 17:31:01 +03:00
set_bit ( video_device [ i ] - > index , used ) ;
2008-07-20 15:43:17 +04:00
}
}
if ( num > = 0 ) {
2009-02-14 17:31:01 +03:00
if ( test_bit ( num , used ) )
2008-07-20 15:43:17 +04:00
return - ENFILE ;
return num ;
}
2009-02-14 17:31:01 +03:00
i = find_first_zero_bit ( used , VIDEO_NUM_DEVICES ) ;
return i = = VIDEO_NUM_DEVICES ? - ENFILE : i ;
2008-07-20 15:43:17 +04:00
}
2008-12-20 03:28:27 +03:00
int video_register_device ( struct video_device * vdev , int type , int nr )
2008-07-20 15:43:17 +04:00
{
2008-12-20 03:28:27 +03:00
return video_register_device_index ( vdev , type , nr , - 1 ) ;
2008-07-20 15:43:17 +04:00
}
EXPORT_SYMBOL ( video_register_device ) ;
/**
2008-07-28 22:39:38 +04:00
* video_register_device_index - register video4linux devices
2008-12-20 03:28:27 +03:00
* @ vdev : video device structure we want to register
2008-07-20 15:43:17 +04:00
* @ type : type of device to register
* @ nr : which device number ( 0 = = / dev / video0 , 1 = = / dev / video1 , . . .
* - 1 = = first free )
2008-07-28 22:39:38 +04:00
* @ index : stream number based on parent device ;
* - 1 if auto assign , requested number otherwise
2008-07-20 15:43:17 +04: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
*/
2008-12-20 03:28:27 +03:00
int video_register_device_index ( struct video_device * vdev , int type , int nr ,
2008-07-20 15:43:17 +04:00
int index )
{
int i = 0 ;
int ret ;
2008-10-04 15:36:54 +04:00
int minor_offset = 0 ;
int minor_cnt = VIDEO_NUM_DEVICES ;
const char * name_base ;
2008-12-20 03:28:27 +03:00
void * priv = video_get_drvdata ( vdev ) ;
2008-07-20 15:43:17 +04:00
2008-12-20 03:28:27 +03:00
/* A minor value of -1 marks this video device as never
having been registered */
2009-03-04 00:51:52 +03:00
vdev - > minor = - 1 ;
2008-08-22 23:41:03 +04:00
2008-12-20 03:28:27 +03:00
/* the release callback MUST be present */
2009-03-04 00:51:52 +03:00
WARN_ON ( ! vdev - > release ) ;
if ( ! vdev - > release )
2008-09-04 00:11:53 +04:00
return - EINVAL ;
2008-12-20 03:28:27 +03:00
/* Part 1: check device type */
2008-07-20 15:43:17 +04:00
switch ( type ) {
case VFL_TYPE_GRABBER :
name_base = " video " ;
break ;
case VFL_TYPE_VTX :
name_base = " vtx " ;
break ;
case VFL_TYPE_VBI :
name_base = " vbi " ;
break ;
case VFL_TYPE_RADIO :
name_base = " radio " ;
break ;
default :
printk ( KERN_ERR " %s called with unknown type: %d \n " ,
__func__ , type ) ;
2008-09-03 23:47:39 +04:00
return - EINVAL ;
2008-07-20 15:43:17 +04:00
}
2008-12-20 03:28:27 +03:00
vdev - > vfl_type = type ;
vdev - > cdev = NULL ;
2009-03-13 16:03:04 +03:00
if ( vdev - > v4l2_dev & & vdev - > v4l2_dev - > dev )
2008-12-23 13:35:17 +03:00
vdev - > parent = vdev - > v4l2_dev - > dev ;
2008-10-04 15:36:54 +04:00
2008-12-20 03:28:27 +03:00
/* Part 2: find a free minor, kernel number and device index. */
2008-10-04 15:36:54 +04:00
# ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
/* Keep the ranges for the first four types for historical
* reasons .
* Newer devices ( not yet in place ) should use the range
* of 128 - 191 and just pick the first free minor there
* ( new style ) . */
switch ( type ) {
case VFL_TYPE_GRABBER :
minor_offset = 0 ;
minor_cnt = 64 ;
break ;
case VFL_TYPE_RADIO :
minor_offset = 64 ;
minor_cnt = 64 ;
break ;
case VFL_TYPE_VTX :
minor_offset = 192 ;
minor_cnt = 32 ;
break ;
case VFL_TYPE_VBI :
minor_offset = 224 ;
minor_cnt = 32 ;
break ;
default :
minor_offset = 128 ;
minor_cnt = 64 ;
break ;
}
# endif
2008-12-20 03:28:27 +03:00
/* Pick a minor number */
2008-07-20 15:43:17 +04:00
mutex_lock ( & videodev_lock ) ;
2008-10-04 15:36:54 +04:00
nr = find_next_zero_bit ( video_nums [ type ] , minor_cnt , nr = = - 1 ? 0 : nr ) ;
if ( nr = = minor_cnt )
nr = find_first_zero_bit ( video_nums [ type ] , minor_cnt ) ;
if ( nr = = minor_cnt ) {
printk ( KERN_ERR " could not get a free kernel number \n " ) ;
mutex_unlock ( & videodev_lock ) ;
return - ENFILE ;
2008-07-20 15:43:17 +04:00
}
2008-10-04 15:36:54 +04:00
# ifdef CONFIG_VIDEO_FIXED_MINOR_RANGES
/* 1-on-1 mapping of kernel number to minor number */
i = nr ;
# else
/* The kernel number and minor numbers are independent */
for ( i = 0 ; i < VIDEO_NUM_DEVICES ; i + + )
if ( video_device [ i ] = = NULL )
break ;
if ( i = = VIDEO_NUM_DEVICES ) {
mutex_unlock ( & videodev_lock ) ;
printk ( KERN_ERR " could not get a free minor \n " ) ;
return - ENFILE ;
}
# endif
2008-12-20 03:28:27 +03:00
vdev - > minor = i + minor_offset ;
vdev - > num = nr ;
2008-10-04 15:36:54 +04:00
set_bit ( nr , video_nums [ type ] ) ;
2008-12-20 03:28:27 +03:00
/* Should not happen since we thought this minor was free */
WARN_ON ( video_device [ vdev - > minor ] ! = NULL ) ;
ret = vdev - > index = get_index ( vdev , index ) ;
2008-07-20 15:43:17 +04:00
mutex_unlock ( & videodev_lock ) ;
if ( ret < 0 ) {
printk ( KERN_ERR " %s: get_index failed \n " , __func__ ) ;
2008-12-20 03:28:27 +03:00
goto cleanup ;
2008-07-20 15:43:17 +04:00
}
2008-12-20 03:28:27 +03:00
/* Part 3: Initialize the character device */
vdev - > cdev = cdev_alloc ( ) ;
if ( vdev - > cdev = = NULL ) {
ret = - ENOMEM ;
goto cleanup ;
}
if ( vdev - > fops - > unlocked_ioctl )
vdev - > cdev - > ops = & v4l2_unlocked_fops ;
else
vdev - > cdev - > ops = & v4l2_fops ;
vdev - > cdev - > owner = vdev - > fops - > owner ;
ret = cdev_add ( vdev - > cdev , MKDEV ( VIDEO_MAJOR , vdev - > minor ) , 1 ) ;
2008-08-30 00:31:35 +04:00
if ( ret < 0 ) {
printk ( KERN_ERR " %s: cdev_add failed \n " , __func__ ) ;
2008-12-20 03:28:27 +03:00
kfree ( vdev - > cdev ) ;
vdev - > cdev = NULL ;
goto cleanup ;
2008-08-30 00:31:35 +04:00
}
2008-12-20 03:28:27 +03:00
/* Part 4: register the device with sysfs */
memset ( & vdev - > dev , 0 , sizeof ( vdev - > dev ) ) ;
2008-08-24 18:18:47 +04:00
/* The memset above cleared the device's drvdata, so
put back the copy we made earlier . */
2008-12-20 03:28:27 +03:00
video_set_drvdata ( vdev , priv ) ;
vdev - > dev . class = & video_class ;
vdev - > dev . devt = MKDEV ( VIDEO_MAJOR , vdev - > minor ) ;
if ( vdev - > parent )
vdev - > dev . parent = vdev - > parent ;
dev_set_name ( & vdev - > dev , " %s%d " , name_base , nr ) ;
ret = device_register ( & vdev - > dev ) ;
2008-07-20 15:43:17 +04:00
if ( ret < 0 ) {
printk ( KERN_ERR " %s: device_register failed \n " , __func__ ) ;
2008-12-20 03:28:27 +03:00
goto cleanup ;
2008-07-20 15:43:17 +04:00
}
2008-12-20 03:28:27 +03:00
/* Register the release callback that will be called when the last
reference to the device goes away . */
vdev - > dev . release = v4l2_device_release ;
2008-07-20 15:43:17 +04:00
2008-12-20 03:28:27 +03:00
/* Part 5: Activate this minor. The char device can now be used. */
mutex_lock ( & videodev_lock ) ;
video_device [ vdev - > minor ] = vdev ;
mutex_unlock ( & videodev_lock ) ;
return 0 ;
2008-08-30 00:31:35 +04:00
2008-12-20 03:28:27 +03:00
cleanup :
2008-07-20 15:43:17 +04:00
mutex_lock ( & videodev_lock ) ;
2008-12-20 03:28:27 +03:00
if ( vdev - > cdev )
cdev_del ( vdev - > cdev ) ;
clear_bit ( vdev - > num , video_nums [ type ] ) ;
2008-07-20 15:43:17 +04:00
mutex_unlock ( & videodev_lock ) ;
2008-12-20 03:28:27 +03:00
/* Mark this video device as never having been registered. */
vdev - > minor = - 1 ;
2008-07-20 15:43:17 +04:00
return ret ;
}
EXPORT_SYMBOL ( video_register_device_index ) ;
/**
* video_unregister_device - unregister a video4linux device
2008-12-20 03:28:27 +03:00
* @ vdev : the device to unregister
2008-07-20 15:43:17 +04:00
*
2008-12-20 03:28:27 +03:00
* This unregisters the passed device . Future open calls will
* be met with errors .
2008-07-20 15:43:17 +04:00
*/
2008-12-20 03:28:27 +03:00
void video_unregister_device ( struct video_device * vdev )
2008-07-20 15:43:17 +04:00
{
2008-12-20 03:28:27 +03:00
/* Check if vdev was ever registered at all */
if ( ! vdev | | vdev - > minor < 0 )
return ;
mutex_lock ( & videodev_lock ) ;
set_bit ( V4L2_FL_UNREGISTERED , & vdev - > flags ) ;
mutex_unlock ( & videodev_lock ) ;
device_unregister ( & vdev - > dev ) ;
2008-07-20 15:43:17 +04:00
}
EXPORT_SYMBOL ( video_unregister_device ) ;
/*
* Initialise video for linux
*/
static int __init videodev_init ( void )
{
2008-08-30 00:31:35 +04:00
dev_t dev = MKDEV ( VIDEO_MAJOR , 0 ) ;
2008-07-20 15:43:17 +04:00
int ret ;
printk ( KERN_INFO " Linux video capture interface: v2.00 \n " ) ;
2008-08-30 00:31:35 +04: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 15:43:17 +04:00
}
ret = class_register ( & video_class ) ;
if ( ret < 0 ) {
2008-08-30 00:31:35 +04:00
unregister_chrdev_region ( dev , VIDEO_NUM_DEVICES ) ;
2008-07-20 15:43:17 +04:00
printk ( KERN_WARNING " video_dev: class_register failed \n " ) ;
return - EIO ;
}
return 0 ;
}
static void __exit videodev_exit ( void )
{
2008-08-30 00:31:35 +04:00
dev_t dev = MKDEV ( VIDEO_MAJOR , 0 ) ;
2008-07-20 15:43:17 +04:00
class_unregister ( & video_class ) ;
2008-08-30 00:31:35 +04:00
unregister_chrdev_region ( dev , VIDEO_NUM_DEVICES ) ;
2008-07-20 15:43:17 +04: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 " ) ;
2009-03-02 21:40:57 +03:00
MODULE_ALIAS_CHARDEV_MAJOR ( VIDEO_MAJOR ) ;
2008-07-20 15:43:17 +04:00
/*
* Local variables :
* c - basic - offset : 8
* End :
*/