2005-04-17 02:20:36 +04:00
/*
* VIDEO MOTION CODECs internal API for video devices
*
* Interface for MJPEG ( and maybe later MPEG / WAVELETS ) codec ' s
* bound to a master device .
*
* ( c ) 2002 Wolfgang Scherr < scherr @ net4you . at >
*
* $ Id : videocodec . c , v 1.1 .2 .8 2003 / 03 / 29 07 : 16 : 04 rbultje Exp $
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*
* 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 .
*
* 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 General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*/
# define VIDEOCODEC_VERSION "v0.2"
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/types.h>
# include <linux/slab.h>
// kernel config is here (procfs flag)
# ifdef CONFIG_PROC_FS
# include <linux/proc_fs.h>
# include <asm/uaccess.h>
# endif
# include "videocodec.h"
static int debug = 0 ;
module_param ( debug , int , 0 ) ;
MODULE_PARM_DESC ( debug , " Debug level (0-4) " ) ;
# define dprintk(num, format, args...) \
do { \
if ( debug > = num ) \
printk ( format , # # args ) ; \
} while ( 0 )
struct attached_list {
struct videocodec * codec ;
struct attached_list * next ;
} ;
struct codec_list {
const struct videocodec * codec ;
int attached ;
struct attached_list * list ;
struct codec_list * next ;
} ;
static struct codec_list * codeclist_top = NULL ;
/* ================================================= */
/* function prototypes of the master/slave interface */
/* ================================================= */
struct videocodec *
videocodec_attach ( struct videocodec_master * master )
{
struct codec_list * h = codeclist_top ;
struct attached_list * a , * ptr ;
struct videocodec * codec ;
int res ;
if ( ! master ) {
dprintk ( 1 , KERN_ERR " videocodec_attach: no data \n " ) ;
return NULL ;
}
dprintk ( 2 ,
" videocodec_attach: '%s', type: %x, flags %lx, magic %lx \n " ,
master - > name , master - > type , master - > flags , master - > magic ) ;
if ( ! h ) {
dprintk ( 1 ,
KERN_ERR
" videocodec_attach: no device available \n " ) ;
return NULL ;
}
while ( h ) {
// attach only if the slave has at least the flags
// expected by the master
if ( ( master - > flags & h - > codec - > flags ) = = master - > flags ) {
dprintk ( 4 , " videocodec_attach: try '%s' \n " ,
h - > codec - > name ) ;
if ( ! try_module_get ( h - > codec - > owner ) )
return NULL ;
codec =
kmalloc ( sizeof ( struct videocodec ) , GFP_KERNEL ) ;
if ( ! codec ) {
dprintk ( 1 ,
KERN_ERR
" videocodec_attach: no mem \n " ) ;
goto out_module_put ;
}
memcpy ( codec , h - > codec , sizeof ( struct videocodec ) ) ;
snprintf ( codec - > name , sizeof ( codec - > name ) ,
" %s[%d] " , codec - > name , h - > attached ) ;
codec - > master_data = master ;
res = codec - > setup ( codec ) ;
if ( res = = 0 ) {
dprintk ( 3 , " videocodec_attach '%s' \n " ,
codec - > name ) ;
2006-01-12 00:40:56 +03:00
ptr = kzalloc ( sizeof ( struct attached_list ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! ptr ) {
dprintk ( 1 ,
KERN_ERR
" videocodec_attach: no memory \n " ) ;
goto out_kfree ;
}
ptr - > codec = codec ;
a = h - > list ;
if ( ! a ) {
h - > list = ptr ;
dprintk ( 4 ,
" videocodec: first element \n " ) ;
} else {
while ( a - > next )
a = a - > next ; // find end
a - > next = ptr ;
dprintk ( 4 ,
" videocodec: in after '%s' \n " ,
h - > codec - > name ) ;
}
h - > attached + = 1 ;
return codec ;
} else {
kfree ( codec ) ;
}
}
h = h - > next ;
}
dprintk ( 1 , KERN_ERR " videocodec_attach: no codec found! \n " ) ;
return NULL ;
out_module_put :
module_put ( h - > codec - > owner ) ;
out_kfree :
kfree ( codec ) ;
return NULL ;
}
int
videocodec_detach ( struct videocodec * codec )
{
struct codec_list * h = codeclist_top ;
struct attached_list * a , * prev ;
int res ;
if ( ! codec ) {
dprintk ( 1 , KERN_ERR " videocodec_detach: no data \n " ) ;
return - EINVAL ;
}
dprintk ( 2 ,
" videocodec_detach: '%s', type: %x, flags %lx, magic %lx \n " ,
codec - > name , codec - > type , codec - > flags , codec - > magic ) ;
if ( ! h ) {
dprintk ( 1 ,
KERN_ERR " videocodec_detach: no device left... \n " ) ;
return - ENXIO ;
}
while ( h ) {
a = h - > list ;
prev = NULL ;
while ( a ) {
if ( codec = = a - > codec ) {
res = a - > codec - > unset ( a - > codec ) ;
if ( res > = 0 ) {
dprintk ( 3 ,
" videocodec_detach: '%s' \n " ,
a - > codec - > name ) ;
a - > codec - > master_data = NULL ;
} else {
dprintk ( 1 ,
KERN_ERR
" videocodec_detach: '%s' \n " ,
a - > codec - > name ) ;
a - > codec - > master_data = NULL ;
}
if ( prev = = NULL ) {
h - > list = a - > next ;
dprintk ( 4 ,
" videocodec: delete first \n " ) ;
} else {
prev - > next = a - > next ;
dprintk ( 4 ,
" videocodec: delete middle \n " ) ;
}
module_put ( a - > codec - > owner ) ;
kfree ( a - > codec ) ;
kfree ( a ) ;
h - > attached - = 1 ;
return 0 ;
}
prev = a ;
a = a - > next ;
}
h = h - > next ;
}
dprintk ( 1 , KERN_ERR " videocodec_detach: given codec not found! \n " ) ;
return - EINVAL ;
}
int
videocodec_register ( const struct videocodec * codec )
{
struct codec_list * ptr , * h = codeclist_top ;
if ( ! codec ) {
dprintk ( 1 , KERN_ERR " videocodec_register: no data! \n " ) ;
return - EINVAL ;
}
dprintk ( 2 ,
" videocodec: register '%s', type: %x, flags %lx, magic %lx \n " ,
codec - > name , codec - > type , codec - > flags , codec - > magic ) ;
2006-01-12 00:40:56 +03:00
ptr = kzalloc ( sizeof ( struct codec_list ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! ptr ) {
dprintk ( 1 , KERN_ERR " videocodec_register: no memory \n " ) ;
return - ENOMEM ;
}
ptr - > codec = codec ;
if ( ! h ) {
codeclist_top = ptr ;
dprintk ( 4 , " videocodec: hooked in as first element \n " ) ;
} else {
while ( h - > next )
h = h - > next ; // find the end
h - > next = ptr ;
dprintk ( 4 , " videocodec: hooked in after '%s' \n " ,
h - > codec - > name ) ;
}
return 0 ;
}
int
videocodec_unregister ( const struct videocodec * codec )
{
struct codec_list * prev = NULL , * h = codeclist_top ;
if ( ! codec ) {
dprintk ( 1 , KERN_ERR " videocodec_unregister: no data! \n " ) ;
return - EINVAL ;
}
dprintk ( 2 ,
" videocodec: unregister '%s', type: %x, flags %lx, magic %lx \n " ,
codec - > name , codec - > type , codec - > flags , codec - > magic ) ;
if ( ! h ) {
dprintk ( 1 ,
KERN_ERR
" videocodec_unregister: no device left... \n " ) ;
return - ENXIO ;
}
while ( h ) {
if ( codec = = h - > codec ) {
if ( h - > attached ) {
dprintk ( 1 ,
KERN_ERR
" videocodec: '%s' is used \n " ,
h - > codec - > name ) ;
return - EBUSY ;
}
dprintk ( 3 , " videocodec: unregister '%s' is ok. \n " ,
h - > codec - > name ) ;
if ( prev = = NULL ) {
codeclist_top = h - > next ;
dprintk ( 4 ,
" videocodec: delete first element \n " ) ;
} else {
prev - > next = h - > next ;
dprintk ( 4 ,
" videocodec: delete middle element \n " ) ;
}
kfree ( h ) ;
return 0 ;
}
prev = h ;
h = h - > next ;
}
dprintk ( 1 ,
KERN_ERR
" videocodec_unregister: given codec not found! \n " ) ;
return - EINVAL ;
}
# ifdef CONFIG_PROC_FS
/* ============ */
/* procfs stuff */
/* ============ */
static char * videocodec_buf = NULL ;
static int videocodec_bufsize = 0 ;
static int
videocodec_build_table ( void )
{
struct codec_list * h = codeclist_top ;
struct attached_list * a ;
int i = 0 , size ;
// sum up amount of slaves plus their attached masters
while ( h ) {
i + = h - > attached + 1 ;
h = h - > next ;
}
# define LINESIZE 100
size = LINESIZE * ( i + 1 ) ;
dprintk ( 3 , " videocodec_build table: %d entries, %d bytes \n " , i ,
size ) ;
2005-11-07 12:01:31 +03:00
kfree ( videocodec_buf ) ;
2006-12-13 11:35:56 +03:00
videocodec_buf = kmalloc ( size , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
2007-03-31 00:34:14 +04:00
if ( ! videocodec_buf )
return 0 ;
2005-04-17 02:20:36 +04:00
i = 0 ;
i + = scnprintf ( videocodec_buf + i , size - 1 ,
" <S>lave or attached <M>aster name type flags magic " ) ;
i + = scnprintf ( videocodec_buf + i , size - i - 1 , " (connected as) \n " ) ;
h = codeclist_top ;
while ( h ) {
if ( i > ( size - LINESIZE ) )
break ; // security check
i + = scnprintf ( videocodec_buf + i , size - i - 1 ,
" S %32s %04x %08lx %08lx (TEMPLATE) \n " ,
h - > codec - > name , h - > codec - > type ,
h - > codec - > flags , h - > codec - > magic ) ;
a = h - > list ;
while ( a ) {
if ( i > ( size - LINESIZE ) )
break ; // security check
i + = scnprintf ( videocodec_buf + i , size - i - 1 ,
" M %32s %04x %08lx %08lx (%s) \n " ,
a - > codec - > master_data - > name ,
a - > codec - > master_data - > type ,
a - > codec - > master_data - > flags ,
a - > codec - > master_data - > magic ,
a - > codec - > name ) ;
a = a - > next ;
}
h = h - > next ;
}
return i ;
}
//The definition:
//typedef int (read_proc_t)(char *page, char **start, off_t off,
// int count, int *eof, void *data);
static int
videocodec_info ( char * buffer ,
char * * buffer_location ,
off_t offset ,
int buffer_length ,
int * eof ,
void * data )
{
int size ;
dprintk ( 3 , " videocodec_info: offset: %ld, len %d / size %d \n " ,
offset , buffer_length , videocodec_bufsize ) ;
if ( offset = = 0 ) {
videocodec_bufsize = videocodec_build_table ( ) ;
}
if ( ( offset < 0 ) | | ( offset > = videocodec_bufsize ) ) {
dprintk ( 4 ,
" videocodec_info: call delivers no result, return 0 \n " ) ;
* eof = 1 ;
return 0 ;
}
if ( buffer_length < ( videocodec_bufsize - offset ) ) {
dprintk ( 4 , " videocodec_info: %ld needed, %d got \n " ,
videocodec_bufsize - offset , buffer_length ) ;
size = buffer_length ;
} else {
dprintk ( 4 , " videocodec_info: last reading of %ld bytes \n " ,
videocodec_bufsize - offset ) ;
size = videocodec_bufsize - offset ;
* eof = 1 ;
}
memcpy ( buffer , videocodec_buf + offset , size ) ;
/* doesn't work... */
/* copy_to_user(buffer, videocodec_buf+offset, size); */
/* *buffer_location = videocodec_buf+offset; */
return size ;
}
# endif
/* ===================== */
/* hook in driver module */
/* ===================== */
static int __init
videocodec_init ( void )
{
# ifdef CONFIG_PROC_FS
static struct proc_dir_entry * videocodec_proc_entry ;
# endif
printk ( KERN_INFO " Linux video codec intermediate layer: %s \n " ,
VIDEOCODEC_VERSION ) ;
# ifdef CONFIG_PROC_FS
videocodec_buf = NULL ;
videocodec_bufsize = 0 ;
videocodec_proc_entry = create_proc_entry ( " videocodecs " , 0 , NULL ) ;
if ( videocodec_proc_entry ) {
videocodec_proc_entry - > read_proc = videocodec_info ;
videocodec_proc_entry - > write_proc = NULL ;
videocodec_proc_entry - > data = NULL ;
videocodec_proc_entry - > owner = THIS_MODULE ;
} else {
dprintk ( 1 , KERN_ERR " videocodec: can't init procfs. \n " ) ;
}
# endif
return 0 ;
}
static void __exit
videocodec_exit ( void )
{
# ifdef CONFIG_PROC_FS
remove_proc_entry ( " videocodecs " , NULL ) ;
2005-11-07 12:01:31 +03:00
kfree ( videocodec_buf ) ;
2005-04-17 02:20:36 +04:00
# endif
}
EXPORT_SYMBOL ( videocodec_attach ) ;
EXPORT_SYMBOL ( videocodec_detach ) ;
EXPORT_SYMBOL ( videocodec_register ) ;
EXPORT_SYMBOL ( videocodec_unregister ) ;
module_init ( videocodec_init ) ;
module_exit ( videocodec_exit ) ;
MODULE_AUTHOR ( " Wolfgang Scherr <scherr@net4you.at> " ) ;
MODULE_DESCRIPTION ( " Intermediate API module for video codecs "
VIDEOCODEC_VERSION ) ;
MODULE_LICENSE ( " GPL " ) ;