2005-04-17 02:20:36 +04:00
/*
* ALSA sequencer device management
* Copyright ( c ) 1999 by Takashi Iwai < tiwai @ suse . de >
*
* 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
*
*
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
*
* This device handler separates the card driver module from sequencer
* stuff ( sequencer core , synth drivers , etc ) , so that user can avoid
* to spend unnecessary resources e . g . if he needs only listening to
* MP3s .
*
* The card ( or lowlevel ) driver creates a sequencer device entry
* via snd_seq_device_new ( ) . This is an entry pointer to communicate
* with the sequencer device " driver " , which is involved with the
* actual part to communicate with the sequencer core .
* Each sequencer device entry has an id string and the corresponding
* driver with the same id is loaded when required . For example ,
* lowlevel codes to access emu8000 chip on sbawe card are included in
* emu8000 - synth module . To activate this module , the hardware
* resources like i / o port are passed via snd_seq_device argument .
*
*/
2015-02-12 12:51:59 +03:00
# include <linux/device.h>
2005-04-17 02:20:36 +04:00
# include <linux/init.h>
2011-07-15 20:38:28 +04:00
# include <linux/module.h>
2005-04-17 02:20:36 +04:00
# include <sound/core.h>
# include <sound/info.h>
# include <sound/seq_device.h>
# include <sound/seq_kernel.h>
# include <sound/initval.h>
# include <linux/kmod.h>
# include <linux/slab.h>
2006-01-16 18:29:08 +03:00
# include <linux/mutex.h>
2005-04-17 02:20:36 +04:00
MODULE_AUTHOR ( " Takashi Iwai <tiwai@suse.de> " ) ;
MODULE_DESCRIPTION ( " ALSA sequencer device management " ) ;
MODULE_LICENSE ( " GPL " ) ;
2015-02-12 12:51:59 +03:00
/*
* bus definition
*/
static int snd_seq_bus_match ( struct device * dev , struct device_driver * drv )
{
struct snd_seq_device * sdev = to_seq_dev ( dev ) ;
struct snd_seq_driver * sdrv = to_seq_drv ( drv ) ;
2005-04-17 02:20:36 +04:00
2015-02-12 12:51:59 +03:00
return strcmp ( sdrv - > id , sdev - > id ) = = 0 & &
sdrv - > argsize = = sdev - > argsize ;
}
2005-04-17 02:20:36 +04:00
2015-02-12 12:51:59 +03:00
static struct bus_type snd_seq_bus_type = {
. name = " snd_seq " ,
. match = snd_seq_bus_match ,
2005-04-17 02:20:36 +04:00
} ;
2015-02-12 12:51:59 +03:00
/*
* proc interface - - just for compatibility
*/
2015-05-27 14:45:45 +03:00
# ifdef CONFIG_SND_PROC_FS
2006-05-17 19:14:51 +04:00
static struct snd_info_entry * info_entry ;
2005-04-17 02:20:36 +04:00
2015-02-12 12:51:59 +03:00
static int print_dev_info ( struct device * dev , void * data )
{
struct snd_seq_device * sdev = to_seq_dev ( dev ) ;
struct snd_info_buffer * buffer = data ;
2005-04-17 02:20:36 +04:00
2015-02-12 12:51:59 +03:00
snd_iprintf ( buffer , " snd-%s,%s,%d \n " , sdev - > id ,
dev - > driver ? " loaded " : " empty " ,
dev - > driver ? 1 : 0 ) ;
return 0 ;
}
2005-04-17 02:20:36 +04:00
2005-11-17 16:04:02 +03:00
static void snd_seq_device_info ( struct snd_info_entry * entry ,
struct snd_info_buffer * buffer )
2005-04-17 02:20:36 +04:00
{
2015-02-12 12:51:59 +03:00
bus_for_each_dev ( & snd_seq_bus_type , NULL , buffer , print_dev_info ) ;
2005-04-17 02:20:36 +04:00
}
2005-12-01 12:43:51 +03:00
# endif
2015-02-12 12:51:59 +03:00
2005-04-17 02:20:36 +04:00
/*
* load all registered drivers ( called from seq_clientmgr . c )
*/
2008-07-09 12:28:41 +04:00
# ifdef CONFIG_MODULES
2015-02-12 16:20:24 +03:00
/* flag to block auto-loading */
2014-10-15 16:06:25 +04:00
static atomic_t snd_seq_in_init = ATOMIC_INIT ( 1 ) ; /* blocked as default */
2005-04-17 02:20:36 +04:00
2015-02-12 12:51:59 +03:00
static int request_seq_drv ( struct device * dev , void * data )
2005-04-17 02:20:36 +04:00
{
2015-02-12 12:51:59 +03:00
struct snd_seq_device * sdev = to_seq_dev ( dev ) ;
2005-04-17 02:20:36 +04:00
2015-02-12 12:51:59 +03:00
if ( ! dev - > driver )
request_module ( " snd-%s " , sdev - > id ) ;
return 0 ;
2014-10-15 16:06:25 +04:00
}
2005-04-17 02:20:36 +04:00
2015-02-12 12:51:59 +03:00
static void autoload_drivers ( struct work_struct * work )
2014-10-15 16:06:25 +04:00
{
2015-02-12 12:51:59 +03:00
/* avoid reentrance */
if ( atomic_inc_return ( & snd_seq_in_init ) = = 1 )
bus_for_each_dev ( & snd_seq_bus_type , NULL , NULL ,
request_seq_drv ) ;
atomic_dec ( & snd_seq_in_init ) ;
2014-10-15 16:06:25 +04:00
}
2015-02-12 12:51:59 +03:00
static DECLARE_WORK ( autoload_work , autoload_drivers ) ;
2014-10-15 16:06:25 +04:00
static void queue_autoload_drivers ( void )
{
2015-02-12 12:51:59 +03:00
schedule_work ( & autoload_work ) ;
2014-10-15 16:06:25 +04:00
}
void snd_seq_autoload_init ( void )
{
atomic_dec ( & snd_seq_in_init ) ;
# ifdef CONFIG_SND_SEQUENCER_MODULE
/* initial autoload only when snd-seq is a module */
queue_autoload_drivers ( ) ;
# endif
}
2015-02-12 00:37:16 +03:00
EXPORT_SYMBOL ( snd_seq_autoload_init ) ;
2015-02-12 16:20:24 +03:00
void snd_seq_autoload_exit ( void )
{
atomic_inc ( & snd_seq_in_init ) ;
}
EXPORT_SYMBOL ( snd_seq_autoload_exit ) ;
2014-10-15 16:06:25 +04:00
void snd_seq_device_load_drivers ( void )
{
queue_autoload_drivers ( ) ;
flush_work ( & autoload_work ) ;
2005-04-17 02:20:36 +04:00
}
2015-02-12 00:37:16 +03:00
EXPORT_SYMBOL ( snd_seq_device_load_drivers ) ;
2017-09-12 13:41:20 +03:00
# define cancel_autoload_drivers() cancel_work_sync(&autoload_work)
2015-02-12 00:39:51 +03:00
# else
2015-02-12 12:51:59 +03:00
# define queue_autoload_drivers() /* NOP */
2017-09-12 13:41:20 +03:00
# define cancel_autoload_drivers() /* NOP */
2015-02-12 00:39:51 +03:00
# endif
2005-04-17 02:20:36 +04:00
2015-02-12 12:51:59 +03:00
/*
* device management
*/
static int snd_seq_device_dev_free ( struct snd_device * device )
{
struct snd_seq_device * dev = device - > device_data ;
2017-09-12 13:41:20 +03:00
cancel_autoload_drivers ( ) ;
2015-02-12 12:51:59 +03:00
put_device ( & dev - > dev ) ;
return 0 ;
}
static int snd_seq_device_dev_register ( struct snd_device * device )
{
struct snd_seq_device * dev = device - > device_data ;
int err ;
err = device_add ( & dev - > dev ) ;
if ( err < 0 )
return err ;
if ( ! dev - > dev . driver )
queue_autoload_drivers ( ) ;
return 0 ;
}
static int snd_seq_device_dev_disconnect ( struct snd_device * device )
{
struct snd_seq_device * dev = device - > device_data ;
device_del ( & dev - > dev ) ;
return 0 ;
}
static void snd_seq_dev_release ( struct device * dev )
{
struct snd_seq_device * sdev = to_seq_dev ( dev ) ;
if ( sdev - > private_free )
sdev - > private_free ( sdev ) ;
kfree ( sdev ) ;
}
2005-04-17 02:20:36 +04:00
/*
* register a sequencer device
2014-02-04 21:21:03 +04:00
* card = card info
2005-04-17 02:20:36 +04:00
* device = device number ( if any )
* id = id of driver
* result = return pointer ( NULL allowed if unnecessary )
*/
2015-02-12 15:40:50 +03:00
int snd_seq_device_new ( struct snd_card * card , int device , const char * id ,
int argsize , struct snd_seq_device * * result )
2005-04-17 02:20:36 +04:00
{
2005-11-17 16:04:02 +03:00
struct snd_seq_device * dev ;
2005-04-17 02:20:36 +04:00
int err ;
2005-11-17 16:04:02 +03:00
static struct snd_device_ops dops = {
2005-04-17 02:20:36 +04:00
. dev_free = snd_seq_device_dev_free ,
. dev_register = snd_seq_device_dev_register ,
. dev_disconnect = snd_seq_device_dev_disconnect ,
} ;
if ( result )
* result = NULL ;
2008-08-08 19:09:09 +04:00
if ( snd_BUG_ON ( ! id ) )
return - EINVAL ;
2005-04-17 02:20:36 +04:00
2015-02-12 12:51:59 +03:00
dev = kzalloc ( sizeof ( * dev ) + argsize , GFP_KERNEL ) ;
if ( ! dev )
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
/* set up device info */
dev - > card = card ;
dev - > device = device ;
2015-02-12 15:40:50 +03:00
dev - > id = id ;
2005-04-17 02:20:36 +04:00
dev - > argsize = argsize ;
2015-02-12 12:51:59 +03:00
device_initialize ( & dev - > dev ) ;
dev - > dev . parent = & card - > card_dev ;
dev - > dev . bus = & snd_seq_bus_type ;
dev - > dev . release = snd_seq_dev_release ;
dev_set_name ( & dev - > dev , " %s-%d-%d " , dev - > id , card - > number , device ) ;
2005-04-17 02:20:36 +04:00
2015-02-12 12:51:59 +03:00
/* add this device to the list */
err = snd_device_new ( card , SNDRV_DEV_SEQUENCER , dev , & dops ) ;
if ( err < 0 ) {
put_device ( & dev - > dev ) ;
2005-04-17 02:20:36 +04:00
return err ;
}
if ( result )
* result = dev ;
return 0 ;
}
2015-02-12 00:37:16 +03:00
EXPORT_SYMBOL ( snd_seq_device_new ) ;
2005-04-17 02:20:36 +04:00
/*
2015-02-12 15:43:22 +03:00
* driver registration
2005-04-17 02:20:36 +04:00
*/
2015-02-12 15:43:22 +03:00
int __snd_seq_driver_register ( struct snd_seq_driver * drv , struct module * mod )
2005-04-17 02:20:36 +04:00
{
2015-02-12 15:43:22 +03:00
if ( WARN_ON ( ! drv - > driver . name | | ! drv - > id ) )
2005-04-17 02:20:36 +04:00
return - EINVAL ;
2015-02-12 15:43:22 +03:00
drv - > driver . bus = & snd_seq_bus_type ;
drv - > driver . owner = mod ;
return driver_register ( & drv - > driver ) ;
2015-02-12 12:51:59 +03:00
}
2015-02-12 15:43:22 +03:00
EXPORT_SYMBOL_GPL ( __snd_seq_driver_register ) ;
2005-04-17 02:20:36 +04:00
2015-02-12 15:43:22 +03:00
void snd_seq_driver_unregister ( struct snd_seq_driver * drv )
2005-04-17 02:20:36 +04:00
{
2015-02-12 15:43:22 +03:00
driver_unregister ( & drv - > driver ) ;
2005-04-17 02:20:36 +04:00
}
2015-02-12 15:43:22 +03:00
EXPORT_SYMBOL_GPL ( snd_seq_driver_unregister ) ;
2005-04-17 02:20:36 +04:00
/*
* module part
*/
2015-02-12 12:51:59 +03:00
static int __init seq_dev_proc_init ( void )
2005-04-17 02:20:36 +04:00
{
2015-05-29 08:01:28 +03:00
# ifdef CONFIG_SND_PROC_FS
2005-11-17 16:04:02 +03:00
info_entry = snd_info_create_module_entry ( THIS_MODULE , " drivers " ,
snd_seq_root ) ;
2005-04-17 02:20:36 +04:00
if ( info_entry = = NULL )
return - ENOMEM ;
info_entry - > content = SNDRV_INFO_CONTENT_TEXT ;
info_entry - > c . text . read = snd_seq_device_info ;
if ( snd_info_register ( info_entry ) < 0 ) {
snd_info_free_entry ( info_entry ) ;
return - ENOMEM ;
}
2005-12-01 12:43:51 +03:00
# endif
2005-04-17 02:20:36 +04:00
return 0 ;
}
2015-02-12 12:51:59 +03:00
static int __init alsa_seq_device_init ( void )
{
int err ;
err = bus_register ( & snd_seq_bus_type ) ;
if ( err < 0 )
return err ;
err = seq_dev_proc_init ( ) ;
if ( err < 0 )
bus_unregister ( & snd_seq_bus_type ) ;
return err ;
}
2005-04-17 02:20:36 +04:00
static void __exit alsa_seq_device_exit ( void )
{
2014-10-15 16:06:25 +04:00
# ifdef CONFIG_MODULES
cancel_work_sync ( & autoload_work ) ;
# endif
2015-05-29 08:01:28 +03:00
# ifdef CONFIG_SND_PROC_FS
2006-06-23 16:37:59 +04:00
snd_info_free_entry ( info_entry ) ;
2005-12-01 12:43:51 +03:00
# endif
2015-02-12 12:51:59 +03:00
bus_unregister ( & snd_seq_bus_type ) ;
2005-04-17 02:20:36 +04:00
}
2015-03-11 14:50:15 +03:00
subsys_initcall ( alsa_seq_device_init )
2005-04-17 02:20:36 +04:00
module_exit ( alsa_seq_device_exit )