2005-04-17 02:20:36 +04:00
/*
* Information interface for ALSA driver
* Copyright ( c ) by Jaroslav Kysela < perex @ suse . cz >
*
*
* 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 . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*
*/
# include <sound/driver.h>
# include <linux/init.h>
# include <linux/time.h>
# include <linux/smp_lock.h>
2005-06-23 11:09:02 +04:00
# include <linux/string.h>
2005-04-17 02:20:36 +04:00
# include <sound/core.h>
# include <sound/minors.h>
# include <sound/info.h>
# include <sound/version.h>
# include <linux/proc_fs.h>
2006-01-16 18:29:08 +03:00
# include <linux/mutex.h>
2005-04-17 02:20:36 +04:00
# include <stdarg.h>
/*
*
*/
2005-12-01 12:42:42 +03:00
# ifdef CONFIG_PROC_FS
2005-04-17 02:20:36 +04:00
int snd_info_check_reserved_words ( const char * str )
{
static char * reserved [ ] =
{
" version " ,
" meminfo " ,
" memdebug " ,
" detect " ,
" devices " ,
" oss " ,
" cards " ,
" timers " ,
" synth " ,
" pcm " ,
" seq " ,
NULL
} ;
char * * xstr = reserved ;
while ( * xstr ) {
if ( ! strcmp ( * xstr , str ) )
return 0 ;
xstr + + ;
}
if ( ! strncmp ( str , " card " , 4 ) )
return 0 ;
return 1 ;
}
2006-01-16 18:29:08 +03:00
static DEFINE_MUTEX ( info_mutex ) ;
2005-04-17 02:20:36 +04:00
2005-11-17 15:58:48 +03:00
struct snd_info_private_data {
struct snd_info_buffer * rbuffer ;
struct snd_info_buffer * wbuffer ;
struct snd_info_entry * entry ;
2005-04-17 02:20:36 +04:00
void * file_private_data ;
2005-11-17 15:58:48 +03:00
} ;
2005-04-17 02:20:36 +04:00
static int snd_info_version_init ( void ) ;
static int snd_info_version_done ( void ) ;
2006-06-23 16:37:59 +04:00
static void snd_info_disconnect ( struct snd_info_entry * entry ) ;
2005-04-17 02:20:36 +04:00
2006-04-28 17:13:40 +04:00
/* resize the proc r/w buffer */
static int resize_info_buffer ( struct snd_info_buffer * buffer ,
unsigned int nsize )
{
char * nbuf ;
nsize = PAGE_ALIGN ( nsize ) ;
nbuf = kmalloc ( nsize , GFP_KERNEL ) ;
if ( ! nbuf )
return - ENOMEM ;
memcpy ( nbuf , buffer - > buffer , buffer - > len ) ;
kfree ( buffer - > buffer ) ;
buffer - > buffer = nbuf ;
buffer - > len = nsize ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
/**
* snd_iprintf - printf on the procfs buffer
* @ buffer : the procfs buffer
* @ fmt : the printf format
*
* Outputs the string on the procfs buffer just like printf ( ) .
*
* Returns the size of output string .
*/
2005-11-17 15:58:48 +03:00
int snd_iprintf ( struct snd_info_buffer * buffer , char * fmt , . . . )
2005-04-17 02:20:36 +04:00
{
va_list args ;
int len , res ;
2006-04-28 17:13:40 +04:00
int err = 0 ;
2005-04-17 02:20:36 +04:00
2006-04-28 17:13:41 +04:00
might_sleep ( ) ;
2005-04-17 02:20:36 +04:00
if ( buffer - > stop | | buffer - > error )
return 0 ;
len = buffer - > len - buffer - > size ;
va_start ( args , fmt ) ;
2006-04-28 17:13:40 +04:00
for ( ; ; ) {
res = vsnprintf ( buffer - > buffer + buffer - > curr , len , fmt , args ) ;
if ( res < len )
break ;
err = resize_info_buffer ( buffer , buffer - > len + PAGE_SIZE ) ;
if ( err < 0 )
break ;
len = buffer - > len - buffer - > size ;
2005-04-17 02:20:36 +04:00
}
2006-04-28 17:13:40 +04:00
va_end ( args ) ;
if ( err < 0 )
return err ;
2005-04-17 02:20:36 +04:00
buffer - > curr + = res ;
buffer - > size + = res ;
return res ;
}
2006-04-28 17:13:39 +04:00
EXPORT_SYMBOL ( snd_iprintf ) ;
2005-04-17 02:20:36 +04:00
/*
*/
2006-05-17 19:14:51 +04:00
static struct proc_dir_entry * snd_proc_root ;
struct snd_info_entry * snd_seq_root ;
2006-04-28 17:13:39 +04:00
EXPORT_SYMBOL ( snd_seq_root ) ;
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_SND_OSSEMUL
2006-05-17 19:14:51 +04:00
struct snd_info_entry * snd_oss_root ;
2005-04-17 02:20:36 +04:00
# endif
static inline void snd_info_entry_prepare ( struct proc_dir_entry * de )
{
de - > owner = THIS_MODULE ;
}
static void snd_remove_proc_entry ( struct proc_dir_entry * parent ,
struct proc_dir_entry * de )
{
if ( de )
remove_proc_entry ( de - > name , parent ) ;
}
static loff_t snd_info_entry_llseek ( struct file * file , loff_t offset , int orig )
{
2005-11-17 15:58:48 +03:00
struct snd_info_private_data * data ;
2005-04-17 02:20:36 +04:00
struct snd_info_entry * entry ;
loff_t ret ;
data = file - > private_data ;
entry = data - > entry ;
lock_kernel ( ) ;
switch ( entry - > content ) {
case SNDRV_INFO_CONTENT_TEXT :
switch ( orig ) {
2006-09-21 13:31:58 +04:00
case SEEK_SET :
2005-04-17 02:20:36 +04:00
file - > f_pos = offset ;
ret = file - > f_pos ;
goto out ;
2006-09-21 13:31:58 +04:00
case SEEK_CUR :
2005-04-17 02:20:36 +04:00
file - > f_pos + = offset ;
ret = file - > f_pos ;
goto out ;
2006-09-21 13:31:58 +04:00
case SEEK_END :
2005-04-17 02:20:36 +04:00
default :
ret = - EINVAL ;
goto out ;
}
break ;
case SNDRV_INFO_CONTENT_DATA :
if ( entry - > c . ops - > llseek ) {
ret = entry - > c . ops - > llseek ( entry ,
data - > file_private_data ,
file , offset , orig ) ;
goto out ;
}
break ;
}
ret = - ENXIO ;
out :
unlock_kernel ( ) ;
return ret ;
}
static ssize_t snd_info_entry_read ( struct file * file , char __user * buffer ,
size_t count , loff_t * offset )
{
2005-11-17 15:58:48 +03:00
struct snd_info_private_data * data ;
2005-04-17 02:20:36 +04:00
struct snd_info_entry * entry ;
2005-11-17 15:58:48 +03:00
struct snd_info_buffer * buf ;
2005-04-17 02:20:36 +04:00
size_t size = 0 ;
loff_t pos ;
data = file - > private_data ;
snd_assert ( data ! = NULL , return - ENXIO ) ;
pos = * offset ;
if ( pos < 0 | | ( long ) pos ! = pos | | ( ssize_t ) count < 0 )
return - EIO ;
if ( ( unsigned long ) pos + ( unsigned long ) count < ( unsigned long ) pos )
return - EIO ;
entry = data - > entry ;
switch ( entry - > content ) {
case SNDRV_INFO_CONTENT_TEXT :
buf = data - > rbuffer ;
if ( buf = = NULL )
return - EIO ;
if ( pos > = buf - > size )
return 0 ;
size = buf - > size - pos ;
size = min ( count , size ) ;
if ( copy_to_user ( buffer , buf - > buffer + pos , size ) )
return - EFAULT ;
break ;
case SNDRV_INFO_CONTENT_DATA :
if ( entry - > c . ops - > read )
size = entry - > c . ops - > read ( entry ,
data - > file_private_data ,
file , buffer , count , pos ) ;
break ;
}
if ( ( ssize_t ) size > 0 )
* offset = pos + size ;
return size ;
}
static ssize_t snd_info_entry_write ( struct file * file , const char __user * buffer ,
size_t count , loff_t * offset )
{
2005-11-17 15:58:48 +03:00
struct snd_info_private_data * data ;
2005-04-17 02:20:36 +04:00
struct snd_info_entry * entry ;
2005-11-17 15:58:48 +03:00
struct snd_info_buffer * buf ;
2006-04-28 17:13:40 +04:00
ssize_t size = 0 ;
2005-04-17 02:20:36 +04:00
loff_t pos ;
data = file - > private_data ;
snd_assert ( data ! = NULL , return - ENXIO ) ;
entry = data - > entry ;
pos = * offset ;
if ( pos < 0 | | ( long ) pos ! = pos | | ( ssize_t ) count < 0 )
return - EIO ;
if ( ( unsigned long ) pos + ( unsigned long ) count < ( unsigned long ) pos )
return - EIO ;
switch ( entry - > content ) {
case SNDRV_INFO_CONTENT_TEXT :
buf = data - > wbuffer ;
if ( buf = = NULL )
return - EIO ;
2006-05-02 17:33:25 +04:00
mutex_lock ( & entry - > access ) ;
2006-04-28 17:13:40 +04:00
if ( pos + count > = buf - > len ) {
if ( resize_info_buffer ( buf , pos + count ) ) {
mutex_unlock ( & entry - > access ) ;
return - ENOMEM ;
}
}
if ( copy_from_user ( buf - > buffer + pos , buffer , count ) ) {
mutex_unlock ( & entry - > access ) ;
2005-04-17 02:20:36 +04:00
return - EFAULT ;
2006-04-28 17:13:40 +04:00
}
buf - > size = pos + count ;
mutex_unlock ( & entry - > access ) ;
size = count ;
2005-04-17 02:20:36 +04:00
break ;
case SNDRV_INFO_CONTENT_DATA :
if ( entry - > c . ops - > write )
size = entry - > c . ops - > write ( entry ,
data - > file_private_data ,
file , buffer , count , pos ) ;
break ;
}
if ( ( ssize_t ) size > 0 )
* offset = pos + size ;
return size ;
}
static int snd_info_entry_open ( struct inode * inode , struct file * file )
{
2005-11-17 15:58:48 +03:00
struct snd_info_entry * entry ;
struct snd_info_private_data * data ;
struct snd_info_buffer * buffer ;
2005-04-17 02:20:36 +04:00
struct proc_dir_entry * p ;
int mode , err ;
2006-01-16 18:29:08 +03:00
mutex_lock ( & info_mutex ) ;
2005-04-17 02:20:36 +04:00
p = PDE ( inode ) ;
2005-11-17 15:58:48 +03:00
entry = p = = NULL ? NULL : ( struct snd_info_entry * ) p - > data ;
2006-06-23 16:37:59 +04:00
if ( entry = = NULL | | ! entry - > p ) {
2006-01-16 18:29:08 +03:00
mutex_unlock ( & info_mutex ) ;
2005-04-17 02:20:36 +04:00
return - ENODEV ;
}
if ( ! try_module_get ( entry - > module ) ) {
err = - EFAULT ;
goto __error1 ;
}
mode = file - > f_flags & O_ACCMODE ;
if ( mode = = O_RDONLY | | mode = = O_RDWR ) {
2006-04-28 17:13:40 +04:00
if ( ( entry - > content = = SNDRV_INFO_CONTENT_DATA & &
2005-04-17 02:20:36 +04:00
entry - > c . ops - > read = = NULL ) ) {
err = - ENODEV ;
goto __error ;
}
}
if ( mode = = O_WRONLY | | mode = = O_RDWR ) {
2006-04-28 17:13:40 +04:00
if ( ( entry - > content = = SNDRV_INFO_CONTENT_DATA & &
2005-04-17 02:20:36 +04:00
entry - > c . ops - > write = = NULL ) ) {
err = - ENODEV ;
goto __error ;
}
}
2005-09-09 16:20:23 +04:00
data = kzalloc ( sizeof ( * data ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( data = = NULL ) {
err = - ENOMEM ;
goto __error ;
}
data - > entry = entry ;
switch ( entry - > content ) {
case SNDRV_INFO_CONTENT_TEXT :
if ( mode = = O_RDONLY | | mode = = O_RDWR ) {
2005-09-09 16:20:23 +04:00
buffer = kzalloc ( sizeof ( * buffer ) , GFP_KERNEL ) ;
2006-04-28 17:13:40 +04:00
if ( buffer = = NULL )
goto __nomem ;
2005-04-17 02:20:36 +04:00
data - > rbuffer = buffer ;
2006-04-28 17:13:40 +04:00
buffer - > len = PAGE_SIZE ;
buffer - > buffer = kmalloc ( buffer - > len , GFP_KERNEL ) ;
if ( buffer - > buffer = = NULL )
goto __nomem ;
2005-04-17 02:20:36 +04:00
}
if ( mode = = O_WRONLY | | mode = = O_RDWR ) {
2005-09-09 16:20:23 +04:00
buffer = kzalloc ( sizeof ( * buffer ) , GFP_KERNEL ) ;
2006-04-28 17:13:40 +04:00
if ( buffer = = NULL )
goto __nomem ;
2005-04-17 02:20:36 +04:00
data - > wbuffer = buffer ;
2006-04-28 17:13:40 +04:00
buffer - > len = PAGE_SIZE ;
buffer - > buffer = kmalloc ( buffer - > len , GFP_KERNEL ) ;
if ( buffer - > buffer = = NULL )
goto __nomem ;
2005-04-17 02:20:36 +04:00
}
break ;
case SNDRV_INFO_CONTENT_DATA : /* data */
if ( entry - > c . ops - > open ) {
if ( ( err = entry - > c . ops - > open ( entry , mode ,
& data - > file_private_data ) ) < 0 ) {
kfree ( data ) ;
goto __error ;
}
}
break ;
}
file - > private_data = data ;
2006-01-16 18:29:08 +03:00
mutex_unlock ( & info_mutex ) ;
2005-04-17 02:20:36 +04:00
if ( entry - > content = = SNDRV_INFO_CONTENT_TEXT & &
( mode = = O_RDONLY | | mode = = O_RDWR ) ) {
if ( entry - > c . text . read ) {
2006-01-16 18:29:08 +03:00
mutex_lock ( & entry - > access ) ;
2005-04-17 02:20:36 +04:00
entry - > c . text . read ( entry , data - > rbuffer ) ;
2006-01-16 18:29:08 +03:00
mutex_unlock ( & entry - > access ) ;
2005-04-17 02:20:36 +04:00
}
}
return 0 ;
2006-04-28 17:13:40 +04:00
__nomem :
if ( data - > rbuffer ) {
kfree ( data - > rbuffer - > buffer ) ;
kfree ( data - > rbuffer ) ;
}
if ( data - > wbuffer ) {
kfree ( data - > wbuffer - > buffer ) ;
kfree ( data - > wbuffer ) ;
}
kfree ( data ) ;
err = - ENOMEM ;
2005-04-17 02:20:36 +04:00
__error :
module_put ( entry - > module ) ;
__error1 :
2006-01-16 18:29:08 +03:00
mutex_unlock ( & info_mutex ) ;
2005-04-17 02:20:36 +04:00
return err ;
}
static int snd_info_entry_release ( struct inode * inode , struct file * file )
{
2005-11-17 15:58:48 +03:00
struct snd_info_entry * entry ;
struct snd_info_private_data * data ;
2005-04-17 02:20:36 +04:00
int mode ;
mode = file - > f_flags & O_ACCMODE ;
data = file - > private_data ;
entry = data - > entry ;
switch ( entry - > content ) {
case SNDRV_INFO_CONTENT_TEXT :
2006-04-28 17:13:40 +04:00
if ( data - > rbuffer ) {
kfree ( data - > rbuffer - > buffer ) ;
2005-04-17 02:20:36 +04:00
kfree ( data - > rbuffer ) ;
}
2006-04-28 17:13:40 +04:00
if ( data - > wbuffer ) {
2005-04-17 02:20:36 +04:00
if ( entry - > c . text . write ) {
entry - > c . text . write ( entry , data - > wbuffer ) ;
if ( data - > wbuffer - > error ) {
snd_printk ( KERN_WARNING " data write error to %s (%i) \n " ,
entry - > name ,
data - > wbuffer - > error ) ;
}
}
2006-04-28 17:13:40 +04:00
kfree ( data - > wbuffer - > buffer ) ;
2005-04-17 02:20:36 +04:00
kfree ( data - > wbuffer ) ;
}
break ;
case SNDRV_INFO_CONTENT_DATA :
if ( entry - > c . ops - > release )
entry - > c . ops - > release ( entry , mode ,
data - > file_private_data ) ;
break ;
}
module_put ( entry - > module ) ;
kfree ( data ) ;
return 0 ;
}
static unsigned int snd_info_entry_poll ( struct file * file , poll_table * wait )
{
2005-11-17 15:58:48 +03:00
struct snd_info_private_data * data ;
2005-04-17 02:20:36 +04:00
struct snd_info_entry * entry ;
unsigned int mask ;
data = file - > private_data ;
if ( data = = NULL )
return 0 ;
entry = data - > entry ;
mask = 0 ;
switch ( entry - > content ) {
case SNDRV_INFO_CONTENT_DATA :
if ( entry - > c . ops - > poll )
return entry - > c . ops - > poll ( entry ,
data - > file_private_data ,
file , wait ) ;
if ( entry - > c . ops - > read )
mask | = POLLIN | POLLRDNORM ;
if ( entry - > c . ops - > write )
mask | = POLLOUT | POLLWRNORM ;
break ;
}
return mask ;
}
2006-01-09 18:44:46 +03:00
static long snd_info_entry_ioctl ( struct file * file , unsigned int cmd ,
unsigned long arg )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:58:48 +03:00
struct snd_info_private_data * data ;
2005-04-17 02:20:36 +04:00
struct snd_info_entry * entry ;
data = file - > private_data ;
if ( data = = NULL )
return 0 ;
entry = data - > entry ;
switch ( entry - > content ) {
case SNDRV_INFO_CONTENT_DATA :
if ( entry - > c . ops - > ioctl )
return entry - > c . ops - > ioctl ( entry ,
data - > file_private_data ,
file , cmd , arg ) ;
break ;
}
return - ENOTTY ;
}
static int snd_info_entry_mmap ( struct file * file , struct vm_area_struct * vma )
{
struct inode * inode = file - > f_dentry - > d_inode ;
2005-11-17 15:58:48 +03:00
struct snd_info_private_data * data ;
2005-04-17 02:20:36 +04:00
struct snd_info_entry * entry ;
data = file - > private_data ;
if ( data = = NULL )
return 0 ;
entry = data - > entry ;
switch ( entry - > content ) {
case SNDRV_INFO_CONTENT_DATA :
if ( entry - > c . ops - > mmap )
return entry - > c . ops - > mmap ( entry ,
data - > file_private_data ,
inode , file , vma ) ;
break ;
}
return - ENXIO ;
}
static struct file_operations snd_info_entry_operations =
{
2006-01-09 18:44:46 +03:00
. owner = THIS_MODULE ,
. llseek = snd_info_entry_llseek ,
. read = snd_info_entry_read ,
. write = snd_info_entry_write ,
. poll = snd_info_entry_poll ,
. unlocked_ioctl = snd_info_entry_ioctl ,
. mmap = snd_info_entry_mmap ,
. open = snd_info_entry_open ,
. release = snd_info_entry_release ,
2005-04-17 02:20:36 +04:00
} ;
/**
* snd_create_proc_entry - create a procfs entry
* @ name : the name of the proc file
* @ mode : the file permission bits , S_Ixxx
* @ parent : the parent proc - directory entry
*
* Creates a new proc file entry with the given name and permission
* on the given directory .
*
* Returns the pointer of new instance or NULL on failure .
*/
static struct proc_dir_entry * snd_create_proc_entry ( const char * name , mode_t mode ,
struct proc_dir_entry * parent )
{
struct proc_dir_entry * p ;
p = create_proc_entry ( name , mode , parent ) ;
if ( p )
snd_info_entry_prepare ( p ) ;
return p ;
}
int __init snd_info_init ( void )
{
struct proc_dir_entry * p ;
p = snd_create_proc_entry ( " asound " , S_IFDIR | S_IRUGO | S_IXUGO , & proc_root ) ;
if ( p = = NULL )
return - ENOMEM ;
snd_proc_root = p ;
# ifdef CONFIG_SND_OSSEMUL
{
2005-11-17 15:58:48 +03:00
struct snd_info_entry * entry ;
2005-04-17 02:20:36 +04:00
if ( ( entry = snd_info_create_module_entry ( THIS_MODULE , " oss " , NULL ) ) = = NULL )
return - ENOMEM ;
entry - > mode = S_IFDIR | S_IRUGO | S_IXUGO ;
if ( snd_info_register ( entry ) < 0 ) {
snd_info_free_entry ( entry ) ;
return - ENOMEM ;
}
snd_oss_root = entry ;
}
# endif
# if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
{
2005-11-17 15:58:48 +03:00
struct snd_info_entry * entry ;
2005-04-17 02:20:36 +04:00
if ( ( entry = snd_info_create_module_entry ( THIS_MODULE , " seq " , NULL ) ) = = NULL )
return - ENOMEM ;
entry - > mode = S_IFDIR | S_IRUGO | S_IXUGO ;
if ( snd_info_register ( entry ) < 0 ) {
snd_info_free_entry ( entry ) ;
return - ENOMEM ;
}
snd_seq_root = entry ;
}
# endif
snd_info_version_init ( ) ;
snd_minor_info_init ( ) ;
snd_minor_info_oss_init ( ) ;
snd_card_info_init ( ) ;
return 0 ;
}
int __exit snd_info_done ( void )
{
snd_card_info_done ( ) ;
snd_minor_info_oss_done ( ) ;
snd_minor_info_done ( ) ;
snd_info_version_done ( ) ;
if ( snd_proc_root ) {
# if defined(CONFIG_SND_SEQUENCER) || defined(CONFIG_SND_SEQUENCER_MODULE)
2006-06-23 16:37:59 +04:00
snd_info_free_entry ( snd_seq_root ) ;
2005-04-17 02:20:36 +04:00
# endif
# ifdef CONFIG_SND_OSSEMUL
2006-06-23 16:37:59 +04:00
snd_info_free_entry ( snd_oss_root ) ;
2005-04-17 02:20:36 +04:00
# endif
snd_remove_proc_entry ( & proc_root , snd_proc_root ) ;
}
return 0 ;
}
/*
*/
/*
* create a card proc file
* called from init . c
*/
2005-11-17 15:58:48 +03:00
int snd_info_card_create ( struct snd_card * card )
2005-04-17 02:20:36 +04:00
{
char str [ 8 ] ;
2005-11-17 15:58:48 +03:00
struct snd_info_entry * entry ;
2005-04-17 02:20:36 +04:00
snd_assert ( card ! = NULL , return - ENXIO ) ;
sprintf ( str , " card%i " , card - > number ) ;
if ( ( entry = snd_info_create_module_entry ( card - > module , str , NULL ) ) = = NULL )
return - ENOMEM ;
entry - > mode = S_IFDIR | S_IRUGO | S_IXUGO ;
if ( snd_info_register ( entry ) < 0 ) {
snd_info_free_entry ( entry ) ;
return - ENOMEM ;
}
card - > proc_root = entry ;
return 0 ;
}
/*
* register the card proc file
* called from init . c
*/
2005-11-17 15:58:48 +03:00
int snd_info_card_register ( struct snd_card * card )
2005-04-17 02:20:36 +04:00
{
struct proc_dir_entry * p ;
snd_assert ( card ! = NULL , return - ENXIO ) ;
if ( ! strcmp ( card - > id , card - > proc_root - > name ) )
return 0 ;
p = proc_symlink ( card - > id , snd_proc_root , card - > proc_root - > name ) ;
if ( p = = NULL )
return - ENOMEM ;
card - > proc_root_link = p ;
return 0 ;
}
/*
* de - register the card proc file
* called from init . c
*/
2006-06-23 16:37:59 +04:00
void snd_info_card_disconnect ( struct snd_card * card )
2005-04-17 02:20:36 +04:00
{
2006-06-23 16:37:59 +04:00
snd_assert ( card ! = NULL , return ) ;
mutex_lock ( & info_mutex ) ;
2005-04-17 02:20:36 +04:00
if ( card - > proc_root_link ) {
snd_remove_proc_entry ( snd_proc_root , card - > proc_root_link ) ;
card - > proc_root_link = NULL ;
}
2006-06-23 16:37:59 +04:00
if ( card - > proc_root )
snd_info_disconnect ( card - > proc_root ) ;
mutex_unlock ( & info_mutex ) ;
}
/*
* release the card proc file resources
* called from init . c
*/
int snd_info_card_free ( struct snd_card * card )
{
snd_assert ( card ! = NULL , return - ENXIO ) ;
snd_info_free_entry ( card - > proc_root ) ;
card - > proc_root = NULL ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
/**
* snd_info_get_line - read one line from the procfs buffer
* @ buffer : the procfs buffer
* @ line : the buffer to store
* @ len : the max . buffer size - 1
*
* Reads one line from the buffer and stores the string .
*
* Returns zero if successful , or 1 if error or EOF .
*/
2005-11-17 15:58:48 +03:00
int snd_info_get_line ( struct snd_info_buffer * buffer , char * line , int len )
2005-04-17 02:20:36 +04:00
{
int c = - 1 ;
if ( len < = 0 | | buffer - > stop | | buffer - > error )
return 1 ;
while ( - - len > 0 ) {
2006-04-28 17:13:40 +04:00
c = buffer - > buffer [ buffer - > curr + + ] ;
2005-04-17 02:20:36 +04:00
if ( c = = ' \n ' ) {
2006-04-28 17:13:40 +04:00
if ( buffer - > curr > = buffer - > size )
2005-04-17 02:20:36 +04:00
buffer - > stop = 1 ;
break ;
}
* line + + = c ;
2006-04-28 17:13:40 +04:00
if ( buffer - > curr > = buffer - > size ) {
2005-04-17 02:20:36 +04:00
buffer - > stop = 1 ;
break ;
}
}
while ( c ! = ' \n ' & & ! buffer - > stop ) {
2006-04-28 17:13:40 +04:00
c = buffer - > buffer [ buffer - > curr + + ] ;
if ( buffer - > curr > = buffer - > size )
2005-04-17 02:20:36 +04:00
buffer - > stop = 1 ;
}
* line = ' \0 ' ;
return 0 ;
}
2006-04-28 17:13:39 +04:00
EXPORT_SYMBOL ( snd_info_get_line ) ;
2005-04-17 02:20:36 +04:00
/**
2005-07-08 15:53:42 +04:00
* snd_info_get_str - parse a string token
2005-04-17 02:20:36 +04:00
* @ dest : the buffer to store the string token
* @ src : the original string
* @ len : the max . length of token - 1
*
* Parses the original string and copy a token to the given
* string buffer .
*
* Returns the updated pointer of the original string so that
* it can be used for the next call .
*/
char * snd_info_get_str ( char * dest , char * src , int len )
{
int c ;
while ( * src = = ' ' | | * src = = ' \t ' )
src + + ;
if ( * src = = ' " ' | | * src = = ' \' ' ) {
c = * src + + ;
while ( - - len > 0 & & * src & & * src ! = c ) {
* dest + + = * src + + ;
}
if ( * src = = c )
src + + ;
} else {
while ( - - len > 0 & & * src & & * src ! = ' ' & & * src ! = ' \t ' ) {
* dest + + = * src + + ;
}
}
* dest = 0 ;
while ( * src = = ' ' | | * src = = ' \t ' )
src + + ;
return src ;
}
2006-04-28 17:13:39 +04:00
EXPORT_SYMBOL ( snd_info_get_str ) ;
2005-04-17 02:20:36 +04:00
/**
* snd_info_create_entry - create an info entry
* @ name : the proc file name
*
* Creates an info entry with the given file name and initializes as
* the default state .
*
* Usually called from other functions such as
* snd_info_create_card_entry ( ) .
*
* Returns the pointer of the new instance , or NULL on failure .
*/
2005-11-17 15:58:48 +03:00
static struct snd_info_entry * snd_info_create_entry ( const char * name )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:58:48 +03:00
struct snd_info_entry * entry ;
2005-09-09 16:20:23 +04:00
entry = kzalloc ( sizeof ( * entry ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( entry = = NULL )
return NULL ;
2005-06-23 11:09:02 +04:00
entry - > name = kstrdup ( name , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( entry - > name = = NULL ) {
kfree ( entry ) ;
return NULL ;
}
entry - > mode = S_IFREG | S_IRUGO ;
entry - > content = SNDRV_INFO_CONTENT_TEXT ;
2006-01-16 18:29:08 +03:00
mutex_init ( & entry - > access ) ;
2006-06-23 16:37:59 +04:00
INIT_LIST_HEAD ( & entry - > children ) ;
INIT_LIST_HEAD ( & entry - > list ) ;
2005-04-17 02:20:36 +04:00
return entry ;
}
/**
* snd_info_create_module_entry - create an info entry for the given module
* @ module : the module pointer
* @ name : the file name
* @ parent : the parent directory
*
* Creates a new info entry and assigns it to the given module .
*
* Returns the pointer of the new instance , or NULL on failure .
*/
2005-11-17 15:58:48 +03:00
struct snd_info_entry * snd_info_create_module_entry ( struct module * module ,
2005-04-17 02:20:36 +04:00
const char * name ,
2005-11-17 15:58:48 +03:00
struct snd_info_entry * parent )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:58:48 +03:00
struct snd_info_entry * entry = snd_info_create_entry ( name ) ;
2005-04-17 02:20:36 +04:00
if ( entry ) {
entry - > module = module ;
entry - > parent = parent ;
}
return entry ;
}
2006-04-28 17:13:39 +04:00
EXPORT_SYMBOL ( snd_info_create_module_entry ) ;
2005-04-17 02:20:36 +04:00
/**
* snd_info_create_card_entry - create an info entry for the given card
* @ card : the card instance
* @ name : the file name
* @ parent : the parent directory
*
* Creates a new info entry and assigns it to the given card .
*
* Returns the pointer of the new instance , or NULL on failure .
*/
2005-11-17 15:58:48 +03:00
struct snd_info_entry * snd_info_create_card_entry ( struct snd_card * card ,
2005-04-17 02:20:36 +04:00
const char * name ,
2005-11-17 15:58:48 +03:00
struct snd_info_entry * parent )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:58:48 +03:00
struct snd_info_entry * entry = snd_info_create_entry ( name ) ;
2005-04-17 02:20:36 +04:00
if ( entry ) {
entry - > module = card - > module ;
entry - > card = card ;
entry - > parent = parent ;
}
return entry ;
}
2006-04-28 17:13:39 +04:00
EXPORT_SYMBOL ( snd_info_create_card_entry ) ;
2006-06-23 16:37:59 +04:00
static void snd_info_disconnect ( struct snd_info_entry * entry )
2005-04-17 02:20:36 +04:00
{
2006-06-23 16:37:59 +04:00
struct list_head * p , * n ;
struct proc_dir_entry * root ;
2005-04-17 02:20:36 +04:00
2006-06-23 16:37:59 +04:00
list_for_each_safe ( p , n , & entry - > children ) {
snd_info_disconnect ( list_entry ( p , struct snd_info_entry , list ) ) ;
}
if ( ! entry - > p )
return ;
list_del_init ( & entry - > list ) ;
root = entry - > parent = = NULL ? snd_proc_root : entry - > parent - > p ;
snd_assert ( root , return ) ;
snd_remove_proc_entry ( root , entry - > p ) ;
entry - > p = NULL ;
2005-04-17 02:20:36 +04:00
}
2006-06-23 16:37:59 +04:00
static int snd_info_dev_free_entry ( struct snd_device * device )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:58:48 +03:00
struct snd_info_entry * entry = device - > device_data ;
2006-06-23 16:37:59 +04:00
snd_info_free_entry ( entry ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2006-06-23 16:37:59 +04:00
static int snd_info_dev_register_entry ( struct snd_device * device )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:58:48 +03:00
struct snd_info_entry * entry = device - > device_data ;
2006-06-23 16:37:59 +04:00
return snd_info_register ( entry ) ;
2005-04-17 02:20:36 +04:00
}
/**
* snd_card_proc_new - create an info entry for the given card
* @ card : the card instance
* @ name : the file name
* @ entryp : the pointer to store the new info entry
*
* Creates a new info entry and assigns it to the given card .
* Unlike snd_info_create_card_entry ( ) , this function registers the
* info entry as an ALSA device component , so that it can be
* unregistered / released without explicit call .
* Also , you don ' t have to register this entry via snd_info_register ( ) ,
* since this will be registered by snd_card_register ( ) automatically .
*
* The parent is assumed as card - > proc_root .
*
* For releasing this entry , use snd_device_free ( ) instead of
* snd_info_free_entry ( ) .
*
* Returns zero if successful , or a negative error code on failure .
*/
2005-11-17 15:58:48 +03:00
int snd_card_proc_new ( struct snd_card * card , const char * name ,
struct snd_info_entry * * entryp )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:58:48 +03:00
static struct snd_device_ops ops = {
2005-04-17 02:20:36 +04:00
. dev_free = snd_info_dev_free_entry ,
. dev_register = snd_info_dev_register_entry ,
2006-06-23 16:37:59 +04:00
/* disconnect is done via snd_info_card_disconnect() */
2005-04-17 02:20:36 +04:00
} ;
2005-11-17 15:58:48 +03:00
struct snd_info_entry * entry ;
2005-04-17 02:20:36 +04:00
int err ;
entry = snd_info_create_card_entry ( card , name , card - > proc_root ) ;
if ( ! entry )
return - ENOMEM ;
if ( ( err = snd_device_new ( card , SNDRV_DEV_INFO , entry , & ops ) ) < 0 ) {
snd_info_free_entry ( entry ) ;
return err ;
}
if ( entryp )
* entryp = entry ;
return 0 ;
}
2006-04-28 17:13:39 +04:00
EXPORT_SYMBOL ( snd_card_proc_new ) ;
2005-04-17 02:20:36 +04:00
/**
* snd_info_free_entry - release the info entry
* @ entry : the info entry
*
* Releases the info entry . Don ' t call this after registered .
*/
2005-11-17 15:58:48 +03:00
void snd_info_free_entry ( struct snd_info_entry * entry )
2005-04-17 02:20:36 +04:00
{
if ( entry = = NULL )
return ;
2006-06-23 16:37:59 +04:00
if ( entry - > p ) {
mutex_lock ( & info_mutex ) ;
snd_info_disconnect ( entry ) ;
mutex_unlock ( & info_mutex ) ;
}
2005-04-17 02:20:36 +04:00
kfree ( entry - > name ) ;
if ( entry - > private_free )
entry - > private_free ( entry ) ;
kfree ( entry ) ;
}
2006-04-28 17:13:39 +04:00
EXPORT_SYMBOL ( snd_info_free_entry ) ;
2005-04-17 02:20:36 +04:00
/**
* snd_info_register - register the info entry
* @ entry : the info entry
*
* Registers the proc info entry .
*
* Returns zero if successful , or a negative error code on failure .
*/
2005-11-17 15:58:48 +03:00
int snd_info_register ( struct snd_info_entry * entry )
2005-04-17 02:20:36 +04:00
{
struct proc_dir_entry * root , * p = NULL ;
snd_assert ( entry ! = NULL , return - ENXIO ) ;
root = entry - > parent = = NULL ? snd_proc_root : entry - > parent - > p ;
2006-01-16 18:29:08 +03:00
mutex_lock ( & info_mutex ) ;
2005-04-17 02:20:36 +04:00
p = snd_create_proc_entry ( entry - > name , entry - > mode , root ) ;
if ( ! p ) {
2006-01-16 18:29:08 +03:00
mutex_unlock ( & info_mutex ) ;
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
}
p - > owner = entry - > module ;
if ( ! S_ISDIR ( entry - > mode ) )
p - > proc_fops = & snd_info_entry_operations ;
p - > size = entry - > size ;
p - > data = entry ;
entry - > p = p ;
2006-06-23 16:37:59 +04:00
if ( entry - > parent )
list_add_tail ( & entry - > list , & entry - > parent - > children ) ;
2006-01-16 18:29:08 +03:00
mutex_unlock ( & info_mutex ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2006-04-28 17:13:39 +04:00
EXPORT_SYMBOL ( snd_info_register ) ;
2005-04-17 02:20:36 +04:00
/*
*/
2006-05-17 19:14:51 +04:00
static struct snd_info_entry * snd_info_version_entry ;
2005-04-17 02:20:36 +04:00
2005-11-17 15:58:48 +03:00
static void snd_info_version_read ( struct snd_info_entry * entry , struct snd_info_buffer * buffer )
2005-04-17 02:20:36 +04:00
{
snd_iprintf ( buffer ,
" Advanced Linux Sound Architecture Driver Version "
CONFIG_SND_VERSION CONFIG_SND_DATE " . \n "
) ;
}
static int __init snd_info_version_init ( void )
{
2005-11-17 15:58:48 +03:00
struct snd_info_entry * entry ;
2005-04-17 02:20:36 +04:00
entry = snd_info_create_module_entry ( THIS_MODULE , " version " , NULL ) ;
if ( entry = = NULL )
return - ENOMEM ;
entry - > c . text . read = snd_info_version_read ;
if ( snd_info_register ( entry ) < 0 ) {
snd_info_free_entry ( entry ) ;
return - ENOMEM ;
}
snd_info_version_entry = entry ;
return 0 ;
}
static int __exit snd_info_version_done ( void )
{
2006-06-23 16:37:59 +04:00
snd_info_free_entry ( snd_info_version_entry ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
# endif /* CONFIG_PROC_FS */