2005-04-17 02:20:36 +04:00
/*
* Timers abstract layer
* 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/delay.h>
# include <linux/init.h>
# include <linux/smp_lock.h>
# include <linux/slab.h>
# include <linux/time.h>
2006-01-16 18:29:08 +03:00
# include <linux/mutex.h>
2005-04-17 02:20:36 +04:00
# include <linux/moduleparam.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/timer.h>
# include <sound/control.h>
# include <sound/info.h>
# include <sound/minors.h>
# include <sound/initval.h>
# include <linux/kmod.h>
# ifdef CONFIG_KERNELD
# include <linux/kerneld.h>
# endif
# if defined(CONFIG_SND_HPET) || defined(CONFIG_SND_HPET_MODULE)
# define DEFAULT_TIMER_LIMIT 3
# elif defined(CONFIG_SND_RTCTIMER) || defined(CONFIG_SND_RTCTIMER_MODULE)
# define DEFAULT_TIMER_LIMIT 2
# else
# define DEFAULT_TIMER_LIMIT 1
# endif
static int timer_limit = DEFAULT_TIMER_LIMIT ;
MODULE_AUTHOR ( " Jaroslav Kysela <perex@suse.cz>, Takashi Iwai <tiwai@suse.de> " ) ;
MODULE_DESCRIPTION ( " ALSA timer interface " ) ;
MODULE_LICENSE ( " GPL " ) ;
module_param ( timer_limit , int , 0444 ) ;
MODULE_PARM_DESC ( timer_limit , " Maximum global timers in system. " ) ;
2005-11-17 15:56:05 +03:00
struct snd_timer_user {
struct snd_timer_instance * timeri ;
2005-10-12 19:20:21 +04:00
int tread ; /* enhanced read with timestamps and events */
2005-04-17 02:20:36 +04:00
unsigned long ticks ;
unsigned long overrun ;
int qhead ;
int qtail ;
int qused ;
int queue_size ;
2005-11-17 15:56:05 +03:00
struct snd_timer_read * queue ;
struct snd_timer_tread * tqueue ;
2005-04-17 02:20:36 +04:00
spinlock_t qlock ;
unsigned long last_resolution ;
unsigned int filter ;
struct timespec tstamp ; /* trigger tstamp */
wait_queue_head_t qchange_sleep ;
struct fasync_struct * fasync ;
2006-01-16 18:29:08 +03:00
struct mutex tread_sem ;
2005-11-17 15:56:05 +03:00
} ;
2005-04-17 02:20:36 +04:00
/* list of timers */
static LIST_HEAD ( snd_timer_list ) ;
/* list of slave instances */
static LIST_HEAD ( snd_timer_slave_list ) ;
/* lock for slave active lists */
static DEFINE_SPINLOCK ( slave_active_lock ) ;
2006-01-16 18:29:08 +03:00
static DEFINE_MUTEX ( register_mutex ) ;
2005-04-17 02:20:36 +04:00
2005-11-17 15:56:05 +03:00
static int snd_timer_free ( struct snd_timer * timer ) ;
static int snd_timer_dev_free ( struct snd_device * device ) ;
static int snd_timer_dev_register ( struct snd_device * device ) ;
static int snd_timer_dev_unregister ( struct snd_device * device ) ;
2005-04-17 02:20:36 +04:00
2005-11-17 15:56:05 +03:00
static void snd_timer_reschedule ( struct snd_timer * timer , unsigned long ticks_left ) ;
2005-04-17 02:20:36 +04:00
/*
* create a timer instance with the given owner string .
* when timer is not NULL , increments the module counter
*/
2005-11-17 15:56:05 +03:00
static struct snd_timer_instance * snd_timer_instance_new ( char * owner ,
struct snd_timer * timer )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:56:05 +03:00
struct snd_timer_instance * timeri ;
2005-09-09 16:20:23 +04:00
timeri = kzalloc ( sizeof ( * timeri ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( timeri = = NULL )
return NULL ;
2005-06-23 11:09:02 +04:00
timeri - > owner = kstrdup ( owner , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! timeri - > owner ) {
kfree ( timeri ) ;
return NULL ;
}
INIT_LIST_HEAD ( & timeri - > open_list ) ;
INIT_LIST_HEAD ( & timeri - > active_list ) ;
INIT_LIST_HEAD ( & timeri - > ack_list ) ;
INIT_LIST_HEAD ( & timeri - > slave_list_head ) ;
INIT_LIST_HEAD ( & timeri - > slave_active_head ) ;
timeri - > timer = timer ;
2005-10-12 19:12:31 +04:00
if ( timer & & ! try_module_get ( timer - > module ) ) {
2005-04-17 02:20:36 +04:00
kfree ( timeri - > owner ) ;
kfree ( timeri ) ;
return NULL ;
}
return timeri ;
}
/*
* find a timer instance from the given timer id
*/
2005-11-17 15:56:05 +03:00
static struct snd_timer * snd_timer_find ( struct snd_timer_id * tid )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:56:05 +03:00
struct snd_timer * timer = NULL ;
2005-04-17 02:20:36 +04:00
struct list_head * p ;
list_for_each ( p , & snd_timer_list ) {
2005-11-17 15:56:05 +03:00
timer = list_entry ( p , struct snd_timer , device_list ) ;
2005-04-17 02:20:36 +04:00
if ( timer - > tmr_class ! = tid - > dev_class )
continue ;
if ( ( timer - > tmr_class = = SNDRV_TIMER_CLASS_CARD | |
timer - > tmr_class = = SNDRV_TIMER_CLASS_PCM ) & &
( timer - > card = = NULL | |
timer - > card - > number ! = tid - > card ) )
continue ;
if ( timer - > tmr_device ! = tid - > device )
continue ;
if ( timer - > tmr_subdevice ! = tid - > subdevice )
continue ;
return timer ;
}
return NULL ;
}
# ifdef CONFIG_KMOD
2005-11-17 15:56:05 +03:00
static void snd_timer_request ( struct snd_timer_id * tid )
2005-04-17 02:20:36 +04:00
{
if ( ! current - > fs - > root )
return ;
switch ( tid - > dev_class ) {
case SNDRV_TIMER_CLASS_GLOBAL :
if ( tid - > device < timer_limit )
request_module ( " snd-timer-%i " , tid - > device ) ;
break ;
case SNDRV_TIMER_CLASS_CARD :
case SNDRV_TIMER_CLASS_PCM :
if ( tid - > card < snd_ecards_limit )
request_module ( " snd-card-%i " , tid - > card ) ;
break ;
default :
break ;
}
}
# endif
/*
* look for a master instance matching with the slave id of the given slave .
* when found , relink the open_link of the slave .
*
* call this with register_mutex down .
*/
2005-11-17 15:56:05 +03:00
static void snd_timer_check_slave ( struct snd_timer_instance * slave )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:56:05 +03:00
struct snd_timer * timer ;
struct snd_timer_instance * master ;
2005-04-17 02:20:36 +04:00
struct list_head * p , * q ;
/* FIXME: it's really dumb to look up all entries.. */
list_for_each ( p , & snd_timer_list ) {
2005-11-17 15:56:05 +03:00
timer = list_entry ( p , struct snd_timer , device_list ) ;
2005-04-17 02:20:36 +04:00
list_for_each ( q , & timer - > open_list_head ) {
2005-11-17 15:56:05 +03:00
master = list_entry ( q , struct snd_timer_instance , open_list ) ;
2005-04-17 02:20:36 +04:00
if ( slave - > slave_class = = master - > slave_class & &
slave - > slave_id = = master - > slave_id ) {
list_del ( & slave - > open_list ) ;
2005-10-12 19:20:21 +04:00
list_add_tail ( & slave - > open_list ,
& master - > slave_list_head ) ;
2005-04-17 02:20:36 +04:00
spin_lock_irq ( & slave_active_lock ) ;
slave - > master = master ;
slave - > timer = master - > timer ;
spin_unlock_irq ( & slave_active_lock ) ;
return ;
}
}
}
}
/*
* look for slave instances matching with the slave id of the given master .
* when found , relink the open_link of slaves .
*
* call this with register_mutex down .
*/
2005-11-17 15:56:05 +03:00
static void snd_timer_check_master ( struct snd_timer_instance * master )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:56:05 +03:00
struct snd_timer_instance * slave ;
2005-04-17 02:20:36 +04:00
struct list_head * p , * n ;
/* check all pending slaves */
list_for_each_safe ( p , n , & snd_timer_slave_list ) {
2005-11-17 15:56:05 +03:00
slave = list_entry ( p , struct snd_timer_instance , open_list ) ;
2005-04-17 02:20:36 +04:00
if ( slave - > slave_class = = master - > slave_class & &
slave - > slave_id = = master - > slave_id ) {
list_del ( p ) ;
list_add_tail ( p , & master - > slave_list_head ) ;
spin_lock_irq ( & slave_active_lock ) ;
slave - > master = master ;
slave - > timer = master - > timer ;
if ( slave - > flags & SNDRV_TIMER_IFLG_RUNNING )
2005-10-12 19:20:21 +04:00
list_add_tail ( & slave - > active_list ,
& master - > slave_active_head ) ;
2005-04-17 02:20:36 +04:00
spin_unlock_irq ( & slave_active_lock ) ;
}
}
}
/*
* open a timer instance
* when opening a master , the slave id must be here given .
*/
2005-11-17 15:56:05 +03:00
int snd_timer_open ( struct snd_timer_instance * * ti ,
char * owner , struct snd_timer_id * tid ,
2005-04-17 02:20:36 +04:00
unsigned int slave_id )
{
2005-11-17 15:56:05 +03:00
struct snd_timer * timer ;
struct snd_timer_instance * timeri = NULL ;
2005-10-12 19:20:21 +04:00
2005-04-17 02:20:36 +04:00
if ( tid - > dev_class = = SNDRV_TIMER_CLASS_SLAVE ) {
/* open a slave instance */
if ( tid - > dev_sclass < = SNDRV_TIMER_SCLASS_NONE | |
tid - > dev_sclass > SNDRV_TIMER_SCLASS_OSS_SEQUENCER ) {
snd_printd ( " invalid slave class %i \n " , tid - > dev_sclass ) ;
return - EINVAL ;
}
2006-01-16 18:29:08 +03:00
mutex_lock ( & register_mutex ) ;
2005-04-17 02:20:36 +04:00
timeri = snd_timer_instance_new ( owner , NULL ) ;
2005-10-12 19:10:35 +04:00
if ( ! timeri ) {
2006-01-16 18:29:08 +03:00
mutex_unlock ( & register_mutex ) ;
2005-10-12 19:10:35 +04:00
return - ENOMEM ;
}
2005-04-17 02:20:36 +04:00
timeri - > slave_class = tid - > dev_sclass ;
timeri - > slave_id = tid - > device ;
timeri - > flags | = SNDRV_TIMER_IFLG_SLAVE ;
list_add_tail ( & timeri - > open_list , & snd_timer_slave_list ) ;
snd_timer_check_slave ( timeri ) ;
2006-01-16 18:29:08 +03:00
mutex_unlock ( & register_mutex ) ;
2005-04-17 02:20:36 +04:00
* ti = timeri ;
return 0 ;
}
/* open a master instance */
2006-01-16 18:29:08 +03:00
mutex_lock ( & register_mutex ) ;
2005-04-17 02:20:36 +04:00
timer = snd_timer_find ( tid ) ;
# ifdef CONFIG_KMOD
if ( timer = = NULL ) {
2006-01-16 18:29:08 +03:00
mutex_unlock ( & register_mutex ) ;
2005-04-17 02:20:36 +04:00
snd_timer_request ( tid ) ;
2006-01-16 18:29:08 +03:00
mutex_lock ( & register_mutex ) ;
2005-04-17 02:20:36 +04:00
timer = snd_timer_find ( tid ) ;
}
# endif
2005-10-12 19:10:35 +04:00
if ( ! timer ) {
2006-01-16 18:29:08 +03:00
mutex_unlock ( & register_mutex ) ;
2005-04-17 02:20:36 +04:00
return - ENODEV ;
}
2005-10-12 19:10:35 +04:00
if ( ! list_empty ( & timer - > open_list_head ) ) {
timeri = list_entry ( timer - > open_list_head . next ,
2005-11-17 15:56:05 +03:00
struct snd_timer_instance , open_list ) ;
2005-10-12 19:10:35 +04:00
if ( timeri - > flags & SNDRV_TIMER_IFLG_EXCLUSIVE ) {
2006-01-16 18:29:08 +03:00
mutex_unlock ( & register_mutex ) ;
2005-10-12 19:10:35 +04:00
return - EBUSY ;
}
}
timeri = snd_timer_instance_new ( owner , timer ) ;
if ( ! timeri ) {
2006-01-16 18:29:08 +03:00
mutex_unlock ( & register_mutex ) ;
2005-10-12 19:10:35 +04:00
return - ENOMEM ;
}
timeri - > slave_class = tid - > dev_sclass ;
timeri - > slave_id = slave_id ;
if ( list_empty ( & timer - > open_list_head ) & & timer - > hw . open )
timer - > hw . open ( timer ) ;
list_add_tail ( & timeri - > open_list , & timer - > open_list_head ) ;
snd_timer_check_master ( timeri ) ;
2006-01-16 18:29:08 +03:00
mutex_unlock ( & register_mutex ) ;
2005-04-17 02:20:36 +04:00
* ti = timeri ;
return 0 ;
}
2005-11-17 15:56:05 +03:00
static int _snd_timer_stop ( struct snd_timer_instance * timeri ,
int keep_flag , int event ) ;
2005-04-17 02:20:36 +04:00
/*
* close a timer instance
*/
2005-11-17 15:56:05 +03:00
int snd_timer_close ( struct snd_timer_instance * timeri )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:56:05 +03:00
struct snd_timer * timer = NULL ;
2005-04-17 02:20:36 +04:00
struct list_head * p , * n ;
2005-11-17 15:56:05 +03:00
struct snd_timer_instance * slave ;
2005-04-17 02:20:36 +04:00
snd_assert ( timeri ! = NULL , return - ENXIO ) ;
/* force to stop the timer */
snd_timer_stop ( timeri ) ;
if ( timeri - > flags & SNDRV_TIMER_IFLG_SLAVE ) {
/* wait, until the active callback is finished */
spin_lock_irq ( & slave_active_lock ) ;
while ( timeri - > flags & SNDRV_TIMER_IFLG_CALLBACK ) {
spin_unlock_irq ( & slave_active_lock ) ;
udelay ( 10 ) ;
spin_lock_irq ( & slave_active_lock ) ;
}
spin_unlock_irq ( & slave_active_lock ) ;
2006-01-16 18:29:08 +03:00
mutex_lock ( & register_mutex ) ;
2005-04-17 02:20:36 +04:00
list_del ( & timeri - > open_list ) ;
2006-01-16 18:29:08 +03:00
mutex_unlock ( & register_mutex ) ;
2005-04-17 02:20:36 +04:00
} else {
timer = timeri - > timer ;
/* wait, until the active callback is finished */
spin_lock_irq ( & timer - > lock ) ;
while ( timeri - > flags & SNDRV_TIMER_IFLG_CALLBACK ) {
spin_unlock_irq ( & timer - > lock ) ;
udelay ( 10 ) ;
spin_lock_irq ( & timer - > lock ) ;
}
spin_unlock_irq ( & timer - > lock ) ;
2006-01-16 18:29:08 +03:00
mutex_lock ( & register_mutex ) ;
2005-04-17 02:20:36 +04:00
list_del ( & timeri - > open_list ) ;
2005-10-12 19:20:21 +04:00
if ( timer & & list_empty ( & timer - > open_list_head ) & &
timer - > hw . close )
2005-04-17 02:20:36 +04:00
timer - > hw . close ( timer ) ;
/* remove slave links */
list_for_each_safe ( p , n , & timeri - > slave_list_head ) {
2005-11-17 15:56:05 +03:00
slave = list_entry ( p , struct snd_timer_instance , open_list ) ;
2005-04-17 02:20:36 +04:00
spin_lock_irq ( & slave_active_lock ) ;
_snd_timer_stop ( slave , 1 , SNDRV_TIMER_EVENT_RESOLUTION ) ;
list_del ( p ) ;
list_add_tail ( p , & snd_timer_slave_list ) ;
slave - > master = NULL ;
slave - > timer = NULL ;
spin_unlock_irq ( & slave_active_lock ) ;
}
2006-01-16 18:29:08 +03:00
mutex_unlock ( & register_mutex ) ;
2005-04-17 02:20:36 +04:00
}
if ( timeri - > private_free )
timeri - > private_free ( timeri ) ;
kfree ( timeri - > owner ) ;
kfree ( timeri ) ;
2005-10-12 19:12:31 +04:00
if ( timer )
module_put ( timer - > module ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2005-11-17 15:56:05 +03:00
unsigned long snd_timer_resolution ( struct snd_timer_instance * timeri )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:56:05 +03:00
struct snd_timer * timer ;
2005-04-17 02:20:36 +04:00
if ( timeri = = NULL )
return 0 ;
if ( ( timer = timeri - > timer ) ! = NULL ) {
if ( timer - > hw . c_resolution )
return timer - > hw . c_resolution ( timer ) ;
return timer - > hw . resolution ;
}
return 0 ;
}
2005-11-17 15:56:05 +03:00
static void snd_timer_notify1 ( struct snd_timer_instance * ti , int event )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:56:05 +03:00
struct snd_timer * timer ;
2005-04-17 02:20:36 +04:00
unsigned long flags ;
unsigned long resolution = 0 ;
2005-11-17 15:56:05 +03:00
struct snd_timer_instance * ts ;
2005-04-17 02:20:36 +04:00
struct list_head * n ;
struct timespec tstamp ;
2005-10-10 13:49:49 +04:00
getnstimeofday ( & tstamp ) ;
2005-10-12 19:20:21 +04:00
snd_assert ( event > = SNDRV_TIMER_EVENT_START & &
event < = SNDRV_TIMER_EVENT_PAUSE , return ) ;
if ( event = = SNDRV_TIMER_EVENT_START | |
event = = SNDRV_TIMER_EVENT_CONTINUE )
2005-04-17 02:20:36 +04:00
resolution = snd_timer_resolution ( ti ) ;
if ( ti - > ccallback )
ti - > ccallback ( ti , SNDRV_TIMER_EVENT_START , & tstamp , resolution ) ;
if ( ti - > flags & SNDRV_TIMER_IFLG_SLAVE )
return ;
timer = ti - > timer ;
if ( timer = = NULL )
return ;
if ( timer - > hw . flags & SNDRV_TIMER_HW_SLAVE )
return ;
spin_lock_irqsave ( & timer - > lock , flags ) ;
list_for_each ( n , & ti - > slave_active_head ) {
2005-11-17 15:56:05 +03:00
ts = list_entry ( n , struct snd_timer_instance , active_list ) ;
2005-04-17 02:20:36 +04:00
if ( ts - > ccallback )
ts - > ccallback ( ti , event + 100 , & tstamp , resolution ) ;
}
spin_unlock_irqrestore ( & timer - > lock , flags ) ;
}
2005-11-17 15:56:05 +03:00
static int snd_timer_start1 ( struct snd_timer * timer , struct snd_timer_instance * timeri ,
2005-10-12 19:20:21 +04:00
unsigned long sticks )
2005-04-17 02:20:36 +04:00
{
list_del ( & timeri - > active_list ) ;
list_add_tail ( & timeri - > active_list , & timer - > active_list_head ) ;
if ( timer - > running ) {
if ( timer - > hw . flags & SNDRV_TIMER_HW_SLAVE )
goto __start_now ;
timer - > flags | = SNDRV_TIMER_FLG_RESCHED ;
timeri - > flags | = SNDRV_TIMER_IFLG_START ;
return 1 ; /* delayed start */
} else {
timer - > sticks = sticks ;
timer - > hw . start ( timer ) ;
__start_now :
timer - > running + + ;
timeri - > flags | = SNDRV_TIMER_IFLG_RUNNING ;
return 0 ;
}
}
2005-11-17 15:56:05 +03:00
static int snd_timer_start_slave ( struct snd_timer_instance * timeri )
2005-04-17 02:20:36 +04:00
{
unsigned long flags ;
spin_lock_irqsave ( & slave_active_lock , flags ) ;
timeri - > flags | = SNDRV_TIMER_IFLG_RUNNING ;
if ( timeri - > master )
2005-10-12 19:20:21 +04:00
list_add_tail ( & timeri - > active_list ,
& timeri - > master - > slave_active_head ) ;
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( & slave_active_lock , flags ) ;
return 1 ; /* delayed start */
}
/*
* start the timer instance
2005-10-12 19:20:21 +04:00
*/
2005-11-17 15:56:05 +03:00
int snd_timer_start ( struct snd_timer_instance * timeri , unsigned int ticks )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:56:05 +03:00
struct snd_timer * timer ;
2005-04-17 02:20:36 +04:00
int result = - EINVAL ;
unsigned long flags ;
if ( timeri = = NULL | | ticks < 1 )
return - EINVAL ;
if ( timeri - > flags & SNDRV_TIMER_IFLG_SLAVE ) {
result = snd_timer_start_slave ( timeri ) ;
snd_timer_notify1 ( timeri , SNDRV_TIMER_EVENT_START ) ;
return result ;
}
timer = timeri - > timer ;
if ( timer = = NULL )
return - EINVAL ;
spin_lock_irqsave ( & timer - > lock , flags ) ;
timeri - > ticks = timeri - > cticks = ticks ;
timeri - > pticks = 0 ;
result = snd_timer_start1 ( timer , timeri , ticks ) ;
spin_unlock_irqrestore ( & timer - > lock , flags ) ;
snd_timer_notify1 ( timeri , SNDRV_TIMER_EVENT_START ) ;
return result ;
}
2005-11-17 15:56:05 +03:00
static int _snd_timer_stop ( struct snd_timer_instance * timeri ,
int keep_flag , int event )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:56:05 +03:00
struct snd_timer * timer ;
2005-04-17 02:20:36 +04:00
unsigned long flags ;
snd_assert ( timeri ! = NULL , return - ENXIO ) ;
if ( timeri - > flags & SNDRV_TIMER_IFLG_SLAVE ) {
if ( ! keep_flag ) {
spin_lock_irqsave ( & slave_active_lock , flags ) ;
timeri - > flags & = ~ SNDRV_TIMER_IFLG_RUNNING ;
spin_unlock_irqrestore ( & slave_active_lock , flags ) ;
}
goto __end ;
}
timer = timeri - > timer ;
if ( ! timer )
return - EINVAL ;
spin_lock_irqsave ( & timer - > lock , flags ) ;
list_del_init ( & timeri - > ack_list ) ;
list_del_init ( & timeri - > active_list ) ;
if ( ( timeri - > flags & SNDRV_TIMER_IFLG_RUNNING ) & &
! ( - - timer - > running ) ) {
timer - > hw . stop ( timer ) ;
if ( timer - > flags & SNDRV_TIMER_FLG_RESCHED ) {
timer - > flags & = ~ SNDRV_TIMER_FLG_RESCHED ;
snd_timer_reschedule ( timer , 0 ) ;
if ( timer - > flags & SNDRV_TIMER_FLG_CHANGE ) {
timer - > flags & = ~ SNDRV_TIMER_FLG_CHANGE ;
timer - > hw . start ( timer ) ;
}
}
}
if ( ! keep_flag )
2005-10-12 19:20:21 +04:00
timeri - > flags & =
~ ( SNDRV_TIMER_IFLG_RUNNING | SNDRV_TIMER_IFLG_START ) ;
2005-04-17 02:20:36 +04:00
spin_unlock_irqrestore ( & timer - > lock , flags ) ;
__end :
if ( event ! = SNDRV_TIMER_EVENT_RESOLUTION )
snd_timer_notify1 ( timeri , event ) ;
return 0 ;
}
/*
* stop the timer instance .
*
* do not call this from the timer callback !
*/
2005-11-17 15:56:05 +03:00
int snd_timer_stop ( struct snd_timer_instance * timeri )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:56:05 +03:00
struct snd_timer * timer ;
2005-04-17 02:20:36 +04:00
unsigned long flags ;
int err ;
err = _snd_timer_stop ( timeri , 0 , SNDRV_TIMER_EVENT_STOP ) ;
if ( err < 0 )
return err ;
timer = timeri - > timer ;
spin_lock_irqsave ( & timer - > lock , flags ) ;
timeri - > cticks = timeri - > ticks ;
timeri - > pticks = 0 ;
spin_unlock_irqrestore ( & timer - > lock , flags ) ;
return 0 ;
}
/*
* start again . . the tick is kept .
*/
2005-11-17 15:56:05 +03:00
int snd_timer_continue ( struct snd_timer_instance * timeri )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:56:05 +03:00
struct snd_timer * timer ;
2005-04-17 02:20:36 +04:00
int result = - EINVAL ;
unsigned long flags ;
if ( timeri = = NULL )
return result ;
if ( timeri - > flags & SNDRV_TIMER_IFLG_SLAVE )
return snd_timer_start_slave ( timeri ) ;
timer = timeri - > timer ;
if ( ! timer )
return - EINVAL ;
spin_lock_irqsave ( & timer - > lock , flags ) ;
if ( ! timeri - > cticks )
timeri - > cticks = 1 ;
timeri - > pticks = 0 ;
result = snd_timer_start1 ( timer , timeri , timer - > sticks ) ;
spin_unlock_irqrestore ( & timer - > lock , flags ) ;
snd_timer_notify1 ( timeri , SNDRV_TIMER_EVENT_CONTINUE ) ;
return result ;
}
/*
* pause . . remember the ticks left
*/
2005-11-17 15:56:05 +03:00
int snd_timer_pause ( struct snd_timer_instance * timeri )
2005-04-17 02:20:36 +04:00
{
return _snd_timer_stop ( timeri , 0 , SNDRV_TIMER_EVENT_PAUSE ) ;
}
/*
* reschedule the timer
*
* start pending instances and check the scheduling ticks .
* when the scheduling ticks is changed set CHANGE flag to reprogram the timer .
*/
2005-11-17 15:56:05 +03:00
static void snd_timer_reschedule ( struct snd_timer * timer , unsigned long ticks_left )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:56:05 +03:00
struct snd_timer_instance * ti ;
2005-04-17 02:20:36 +04:00
unsigned long ticks = ~ 0UL ;
struct list_head * p ;
list_for_each ( p , & timer - > active_list_head ) {
2005-11-17 15:56:05 +03:00
ti = list_entry ( p , struct snd_timer_instance , active_list ) ;
2005-04-17 02:20:36 +04:00
if ( ti - > flags & SNDRV_TIMER_IFLG_START ) {
ti - > flags & = ~ SNDRV_TIMER_IFLG_START ;
ti - > flags | = SNDRV_TIMER_IFLG_RUNNING ;
timer - > running + + ;
}
if ( ti - > flags & SNDRV_TIMER_IFLG_RUNNING ) {
if ( ticks > ti - > cticks )
ticks = ti - > cticks ;
}
}
if ( ticks = = ~ 0UL ) {
timer - > flags & = ~ SNDRV_TIMER_FLG_RESCHED ;
return ;
}
if ( ticks > timer - > hw . ticks )
ticks = timer - > hw . ticks ;
if ( ticks_left ! = ticks )
timer - > flags | = SNDRV_TIMER_FLG_CHANGE ;
timer - > sticks = ticks ;
}
/*
* timer tasklet
*
*/
static void snd_timer_tasklet ( unsigned long arg )
{
2005-11-17 15:56:05 +03:00
struct snd_timer * timer = ( struct snd_timer * ) arg ;
struct snd_timer_instance * ti ;
2005-04-17 02:20:36 +04:00
struct list_head * p ;
unsigned long resolution , ticks ;
spin_lock ( & timer - > lock ) ;
/* now process all callbacks */
while ( ! list_empty ( & timer - > sack_list_head ) ) {
p = timer - > sack_list_head . next ; /* get first item */
2005-11-17 15:56:05 +03:00
ti = list_entry ( p , struct snd_timer_instance , ack_list ) ;
2005-04-17 02:20:36 +04:00
/* remove from ack_list and make empty */
list_del_init ( p ) ;
2005-10-12 19:20:21 +04:00
2005-04-17 02:20:36 +04:00
ticks = ti - > pticks ;
ti - > pticks = 0 ;
resolution = ti - > resolution ;
ti - > flags | = SNDRV_TIMER_IFLG_CALLBACK ;
spin_unlock ( & timer - > lock ) ;
if ( ti - > callback )
ti - > callback ( ti , resolution , ticks ) ;
spin_lock ( & timer - > lock ) ;
ti - > flags & = ~ SNDRV_TIMER_IFLG_CALLBACK ;
}
spin_unlock ( & timer - > lock ) ;
}
/*
* timer interrupt
*
* ticks_left is usually equal to timer - > sticks .
*
*/
2005-11-17 15:56:05 +03:00
void snd_timer_interrupt ( struct snd_timer * timer , unsigned long ticks_left )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:56:05 +03:00
struct snd_timer_instance * ti , * ts ;
2005-04-17 02:20:36 +04:00
unsigned long resolution , ticks ;
2005-10-12 19:20:21 +04:00
struct list_head * p , * q , * n , * ack_list_head ;
2005-11-18 20:52:14 +03:00
unsigned long flags ;
2005-04-17 02:20:36 +04:00
int use_tasklet = 0 ;
if ( timer = = NULL )
return ;
2005-11-18 20:52:14 +03:00
spin_lock_irqsave ( & timer - > lock , flags ) ;
2005-04-17 02:20:36 +04:00
/* remember the current resolution */
if ( timer - > hw . c_resolution )
resolution = timer - > hw . c_resolution ( timer ) ;
else
resolution = timer - > hw . resolution ;
/* loop for all active instances
2005-10-12 19:20:21 +04:00
* Here we cannot use list_for_each because the active_list of a
* processed instance is relinked to done_list_head before the callback
* is called .
2005-04-17 02:20:36 +04:00
*/
list_for_each_safe ( p , n , & timer - > active_list_head ) {
2005-11-17 15:56:05 +03:00
ti = list_entry ( p , struct snd_timer_instance , active_list ) ;
2005-04-17 02:20:36 +04:00
if ( ! ( ti - > flags & SNDRV_TIMER_IFLG_RUNNING ) )
continue ;
ti - > pticks + = ticks_left ;
ti - > resolution = resolution ;
if ( ti - > cticks < ticks_left )
ti - > cticks = 0 ;
else
ti - > cticks - = ticks_left ;
if ( ti - > cticks ) /* not expired */
continue ;
if ( ti - > flags & SNDRV_TIMER_IFLG_AUTO ) {
ti - > cticks = ti - > ticks ;
} else {
ti - > flags & = ~ SNDRV_TIMER_IFLG_RUNNING ;
if ( - - timer - > running )
list_del ( p ) ;
}
2005-10-12 19:20:21 +04:00
if ( ( timer - > hw . flags & SNDRV_TIMER_HW_TASKLET ) | |
( ti - > flags & SNDRV_TIMER_IFLG_FAST ) )
ack_list_head = & timer - > ack_list_head ;
else
ack_list_head = & timer - > sack_list_head ;
if ( list_empty ( & ti - > ack_list ) )
list_add_tail ( & ti - > ack_list , ack_list_head ) ;
2005-04-17 02:20:36 +04:00
list_for_each ( q , & ti - > slave_active_head ) {
2005-11-17 15:56:05 +03:00
ts = list_entry ( q , struct snd_timer_instance , active_list ) ;
2005-04-17 02:20:36 +04:00
ts - > pticks = ti - > pticks ;
ts - > resolution = resolution ;
2005-10-12 19:20:21 +04:00
if ( list_empty ( & ts - > ack_list ) )
list_add_tail ( & ts - > ack_list , ack_list_head ) ;
2005-04-17 02:20:36 +04:00
}
}
if ( timer - > flags & SNDRV_TIMER_FLG_RESCHED )
snd_timer_reschedule ( timer , ticks_left ) ;
if ( timer - > running ) {
if ( timer - > hw . flags & SNDRV_TIMER_HW_STOP ) {
timer - > hw . stop ( timer ) ;
timer - > flags | = SNDRV_TIMER_FLG_CHANGE ;
}
if ( ! ( timer - > hw . flags & SNDRV_TIMER_HW_AUTO ) | |
( timer - > flags & SNDRV_TIMER_FLG_CHANGE ) ) {
/* restart timer */
timer - > flags & = ~ SNDRV_TIMER_FLG_CHANGE ;
timer - > hw . start ( timer ) ;
}
} else {
timer - > hw . stop ( timer ) ;
}
/* now process all fast callbacks */
while ( ! list_empty ( & timer - > ack_list_head ) ) {
p = timer - > ack_list_head . next ; /* get first item */
2005-11-17 15:56:05 +03:00
ti = list_entry ( p , struct snd_timer_instance , ack_list ) ;
2005-10-12 19:20:21 +04:00
2005-04-17 02:20:36 +04:00
/* remove from ack_list and make empty */
list_del_init ( p ) ;
2005-10-12 19:20:21 +04:00
2005-04-17 02:20:36 +04:00
ticks = ti - > pticks ;
ti - > pticks = 0 ;
ti - > flags | = SNDRV_TIMER_IFLG_CALLBACK ;
spin_unlock ( & timer - > lock ) ;
if ( ti - > callback )
ti - > callback ( ti , resolution , ticks ) ;
spin_lock ( & timer - > lock ) ;
ti - > flags & = ~ SNDRV_TIMER_IFLG_CALLBACK ;
}
/* do we have any slow callbacks? */
use_tasklet = ! list_empty ( & timer - > sack_list_head ) ;
2005-11-18 20:52:14 +03:00
spin_unlock_irqrestore ( & timer - > lock , flags ) ;
2005-04-17 02:20:36 +04:00
if ( use_tasklet )
tasklet_hi_schedule ( & timer - > task_queue ) ;
}
/*
*/
2005-11-17 15:56:05 +03:00
int snd_timer_new ( struct snd_card * card , char * id , struct snd_timer_id * tid ,
struct snd_timer * * rtimer )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:56:05 +03:00
struct snd_timer * timer ;
2005-04-17 02:20:36 +04:00
int err ;
2005-11-17 15:56:05 +03:00
static struct snd_device_ops ops = {
2005-04-17 02:20:36 +04:00
. dev_free = snd_timer_dev_free ,
. dev_register = snd_timer_dev_register ,
. dev_unregister = snd_timer_dev_unregister
} ;
snd_assert ( tid ! = NULL , return - EINVAL ) ;
snd_assert ( rtimer ! = NULL , return - EINVAL ) ;
* rtimer = NULL ;
2005-09-09 16:20:23 +04:00
timer = kzalloc ( sizeof ( * timer ) , GFP_KERNEL ) ;
2005-11-17 19:44:01 +03:00
if ( timer = = NULL ) {
snd_printk ( KERN_ERR " timer: cannot allocate \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
timer - > tmr_class = tid - > dev_class ;
timer - > card = card ;
timer - > tmr_device = tid - > device ;
timer - > tmr_subdevice = tid - > subdevice ;
if ( id )
strlcpy ( timer - > id , id , sizeof ( timer - > id ) ) ;
INIT_LIST_HEAD ( & timer - > device_list ) ;
INIT_LIST_HEAD ( & timer - > open_list_head ) ;
INIT_LIST_HEAD ( & timer - > active_list_head ) ;
INIT_LIST_HEAD ( & timer - > ack_list_head ) ;
INIT_LIST_HEAD ( & timer - > sack_list_head ) ;
spin_lock_init ( & timer - > lock ) ;
2005-10-12 19:20:21 +04:00
tasklet_init ( & timer - > task_queue , snd_timer_tasklet ,
( unsigned long ) timer ) ;
2005-04-17 02:20:36 +04:00
if ( card ! = NULL ) {
2005-10-12 19:12:31 +04:00
timer - > module = card - > module ;
2005-10-12 19:20:21 +04:00
err = snd_device_new ( card , SNDRV_DEV_TIMER , timer , & ops ) ;
if ( err < 0 ) {
2005-04-17 02:20:36 +04:00
snd_timer_free ( timer ) ;
return err ;
}
}
* rtimer = timer ;
return 0 ;
}
2005-11-17 15:56:05 +03:00
static int snd_timer_free ( struct snd_timer * timer )
2005-04-17 02:20:36 +04:00
{
snd_assert ( timer ! = NULL , return - ENXIO ) ;
if ( timer - > private_free )
timer - > private_free ( timer ) ;
kfree ( timer ) ;
return 0 ;
}
2005-11-17 15:56:05 +03:00
static int snd_timer_dev_free ( struct snd_device * device )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:56:05 +03:00
struct snd_timer * timer = device - > device_data ;
2005-04-17 02:20:36 +04:00
return snd_timer_free ( timer ) ;
}
2005-11-17 15:56:05 +03:00
static int snd_timer_dev_register ( struct snd_device * dev )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:56:05 +03:00
struct snd_timer * timer = dev - > device_data ;
struct snd_timer * timer1 ;
2005-04-17 02:20:36 +04:00
struct list_head * p ;
2005-10-12 19:20:21 +04:00
snd_assert ( timer ! = NULL & & timer - > hw . start ! = NULL & &
timer - > hw . stop ! = NULL , return - ENXIO ) ;
2005-04-17 02:20:36 +04:00
if ( ! ( timer - > hw . flags & SNDRV_TIMER_HW_SLAVE ) & &
! timer - > hw . resolution & & timer - > hw . c_resolution = = NULL )
return - EINVAL ;
2006-01-16 18:29:08 +03:00
mutex_lock ( & register_mutex ) ;
2005-04-17 02:20:36 +04:00
list_for_each ( p , & snd_timer_list ) {
2005-11-17 15:56:05 +03:00
timer1 = list_entry ( p , struct snd_timer , device_list ) ;
2005-04-17 02:20:36 +04:00
if ( timer1 - > tmr_class > timer - > tmr_class )
break ;
if ( timer1 - > tmr_class < timer - > tmr_class )
continue ;
if ( timer1 - > card & & timer - > card ) {
if ( timer1 - > card - > number > timer - > card - > number )
break ;
if ( timer1 - > card - > number < timer - > card - > number )
continue ;
}
if ( timer1 - > tmr_device > timer - > tmr_device )
break ;
if ( timer1 - > tmr_device < timer - > tmr_device )
continue ;
if ( timer1 - > tmr_subdevice > timer - > tmr_subdevice )
break ;
if ( timer1 - > tmr_subdevice < timer - > tmr_subdevice )
continue ;
/* conflicts.. */
2006-01-16 18:29:08 +03:00
mutex_unlock ( & register_mutex ) ;
2005-04-17 02:20:36 +04:00
return - EBUSY ;
}
list_add_tail ( & timer - > device_list , p ) ;
2006-01-16 18:29:08 +03:00
mutex_unlock ( & register_mutex ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
2005-11-17 15:56:05 +03:00
static int snd_timer_unregister ( struct snd_timer * timer )
2005-04-17 02:20:36 +04:00
{
struct list_head * p , * n ;
2005-11-17 15:56:05 +03:00
struct snd_timer_instance * ti ;
2005-04-17 02:20:36 +04:00
snd_assert ( timer ! = NULL , return - ENXIO ) ;
2006-01-16 18:29:08 +03:00
mutex_lock ( & register_mutex ) ;
2005-04-17 02:20:36 +04:00
if ( ! list_empty ( & timer - > open_list_head ) ) {
snd_printk ( KERN_WARNING " timer 0x%lx is busy? \n " , ( long ) timer ) ;
list_for_each_safe ( p , n , & timer - > open_list_head ) {
list_del_init ( p ) ;
2005-11-17 15:56:05 +03:00
ti = list_entry ( p , struct snd_timer_instance , open_list ) ;
2005-04-17 02:20:36 +04:00
ti - > timer = NULL ;
}
}
list_del ( & timer - > device_list ) ;
2006-01-16 18:29:08 +03:00
mutex_unlock ( & register_mutex ) ;
2005-04-17 02:20:36 +04:00
return snd_timer_free ( timer ) ;
}
2005-11-17 15:56:05 +03:00
static int snd_timer_dev_unregister ( struct snd_device * device )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:56:05 +03:00
struct snd_timer * timer = device - > device_data ;
2005-04-17 02:20:36 +04:00
return snd_timer_unregister ( timer ) ;
}
2005-11-17 15:56:05 +03:00
void snd_timer_notify ( struct snd_timer * timer , int event , struct timespec * tstamp )
2005-04-17 02:20:36 +04:00
{
unsigned long flags ;
unsigned long resolution = 0 ;
2005-11-17 15:56:05 +03:00
struct snd_timer_instance * ti , * ts ;
2005-04-17 02:20:36 +04:00
struct list_head * p , * n ;
2005-10-10 13:46:31 +04:00
if ( ! ( timer - > hw . flags & SNDRV_TIMER_HW_SLAVE ) )
return ;
2005-10-12 19:20:21 +04:00
snd_assert ( event > = SNDRV_TIMER_EVENT_MSTART & &
event < = SNDRV_TIMER_EVENT_MRESUME , return ) ;
2005-04-17 02:20:36 +04:00
spin_lock_irqsave ( & timer - > lock , flags ) ;
2005-08-16 13:09:05 +04:00
if ( event = = SNDRV_TIMER_EVENT_MSTART | |
event = = SNDRV_TIMER_EVENT_MCONTINUE | |
event = = SNDRV_TIMER_EVENT_MRESUME ) {
2005-04-17 02:20:36 +04:00
if ( timer - > hw . c_resolution )
resolution = timer - > hw . c_resolution ( timer ) ;
else
resolution = timer - > hw . resolution ;
}
list_for_each ( p , & timer - > active_list_head ) {
2005-11-17 15:56:05 +03:00
ti = list_entry ( p , struct snd_timer_instance , active_list ) ;
2005-04-17 02:20:36 +04:00
if ( ti - > ccallback )
ti - > ccallback ( ti , event , tstamp , resolution ) ;
list_for_each ( n , & ti - > slave_active_head ) {
2005-11-17 15:56:05 +03:00
ts = list_entry ( n , struct snd_timer_instance , active_list ) ;
2005-04-17 02:20:36 +04:00
if ( ts - > ccallback )
ts - > ccallback ( ts , event , tstamp , resolution ) ;
}
}
spin_unlock_irqrestore ( & timer - > lock , flags ) ;
}
/*
* exported functions for global timers
*/
2005-11-17 15:56:05 +03:00
int snd_timer_global_new ( char * id , int device , struct snd_timer * * rtimer )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:56:05 +03:00
struct snd_timer_id tid ;
2005-10-12 19:20:21 +04:00
2005-04-17 02:20:36 +04:00
tid . dev_class = SNDRV_TIMER_CLASS_GLOBAL ;
tid . dev_sclass = SNDRV_TIMER_SCLASS_NONE ;
tid . card = - 1 ;
tid . device = device ;
tid . subdevice = 0 ;
return snd_timer_new ( NULL , id , & tid , rtimer ) ;
}
2005-11-17 15:56:05 +03:00
int snd_timer_global_free ( struct snd_timer * timer )
2005-04-17 02:20:36 +04:00
{
return snd_timer_free ( timer ) ;
}
2005-11-17 15:56:05 +03:00
int snd_timer_global_register ( struct snd_timer * timer )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:56:05 +03:00
struct snd_device dev ;
2005-04-17 02:20:36 +04:00
memset ( & dev , 0 , sizeof ( dev ) ) ;
dev . device_data = timer ;
return snd_timer_dev_register ( & dev ) ;
}
2005-11-17 15:56:05 +03:00
int snd_timer_global_unregister ( struct snd_timer * timer )
2005-04-17 02:20:36 +04:00
{
return snd_timer_unregister ( timer ) ;
}
2005-10-12 19:20:21 +04:00
/*
2005-04-17 02:20:36 +04:00
* System timer
*/
struct snd_timer_system_private {
struct timer_list tlist ;
struct timer * timer ;
unsigned long last_expires ;
unsigned long last_jiffies ;
unsigned long correction ;
} ;
static void snd_timer_s_function ( unsigned long data )
{
2005-11-17 15:56:05 +03:00
struct snd_timer * timer = ( struct snd_timer * ) data ;
2005-04-17 02:20:36 +04:00
struct snd_timer_system_private * priv = timer - > private_data ;
unsigned long jiff = jiffies ;
if ( time_after ( jiff , priv - > last_expires ) )
priv - > correction = ( long ) jiff - ( long ) priv - > last_expires ;
snd_timer_interrupt ( timer , ( long ) jiff - ( long ) priv - > last_jiffies ) ;
}
2005-11-17 15:56:05 +03:00
static int snd_timer_s_start ( struct snd_timer * timer )
2005-04-17 02:20:36 +04:00
{
struct snd_timer_system_private * priv ;
unsigned long njiff ;
priv = ( struct snd_timer_system_private * ) timer - > private_data ;
njiff = ( priv - > last_jiffies = jiffies ) ;
if ( priv - > correction > timer - > sticks - 1 ) {
priv - > correction - = timer - > sticks - 1 ;
njiff + + ;
} else {
njiff + = timer - > sticks - priv - > correction ;
priv - > correction - = timer - > sticks ;
}
priv - > last_expires = priv - > tlist . expires = njiff ;
add_timer ( & priv - > tlist ) ;
return 0 ;
}
2005-11-17 15:56:05 +03:00
static int snd_timer_s_stop ( struct snd_timer * timer )
2005-04-17 02:20:36 +04:00
{
struct snd_timer_system_private * priv ;
unsigned long jiff ;
priv = ( struct snd_timer_system_private * ) timer - > private_data ;
del_timer ( & priv - > tlist ) ;
jiff = jiffies ;
if ( time_before ( jiff , priv - > last_expires ) )
timer - > sticks = priv - > last_expires - jiff ;
else
timer - > sticks = 1 ;
return 0 ;
}
2005-11-17 15:56:05 +03:00
static struct snd_timer_hardware snd_timer_system =
2005-04-17 02:20:36 +04:00
{
. flags = SNDRV_TIMER_HW_FIRST | SNDRV_TIMER_HW_TASKLET ,
. resolution = 1000000000L / HZ ,
. ticks = 10000000L ,
. start = snd_timer_s_start ,
. stop = snd_timer_s_stop
} ;
2005-11-17 15:56:05 +03:00
static void snd_timer_free_system ( struct snd_timer * timer )
2005-04-17 02:20:36 +04:00
{
kfree ( timer - > private_data ) ;
}
static int snd_timer_register_system ( void )
{
2005-11-17 15:56:05 +03:00
struct snd_timer * timer ;
2005-04-17 02:20:36 +04:00
struct snd_timer_system_private * priv ;
int err ;
2005-10-12 19:20:21 +04:00
err = snd_timer_global_new ( " system " , SNDRV_TIMER_GLOBAL_SYSTEM , & timer ) ;
if ( err < 0 )
2005-04-17 02:20:36 +04:00
return err ;
strcpy ( timer - > name , " system timer " ) ;
timer - > hw = snd_timer_system ;
2005-09-09 16:20:23 +04:00
priv = kzalloc ( sizeof ( * priv ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( priv = = NULL ) {
snd_timer_free ( timer ) ;
return - ENOMEM ;
}
init_timer ( & priv - > tlist ) ;
priv - > tlist . function = snd_timer_s_function ;
priv - > tlist . data = ( unsigned long ) timer ;
timer - > private_data = priv ;
timer - > private_free = snd_timer_free_system ;
return snd_timer_global_register ( timer ) ;
}
2005-12-01 12:42:42 +03:00
# ifdef CONFIG_PROC_FS
2005-04-17 02:20:36 +04:00
/*
* Info interface
*/
2005-11-17 15:56:05 +03:00
static void snd_timer_proc_read ( struct snd_info_entry * entry ,
struct snd_info_buffer * buffer )
2005-04-17 02:20:36 +04:00
{
unsigned long flags ;
2005-11-17 15:56:05 +03:00
struct snd_timer * timer ;
struct snd_timer_instance * ti ;
2005-04-17 02:20:36 +04:00
struct list_head * p , * q ;
2006-01-16 18:29:08 +03:00
mutex_lock ( & register_mutex ) ;
2005-04-17 02:20:36 +04:00
list_for_each ( p , & snd_timer_list ) {
2005-11-17 15:56:05 +03:00
timer = list_entry ( p , struct snd_timer , device_list ) ;
2005-04-17 02:20:36 +04:00
switch ( timer - > tmr_class ) {
case SNDRV_TIMER_CLASS_GLOBAL :
snd_iprintf ( buffer , " G%i: " , timer - > tmr_device ) ;
break ;
case SNDRV_TIMER_CLASS_CARD :
2005-10-12 19:20:21 +04:00
snd_iprintf ( buffer , " C%i-%i: " ,
timer - > card - > number , timer - > tmr_device ) ;
2005-04-17 02:20:36 +04:00
break ;
case SNDRV_TIMER_CLASS_PCM :
2005-10-12 19:20:21 +04:00
snd_iprintf ( buffer , " P%i-%i-%i: " , timer - > card - > number ,
timer - > tmr_device , timer - > tmr_subdevice ) ;
2005-04-17 02:20:36 +04:00
break ;
default :
2005-10-12 19:20:21 +04:00
snd_iprintf ( buffer , " ?%i-%i-%i-%i: " , timer - > tmr_class ,
timer - > card ? timer - > card - > number : - 1 ,
timer - > tmr_device , timer - > tmr_subdevice ) ;
2005-04-17 02:20:36 +04:00
}
snd_iprintf ( buffer , " %s : " , timer - > name ) ;
if ( timer - > hw . resolution )
2005-10-12 19:20:21 +04:00
snd_iprintf ( buffer , " %lu.%03luus (%lu ticks) " ,
timer - > hw . resolution / 1000 ,
timer - > hw . resolution % 1000 ,
timer - > hw . ticks ) ;
2005-04-17 02:20:36 +04:00
if ( timer - > hw . flags & SNDRV_TIMER_HW_SLAVE )
snd_iprintf ( buffer , " SLAVE " ) ;
snd_iprintf ( buffer , " \n " ) ;
spin_lock_irqsave ( & timer - > lock , flags ) ;
list_for_each ( q , & timer - > open_list_head ) {
2005-11-17 15:56:05 +03:00
ti = list_entry ( q , struct snd_timer_instance , open_list ) ;
2005-10-12 19:20:21 +04:00
snd_iprintf ( buffer , " Client %s : %s \n " ,
ti - > owner ? ti - > owner : " unknown " ,
ti - > flags & ( SNDRV_TIMER_IFLG_START |
SNDRV_TIMER_IFLG_RUNNING )
? " running " : " stopped " ) ;
2005-04-17 02:20:36 +04:00
}
spin_unlock_irqrestore ( & timer - > lock , flags ) ;
}
2006-01-16 18:29:08 +03:00
mutex_unlock ( & register_mutex ) ;
2005-04-17 02:20:36 +04:00
}
2005-12-01 12:42:42 +03:00
static struct snd_info_entry * snd_timer_proc_entry = NULL ;
static void __init snd_timer_proc_init ( void )
{
struct snd_info_entry * entry ;
entry = snd_info_create_module_entry ( THIS_MODULE , " timers " , NULL ) ;
if ( entry ! = NULL ) {
entry - > c . text . read_size = SNDRV_TIMER_DEVICES * 128 ;
entry - > c . text . read = snd_timer_proc_read ;
if ( snd_info_register ( entry ) < 0 ) {
snd_info_free_entry ( entry ) ;
entry = NULL ;
}
}
snd_timer_proc_entry = entry ;
}
static void __exit snd_timer_proc_done ( void )
{
snd_info_unregister ( snd_timer_proc_entry ) ;
}
# else /* !CONFIG_PROC_FS */
# define snd_timer_proc_init()
# define snd_timer_proc_done()
# endif
2005-04-17 02:20:36 +04:00
/*
* USER SPACE interface
*/
2005-11-17 15:56:05 +03:00
static void snd_timer_user_interrupt ( struct snd_timer_instance * timeri ,
2005-04-17 02:20:36 +04:00
unsigned long resolution ,
unsigned long ticks )
{
2005-11-17 15:56:05 +03:00
struct snd_timer_user * tu = timeri - > callback_data ;
struct snd_timer_read * r ;
2005-04-17 02:20:36 +04:00
int prev ;
2005-10-12 19:20:21 +04:00
2005-04-17 02:20:36 +04:00
spin_lock ( & tu - > qlock ) ;
if ( tu - > qused > 0 ) {
prev = tu - > qtail = = 0 ? tu - > queue_size - 1 : tu - > qtail - 1 ;
r = & tu - > queue [ prev ] ;
if ( r - > resolution = = resolution ) {
r - > ticks + = ticks ;
goto __wake ;
}
}
if ( tu - > qused > = tu - > queue_size ) {
tu - > overrun + + ;
} else {
r = & tu - > queue [ tu - > qtail + + ] ;
tu - > qtail % = tu - > queue_size ;
r - > resolution = resolution ;
r - > ticks = ticks ;
tu - > qused + + ;
}
__wake :
spin_unlock ( & tu - > qlock ) ;
kill_fasync ( & tu - > fasync , SIGIO , POLL_IN ) ;
wake_up ( & tu - > qchange_sleep ) ;
}
2005-11-17 15:56:05 +03:00
static void snd_timer_user_append_to_tqueue ( struct snd_timer_user * tu ,
struct snd_timer_tread * tread )
2005-04-17 02:20:36 +04:00
{
if ( tu - > qused > = tu - > queue_size ) {
tu - > overrun + + ;
} else {
memcpy ( & tu - > tqueue [ tu - > qtail + + ] , tread , sizeof ( * tread ) ) ;
tu - > qtail % = tu - > queue_size ;
tu - > qused + + ;
}
}
2005-11-17 15:56:05 +03:00
static void snd_timer_user_ccallback ( struct snd_timer_instance * timeri ,
int event ,
2005-04-17 02:20:36 +04:00
struct timespec * tstamp ,
unsigned long resolution )
{
2005-11-17 15:56:05 +03:00
struct snd_timer_user * tu = timeri - > callback_data ;
struct snd_timer_tread r1 ;
2005-04-17 02:20:36 +04:00
2005-10-12 19:20:21 +04:00
if ( event > = SNDRV_TIMER_EVENT_START & &
event < = SNDRV_TIMER_EVENT_PAUSE )
2005-04-17 02:20:36 +04:00
tu - > tstamp = * tstamp ;
if ( ( tu - > filter & ( 1 < < event ) ) = = 0 | | ! tu - > tread )
return ;
r1 . event = event ;
r1 . tstamp = * tstamp ;
r1 . val = resolution ;
spin_lock ( & tu - > qlock ) ;
snd_timer_user_append_to_tqueue ( tu , & r1 ) ;
spin_unlock ( & tu - > qlock ) ;
kill_fasync ( & tu - > fasync , SIGIO , POLL_IN ) ;
wake_up ( & tu - > qchange_sleep ) ;
}
2005-11-17 15:56:05 +03:00
static void snd_timer_user_tinterrupt ( struct snd_timer_instance * timeri ,
2005-04-17 02:20:36 +04:00
unsigned long resolution ,
unsigned long ticks )
{
2005-11-17 15:56:05 +03:00
struct snd_timer_user * tu = timeri - > callback_data ;
struct snd_timer_tread * r , r1 ;
2005-04-17 02:20:36 +04:00
struct timespec tstamp ;
int prev , append = 0 ;
2005-10-10 13:49:49 +04:00
memset ( & tstamp , 0 , sizeof ( tstamp ) ) ;
2005-04-17 02:20:36 +04:00
spin_lock ( & tu - > qlock ) ;
2005-10-12 19:20:21 +04:00
if ( ( tu - > filter & ( ( 1 < < SNDRV_TIMER_EVENT_RESOLUTION ) |
( 1 < < SNDRV_TIMER_EVENT_TICK ) ) ) = = 0 ) {
2005-04-17 02:20:36 +04:00
spin_unlock ( & tu - > qlock ) ;
return ;
}
if ( tu - > last_resolution ! = resolution | | ticks > 0 )
2005-10-10 13:49:49 +04:00
getnstimeofday ( & tstamp ) ;
2005-10-12 19:20:21 +04:00
if ( ( tu - > filter & ( 1 < < SNDRV_TIMER_EVENT_RESOLUTION ) ) & &
tu - > last_resolution ! = resolution ) {
2005-04-17 02:20:36 +04:00
r1 . event = SNDRV_TIMER_EVENT_RESOLUTION ;
r1 . tstamp = tstamp ;
r1 . val = resolution ;
snd_timer_user_append_to_tqueue ( tu , & r1 ) ;
tu - > last_resolution = resolution ;
append + + ;
}
if ( ( tu - > filter & ( 1 < < SNDRV_TIMER_EVENT_TICK ) ) = = 0 )
goto __wake ;
if ( ticks = = 0 )
goto __wake ;
if ( tu - > qused > 0 ) {
prev = tu - > qtail = = 0 ? tu - > queue_size - 1 : tu - > qtail - 1 ;
r = & tu - > tqueue [ prev ] ;
if ( r - > event = = SNDRV_TIMER_EVENT_TICK ) {
r - > tstamp = tstamp ;
r - > val + = ticks ;
append + + ;
goto __wake ;
}
}
r1 . event = SNDRV_TIMER_EVENT_TICK ;
r1 . tstamp = tstamp ;
r1 . val = ticks ;
snd_timer_user_append_to_tqueue ( tu , & r1 ) ;
append + + ;
__wake :
spin_unlock ( & tu - > qlock ) ;
if ( append = = 0 )
return ;
kill_fasync ( & tu - > fasync , SIGIO , POLL_IN ) ;
wake_up ( & tu - > qchange_sleep ) ;
}
static int snd_timer_user_open ( struct inode * inode , struct file * file )
{
2005-11-17 15:56:05 +03:00
struct snd_timer_user * tu ;
2005-10-12 19:20:21 +04:00
2005-09-09 16:20:23 +04:00
tu = kzalloc ( sizeof ( * tu ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( tu = = NULL )
return - ENOMEM ;
spin_lock_init ( & tu - > qlock ) ;
init_waitqueue_head ( & tu - > qchange_sleep ) ;
2006-01-16 18:29:08 +03:00
mutex_init ( & tu - > tread_sem ) ;
2005-04-17 02:20:36 +04:00
tu - > ticks = 1 ;
tu - > queue_size = 128 ;
2005-11-17 15:56:05 +03:00
tu - > queue = kmalloc ( tu - > queue_size * sizeof ( struct snd_timer_read ) ,
2005-10-12 19:20:21 +04:00
GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( tu - > queue = = NULL ) {
kfree ( tu ) ;
return - ENOMEM ;
}
file - > private_data = tu ;
return 0 ;
}
static int snd_timer_user_release ( struct inode * inode , struct file * file )
{
2005-11-17 15:56:05 +03:00
struct snd_timer_user * tu ;
2005-04-17 02:20:36 +04:00
if ( file - > private_data ) {
tu = file - > private_data ;
file - > private_data = NULL ;
fasync_helper ( - 1 , file , 0 , & tu - > fasync ) ;
if ( tu - > timeri )
snd_timer_close ( tu - > timeri ) ;
kfree ( tu - > queue ) ;
kfree ( tu - > tqueue ) ;
kfree ( tu ) ;
}
return 0 ;
}
2005-11-17 15:56:05 +03:00
static void snd_timer_user_zero_id ( struct snd_timer_id * id )
2005-04-17 02:20:36 +04:00
{
id - > dev_class = SNDRV_TIMER_CLASS_NONE ;
id - > dev_sclass = SNDRV_TIMER_SCLASS_NONE ;
id - > card = - 1 ;
id - > device = - 1 ;
id - > subdevice = - 1 ;
}
2005-11-17 15:56:05 +03:00
static void snd_timer_user_copy_id ( struct snd_timer_id * id , struct snd_timer * timer )
2005-04-17 02:20:36 +04:00
{
id - > dev_class = timer - > tmr_class ;
id - > dev_sclass = SNDRV_TIMER_SCLASS_NONE ;
id - > card = timer - > card ? timer - > card - > number : - 1 ;
id - > device = timer - > tmr_device ;
id - > subdevice = timer - > tmr_subdevice ;
}
2005-11-17 15:56:05 +03:00
static int snd_timer_user_next_device ( struct snd_timer_id __user * _tid )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:56:05 +03:00
struct snd_timer_id id ;
struct snd_timer * timer ;
2005-04-17 02:20:36 +04:00
struct list_head * p ;
2005-10-12 19:20:21 +04:00
2005-04-17 02:20:36 +04:00
if ( copy_from_user ( & id , _tid , sizeof ( id ) ) )
return - EFAULT ;
2006-01-16 18:29:08 +03:00
mutex_lock ( & register_mutex ) ;
2005-04-17 02:20:36 +04:00
if ( id . dev_class < 0 ) { /* first item */
if ( list_empty ( & snd_timer_list ) )
snd_timer_user_zero_id ( & id ) ;
else {
2005-10-12 19:14:55 +04:00
timer = list_entry ( snd_timer_list . next ,
2005-11-17 15:56:05 +03:00
struct snd_timer , device_list ) ;
2005-04-17 02:20:36 +04:00
snd_timer_user_copy_id ( & id , timer ) ;
}
} else {
switch ( id . dev_class ) {
case SNDRV_TIMER_CLASS_GLOBAL :
id . device = id . device < 0 ? 0 : id . device + 1 ;
list_for_each ( p , & snd_timer_list ) {
2005-11-17 15:56:05 +03:00
timer = list_entry ( p , struct snd_timer , device_list ) ;
2005-04-17 02:20:36 +04:00
if ( timer - > tmr_class > SNDRV_TIMER_CLASS_GLOBAL ) {
snd_timer_user_copy_id ( & id , timer ) ;
break ;
}
if ( timer - > tmr_device > = id . device ) {
snd_timer_user_copy_id ( & id , timer ) ;
break ;
}
}
if ( p = = & snd_timer_list )
snd_timer_user_zero_id ( & id ) ;
break ;
case SNDRV_TIMER_CLASS_CARD :
case SNDRV_TIMER_CLASS_PCM :
if ( id . card < 0 ) {
id . card = 0 ;
} else {
if ( id . card < 0 ) {
id . card = 0 ;
} else {
if ( id . device < 0 ) {
id . device = 0 ;
} else {
2005-10-12 19:20:21 +04:00
if ( id . subdevice < 0 ) {
id . subdevice = 0 ;
} else {
id . subdevice + + ;
}
2005-04-17 02:20:36 +04:00
}
}
}
list_for_each ( p , & snd_timer_list ) {
2005-11-17 15:56:05 +03:00
timer = list_entry ( p , struct snd_timer , device_list ) ;
2005-04-17 02:20:36 +04:00
if ( timer - > tmr_class > id . dev_class ) {
snd_timer_user_copy_id ( & id , timer ) ;
break ;
}
if ( timer - > tmr_class < id . dev_class )
continue ;
if ( timer - > card - > number > id . card ) {
snd_timer_user_copy_id ( & id , timer ) ;
break ;
}
if ( timer - > card - > number < id . card )
continue ;
if ( timer - > tmr_device > id . device ) {
snd_timer_user_copy_id ( & id , timer ) ;
break ;
}
if ( timer - > tmr_device < id . device )
continue ;
if ( timer - > tmr_subdevice > id . subdevice ) {
snd_timer_user_copy_id ( & id , timer ) ;
break ;
}
if ( timer - > tmr_subdevice < id . subdevice )
continue ;
snd_timer_user_copy_id ( & id , timer ) ;
break ;
}
if ( p = = & snd_timer_list )
snd_timer_user_zero_id ( & id ) ;
break ;
default :
snd_timer_user_zero_id ( & id ) ;
}
}
2006-01-16 18:29:08 +03:00
mutex_unlock ( & register_mutex ) ;
2005-04-17 02:20:36 +04:00
if ( copy_to_user ( _tid , & id , sizeof ( * _tid ) ) )
return - EFAULT ;
return 0 ;
2005-10-12 19:20:21 +04:00
}
2005-04-17 02:20:36 +04:00
2005-10-12 19:20:21 +04:00
static int snd_timer_user_ginfo ( struct file * file ,
2005-11-17 15:56:05 +03:00
struct snd_timer_ginfo __user * _ginfo )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:56:05 +03:00
struct snd_timer_ginfo * ginfo ;
struct snd_timer_id tid ;
struct snd_timer * t ;
2005-04-17 02:20:36 +04:00
struct list_head * p ;
int err = 0 ;
ginfo = kmalloc ( sizeof ( * ginfo ) , GFP_KERNEL ) ;
if ( ! ginfo )
return - ENOMEM ;
if ( copy_from_user ( ginfo , _ginfo , sizeof ( * ginfo ) ) ) {
kfree ( ginfo ) ;
return - EFAULT ;
}
tid = ginfo - > tid ;
memset ( ginfo , 0 , sizeof ( * ginfo ) ) ;
ginfo - > tid = tid ;
2006-01-16 18:29:08 +03:00
mutex_lock ( & register_mutex ) ;
2005-04-17 02:20:36 +04:00
t = snd_timer_find ( & tid ) ;
if ( t ! = NULL ) {
ginfo - > card = t - > card ? t - > card - > number : - 1 ;
if ( t - > hw . flags & SNDRV_TIMER_HW_SLAVE )
ginfo - > flags | = SNDRV_TIMER_FLG_SLAVE ;
strlcpy ( ginfo - > id , t - > id , sizeof ( ginfo - > id ) ) ;
strlcpy ( ginfo - > name , t - > name , sizeof ( ginfo - > name ) ) ;
ginfo - > resolution = t - > hw . resolution ;
if ( t - > hw . resolution_min > 0 ) {
ginfo - > resolution_min = t - > hw . resolution_min ;
ginfo - > resolution_max = t - > hw . resolution_max ;
}
list_for_each ( p , & t - > open_list_head ) {
ginfo - > clients + + ;
}
} else {
err = - ENODEV ;
}
2006-01-16 18:29:08 +03:00
mutex_unlock ( & register_mutex ) ;
2005-04-17 02:20:36 +04:00
if ( err > = 0 & & copy_to_user ( _ginfo , ginfo , sizeof ( * ginfo ) ) )
err = - EFAULT ;
kfree ( ginfo ) ;
return err ;
}
2005-10-12 19:20:21 +04:00
static int snd_timer_user_gparams ( struct file * file ,
2005-11-17 15:56:05 +03:00
struct snd_timer_gparams __user * _gparams )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:56:05 +03:00
struct snd_timer_gparams gparams ;
struct snd_timer * t ;
2005-04-17 02:20:36 +04:00
int err ;
if ( copy_from_user ( & gparams , _gparams , sizeof ( gparams ) ) )
return - EFAULT ;
2006-01-16 18:29:08 +03:00
mutex_lock ( & register_mutex ) ;
2005-04-17 02:20:36 +04:00
t = snd_timer_find ( & gparams . tid ) ;
2005-10-12 19:20:21 +04:00
if ( ! t ) {
2005-04-17 02:20:36 +04:00
err = - ENODEV ;
2005-10-12 19:20:21 +04:00
goto _error ;
}
if ( ! list_empty ( & t - > open_list_head ) ) {
err = - EBUSY ;
goto _error ;
}
if ( ! t - > hw . set_period ) {
err = - ENOSYS ;
goto _error ;
2005-04-17 02:20:36 +04:00
}
2005-10-12 19:20:21 +04:00
err = t - > hw . set_period ( t , gparams . period_num , gparams . period_den ) ;
_error :
2006-01-16 18:29:08 +03:00
mutex_unlock ( & register_mutex ) ;
2005-04-17 02:20:36 +04:00
return err ;
}
2005-10-12 19:20:21 +04:00
static int snd_timer_user_gstatus ( struct file * file ,
2005-11-17 15:56:05 +03:00
struct snd_timer_gstatus __user * _gstatus )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:56:05 +03:00
struct snd_timer_gstatus gstatus ;
struct snd_timer_id tid ;
struct snd_timer * t ;
2005-04-17 02:20:36 +04:00
int err = 0 ;
if ( copy_from_user ( & gstatus , _gstatus , sizeof ( gstatus ) ) )
return - EFAULT ;
tid = gstatus . tid ;
memset ( & gstatus , 0 , sizeof ( gstatus ) ) ;
gstatus . tid = tid ;
2006-01-16 18:29:08 +03:00
mutex_lock ( & register_mutex ) ;
2005-04-17 02:20:36 +04:00
t = snd_timer_find ( & tid ) ;
if ( t ! = NULL ) {
if ( t - > hw . c_resolution )
gstatus . resolution = t - > hw . c_resolution ( t ) ;
else
gstatus . resolution = t - > hw . resolution ;
if ( t - > hw . precise_resolution ) {
2005-10-12 19:20:21 +04:00
t - > hw . precise_resolution ( t , & gstatus . resolution_num ,
& gstatus . resolution_den ) ;
2005-04-17 02:20:36 +04:00
} else {
gstatus . resolution_num = gstatus . resolution ;
gstatus . resolution_den = 1000000000uL ;
}
} else {
err = - ENODEV ;
}
2006-01-16 18:29:08 +03:00
mutex_unlock ( & register_mutex ) ;
2005-04-17 02:20:36 +04:00
if ( err > = 0 & & copy_to_user ( _gstatus , & gstatus , sizeof ( gstatus ) ) )
err = - EFAULT ;
return err ;
}
2005-10-12 19:20:21 +04:00
static int snd_timer_user_tselect ( struct file * file ,
2005-11-17 15:56:05 +03:00
struct snd_timer_select __user * _tselect )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:56:05 +03:00
struct snd_timer_user * tu ;
struct snd_timer_select tselect ;
2005-04-17 02:20:36 +04:00
char str [ 32 ] ;
2005-04-04 18:44:58 +04:00
int err = 0 ;
2005-10-12 19:20:21 +04:00
2005-04-17 02:20:36 +04:00
tu = file - > private_data ;
2006-01-16 18:29:08 +03:00
mutex_lock ( & tu - > tread_sem ) ;
2005-04-04 18:44:58 +04:00
if ( tu - > timeri ) {
2005-04-17 02:20:36 +04:00
snd_timer_close ( tu - > timeri ) ;
2005-04-04 18:44:58 +04:00
tu - > timeri = NULL ;
}
if ( copy_from_user ( & tselect , _tselect , sizeof ( tselect ) ) ) {
err = - EFAULT ;
goto __err ;
}
2005-04-17 02:20:36 +04:00
sprintf ( str , " application %i " , current - > pid ) ;
if ( tselect . id . dev_class ! = SNDRV_TIMER_CLASS_SLAVE )
tselect . id . dev_sclass = SNDRV_TIMER_SCLASS_APPLICATION ;
2005-10-12 19:20:21 +04:00
err = snd_timer_open ( & tu - > timeri , str , & tselect . id , current - > pid ) ;
if ( err < 0 )
2005-04-04 18:44:58 +04:00
goto __err ;
2005-04-17 02:20:36 +04:00
[ALSA] Remove redundant NULL checks before kfree
Timer Midlevel,ALSA sequencer,ALSA<-OSS sequencer,Digigram VX core
I2C tea6330t,GUS Library,VIA82xx driver,VIA82xx-modem driver
CA0106 driver,CS46xx driver,EMU10K1/EMU10K2 driver,YMFPCI driver
Digigram VX Pocket driver,Common EMU synth,USB generic driver,USB USX2Y
Checking a pointer for NULL before calling kfree() on it is redundant,
kfree() deals with NULL pointers just fine.
This patch removes such checks from sound/
This patch also makes another, but closely related, change.
It avoids casting pointers about to be kfree()'ed.
Signed-off-by: Jesper Juhl <juhl-lkml@dif.dk>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
2005-05-30 19:30:32 +04:00
kfree ( tu - > queue ) ;
tu - > queue = NULL ;
kfree ( tu - > tqueue ) ;
tu - > tqueue = NULL ;
2005-04-17 02:20:36 +04:00
if ( tu - > tread ) {
2005-11-17 15:56:05 +03:00
tu - > tqueue = kmalloc ( tu - > queue_size * sizeof ( struct snd_timer_tread ) ,
2005-10-12 19:20:21 +04:00
GFP_KERNEL ) ;
2005-04-04 18:44:58 +04:00
if ( tu - > tqueue = = NULL )
err = - ENOMEM ;
2005-04-17 02:20:36 +04:00
} else {
2005-11-17 15:56:05 +03:00
tu - > queue = kmalloc ( tu - > queue_size * sizeof ( struct snd_timer_read ) ,
2005-10-12 19:20:21 +04:00
GFP_KERNEL ) ;
2005-04-04 18:44:58 +04:00
if ( tu - > queue = = NULL )
err = - ENOMEM ;
2005-04-17 02:20:36 +04:00
}
2005-10-12 19:20:21 +04:00
2005-04-04 18:44:58 +04:00
if ( err < 0 ) {
snd_timer_close ( tu - > timeri ) ;
tu - > timeri = NULL ;
} else {
tu - > timeri - > flags | = SNDRV_TIMER_IFLG_FAST ;
2005-10-12 19:20:21 +04:00
tu - > timeri - > callback = tu - > tread
? snd_timer_user_tinterrupt : snd_timer_user_interrupt ;
2005-04-04 18:44:58 +04:00
tu - > timeri - > ccallback = snd_timer_user_ccallback ;
tu - > timeri - > callback_data = ( void * ) tu ;
}
__err :
2006-01-16 18:29:08 +03:00
mutex_unlock ( & tu - > tread_sem ) ;
2005-04-04 18:44:58 +04:00
return err ;
2005-04-17 02:20:36 +04:00
}
2005-10-12 19:20:21 +04:00
static int snd_timer_user_info ( struct file * file ,
2005-11-17 15:56:05 +03:00
struct snd_timer_info __user * _info )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:56:05 +03:00
struct snd_timer_user * tu ;
struct snd_timer_info * info ;
struct snd_timer * t ;
2005-04-17 02:20:36 +04:00
int err = 0 ;
tu = file - > private_data ;
snd_assert ( tu - > timeri ! = NULL , return - ENXIO ) ;
t = tu - > timeri - > timer ;
snd_assert ( t ! = NULL , return - ENXIO ) ;
2005-09-09 16:20:23 +04:00
info = kzalloc ( sizeof ( * info ) , GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ! info )
return - ENOMEM ;
info - > card = t - > card ? t - > card - > number : - 1 ;
if ( t - > hw . flags & SNDRV_TIMER_HW_SLAVE )
info - > flags | = SNDRV_TIMER_FLG_SLAVE ;
strlcpy ( info - > id , t - > id , sizeof ( info - > id ) ) ;
strlcpy ( info - > name , t - > name , sizeof ( info - > name ) ) ;
info - > resolution = t - > hw . resolution ;
if ( copy_to_user ( _info , info , sizeof ( * _info ) ) )
err = - EFAULT ;
kfree ( info ) ;
return err ;
}
2005-10-12 19:20:21 +04:00
static int snd_timer_user_params ( struct file * file ,
2005-11-17 15:56:05 +03:00
struct snd_timer_params __user * _params )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:56:05 +03:00
struct snd_timer_user * tu ;
struct snd_timer_params params ;
struct snd_timer * t ;
struct snd_timer_read * tr ;
struct snd_timer_tread * ttr ;
2005-04-17 02:20:36 +04:00
int err ;
2005-10-12 19:20:21 +04:00
2005-04-17 02:20:36 +04:00
tu = file - > private_data ;
snd_assert ( tu - > timeri ! = NULL , return - ENXIO ) ;
t = tu - > timeri - > timer ;
snd_assert ( t ! = NULL , return - ENXIO ) ;
if ( copy_from_user ( & params , _params , sizeof ( params ) ) )
return - EFAULT ;
if ( ! ( t - > hw . flags & SNDRV_TIMER_HW_SLAVE ) & & params . ticks < 1 ) {
err = - EINVAL ;
goto _end ;
}
2005-10-12 19:20:21 +04:00
if ( params . queue_size > 0 & &
( params . queue_size < 32 | | params . queue_size > 1024 ) ) {
2005-04-17 02:20:36 +04:00
err = - EINVAL ;
goto _end ;
}
if ( params . filter & ~ ( ( 1 < < SNDRV_TIMER_EVENT_RESOLUTION ) |
( 1 < < SNDRV_TIMER_EVENT_TICK ) |
( 1 < < SNDRV_TIMER_EVENT_START ) |
( 1 < < SNDRV_TIMER_EVENT_STOP ) |
( 1 < < SNDRV_TIMER_EVENT_CONTINUE ) |
( 1 < < SNDRV_TIMER_EVENT_PAUSE ) |
2005-08-16 13:09:05 +04:00
( 1 < < SNDRV_TIMER_EVENT_SUSPEND ) |
( 1 < < SNDRV_TIMER_EVENT_RESUME ) |
2005-04-17 02:20:36 +04:00
( 1 < < SNDRV_TIMER_EVENT_MSTART ) |
( 1 < < SNDRV_TIMER_EVENT_MSTOP ) |
( 1 < < SNDRV_TIMER_EVENT_MCONTINUE ) |
2005-08-16 15:05:43 +04:00
( 1 < < SNDRV_TIMER_EVENT_MPAUSE ) |
( 1 < < SNDRV_TIMER_EVENT_MSUSPEND ) |
2005-08-16 13:09:05 +04:00
( 1 < < SNDRV_TIMER_EVENT_MRESUME ) ) ) {
2005-04-17 02:20:36 +04:00
err = - EINVAL ;
goto _end ;
}
snd_timer_stop ( tu - > timeri ) ;
spin_lock_irq ( & t - > lock ) ;
tu - > timeri - > flags & = ~ ( SNDRV_TIMER_IFLG_AUTO |
SNDRV_TIMER_IFLG_EXCLUSIVE |
SNDRV_TIMER_IFLG_EARLY_EVENT ) ;
if ( params . flags & SNDRV_TIMER_PSFLG_AUTO )
tu - > timeri - > flags | = SNDRV_TIMER_IFLG_AUTO ;
if ( params . flags & SNDRV_TIMER_PSFLG_EXCLUSIVE )
tu - > timeri - > flags | = SNDRV_TIMER_IFLG_EXCLUSIVE ;
if ( params . flags & SNDRV_TIMER_PSFLG_EARLY_EVENT )
tu - > timeri - > flags | = SNDRV_TIMER_IFLG_EARLY_EVENT ;
spin_unlock_irq ( & t - > lock ) ;
2005-10-12 19:20:21 +04:00
if ( params . queue_size > 0 & &
( unsigned int ) tu - > queue_size ! = params . queue_size ) {
2005-04-17 02:20:36 +04:00
if ( tu - > tread ) {
2005-10-12 19:20:21 +04:00
ttr = kmalloc ( params . queue_size * sizeof ( * ttr ) ,
GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( ttr ) {
kfree ( tu - > tqueue ) ;
tu - > queue_size = params . queue_size ;
tu - > tqueue = ttr ;
}
} else {
2005-10-12 19:20:21 +04:00
tr = kmalloc ( params . queue_size * sizeof ( * tr ) ,
GFP_KERNEL ) ;
2005-04-17 02:20:36 +04:00
if ( tr ) {
kfree ( tu - > queue ) ;
tu - > queue_size = params . queue_size ;
tu - > queue = tr ;
}
}
}
tu - > qhead = tu - > qtail = tu - > qused = 0 ;
if ( tu - > timeri - > flags & SNDRV_TIMER_IFLG_EARLY_EVENT ) {
if ( tu - > tread ) {
2005-11-17 15:56:05 +03:00
struct snd_timer_tread tread ;
2005-04-17 02:20:36 +04:00
tread . event = SNDRV_TIMER_EVENT_EARLY ;
tread . tstamp . tv_sec = 0 ;
tread . tstamp . tv_nsec = 0 ;
tread . val = 0 ;
snd_timer_user_append_to_tqueue ( tu , & tread ) ;
} else {
2005-11-17 15:56:05 +03:00
struct snd_timer_read * r = & tu - > queue [ 0 ] ;
2005-04-17 02:20:36 +04:00
r - > resolution = 0 ;
r - > ticks = 0 ;
tu - > qused + + ;
tu - > qtail + + ;
}
}
tu - > filter = params . filter ;
tu - > ticks = params . ticks ;
err = 0 ;
_end :
if ( copy_to_user ( _params , & params , sizeof ( params ) ) )
return - EFAULT ;
return err ;
}
2005-10-12 19:20:21 +04:00
static int snd_timer_user_status ( struct file * file ,
2005-11-17 15:56:05 +03:00
struct snd_timer_status __user * _status )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:56:05 +03:00
struct snd_timer_user * tu ;
struct snd_timer_status status ;
2005-10-12 19:20:21 +04:00
2005-04-17 02:20:36 +04:00
tu = file - > private_data ;
snd_assert ( tu - > timeri ! = NULL , return - ENXIO ) ;
memset ( & status , 0 , sizeof ( status ) ) ;
status . tstamp = tu - > tstamp ;
status . resolution = snd_timer_resolution ( tu - > timeri ) ;
status . lost = tu - > timeri - > lost ;
status . overrun = tu - > overrun ;
spin_lock_irq ( & tu - > qlock ) ;
status . queue = tu - > qused ;
spin_unlock_irq ( & tu - > qlock ) ;
if ( copy_to_user ( _status , & status , sizeof ( status ) ) )
return - EFAULT ;
return 0 ;
}
static int snd_timer_user_start ( struct file * file )
{
int err ;
2005-11-17 15:56:05 +03:00
struct snd_timer_user * tu ;
2005-10-12 19:20:21 +04:00
2005-04-17 02:20:36 +04:00
tu = file - > private_data ;
snd_assert ( tu - > timeri ! = NULL , return - ENXIO ) ;
snd_timer_stop ( tu - > timeri ) ;
tu - > timeri - > lost = 0 ;
tu - > last_resolution = 0 ;
return ( err = snd_timer_start ( tu - > timeri , tu - > ticks ) ) < 0 ? err : 0 ;
}
static int snd_timer_user_stop ( struct file * file )
{
int err ;
2005-11-17 15:56:05 +03:00
struct snd_timer_user * tu ;
2005-10-12 19:20:21 +04:00
2005-04-17 02:20:36 +04:00
tu = file - > private_data ;
snd_assert ( tu - > timeri ! = NULL , return - ENXIO ) ;
return ( err = snd_timer_stop ( tu - > timeri ) ) < 0 ? err : 0 ;
}
static int snd_timer_user_continue ( struct file * file )
{
int err ;
2005-11-17 15:56:05 +03:00
struct snd_timer_user * tu ;
2005-10-12 19:20:21 +04:00
2005-04-17 02:20:36 +04:00
tu = file - > private_data ;
snd_assert ( tu - > timeri ! = NULL , return - ENXIO ) ;
tu - > timeri - > lost = 0 ;
return ( err = snd_timer_continue ( tu - > timeri ) ) < 0 ? err : 0 ;
}
2005-05-15 17:04:14 +04:00
static int snd_timer_user_pause ( struct file * file )
{
int err ;
2005-11-17 15:56:05 +03:00
struct snd_timer_user * tu ;
2005-10-12 19:20:21 +04:00
2005-05-15 17:04:14 +04:00
tu = file - > private_data ;
snd_assert ( tu - > timeri ! = NULL , return - ENXIO ) ;
2005-05-16 15:51:39 +04:00
return ( err = snd_timer_pause ( tu - > timeri ) ) < 0 ? err : 0 ;
2005-05-15 17:04:14 +04:00
}
2005-05-15 17:43:54 +04:00
enum {
SNDRV_TIMER_IOCTL_START_OLD = _IO ( ' T ' , 0x20 ) ,
SNDRV_TIMER_IOCTL_STOP_OLD = _IO ( ' T ' , 0x21 ) ,
SNDRV_TIMER_IOCTL_CONTINUE_OLD = _IO ( ' T ' , 0x22 ) ,
SNDRV_TIMER_IOCTL_PAUSE_OLD = _IO ( ' T ' , 0x23 ) ,
} ;
2005-10-12 19:20:21 +04:00
static long snd_timer_user_ioctl ( struct file * file , unsigned int cmd ,
unsigned long arg )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:56:05 +03:00
struct snd_timer_user * tu ;
2005-04-17 02:20:36 +04:00
void __user * argp = ( void __user * ) arg ;
int __user * p = argp ;
2005-10-12 19:20:21 +04:00
2005-04-17 02:20:36 +04:00
tu = file - > private_data ;
switch ( cmd ) {
case SNDRV_TIMER_IOCTL_PVERSION :
return put_user ( SNDRV_TIMER_VERSION , p ) ? - EFAULT : 0 ;
case SNDRV_TIMER_IOCTL_NEXT_DEVICE :
return snd_timer_user_next_device ( argp ) ;
case SNDRV_TIMER_IOCTL_TREAD :
{
int xarg ;
2005-10-12 19:20:21 +04:00
2006-01-16 18:29:08 +03:00
mutex_lock ( & tu - > tread_sem ) ;
2005-04-04 18:44:58 +04:00
if ( tu - > timeri ) { /* too late */
2006-01-16 18:29:08 +03:00
mutex_unlock ( & tu - > tread_sem ) ;
2005-04-17 02:20:36 +04:00
return - EBUSY ;
2005-04-04 18:44:58 +04:00
}
if ( get_user ( xarg , p ) ) {
2006-01-16 18:29:08 +03:00
mutex_unlock ( & tu - > tread_sem ) ;
2005-04-17 02:20:36 +04:00
return - EFAULT ;
2005-04-04 18:44:58 +04:00
}
2005-04-17 02:20:36 +04:00
tu - > tread = xarg ? 1 : 0 ;
2006-01-16 18:29:08 +03:00
mutex_unlock ( & tu - > tread_sem ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
case SNDRV_TIMER_IOCTL_GINFO :
return snd_timer_user_ginfo ( file , argp ) ;
case SNDRV_TIMER_IOCTL_GPARAMS :
return snd_timer_user_gparams ( file , argp ) ;
case SNDRV_TIMER_IOCTL_GSTATUS :
return snd_timer_user_gstatus ( file , argp ) ;
case SNDRV_TIMER_IOCTL_SELECT :
return snd_timer_user_tselect ( file , argp ) ;
case SNDRV_TIMER_IOCTL_INFO :
return snd_timer_user_info ( file , argp ) ;
case SNDRV_TIMER_IOCTL_PARAMS :
return snd_timer_user_params ( file , argp ) ;
case SNDRV_TIMER_IOCTL_STATUS :
return snd_timer_user_status ( file , argp ) ;
case SNDRV_TIMER_IOCTL_START :
2005-05-15 17:43:54 +04:00
case SNDRV_TIMER_IOCTL_START_OLD :
2005-04-17 02:20:36 +04:00
return snd_timer_user_start ( file ) ;
case SNDRV_TIMER_IOCTL_STOP :
2005-05-15 17:43:54 +04:00
case SNDRV_TIMER_IOCTL_STOP_OLD :
2005-04-17 02:20:36 +04:00
return snd_timer_user_stop ( file ) ;
case SNDRV_TIMER_IOCTL_CONTINUE :
2005-05-15 17:43:54 +04:00
case SNDRV_TIMER_IOCTL_CONTINUE_OLD :
2005-04-17 02:20:36 +04:00
return snd_timer_user_continue ( file ) ;
2005-05-15 17:04:14 +04:00
case SNDRV_TIMER_IOCTL_PAUSE :
2005-05-15 17:43:54 +04:00
case SNDRV_TIMER_IOCTL_PAUSE_OLD :
2005-05-15 17:04:14 +04:00
return snd_timer_user_pause ( file ) ;
2005-04-17 02:20:36 +04:00
}
return - ENOTTY ;
}
static int snd_timer_user_fasync ( int fd , struct file * file , int on )
{
2005-11-17 15:56:05 +03:00
struct snd_timer_user * tu ;
2005-04-17 02:20:36 +04:00
int err ;
2005-10-12 19:20:21 +04:00
2005-04-17 02:20:36 +04:00
tu = file - > private_data ;
err = fasync_helper ( fd , file , on , & tu - > fasync ) ;
if ( err < 0 )
return err ;
return 0 ;
}
2005-10-12 19:20:21 +04:00
static ssize_t snd_timer_user_read ( struct file * file , char __user * buffer ,
size_t count , loff_t * offset )
2005-04-17 02:20:36 +04:00
{
2005-11-17 15:56:05 +03:00
struct snd_timer_user * tu ;
2005-04-17 02:20:36 +04:00
long result = 0 , unit ;
int err = 0 ;
2005-10-12 19:20:21 +04:00
2005-04-17 02:20:36 +04:00
tu = file - > private_data ;
2005-11-17 15:56:05 +03:00
unit = tu - > tread ? sizeof ( struct snd_timer_tread ) : sizeof ( struct snd_timer_read ) ;
2005-04-17 02:20:36 +04:00
spin_lock_irq ( & tu - > qlock ) ;
while ( ( long ) count - result > = unit ) {
while ( ! tu - > qused ) {
wait_queue_t wait ;
if ( ( file - > f_flags & O_NONBLOCK ) ! = 0 | | result > 0 ) {
err = - EAGAIN ;
break ;
}
set_current_state ( TASK_INTERRUPTIBLE ) ;
init_waitqueue_entry ( & wait , current ) ;
add_wait_queue ( & tu - > qchange_sleep , & wait ) ;
spin_unlock_irq ( & tu - > qlock ) ;
schedule ( ) ;
spin_lock_irq ( & tu - > qlock ) ;
remove_wait_queue ( & tu - > qchange_sleep , & wait ) ;
if ( signal_pending ( current ) ) {
err = - ERESTARTSYS ;
break ;
}
}
spin_unlock_irq ( & tu - > qlock ) ;
if ( err < 0 )
goto _error ;
if ( tu - > tread ) {
2005-10-12 19:20:21 +04:00
if ( copy_to_user ( buffer , & tu - > tqueue [ tu - > qhead + + ] ,
2005-11-17 15:56:05 +03:00
sizeof ( struct snd_timer_tread ) ) ) {
2005-04-17 02:20:36 +04:00
err = - EFAULT ;
goto _error ;
}
} else {
2005-10-12 19:20:21 +04:00
if ( copy_to_user ( buffer , & tu - > queue [ tu - > qhead + + ] ,
2005-11-17 15:56:05 +03:00
sizeof ( struct snd_timer_read ) ) ) {
2005-04-17 02:20:36 +04:00
err = - EFAULT ;
goto _error ;
}
}
tu - > qhead % = tu - > queue_size ;
result + = unit ;
buffer + = unit ;
spin_lock_irq ( & tu - > qlock ) ;
tu - > qused - - ;
}
spin_unlock_irq ( & tu - > qlock ) ;
_error :
return result > 0 ? result : err ;
}
static unsigned int snd_timer_user_poll ( struct file * file , poll_table * wait )
{
unsigned int mask ;
2005-11-17 15:56:05 +03:00
struct snd_timer_user * tu ;
2005-04-17 02:20:36 +04:00
tu = file - > private_data ;
poll_wait ( file , & tu - > qchange_sleep , wait ) ;
2005-10-12 19:20:21 +04:00
2005-04-17 02:20:36 +04:00
mask = 0 ;
if ( tu - > qused )
mask | = POLLIN | POLLRDNORM ;
return mask ;
}
# ifdef CONFIG_COMPAT
# include "timer_compat.c"
# else
# define snd_timer_user_ioctl_compat NULL
# endif
static struct file_operations snd_timer_f_ops =
{
. owner = THIS_MODULE ,
. read = snd_timer_user_read ,
. open = snd_timer_user_open ,
. release = snd_timer_user_release ,
. poll = snd_timer_user_poll ,
. unlocked_ioctl = snd_timer_user_ioctl ,
. compat_ioctl = snd_timer_user_ioctl_compat ,
. fasync = snd_timer_user_fasync ,
} ;
/*
* ENTRY functions
*/
static int __init alsa_timer_init ( void )
{
int err ;
# ifdef SNDRV_OSS_INFO_DEV_TIMERS
2005-10-12 19:20:21 +04:00
snd_oss_info_register ( SNDRV_OSS_INFO_DEV_TIMERS , SNDRV_CARDS - 1 ,
" system timer " ) ;
2005-04-17 02:20:36 +04:00
# endif
2005-12-01 12:42:42 +03:00
2005-04-17 02:20:36 +04:00
if ( ( err = snd_timer_register_system ( ) ) < 0 )
2005-10-12 19:20:21 +04:00
snd_printk ( KERN_ERR " unable to register system timer (%i) \n " ,
err ) ;
2005-11-20 16:06:59 +03:00
if ( ( err = snd_register_device ( SNDRV_DEVICE_TYPE_TIMER , NULL , 0 ,
& snd_timer_f_ops , NULL , " timer " ) ) < 0 )
2005-10-12 19:20:21 +04:00
snd_printk ( KERN_ERR " unable to register timer device (%i) \n " ,
err ) ;
2005-12-01 12:42:42 +03:00
snd_timer_proc_init ( ) ;
2005-04-17 02:20:36 +04:00
return 0 ;
}
static void __exit alsa_timer_exit ( void )
{
struct list_head * p , * n ;
snd_unregister_device ( SNDRV_DEVICE_TYPE_TIMER , NULL , 0 ) ;
/* unregister the system timer */
list_for_each_safe ( p , n , & snd_timer_list ) {
2005-11-17 15:56:05 +03:00
struct snd_timer * timer = list_entry ( p , struct snd_timer , device_list ) ;
2005-04-17 02:20:36 +04:00
snd_timer_unregister ( timer ) ;
}
2005-12-01 12:42:42 +03:00
snd_timer_proc_done ( ) ;
2005-04-17 02:20:36 +04:00
# ifdef SNDRV_OSS_INFO_DEV_TIMERS
snd_oss_info_unregister ( SNDRV_OSS_INFO_DEV_TIMERS , SNDRV_CARDS - 1 ) ;
# endif
}
module_init ( alsa_timer_init )
module_exit ( alsa_timer_exit )
EXPORT_SYMBOL ( snd_timer_open ) ;
EXPORT_SYMBOL ( snd_timer_close ) ;
EXPORT_SYMBOL ( snd_timer_resolution ) ;
EXPORT_SYMBOL ( snd_timer_start ) ;
EXPORT_SYMBOL ( snd_timer_stop ) ;
EXPORT_SYMBOL ( snd_timer_continue ) ;
EXPORT_SYMBOL ( snd_timer_pause ) ;
EXPORT_SYMBOL ( snd_timer_new ) ;
EXPORT_SYMBOL ( snd_timer_notify ) ;
EXPORT_SYMBOL ( snd_timer_global_new ) ;
EXPORT_SYMBOL ( snd_timer_global_free ) ;
EXPORT_SYMBOL ( snd_timer_global_register ) ;
EXPORT_SYMBOL ( snd_timer_global_unregister ) ;
EXPORT_SYMBOL ( snd_timer_interrupt ) ;