2005-04-16 15:20:36 -07:00
/*
* Device management routines
* 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/slab.h>
# include <linux/time.h>
# include <linux/errno.h>
# include <sound/core.h>
/**
* snd_device_new - create an ALSA device component
* @ card : the card instance
2005-06-29 19:30:42 +02:00
* @ type : the device type , SNDRV_DEV_XXX
2005-04-16 15:20:36 -07: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 .
*
* Returns zero if successful , or a negative error code on failure .
*/
int snd_device_new ( snd_card_t * card , snd_device_type_t type ,
void * device_data , snd_device_ops_t * ops )
{
snd_device_t * dev ;
2005-06-29 19:30:42 +02:00
snd_assert ( card ! = NULL , return - ENXIO ) ;
snd_assert ( device_data ! = NULL , return - ENXIO ) ;
snd_assert ( ops ! = NULL , return - ENXIO ) ;
2005-09-09 14:20:23 +02:00
dev = kzalloc ( sizeof ( * dev ) , GFP_KERNEL ) ;
2005-04-16 15:20:36 -07:00
if ( dev = = NULL )
return - ENOMEM ;
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 ;
}
/**
* 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
* callback , dev_unregister or dev_free , corresponding to the state .
* Then release the device .
*
* Returns zero if successful , or a negative error code on failure or if the
* device not found .
*/
int snd_device_free ( snd_card_t * card , void * device_data )
{
struct list_head * list ;
snd_device_t * dev ;
snd_assert ( card ! = NULL , return - ENXIO ) ;
snd_assert ( device_data ! = NULL , return - ENXIO ) ;
list_for_each ( list , & card - > devices ) {
dev = snd_device ( list ) ;
if ( dev - > device_data ! = device_data )
continue ;
/* unlink */
list_del ( & dev - > list ) ;
if ( ( dev - > state = = SNDRV_DEV_REGISTERED | | dev - > state = = SNDRV_DEV_DISCONNECTED ) & &
dev - > ops - > dev_unregister ) {
if ( dev - > ops - > dev_unregister ( dev ) )
snd_printk ( KERN_ERR " device unregister failure \n " ) ;
} else {
if ( dev - > ops - > dev_free ) {
if ( dev - > ops - > dev_free ( dev ) )
snd_printk ( KERN_ERR " device free failure \n " ) ;
}
}
kfree ( dev ) ;
return 0 ;
}
snd_printd ( " device free %p (from %p), not found \n " , device_data , __builtin_return_address ( 0 ) ) ;
return - ENXIO ;
}
/**
2005-06-29 19:30:42 +02:00
* snd_device_disconnect - disconnect the device
2005-04-16 15:20:36 -07: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 ( ) .
*
* Returns zero if successful , or a negative error code on failure or if the
* device not found .
*/
int snd_device_disconnect ( snd_card_t * card , void * device_data )
{
struct list_head * list ;
snd_device_t * dev ;
2005-06-29 19:30:42 +02:00
2005-04-16 15:20:36 -07:00
snd_assert ( card ! = NULL , return - ENXIO ) ;
snd_assert ( device_data ! = NULL , return - ENXIO ) ;
list_for_each ( list , & card - > devices ) {
dev = snd_device ( list ) ;
if ( dev - > device_data ! = device_data )
continue ;
if ( dev - > state = = SNDRV_DEV_REGISTERED & & dev - > ops - > dev_disconnect ) {
if ( dev - > ops - > dev_disconnect ( dev ) )
snd_printk ( KERN_ERR " device disconnect failure \n " ) ;
dev - > state = SNDRV_DEV_DISCONNECTED ;
}
return 0 ;
}
snd_printd ( " device disconnect %p (from %p), not found \n " , device_data , __builtin_return_address ( 0 ) ) ;
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 ( ) .
*
* Returns zero if successful , or a negative error code on failure or if the
* device not found .
*/
int snd_device_register ( snd_card_t * card , void * device_data )
{
struct list_head * list ;
snd_device_t * dev ;
int err ;
2005-06-29 19:30:42 +02:00
snd_assert ( card ! = NULL , return - ENXIO ) ;
snd_assert ( device_data ! = NULL , return - ENXIO ) ;
2005-04-16 15:20:36 -07:00
list_for_each ( list , & card - > devices ) {
dev = snd_device ( list ) ;
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 ;
}
return - EBUSY ;
}
snd_BUG ( ) ;
return - ENXIO ;
}
/*
* register all the devices on the card .
* called from init . c
*/
int snd_device_register_all ( snd_card_t * card )
{
struct list_head * list ;
snd_device_t * dev ;
int err ;
snd_assert ( card ! = NULL , return - ENXIO ) ;
list_for_each ( list , & card - > devices ) {
dev = snd_device ( list ) ;
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
*/
int snd_device_disconnect_all ( snd_card_t * card )
{
snd_device_t * dev ;
struct list_head * list ;
int err = 0 ;
snd_assert ( card ! = NULL , return - ENXIO ) ;
list_for_each ( list , & card - > devices ) {
dev = snd_device ( list ) ;
if ( snd_device_disconnect ( card , dev - > device_data ) < 0 )
err = - ENXIO ;
}
return err ;
}
/*
* release all the devices on the card .
* called from init . c
*/
int snd_device_free_all ( snd_card_t * card , snd_device_cmd_t cmd )
{
snd_device_t * dev ;
struct list_head * list ;
int err ;
unsigned int range_low , range_high ;
snd_assert ( card ! = NULL , return - ENXIO ) ;
range_low = cmd * SNDRV_DEV_TYPE_RANGE_SIZE ;
range_high = range_low + SNDRV_DEV_TYPE_RANGE_SIZE - 1 ;
__again :
list_for_each ( list , & card - > devices ) {
dev = snd_device ( list ) ;
if ( dev - > type > = range_low & & dev - > type < = range_high ) {
if ( ( err = snd_device_free ( card , dev - > device_data ) ) < 0 )
return err ;
goto __again ;
}
}
return 0 ;
}