2005-04-17 02:20:36 +04:00
/*
* Initialization routines
2007-10-15 11:50:19 +04:00
* Copyright ( c ) by Jaroslav Kysela < perex @ perex . cz >
2005-04-17 02:20:36 +04:00
*
*
* 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 <linux/init.h>
# include <linux/sched.h>
2011-07-15 20:38:28 +04:00
# include <linux/module.h>
2012-01-22 20:23:42 +04:00
# include <linux/device.h>
2005-04-17 02:20:36 +04:00
# include <linux/file.h>
# include <linux/slab.h>
# include <linux/time.h>
# include <linux/ctype.h>
# include <linux/pm.h>
2005-10-29 22:07:23 +04:00
2005-04-17 02:20:36 +04:00
# include <sound/core.h>
# include <sound/control.h>
# include <sound/info.h>
2009-09-07 17:50:18 +04:00
/* monitor files for graceful shutdown (hotplug) */
struct snd_monitor_file {
struct file * file ;
const struct file_operations * disconnected_f_op ;
struct list_head shutdown_list ; /* still need to shutdown */
struct list_head list ; /* link of monitor files */
} ;
2006-10-06 18:08:27 +04:00
static DEFINE_SPINLOCK ( shutdown_lock ) ;
static LIST_HEAD ( shutdown_files ) ;
2007-02-12 11:55:37 +03:00
static const struct file_operations snd_shutdown_f_ops ;
2005-04-17 02:20:36 +04:00
2006-05-17 19:14:51 +04:00
static unsigned int snd_cards_lock ; /* locked for registering/using */
struct snd_card * snd_cards [ SNDRV_CARDS ] ;
2006-04-28 17:13:39 +04:00
EXPORT_SYMBOL ( snd_cards ) ;
2006-05-15 21:49:05 +04:00
static DEFINE_MUTEX ( snd_card_mutex ) ;
2005-04-17 02:20:36 +04:00
2007-10-26 17:10:15 +04:00
static char * slots [ SNDRV_CARDS ] ;
module_param_array ( slots , charp , NULL , 0444 ) ;
MODULE_PARM_DESC ( slots , " Module names assigned to the slots. " ) ;
2008-05-27 19:59:24 +04:00
/* return non-zero if the given index is reserved for the given
2007-10-26 17:10:15 +04:00
* module via slots option
*/
2008-05-27 19:59:24 +04:00
static int module_slot_match ( struct module * module , int idx )
2007-10-26 17:10:15 +04:00
{
2008-05-27 19:59:24 +04:00
int match = 1 ;
2007-10-26 17:10:15 +04:00
# ifdef MODULE
2008-05-27 19:59:24 +04:00
const char * s1 , * s2 ;
2007-10-26 17:10:15 +04:00
if ( ! module | | ! module - > name | | ! slots [ idx ] )
return 0 ;
2008-05-27 19:59:24 +04:00
s1 = module - > name ;
s2 = slots [ idx ] ;
if ( * s2 = = ' ! ' ) {
match = 0 ; /* negative match */
s2 + + ;
}
2007-10-26 17:10:15 +04:00
/* compare module name strings
* hyphens are handled as equivalent with underscore
*/
for ( ; ; ) {
char c1 = * s1 + + ;
char c2 = * s2 + + ;
if ( c1 = = ' - ' )
c1 = ' _ ' ;
if ( c2 = = ' - ' )
c2 = ' _ ' ;
if ( c1 ! = c2 )
2008-05-27 19:59:24 +04:00
return ! match ;
2007-10-26 17:10:15 +04:00
if ( ! c1 )
break ;
}
2008-05-27 19:59:24 +04:00
# endif /* MODULE */
return match ;
2007-10-26 17:10:15 +04:00
}
2005-04-17 02:20:36 +04:00
# if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
2005-11-17 15:51:18 +03:00
int ( * snd_mixer_oss_notify_callback ) ( struct snd_card * card , int free_flag ) ;
2006-04-28 17:13:39 +04:00
EXPORT_SYMBOL ( snd_mixer_oss_notify_callback ) ;
2005-04-17 02:20:36 +04:00
# endif
2005-12-01 12:42:42 +03:00
# ifdef CONFIG_PROC_FS
2005-11-17 15:51:18 +03:00
static void snd_card_id_read ( struct snd_info_entry * entry ,
struct snd_info_buffer * buffer )
2005-04-17 02:20:36 +04:00
{
snd_iprintf ( buffer , " %s \n " , entry - > card - > id ) ;
}
2005-12-01 12:42:42 +03:00
static inline int init_info_for_card ( struct snd_card * card )
{
int err ;
struct snd_info_entry * entry ;
if ( ( err = snd_info_card_register ( card ) ) < 0 ) {
snd_printd ( " unable to create card info \n " ) ;
return err ;
}
if ( ( entry = snd_info_create_card_entry ( card , " id " , card - > proc_root ) ) = = NULL ) {
snd_printd ( " unable to create card entry \n " ) ;
return err ;
}
entry - > c . text . read = snd_card_id_read ;
if ( snd_info_register ( entry ) < 0 ) {
snd_info_free_entry ( entry ) ;
entry = NULL ;
}
card - > proc_id = entry ;
return 0 ;
}
# else /* !CONFIG_PROC_FS */
# define init_info_for_card(card)
# endif
2005-04-17 02:20:36 +04:00
/**
2008-12-28 18:32:08 +03:00
* snd_card_create - create and initialize a soundcard structure
2005-04-17 02:20:36 +04:00
* @ idx : card index ( address ) [ 0 . . . ( SNDRV_CARDS - 1 ) ]
* @ xid : card identification ( ASCII string )
* @ module : top level module for locking
* @ extra_size : allocate this extra size after the main soundcard structure
2008-12-28 18:32:08 +03:00
* @ card_ret : the pointer to store the created card instance
2005-04-17 02:20:36 +04:00
*
* Creates and initializes a soundcard structure .
*
2008-12-28 18:32:08 +03:00
* The function allocates snd_card instance via kzalloc with the given
* space for the driver to use freely . The allocated struct is stored
* in the given card_ret pointer .
*
* Returns zero if successful or a negative error code .
2005-04-17 02:20:36 +04:00
*/
2008-12-28 18:32:08 +03:00
int snd_card_create ( int idx , const char * xid ,
struct module * module , int extra_size ,
struct snd_card * * card_ret )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:51:18 +03:00
struct snd_card * card ;
2008-05-27 19:59:24 +04:00
int err , idx2 ;
2005-04-17 02:20:36 +04:00
2008-12-28 18:32:08 +03:00
if ( snd_BUG_ON ( ! card_ret ) )
return - EINVAL ;
* card_ret = NULL ;
2005-04-17 02:20:36 +04:00
if ( extra_size < 0 )
extra_size = 0 ;
2005-09-09 16:20:23 +04:00
card = kzalloc ( sizeof ( * card ) + extra_size , GFP_KERNEL ) ;
2008-12-28 18:32:08 +03:00
if ( ! card )
return - ENOMEM ;
2009-06-02 14:02:38 +04:00
if ( xid )
2009-06-04 02:12:18 +04:00
strlcpy ( card - > id , xid , sizeof ( card - > id ) ) ;
2005-04-17 02:20:36 +04:00
err = 0 ;
2006-05-15 21:49:05 +04:00
mutex_lock ( & snd_card_mutex ) ;
2005-04-17 02:20:36 +04:00
if ( idx < 0 ) {
for ( idx2 = 0 ; idx2 < SNDRV_CARDS ; idx2 + + )
2007-01-16 19:49:21 +03:00
/* idx == -1 == 0xffff means: take any free slot */
2005-04-17 02:20:36 +04:00
if ( ~ snd_cards_lock & idx & 1 < < idx2 ) {
2008-05-27 19:59:24 +04:00
if ( module_slot_match ( module , idx2 ) ) {
idx = idx2 ;
break ;
}
}
}
if ( idx < 0 ) {
for ( idx2 = 0 ; idx2 < SNDRV_CARDS ; idx2 + + )
/* idx == -1 == 0xffff means: take any free slot */
if ( ~ snd_cards_lock & idx & 1 < < idx2 ) {
if ( ! slots [ idx2 ] | | ! * slots [ idx2 ] ) {
idx = idx2 ;
break ;
}
2005-04-17 02:20:36 +04:00
}
2007-01-16 19:49:21 +03:00
}
2008-05-27 19:59:24 +04:00
if ( idx < 0 )
err = - ENODEV ;
else if ( idx < snd_ecards_limit ) {
if ( snd_cards_lock & ( 1 < < idx ) )
err = - EBUSY ; /* invalid */
} else if ( idx > = SNDRV_CARDS )
err = - ENODEV ;
if ( err < 0 ) {
2006-05-15 21:49:05 +04:00
mutex_unlock ( & snd_card_mutex ) ;
2007-01-16 19:49:21 +03:00
snd_printk ( KERN_ERR " cannot find the slot for index %d (range 0-%i), error: %d \n " ,
idx , snd_ecards_limit - 1 , err ) ;
2005-04-17 02:20:36 +04:00
goto __error ;
}
snd_cards_lock | = 1 < < idx ; /* lock it */
2008-05-27 19:59:24 +04:00
if ( idx > = snd_ecards_limit )
snd_ecards_limit = idx + 1 ; /* increase the limit */
2006-05-15 21:49:05 +04:00
mutex_unlock ( & snd_card_mutex ) ;
2005-04-17 02:20:36 +04:00
card - > number = idx ;
card - > module = module ;
INIT_LIST_HEAD ( & card - > devices ) ;
init_rwsem ( & card - > controls_rwsem ) ;
rwlock_init ( & card - > ctl_files_rwlock ) ;
INIT_LIST_HEAD ( & card - > controls ) ;
INIT_LIST_HEAD ( & card - > ctl_files ) ;
spin_lock_init ( & card - > files_lock ) ;
2009-02-23 18:35:21 +03:00
INIT_LIST_HEAD ( & card - > files_list ) ;
2005-04-17 02:20:36 +04:00
init_waitqueue_head ( & card - > shutdown_sleep ) ;
2012-10-16 15:05:59 +04:00
atomic_set ( & card - > refcount , 0 ) ;
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_PM
2006-01-16 18:29:08 +03:00
mutex_init ( & card - > power_lock ) ;
2005-04-17 02:20:36 +04:00
init_waitqueue_head ( & card - > power_sleep ) ;
# endif
/* the control interface cannot be accessed from the user space until */
/* snd_cards_bitmask and snd_cards are set with snd_card_register */
2008-12-28 18:32:08 +03:00
err = snd_ctl_create ( card ) ;
if ( err < 0 ) {
snd_printk ( KERN_ERR " unable to register control minors \n " ) ;
2005-04-17 02:20:36 +04:00
goto __error ;
}
2008-12-28 18:32:08 +03:00
err = snd_info_card_create ( card ) ;
if ( err < 0 ) {
snd_printk ( KERN_ERR " unable to create card info \n " ) ;
2005-04-17 02:20:36 +04:00
goto __error_ctl ;
}
if ( extra_size > 0 )
2005-11-17 15:51:18 +03:00
card - > private_data = ( char * ) card + sizeof ( struct snd_card ) ;
2008-12-28 18:32:08 +03:00
* card_ret = card ;
return 0 ;
2005-04-17 02:20:36 +04:00
__error_ctl :
snd_device_free_all ( card , SNDRV_DEV_CMD_PRE ) ;
__error :
kfree ( card ) ;
2008-12-28 18:32:08 +03:00
return err ;
2005-04-17 02:20:36 +04:00
}
2008-12-28 18:32:08 +03:00
EXPORT_SYMBOL ( snd_card_create ) ;
2006-04-28 17:13:39 +04:00
2006-05-15 21:49:05 +04:00
/* return non-zero if a card is already locked */
int snd_card_locked ( int card )
{
int locked ;
mutex_lock ( & snd_card_mutex ) ;
locked = snd_cards_lock & ( 1 < < card ) ;
mutex_unlock ( & snd_card_mutex ) ;
return locked ;
}
2006-03-03 16:08:43 +03:00
static loff_t snd_disconnect_llseek ( struct file * file , loff_t offset , int orig )
{
return - ENODEV ;
}
static ssize_t snd_disconnect_read ( struct file * file , char __user * buf ,
size_t count , loff_t * offset )
{
return - ENODEV ;
}
static ssize_t snd_disconnect_write ( struct file * file , const char __user * buf ,
size_t count , loff_t * offset )
{
return - ENODEV ;
}
2006-10-06 18:08:27 +04:00
static int snd_disconnect_release ( struct inode * inode , struct file * file )
{
struct snd_monitor_file * df = NULL , * _df ;
spin_lock ( & shutdown_lock ) ;
list_for_each_entry ( _df , & shutdown_files , shutdown_list ) {
if ( _df - > file = = file ) {
df = _df ;
2009-02-23 18:35:21 +03:00
list_del_init ( & df - > shutdown_list ) ;
2006-10-06 18:08:27 +04:00
break ;
}
}
spin_unlock ( & shutdown_lock ) ;
2008-11-01 02:28:30 +03:00
if ( likely ( df ) ) {
if ( ( file - > f_flags & FASYNC ) & & df - > disconnected_f_op - > fasync )
df - > disconnected_f_op - > fasync ( - 1 , file , 0 ) ;
2006-10-06 18:08:27 +04:00
return df - > disconnected_f_op - > release ( inode , file ) ;
2008-11-01 02:28:30 +03:00
}
2006-10-06 18:08:27 +04:00
2008-03-04 02:32:18 +03:00
panic ( " %s(%p, %p) failed! " , __func__ , inode , file ) ;
2006-10-06 18:08:27 +04:00
}
2005-04-17 02:20:36 +04:00
static unsigned int snd_disconnect_poll ( struct file * file , poll_table * wait )
{
return POLLERR | POLLNVAL ;
}
2006-03-03 16:08:43 +03:00
static long snd_disconnect_ioctl ( struct file * file ,
unsigned int cmd , unsigned long arg )
{
return - ENODEV ;
}
static int snd_disconnect_mmap ( struct file * file , struct vm_area_struct * vma )
{
return - ENODEV ;
}
static int snd_disconnect_fasync ( int fd , struct file * file , int on )
{
return - ENODEV ;
}
2007-02-12 11:55:37 +03:00
static const struct file_operations snd_shutdown_f_ops =
2006-10-06 18:08:27 +04:00
{
. owner = THIS_MODULE ,
. llseek = snd_disconnect_llseek ,
. read = snd_disconnect_read ,
. write = snd_disconnect_write ,
. release = snd_disconnect_release ,
. poll = snd_disconnect_poll ,
. unlocked_ioctl = snd_disconnect_ioctl ,
# ifdef CONFIG_COMPAT
. compat_ioctl = snd_disconnect_ioctl ,
# endif
. mmap = snd_disconnect_mmap ,
. fasync = snd_disconnect_fasync
} ;
2005-04-17 02:20:36 +04:00
/**
* snd_card_disconnect - disconnect all APIs from the file - operations ( user space )
* @ card : soundcard structure
*
* Disconnects all APIs from the file - operations ( user space ) .
*
* Returns zero , otherwise a negative error code .
*
* Note : The current implementation replaces all active file - > f_op with special
* dummy file operations ( they do nothing except release ) .
*/
2005-11-17 15:51:18 +03:00
int snd_card_disconnect ( struct snd_card * card )
2005-04-17 02:20:36 +04:00
{
struct snd_monitor_file * mfile ;
int err ;
2008-04-17 14:52:02 +04:00
if ( ! card )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
spin_lock ( & card - > files_lock ) ;
if ( card - > shutdown ) {
spin_unlock ( & card - > files_lock ) ;
return 0 ;
}
card - > shutdown = 1 ;
spin_unlock ( & card - > files_lock ) ;
/* phase 1: disable fops (user space) operations for ALSA API */
2006-05-15 21:49:05 +04:00
mutex_lock ( & snd_card_mutex ) ;
2005-04-17 02:20:36 +04:00
snd_cards [ card - > number ] = NULL ;
2008-04-17 14:52:02 +04:00
snd_cards_lock & = ~ ( 1 < < card - > number ) ;
2006-05-15 21:49:05 +04:00
mutex_unlock ( & snd_card_mutex ) ;
2005-04-17 02:20:36 +04:00
/* phase 2: replace file->f_op with special dummy operations */
spin_lock ( & card - > files_lock ) ;
2009-02-23 18:35:21 +03:00
list_for_each_entry ( mfile , & card - > files_list , list ) {
2005-04-17 02:20:36 +04:00
/* it's critical part, use endless loop */
/* we have no room to fail */
2006-10-06 18:08:27 +04:00
mfile - > disconnected_f_op = mfile - > file - > f_op ;
2005-04-17 02:20:36 +04:00
2006-10-06 18:08:27 +04:00
spin_lock ( & shutdown_lock ) ;
list_add ( & mfile - > shutdown_list , & shutdown_files ) ;
spin_unlock ( & shutdown_lock ) ;
2005-04-17 02:20:36 +04:00
2006-10-06 18:08:27 +04:00
mfile - > file - > f_op = & snd_shutdown_f_ops ;
2008-01-13 14:03:53 +03:00
fops_get ( mfile - > file - > f_op ) ;
2005-04-17 02:20:36 +04:00
}
spin_unlock ( & card - > files_lock ) ;
/* phase 3: notify all connected devices about disconnection */
/* at this point, they cannot respond to any calls except release() */
# if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
if ( snd_mixer_oss_notify_callback )
snd_mixer_oss_notify_callback ( card , SND_MIXER_OSS_NOTIFY_DISCONNECT ) ;
# endif
/* notify all devices that we are disconnected */
err = snd_device_disconnect_all ( card ) ;
if ( err < 0 )
snd_printk ( KERN_ERR " not all devices for card %i can be disconnected \n " , card - > number ) ;
2006-06-23 16:37:59 +04:00
snd_info_card_disconnect ( card ) ;
2008-04-17 14:50:47 +04:00
if ( card - > card_dev ) {
device_unregister ( card - > card_dev ) ;
card - > card_dev = NULL ;
}
2008-04-17 14:52:02 +04:00
# ifdef CONFIG_PM
wake_up ( & card - > power_sleep ) ;
2008-04-17 14:50:47 +04:00
# endif
2005-04-17 02:20:36 +04:00
return 0 ;
}
2006-04-28 17:13:39 +04:00
EXPORT_SYMBOL ( snd_card_disconnect ) ;
2005-04-17 02:20:36 +04:00
/**
* snd_card_free - frees given soundcard structure
* @ card : soundcard structure
*
* This function releases the soundcard structure and the all assigned
* devices automatically . That is , you don ' t have to release the devices
* by yourself .
*
* Returns zero . Frees all associated devices and frees the control
* interface associated to given soundcard .
*/
2006-06-23 16:38:23 +04:00
static int snd_card_do_free ( struct snd_card * card )
2005-04-17 02:20:36 +04:00
{
# if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
if ( snd_mixer_oss_notify_callback )
snd_mixer_oss_notify_callback ( card , SND_MIXER_OSS_NOTIFY_FREE ) ;
# endif
if ( snd_device_free_all ( card , SNDRV_DEV_CMD_PRE ) < 0 ) {
snd_printk ( KERN_ERR " unable to free all devices (pre) \n " ) ;
/* Fatal, but this situation should never occur */
}
if ( snd_device_free_all ( card , SNDRV_DEV_CMD_NORMAL ) < 0 ) {
snd_printk ( KERN_ERR " unable to free all devices (normal) \n " ) ;
/* Fatal, but this situation should never occur */
}
if ( snd_device_free_all ( card , SNDRV_DEV_CMD_POST ) < 0 ) {
snd_printk ( KERN_ERR " unable to free all devices (post) \n " ) ;
/* Fatal, but this situation should never occur */
}
if ( card - > private_free )
card - > private_free ( card ) ;
2006-06-23 16:37:59 +04:00
snd_info_free_entry ( card - > proc_id ) ;
2005-04-17 02:20:36 +04:00
if ( snd_info_card_free ( card ) < 0 ) {
snd_printk ( KERN_WARNING " unable to free card info \n " ) ;
/* Not fatal error */
}
2006-06-23 16:38:23 +04:00
kfree ( card ) ;
return 0 ;
}
2012-10-16 15:05:59 +04:00
/**
* snd_card_unref - release the reference counter
* @ card : the card instance
*
* Decrements the reference counter . When it reaches to zero , wake up
* the sleeper and call the destructor if needed .
*/
void snd_card_unref ( struct snd_card * card )
{
if ( atomic_dec_and_test ( & card - > refcount ) ) {
wake_up ( & card - > shutdown_sleep ) ;
if ( card - > free_on_last_close )
snd_card_do_free ( card ) ;
}
}
EXPORT_SYMBOL ( snd_card_unref ) ;
2006-06-23 16:38:23 +04:00
int snd_card_free_when_closed ( struct snd_card * card )
{
2012-10-16 15:05:59 +04:00
int ret ;
2006-06-23 16:38:23 +04:00
2012-10-16 15:05:59 +04:00
atomic_inc ( & card - > refcount ) ;
ret = snd_card_disconnect ( card ) ;
if ( ret ) {
atomic_dec ( & card - > refcount ) ;
return ret ;
}
2006-06-23 16:38:23 +04:00
2012-10-16 15:05:59 +04:00
card - > free_on_last_close = 1 ;
if ( atomic_dec_and_test ( & card - > refcount ) )
2006-06-23 16:38:23 +04:00
snd_card_do_free ( card ) ;
return 0 ;
}
EXPORT_SYMBOL ( snd_card_free_when_closed ) ;
int snd_card_free ( struct snd_card * card )
{
2008-04-17 14:52:02 +04:00
int ret = snd_card_disconnect ( card ) ;
2006-06-23 16:38:23 +04:00
if ( ret )
return ret ;
/* wait, until all devices are ready for the free operation */
2012-10-16 15:05:59 +04:00
wait_event ( card - > shutdown_sleep , ! atomic_read ( & card - > refcount ) ) ;
2006-06-23 16:38:23 +04:00
snd_card_do_free ( card ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2006-04-28 17:13:39 +04:00
EXPORT_SYMBOL ( snd_card_free ) ;
2012-03-09 20:41:53 +04:00
/* retrieve the last word of shortname or longname */
static const char * retrieve_id_from_card_name ( const char * name )
2005-04-17 02:20:36 +04:00
{
2012-03-09 20:41:53 +04:00
const char * spos = name ;
while ( * name ) {
if ( isspace ( * name ) & & isalnum ( name [ 1 ] ) )
spos = name + 1 ;
name + + ;
2005-04-17 02:20:36 +04:00
}
2012-03-09 20:41:53 +04:00
return spos ;
}
/* return true if the given id string doesn't conflict any other card ids */
static bool card_id_ok ( struct snd_card * card , const char * id )
{
int i ;
if ( ! snd_info_check_reserved_words ( id ) )
return false ;
for ( i = 0 ; i < snd_ecards_limit ; i + + ) {
if ( snd_cards [ i ] & & snd_cards [ i ] ! = card & &
! strcmp ( snd_cards [ i ] - > id , id ) )
return false ;
2005-04-17 02:20:36 +04:00
}
2012-03-09 20:41:53 +04:00
return true ;
}
2005-04-17 02:20:36 +04:00
2012-03-09 20:41:53 +04:00
/* copy to card->id only with valid letters from nid */
static void copy_valid_id_string ( struct snd_card * card , const char * src ,
const char * nid )
{
char * id = card - > id ;
while ( * nid & & ! isalnum ( * nid ) )
nid + + ;
if ( isdigit ( * nid ) )
* id + + = isalpha ( * src ) ? * src : ' D ' ;
while ( * nid & & ( size_t ) ( id - card - > id ) < sizeof ( card - > id ) - 1 ) {
if ( isalnum ( * nid ) )
* id + + = * nid ;
nid + + ;
}
* id = 0 ;
}
/* Set card->id from the given string
* If the string conflicts with other ids , add a suffix to make it unique .
*/
static void snd_card_set_id_no_lock ( struct snd_card * card , const char * src ,
const char * nid )
{
int len , loops ;
bool with_suffix ;
bool is_default = false ;
char * id ;
2005-04-17 02:20:36 +04:00
2012-03-09 20:41:53 +04:00
copy_valid_id_string ( card , src , nid ) ;
id = card - > id ;
again :
/* use "Default" for obviously invalid strings
* ( " card " conflicts with proc directories )
*/
if ( ! * id | | ! strncmp ( id , " card " , 4 ) ) {
2011-04-04 14:43:23 +04:00
strcpy ( id , " Default " ) ;
2012-03-09 20:41:53 +04:00
is_default = true ;
}
2005-04-17 02:20:36 +04:00
2012-03-09 20:41:53 +04:00
with_suffix = false ;
for ( loops = 0 ; loops < SNDRV_CARDS ; loops + + ) {
if ( card_id_ok ( card , id ) )
return ; /* OK */
2005-04-17 02:20:36 +04:00
len = strlen ( id ) ;
2012-03-09 20:41:53 +04:00
if ( ! with_suffix ) {
/* add the "_X" suffix */
char * spos = id + len ;
if ( len > sizeof ( card - > id ) - 3 )
spos = id + sizeof ( card - > id ) - 3 ;
strcpy ( spos , " _1 " ) ;
with_suffix = true ;
2005-04-17 02:20:36 +04:00
} else {
2012-03-09 20:41:53 +04:00
/* modify the existing suffix */
if ( id [ len - 1 ] ! = ' 9 ' )
id [ len - 1 ] + + ;
else
id [ len - 1 ] = ' A ' ;
2005-04-17 02:20:36 +04:00
}
}
2012-03-09 20:41:53 +04:00
/* fallback to the default id */
if ( ! is_default ) {
* id = 0 ;
goto again ;
}
/* last resort... */
snd_printk ( KERN_ERR " unable to set card id (%s) \n " , id ) ;
if ( card - > proc_root - > name )
strcpy ( card - > id , card - > proc_root - > name ) ;
2005-04-17 02:20:36 +04:00
}
2009-06-03 23:43:29 +04:00
/**
* snd_card_set_id - set card identification name
* @ card : soundcard structure
* @ nid : new identification string
*
* This function sets the card identification and checks for name
* collisions .
*/
void snd_card_set_id ( struct snd_card * card , const char * nid )
{
2009-06-04 02:12:18 +04:00
/* check if user specified own card->id */
if ( card - > id [ 0 ] ! = ' \0 ' )
return ;
mutex_lock ( & snd_card_mutex ) ;
2012-03-09 20:41:53 +04:00
snd_card_set_id_no_lock ( card , nid , nid ) ;
2009-06-04 02:12:18 +04:00
mutex_unlock ( & snd_card_mutex ) ;
2009-06-03 23:43:29 +04:00
}
2009-06-02 14:02:38 +04:00
EXPORT_SYMBOL ( snd_card_set_id ) ;
2008-11-11 18:51:02 +03:00
static ssize_t
card_id_show_attr ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct snd_card * card = dev_get_drvdata ( dev ) ;
return snprintf ( buf , PAGE_SIZE , " %s \n " , card ? card - > id : " (null) " ) ;
}
static ssize_t
card_id_store_attr ( struct device * dev , struct device_attribute * attr ,
const char * buf , size_t count )
{
struct snd_card * card = dev_get_drvdata ( dev ) ;
char buf1 [ sizeof ( card - > id ) ] ;
size_t copy = count > sizeof ( card - > id ) - 1 ?
sizeof ( card - > id ) - 1 : count ;
size_t idx ;
int c ;
for ( idx = 0 ; idx < copy ; idx + + ) {
c = buf [ idx ] ;
if ( ! isalnum ( c ) & & c ! = ' _ ' & & c ! = ' - ' )
return - EINVAL ;
}
memcpy ( buf1 , buf , copy ) ;
buf1 [ copy ] = ' \0 ' ;
mutex_lock ( & snd_card_mutex ) ;
2012-03-09 20:41:53 +04:00
if ( ! card_id_ok ( NULL , buf1 ) ) {
2008-11-11 18:51:02 +03:00
mutex_unlock ( & snd_card_mutex ) ;
return - EEXIST ;
}
strcpy ( card - > id , buf1 ) ;
2008-11-12 18:31:37 +03:00
snd_info_card_id_change ( card ) ;
2008-11-11 18:51:02 +03:00
mutex_unlock ( & snd_card_mutex ) ;
return count ;
}
static struct device_attribute card_id_attrs =
__ATTR ( id , S_IRUGO | S_IWUSR , card_id_show_attr , card_id_store_attr ) ;
static ssize_t
card_number_show_attr ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct snd_card * card = dev_get_drvdata ( dev ) ;
return snprintf ( buf , PAGE_SIZE , " %i \n " , card ? card - > number : - 1 ) ;
}
static struct device_attribute card_number_attrs =
__ATTR ( number , S_IRUGO , card_number_show_attr , NULL ) ;
2005-04-17 02:20:36 +04:00
/**
* snd_card_register - register the soundcard
* @ card : soundcard structure
*
* This function registers all the devices assigned to the soundcard .
* Until calling this , the ALSA control interface is blocked from the
* external accesses . Thus , you should call this function at the end
* of the initialization of the card .
*
tree-wide: fix comment/printk typos
"gadget", "through", "command", "maintain", "maintain", "controller", "address",
"between", "initiali[zs]e", "instead", "function", "select", "already",
"equal", "access", "management", "hierarchy", "registration", "interest",
"relative", "memory", "offset", "already",
Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
Signed-off-by: Jiri Kosina <jkosina@suse.cz>
2010-11-01 22:38:34 +03:00
* Returns zero otherwise a negative error code if the registration failed .
2005-04-17 02:20:36 +04:00
*/
2005-11-17 15:51:18 +03:00
int snd_card_register ( struct snd_card * card )
2005-04-17 02:20:36 +04:00
{
int err ;
2008-08-08 19:09:09 +04:00
if ( snd_BUG_ON ( ! card ) )
return - EINVAL ;
2010-09-05 09:33:14 +04:00
2007-01-26 14:40:31 +03:00
if ( ! card - > card_dev ) {
2008-07-22 07:03:34 +04:00
card - > card_dev = device_create ( sound_class , card - > dev ,
2008-11-11 18:51:02 +03:00
MKDEV ( 0 , 0 ) , card ,
2008-07-22 07:03:34 +04:00
" card%i " , card - > number ) ;
2007-01-26 14:40:31 +03:00
if ( IS_ERR ( card - > card_dev ) )
card - > card_dev = NULL ;
2006-08-08 09:19:37 +04:00
}
2010-09-05 09:33:14 +04:00
2005-04-17 02:20:36 +04:00
if ( ( err = snd_device_register_all ( card ) ) < 0 )
return err ;
2006-05-15 21:49:05 +04:00
mutex_lock ( & snd_card_mutex ) ;
2005-04-17 02:20:36 +04:00
if ( snd_cards [ card - > number ] ) {
/* already registered */
2006-05-15 21:49:05 +04:00
mutex_unlock ( & snd_card_mutex ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2012-03-09 20:41:53 +04:00
if ( * card - > id ) {
/* make a unique id name from the given string */
char tmpid [ sizeof ( card - > id ) ] ;
memcpy ( tmpid , card - > id , sizeof ( card - > id ) ) ;
snd_card_set_id_no_lock ( card , tmpid , tmpid ) ;
} else {
/* create an id from either shortname or longname */
const char * src ;
src = * card - > shortname ? card - > shortname : card - > longname ;
snd_card_set_id_no_lock ( card , src ,
retrieve_id_from_card_name ( src ) ) ;
}
2005-04-17 02:20:36 +04:00
snd_cards [ card - > number ] = card ;
2006-05-15 21:49:05 +04:00
mutex_unlock ( & snd_card_mutex ) ;
2005-12-01 12:42:42 +03:00
init_info_for_card ( card ) ;
2005-04-17 02:20:36 +04:00
# if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE)
if ( snd_mixer_oss_notify_callback )
snd_mixer_oss_notify_callback ( card , SND_MIXER_OSS_NOTIFY_REGISTER ) ;
2008-11-11 18:51:02 +03:00
# endif
if ( card - > card_dev ) {
2008-11-18 20:25:06 +03:00
err = device_create_file ( card - > card_dev , & card_id_attrs ) ;
if ( err < 0 )
return err ;
err = device_create_file ( card - > card_dev , & card_number_attrs ) ;
if ( err < 0 )
return err ;
2008-11-11 18:51:02 +03:00
}
2010-09-05 09:33:14 +04:00
2005-04-17 02:20:36 +04:00
return 0 ;
}
2006-04-28 17:13:39 +04:00
EXPORT_SYMBOL ( snd_card_register ) ;
2005-12-01 12:42:42 +03:00
# ifdef CONFIG_PROC_FS
2006-05-17 19:14:51 +04:00
static struct snd_info_entry * snd_card_info_entry ;
2005-04-17 02:20:36 +04:00
2005-11-17 17:55:49 +03:00
static void snd_card_info_read ( struct snd_info_entry * entry ,
struct snd_info_buffer * buffer )
2005-04-17 02:20:36 +04:00
{
int idx , count ;
2005-11-17 15:51:18 +03:00
struct snd_card * card ;
2005-04-17 02:20:36 +04:00
for ( idx = count = 0 ; idx < SNDRV_CARDS ; idx + + ) {
2006-05-15 21:49:05 +04:00
mutex_lock ( & snd_card_mutex ) ;
2005-04-17 02:20:36 +04:00
if ( ( card = snd_cards [ idx ] ) ! = NULL ) {
count + + ;
2005-11-20 16:09:05 +03:00
snd_iprintf ( buffer , " %2i [%-15s]: %s - %s \n " ,
2005-04-17 02:20:36 +04:00
idx ,
card - > id ,
card - > driver ,
card - > shortname ) ;
2005-11-20 16:09:05 +03:00
snd_iprintf ( buffer , " %s \n " ,
2005-04-17 02:20:36 +04:00
card - > longname ) ;
}
2006-05-15 21:49:05 +04:00
mutex_unlock ( & snd_card_mutex ) ;
2005-04-17 02:20:36 +04:00
}
if ( ! count )
snd_iprintf ( buffer , " --- no soundcards --- \n " ) ;
}
2005-12-01 12:42:42 +03:00
# ifdef CONFIG_SND_OSSEMUL
2005-04-17 02:20:36 +04:00
2005-11-17 15:51:18 +03:00
void snd_card_info_read_oss ( struct snd_info_buffer * buffer )
2005-04-17 02:20:36 +04:00
{
int idx , count ;
2005-11-17 15:51:18 +03:00
struct snd_card * card ;
2005-04-17 02:20:36 +04:00
for ( idx = count = 0 ; idx < SNDRV_CARDS ; idx + + ) {
2006-05-15 21:49:05 +04:00
mutex_lock ( & snd_card_mutex ) ;
2005-04-17 02:20:36 +04:00
if ( ( card = snd_cards [ idx ] ) ! = NULL ) {
count + + ;
snd_iprintf ( buffer , " %s \n " , card - > longname ) ;
}
2006-05-15 21:49:05 +04:00
mutex_unlock ( & snd_card_mutex ) ;
2005-04-17 02:20:36 +04:00
}
if ( ! count ) {
snd_iprintf ( buffer , " --- no soundcards --- \n " ) ;
}
}
# endif
# ifdef MODULE
2005-11-17 15:51:18 +03:00
static struct snd_info_entry * snd_card_module_info_entry ;
static void snd_card_module_info_read ( struct snd_info_entry * entry ,
struct snd_info_buffer * buffer )
2005-04-17 02:20:36 +04:00
{
int idx ;
2005-11-17 15:51:18 +03:00
struct snd_card * card ;
2005-04-17 02:20:36 +04:00
for ( idx = 0 ; idx < SNDRV_CARDS ; idx + + ) {
2006-05-15 21:49:05 +04:00
mutex_lock ( & snd_card_mutex ) ;
2005-04-17 02:20:36 +04:00
if ( ( card = snd_cards [ idx ] ) ! = NULL )
2005-11-20 16:09:05 +03:00
snd_iprintf ( buffer , " %2i %s \n " ,
idx , card - > module - > name ) ;
2006-05-15 21:49:05 +04:00
mutex_unlock ( & snd_card_mutex ) ;
2005-04-17 02:20:36 +04:00
}
}
# endif
int __init snd_card_info_init ( void )
{
2005-11-17 15:51:18 +03:00
struct snd_info_entry * entry ;
2005-04-17 02:20:36 +04:00
entry = snd_info_create_module_entry ( THIS_MODULE , " cards " , NULL ) ;
2005-10-10 13:46:31 +04:00
if ( ! entry )
return - ENOMEM ;
2005-04-17 02:20:36 +04:00
entry - > c . text . read = snd_card_info_read ;
if ( snd_info_register ( entry ) < 0 ) {
snd_info_free_entry ( entry ) ;
return - ENOMEM ;
}
snd_card_info_entry = entry ;
# ifdef MODULE
entry = snd_info_create_module_entry ( THIS_MODULE , " modules " , NULL ) ;
if ( entry ) {
entry - > c . text . read = snd_card_module_info_read ;
if ( snd_info_register ( entry ) < 0 )
snd_info_free_entry ( entry ) ;
else
snd_card_module_info_entry = entry ;
}
# endif
return 0 ;
}
int __exit snd_card_info_done ( void )
{
2006-06-23 16:37:59 +04:00
snd_info_free_entry ( snd_card_info_entry ) ;
2005-04-17 02:20:36 +04:00
# ifdef MODULE
2006-06-23 16:37:59 +04:00
snd_info_free_entry ( snd_card_module_info_entry ) ;
2005-04-17 02:20:36 +04:00
# endif
return 0 ;
}
2005-12-01 12:42:42 +03:00
# endif /* CONFIG_PROC_FS */
2005-04-17 02:20:36 +04:00
/**
* snd_component_add - add a component string
* @ card : soundcard structure
* @ component : the component id string
*
* This function adds the component id string to the supported list .
* The component can be referred from the alsa - lib .
*
* Returns zero otherwise a negative error code .
*/
2005-11-17 15:51:18 +03:00
int snd_component_add ( struct snd_card * card , const char * component )
2005-04-17 02:20:36 +04:00
{
char * ptr ;
int len = strlen ( component ) ;
ptr = strstr ( card - > components , component ) ;
if ( ptr ! = NULL ) {
if ( ptr [ len ] = = ' \0 ' | | ptr [ len ] = = ' ' ) /* already there */
return 1 ;
}
if ( strlen ( card - > components ) + 1 + len + 1 > sizeof ( card - > components ) ) {
snd_BUG ( ) ;
return - ENOMEM ;
}
if ( card - > components [ 0 ] ! = ' \0 ' )
strcat ( card - > components , " " ) ;
strcat ( card - > components , component ) ;
return 0 ;
}
2006-04-28 17:13:39 +04:00
EXPORT_SYMBOL ( snd_component_add ) ;
2005-04-17 02:20:36 +04:00
/**
* snd_card_file_add - add the file to the file list of the card
* @ card : soundcard structure
* @ file : file pointer
*
* This function adds the file to the file linked - list of the card .
* This linked - list is used to keep tracking the connection state ,
* and to avoid the release of busy resources by hotplug .
*
* Returns zero or a negative error code .
*/
2005-11-17 15:51:18 +03:00
int snd_card_file_add ( struct snd_card * card , struct file * file )
2005-04-17 02:20:36 +04:00
{
struct snd_monitor_file * mfile ;
mfile = kmalloc ( sizeof ( * mfile ) , GFP_KERNEL ) ;
if ( mfile = = NULL )
return - ENOMEM ;
mfile - > file = file ;
2006-10-06 18:08:27 +04:00
mfile - > disconnected_f_op = NULL ;
2011-03-24 11:50:15 +03:00
INIT_LIST_HEAD ( & mfile - > shutdown_list ) ;
2005-04-17 02:20:36 +04:00
spin_lock ( & card - > files_lock ) ;
if ( card - > shutdown ) {
spin_unlock ( & card - > files_lock ) ;
kfree ( mfile ) ;
return - ENODEV ;
}
2009-02-23 18:35:21 +03:00
list_add ( & mfile - > list , & card - > files_list ) ;
2012-10-16 15:05:59 +04:00
atomic_inc ( & card - > refcount ) ;
2005-04-17 02:20:36 +04:00
spin_unlock ( & card - > files_lock ) ;
return 0 ;
}
2006-04-28 17:13:39 +04:00
EXPORT_SYMBOL ( snd_card_file_add ) ;
2005-04-17 02:20:36 +04:00
/**
* snd_card_file_remove - remove the file from the file list
* @ card : soundcard structure
* @ file : file pointer
*
* This function removes the file formerly added to the card via
* snd_card_file_add ( ) function .
2006-06-23 16:38:26 +04:00
* If all files are removed and snd_card_free_when_closed ( ) was
* called beforehand , it processes the pending release of
* resources .
2005-04-17 02:20:36 +04:00
*
* Returns zero or a negative error code .
*/
2005-11-17 15:51:18 +03:00
int snd_card_file_remove ( struct snd_card * card , struct file * file )
2005-04-17 02:20:36 +04:00
{
2009-02-23 18:35:21 +03:00
struct snd_monitor_file * mfile , * found = NULL ;
2005-04-17 02:20:36 +04:00
spin_lock ( & card - > files_lock ) ;
2009-02-23 18:35:21 +03:00
list_for_each_entry ( mfile , & card - > files_list , list ) {
2005-04-17 02:20:36 +04:00
if ( mfile - > file = = file ) {
2009-02-23 18:35:21 +03:00
list_del ( & mfile - > list ) ;
2011-03-24 11:50:15 +03:00
spin_lock ( & shutdown_lock ) ;
list_del ( & mfile - > shutdown_list ) ;
spin_unlock ( & shutdown_lock ) ;
2009-02-23 18:35:21 +03:00
if ( mfile - > disconnected_f_op )
fops_put ( mfile - > disconnected_f_op ) ;
found = mfile ;
2005-04-17 02:20:36 +04:00
break ;
}
2006-10-06 18:08:27 +04:00
}
2006-06-23 16:38:23 +04:00
spin_unlock ( & card - > files_lock ) ;
2009-02-23 18:35:21 +03:00
if ( ! found ) {
2005-04-17 02:20:36 +04:00
snd_printk ( KERN_ERR " ALSA card file remove problem (%p) \n " , file ) ;
return - ENOENT ;
}
2009-02-23 18:35:21 +03:00
kfree ( found ) ;
2012-10-16 15:05:59 +04:00
snd_card_unref ( card ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2006-04-28 17:13:39 +04:00
EXPORT_SYMBOL ( snd_card_file_remove ) ;
2005-04-17 02:20:36 +04:00
# ifdef CONFIG_PM
/**
* snd_power_wait - wait until the power - state is changed .
* @ card : soundcard structure
* @ power_state : expected power state
*
* Waits until the power - state is changed .
*
* Note : the power lock must be active before call .
*/
2006-03-27 14:38:07 +04:00
int snd_power_wait ( struct snd_card * card , unsigned int power_state )
2005-04-17 02:20:36 +04:00
{
wait_queue_t wait ;
int result = 0 ;
/* fastpath */
if ( snd_power_get_state ( card ) = = power_state )
return 0 ;
init_waitqueue_entry ( & wait , current ) ;
add_wait_queue ( & card - > power_sleep , & wait ) ;
while ( 1 ) {
if ( card - > shutdown ) {
result = - ENODEV ;
break ;
}
if ( snd_power_get_state ( card ) = = power_state )
break ;
set_current_state ( TASK_UNINTERRUPTIBLE ) ;
snd_power_unlock ( card ) ;
schedule_timeout ( 30 * HZ ) ;
snd_power_lock ( card ) ;
}
remove_wait_queue ( & card - > power_sleep , & wait ) ;
return result ;
}
2006-04-28 17:13:39 +04:00
EXPORT_SYMBOL ( snd_power_wait ) ;
2005-04-17 02:20:36 +04:00
# endif /* CONFIG_PM */