2005-04-17 02:20:36 +04:00
/*
* Device management 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/slab.h>
# include <linux/time.h>
2011-09-22 17:34:58 +04:00
# include <linux/export.h>
2005-04-17 02:20:36 +04:00
# include <linux/errno.h>
# include <sound/core.h>
/**
* snd_device_new - create an ALSA device component
* @ card : the card instance
2005-06-29 21:30:42 +04:00
* @ type : the device type , SNDRV_DEV_XXX
2005-04-17 02:20:36 +04:00
* @ device_data : the data pointer of this device
* @ ops : the operator table
*
* Creates a new device component for the given data pointer .
* The device will be assigned to the card and managed together
* by the card .
*
* The data pointer plays a role as the identifier , too , so the
* pointer address must be unique and unchanged .
*
2013-03-12 01:05:14 +04:00
* Return : Zero if successful , or a negative error code on failure .
2005-04-17 02:20:36 +04:00
*/
2005-11-17 15:51:18 +03:00
int snd_device_new ( struct snd_card * card , snd_device_type_t type ,
void * device_data , struct snd_device_ops * ops )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:51:18 +03:00
struct snd_device * dev ;
2005-04-17 02:20:36 +04:00
2008-08-08 19:09:09 +04:00
if ( snd_BUG_ON ( ! card | | ! device_data | | ! ops ) )
return - ENXIO ;
2005-09-09 16:20:23 +04:00
dev = kzalloc ( sizeof ( * dev ) , GFP_KERNEL ) ;
2005-11-17 19:44:01 +03:00
if ( dev = = NULL ) {
snd_printk ( KERN_ERR " Cannot allocate device \n " ) ;
2005-04-17 02:20:36 +04:00
return - ENOMEM ;
2005-11-17 19:44:01 +03:00
}
2005-04-17 02:20:36 +04:00
dev - > card = card ;
dev - > type = type ;
dev - > state = SNDRV_DEV_BUILD ;
dev - > device_data = device_data ;
dev - > ops = ops ;
list_add ( & dev - > list , & card - > devices ) ; /* add to the head of list */
return 0 ;
}
2006-04-28 17:13:39 +04:00
EXPORT_SYMBOL ( snd_device_new ) ;
2005-04-17 02:20:36 +04:00
/**
* snd_device_free - release the device from the card
* @ card : the card instance
* @ device_data : the data pointer to release
*
* Removes the device from the list on the card and invokes the
2006-06-23 16:38:23 +04:00
* callbacks , dev_disconnect and dev_free , corresponding to the state .
2005-04-17 02:20:36 +04:00
* Then release the device .
*
2013-03-12 01:05:14 +04:00
* Return : Zero if successful , or a negative error code on failure or if the
2005-04-17 02:20:36 +04:00
* device not found .
*/
2005-11-17 15:51:18 +03:00
int snd_device_free ( struct snd_card * card , void * device_data )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:51:18 +03:00
struct snd_device * dev ;
2005-04-17 02:20:36 +04:00
2008-08-08 19:09:09 +04:00
if ( snd_BUG_ON ( ! card | | ! device_data ) )
return - ENXIO ;
2006-10-05 18:02:22 +04:00
list_for_each_entry ( dev , & card - > devices , list ) {
2005-04-17 02:20:36 +04:00
if ( dev - > device_data ! = device_data )
continue ;
/* unlink */
list_del ( & dev - > list ) ;
2006-06-23 16:38:23 +04:00
if ( dev - > state = = SNDRV_DEV_REGISTERED & &
dev - > ops - > dev_disconnect )
if ( dev - > ops - > dev_disconnect ( dev ) )
snd_printk ( KERN_ERR
" device disconnect failure \n " ) ;
if ( dev - > ops - > dev_free ) {
if ( dev - > ops - > dev_free ( dev ) )
snd_printk ( KERN_ERR " device free failure \n " ) ;
2005-04-17 02:20:36 +04:00
}
kfree ( dev ) ;
return 0 ;
}
2008-10-16 18:17:30 +04:00
snd_printd ( " device free %p (from %pF), not found \n " , device_data ,
2005-11-17 15:51:18 +03:00
__builtin_return_address ( 0 ) ) ;
2005-04-17 02:20:36 +04:00
return - ENXIO ;
}
2006-04-28 17:13:39 +04:00
EXPORT_SYMBOL ( snd_device_free ) ;
2005-04-17 02:20:36 +04:00
/**
2005-06-29 21:30:42 +04:00
* snd_device_disconnect - disconnect the device
2005-04-17 02:20:36 +04:00
* @ card : the card instance
* @ device_data : the data pointer to disconnect
*
* Turns the device into the disconnection state , invoking
* dev_disconnect callback , if the device was already registered .
*
* Usually called from snd_card_disconnect ( ) .
*
2013-03-12 01:05:14 +04:00
* Return : Zero if successful , or a negative error code on failure or if the
2005-04-17 02:20:36 +04:00
* device not found .
*/
2005-11-17 15:51:18 +03:00
int snd_device_disconnect ( struct snd_card * card , void * device_data )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:51:18 +03:00
struct snd_device * dev ;
2005-06-29 21:30:42 +04:00
2008-08-08 19:09:09 +04:00
if ( snd_BUG_ON ( ! card | | ! device_data ) )
return - ENXIO ;
2006-10-05 18:02:22 +04:00
list_for_each_entry ( dev , & card - > devices , list ) {
2005-04-17 02:20:36 +04:00
if ( dev - > device_data ! = device_data )
continue ;
2005-11-17 15:51:18 +03:00
if ( dev - > state = = SNDRV_DEV_REGISTERED & &
dev - > ops - > dev_disconnect ) {
2005-04-17 02:20:36 +04:00
if ( dev - > ops - > dev_disconnect ( dev ) )
snd_printk ( KERN_ERR " device disconnect failure \n " ) ;
dev - > state = SNDRV_DEV_DISCONNECTED ;
}
return 0 ;
}
2008-10-16 18:17:30 +04:00
snd_printd ( " device disconnect %p (from %pF), not found \n " , device_data ,
2005-11-17 15:51:18 +03:00
__builtin_return_address ( 0 ) ) ;
2005-04-17 02:20:36 +04:00
return - ENXIO ;
}
/**
* snd_device_register - register the device
* @ card : the card instance
* @ device_data : the data pointer to register
*
* Registers the device which was already created via
* snd_device_new ( ) . Usually this is called from snd_card_register ( ) ,
* but it can be called later if any new devices are created after
* invocation of snd_card_register ( ) .
*
2013-03-12 01:05:14 +04:00
* Return : Zero if successful , or a negative error code on failure or if the
2005-04-17 02:20:36 +04:00
* device not found .
*/
2005-11-17 15:51:18 +03:00
int snd_device_register ( struct snd_card * card , void * device_data )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:51:18 +03:00
struct snd_device * dev ;
2005-04-17 02:20:36 +04:00
int err ;
2005-06-29 21:30:42 +04:00
2008-08-08 19:09:09 +04:00
if ( snd_BUG_ON ( ! card | | ! device_data ) )
return - ENXIO ;
2006-10-05 18:02:22 +04:00
list_for_each_entry ( dev , & card - > devices , list ) {
2005-04-17 02:20:36 +04:00
if ( dev - > device_data ! = device_data )
continue ;
if ( dev - > state = = SNDRV_DEV_BUILD & & dev - > ops - > dev_register ) {
if ( ( err = dev - > ops - > dev_register ( dev ) ) < 0 )
return err ;
dev - > state = SNDRV_DEV_REGISTERED ;
return 0 ;
}
2005-11-17 19:44:01 +03:00
snd_printd ( " snd_device_register busy \n " ) ;
2005-04-17 02:20:36 +04:00
return - EBUSY ;
}
snd_BUG ( ) ;
return - ENXIO ;
}
2006-04-28 17:13:39 +04:00
EXPORT_SYMBOL ( snd_device_register ) ;
2005-04-17 02:20:36 +04:00
/*
* register all the devices on the card .
* called from init . c
*/
2005-11-17 15:51:18 +03:00
int snd_device_register_all ( struct snd_card * card )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:51:18 +03:00
struct snd_device * dev ;
2005-04-17 02:20:36 +04:00
int err ;
2008-08-08 19:09:09 +04:00
if ( snd_BUG_ON ( ! card ) )
return - ENXIO ;
2006-10-05 18:02:22 +04:00
list_for_each_entry ( dev , & card - > devices , list ) {
2005-04-17 02:20:36 +04:00
if ( dev - > state = = SNDRV_DEV_BUILD & & dev - > ops - > dev_register ) {
if ( ( err = dev - > ops - > dev_register ( dev ) ) < 0 )
return err ;
dev - > state = SNDRV_DEV_REGISTERED ;
}
}
return 0 ;
}
/*
* disconnect all the devices on the card .
* called from init . c
*/
2005-11-17 15:51:18 +03:00
int snd_device_disconnect_all ( struct snd_card * card )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:51:18 +03:00
struct snd_device * dev ;
2005-04-17 02:20:36 +04:00
int err = 0 ;
2008-08-08 19:09:09 +04:00
if ( snd_BUG_ON ( ! card ) )
return - ENXIO ;
2006-10-05 18:02:22 +04:00
list_for_each_entry ( dev , & card - > devices , list ) {
2005-04-17 02:20:36 +04:00
if ( snd_device_disconnect ( card , dev - > device_data ) < 0 )
err = - ENXIO ;
}
return err ;
}
/*
* release all the devices on the card .
* called from init . c
*/
2005-11-17 15:51:18 +03:00
int snd_device_free_all ( struct snd_card * card , snd_device_cmd_t cmd )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:51:18 +03:00
struct snd_device * dev ;
2005-04-17 02:20:36 +04:00
int err ;
2011-02-14 13:00:47 +03:00
unsigned int range_low , range_high , type ;
2005-04-17 02:20:36 +04:00
2008-08-08 19:09:09 +04:00
if ( snd_BUG_ON ( ! card ) )
return - ENXIO ;
2011-02-14 13:00:47 +03:00
range_low = ( __force unsigned int ) cmd * SNDRV_DEV_TYPE_RANGE_SIZE ;
2005-04-17 02:20:36 +04:00
range_high = range_low + SNDRV_DEV_TYPE_RANGE_SIZE - 1 ;
__again :
2006-10-05 18:02:22 +04:00
list_for_each_entry ( dev , & card - > devices , list ) {
2011-02-14 13:00:47 +03:00
type = ( __force unsigned int ) dev - > type ;
if ( type > = range_low & & type < = range_high ) {
2005-04-17 02:20:36 +04:00
if ( ( err = snd_device_free ( card , dev - > device_data ) ) < 0 )
return err ;
goto __again ;
}
}
return 0 ;
}