2019-05-27 08:55:05 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2005-04-16 15:20:36 -07:00
/*
* ALSA sequencer Timer
* Copyright ( c ) 1998 - 1999 by Frank van de Pol < fvdpol @ coil . demon . nl >
2007-10-15 09:50:19 +02:00
* Jaroslav Kysela < perex @ perex . cz >
2005-04-16 15:20:36 -07:00
*/
# include <sound/core.h>
# include <linux/slab.h>
# include "seq_timer.h"
# include "seq_queue.h"
# include "seq_info.h"
2005-10-19 14:38:30 +02:00
/* allowed sequencer timer frequencies, in Hz */
# define MIN_FREQUENCY 10
# define MAX_FREQUENCY 6250
# define DEFAULT_FREQUENCY 1000
2005-04-16 15:20:36 -07:00
# define SKEW_BASE 0x10000 /* 16bit shift */
2010-01-18 15:40:56 +01:00
static void snd_seq_timer_set_tick_resolution ( struct snd_seq_timer * tmr )
2005-04-16 15:20:36 -07:00
{
2010-01-18 15:40:56 +01:00
if ( tmr - > tempo < 1000000 )
tmr - > tick . resolution = ( tmr - > tempo * 1000 ) / tmr - > ppq ;
2005-04-16 15:20:36 -07:00
else {
/* might overflow.. */
unsigned int s ;
2010-01-18 15:40:56 +01:00
s = tmr - > tempo % tmr - > ppq ;
s = ( s * 1000 ) / tmr - > ppq ;
tmr - > tick . resolution = ( tmr - > tempo / tmr - > ppq ) * 1000 ;
tmr - > tick . resolution + = s ;
2005-04-16 15:20:36 -07:00
}
2010-01-18 15:40:56 +01:00
if ( tmr - > tick . resolution < = 0 )
tmr - > tick . resolution = 1 ;
snd_seq_timer_update_tick ( & tmr - > tick , 0 ) ;
2005-04-16 15:20:36 -07:00
}
/* create new timer (constructor) */
2005-11-17 14:04:02 +01:00
struct snd_seq_timer * snd_seq_timer_new ( void )
2005-04-16 15:20:36 -07:00
{
2005-11-17 14:04:02 +01:00
struct snd_seq_timer * tmr ;
2005-04-16 15:20:36 -07:00
2005-09-09 14:20:49 +02:00
tmr = kzalloc ( sizeof ( * tmr ) , GFP_KERNEL ) ;
2015-03-10 15:41:18 +01:00
if ( ! tmr )
2005-04-16 15:20:36 -07:00
return NULL ;
spin_lock_init ( & tmr - > lock ) ;
/* reset setup to defaults */
snd_seq_timer_defaults ( tmr ) ;
/* reset time */
snd_seq_timer_reset ( tmr ) ;
return tmr ;
}
/* delete timer (destructor) */
2005-11-17 14:04:02 +01:00
void snd_seq_timer_delete ( struct snd_seq_timer * * tmr )
2005-04-16 15:20:36 -07:00
{
2005-11-17 14:04:02 +01:00
struct snd_seq_timer * t = * tmr ;
2005-04-16 15:20:36 -07:00
* tmr = NULL ;
if ( t = = NULL ) {
2014-02-04 18:24:34 +01:00
pr_debug ( " ALSA: seq: snd_seq_timer_delete() called with NULL timer \n " ) ;
2005-04-16 15:20:36 -07:00
return ;
}
t - > running = 0 ;
/* reset time */
snd_seq_timer_stop ( t ) ;
snd_seq_timer_reset ( t ) ;
kfree ( t ) ;
}
2005-11-17 14:04:02 +01:00
void snd_seq_timer_defaults ( struct snd_seq_timer * tmr )
2005-04-16 15:20:36 -07:00
{
2016-01-30 23:30:25 +01:00
unsigned long flags ;
spin_lock_irqsave ( & tmr - > lock , flags ) ;
2005-04-16 15:20:36 -07:00
/* setup defaults */
tmr - > ppq = 96 ; /* 96 PPQ */
tmr - > tempo = 500000 ; /* 120 BPM */
2010-01-18 15:40:56 +01:00
snd_seq_timer_set_tick_resolution ( tmr ) ;
2005-04-16 15:20:36 -07:00
tmr - > running = 0 ;
tmr - > type = SNDRV_SEQ_TIMER_ALSA ;
tmr - > alsa_id . dev_class = seq_default_timer_class ;
tmr - > alsa_id . dev_sclass = seq_default_timer_sclass ;
tmr - > alsa_id . card = seq_default_timer_card ;
tmr - > alsa_id . device = seq_default_timer_device ;
tmr - > alsa_id . subdevice = seq_default_timer_subdevice ;
tmr - > preferred_resolution = seq_default_timer_resolution ;
tmr - > skew = tmr - > skew_base = SKEW_BASE ;
2016-01-30 23:30:25 +01:00
spin_unlock_irqrestore ( & tmr - > lock , flags ) ;
2005-04-16 15:20:36 -07:00
}
2016-01-30 23:30:25 +01:00
static void seq_timer_reset ( struct snd_seq_timer * tmr )
2005-04-16 15:20:36 -07:00
{
/* reset time & songposition */
tmr - > cur_time . tv_sec = 0 ;
tmr - > cur_time . tv_nsec = 0 ;
tmr - > tick . cur_tick = 0 ;
tmr - > tick . fraction = 0 ;
2016-01-30 23:30:25 +01:00
}
void snd_seq_timer_reset ( struct snd_seq_timer * tmr )
{
unsigned long flags ;
2005-04-16 15:20:36 -07:00
2016-01-30 23:30:25 +01:00
spin_lock_irqsave ( & tmr - > lock , flags ) ;
seq_timer_reset ( tmr ) ;
2005-04-16 15:20:36 -07:00
spin_unlock_irqrestore ( & tmr - > lock , flags ) ;
}
/* called by timer interrupt routine. the period time since previous invocation is passed */
2005-11-17 14:04:02 +01:00
static void snd_seq_timer_interrupt ( struct snd_timer_instance * timeri ,
2005-04-16 15:20:36 -07:00
unsigned long resolution ,
unsigned long ticks )
{
unsigned long flags ;
2005-11-17 14:04:02 +01:00
struct snd_seq_queue * q = timeri - > callback_data ;
struct snd_seq_timer * tmr ;
2005-04-16 15:20:36 -07:00
if ( q = = NULL )
return ;
tmr = q - > timer ;
if ( tmr = = NULL )
return ;
2016-01-30 23:30:25 +01:00
spin_lock_irqsave ( & tmr - > lock , flags ) ;
if ( ! tmr - > running ) {
spin_unlock_irqrestore ( & tmr - > lock , flags ) ;
2005-04-16 15:20:36 -07:00
return ;
2016-01-30 23:30:25 +01:00
}
2005-04-16 15:20:36 -07:00
resolution * = ticks ;
if ( tmr - > skew ! = tmr - > skew_base ) {
/* FIXME: assuming skew_base = 0x10000 */
resolution = ( resolution > > 16 ) * tmr - > skew +
( ( ( resolution & 0xffff ) * tmr - > skew ) > > 16 ) ;
}
/* update timer */
snd_seq_inc_time_nsec ( & tmr - > cur_time , resolution ) ;
/* calculate current tick */
snd_seq_timer_update_tick ( & tmr - > tick , resolution ) ;
/* register actual time of this timer update */
2016-06-17 17:10:32 +02:00
ktime_get_ts64 ( & tmr - > last_update ) ;
2005-04-16 15:20:36 -07:00
spin_unlock_irqrestore ( & tmr - > lock , flags ) ;
/* check queues and dispatch events */
snd_seq_check_queue ( q , 1 , 0 ) ;
}
/* set current tempo */
2005-11-17 14:04:02 +01:00
int snd_seq_timer_set_tempo ( struct snd_seq_timer * tmr , int tempo )
2005-04-16 15:20:36 -07:00
{
unsigned long flags ;
2008-08-08 17:09:09 +02:00
if ( snd_BUG_ON ( ! tmr ) )
return - EINVAL ;
2005-04-16 15:20:36 -07:00
if ( tempo < = 0 )
return - EINVAL ;
spin_lock_irqsave ( & tmr - > lock , flags ) ;
if ( ( unsigned int ) tempo ! = tmr - > tempo ) {
tmr - > tempo = tempo ;
2010-01-18 15:40:56 +01:00
snd_seq_timer_set_tick_resolution ( tmr ) ;
2005-04-16 15:20:36 -07:00
}
spin_unlock_irqrestore ( & tmr - > lock , flags ) ;
return 0 ;
}
2018-01-15 16:48:36 +01:00
/* set current tempo and ppq in a shot */
int snd_seq_timer_set_tempo_ppq ( struct snd_seq_timer * tmr , int tempo , int ppq )
2005-04-16 15:20:36 -07:00
{
2018-01-15 16:48:36 +01:00
int changed ;
2005-04-16 15:20:36 -07:00
unsigned long flags ;
2008-08-08 17:09:09 +02:00
if ( snd_BUG_ON ( ! tmr ) )
return - EINVAL ;
2018-01-15 16:48:36 +01:00
if ( tempo < = 0 | | ppq < = 0 )
2005-04-16 15:20:36 -07:00
return - EINVAL ;
spin_lock_irqsave ( & tmr - > lock , flags ) ;
if ( tmr - > running & & ( ppq ! = tmr - > ppq ) ) {
/* refuse to change ppq on running timers */
/* because it will upset the song position (ticks) */
spin_unlock_irqrestore ( & tmr - > lock , flags ) ;
2014-02-04 18:24:34 +01:00
pr_debug ( " ALSA: seq: cannot change ppq of a running timer \n " ) ;
2005-04-16 15:20:36 -07:00
return - EBUSY ;
}
2018-01-15 16:48:36 +01:00
changed = ( tempo ! = tmr - > tempo ) | | ( ppq ! = tmr - > ppq ) ;
tmr - > tempo = tempo ;
2005-04-16 15:20:36 -07:00
tmr - > ppq = ppq ;
2018-01-15 16:48:36 +01:00
if ( changed )
snd_seq_timer_set_tick_resolution ( tmr ) ;
2005-04-16 15:20:36 -07:00
spin_unlock_irqrestore ( & tmr - > lock , flags ) ;
return 0 ;
}
/* set current tick position */
2005-11-17 14:04:02 +01:00
int snd_seq_timer_set_position_tick ( struct snd_seq_timer * tmr ,
snd_seq_tick_time_t position )
2005-04-16 15:20:36 -07:00
{
unsigned long flags ;
2008-08-08 17:09:09 +02:00
if ( snd_BUG_ON ( ! tmr ) )
return - EINVAL ;
2005-04-16 15:20:36 -07:00
spin_lock_irqsave ( & tmr - > lock , flags ) ;
tmr - > tick . cur_tick = position ;
tmr - > tick . fraction = 0 ;
spin_unlock_irqrestore ( & tmr - > lock , flags ) ;
return 0 ;
}
/* set current real-time position */
2005-11-17 14:04:02 +01:00
int snd_seq_timer_set_position_time ( struct snd_seq_timer * tmr ,
snd_seq_real_time_t position )
2005-04-16 15:20:36 -07:00
{
unsigned long flags ;
2008-08-08 17:09:09 +02:00
if ( snd_BUG_ON ( ! tmr ) )
return - EINVAL ;
2005-04-16 15:20:36 -07:00
snd_seq_sanity_real_time ( & position ) ;
spin_lock_irqsave ( & tmr - > lock , flags ) ;
tmr - > cur_time = position ;
spin_unlock_irqrestore ( & tmr - > lock , flags ) ;
return 0 ;
}
/* set timer skew */
2005-11-17 14:04:02 +01:00
int snd_seq_timer_set_skew ( struct snd_seq_timer * tmr , unsigned int skew ,
unsigned int base )
2005-04-16 15:20:36 -07:00
{
unsigned long flags ;
2008-08-08 17:09:09 +02:00
if ( snd_BUG_ON ( ! tmr ) )
return - EINVAL ;
2005-04-16 15:20:36 -07:00
/* FIXME */
if ( base ! = SKEW_BASE ) {
2014-02-04 18:24:34 +01:00
pr_debug ( " ALSA: seq: invalid skew base 0x%x \n " , base ) ;
2005-04-16 15:20:36 -07:00
return - EINVAL ;
}
spin_lock_irqsave ( & tmr - > lock , flags ) ;
tmr - > skew = skew ;
spin_unlock_irqrestore ( & tmr - > lock , flags ) ;
return 0 ;
}
2005-11-17 14:04:02 +01:00
int snd_seq_timer_open ( struct snd_seq_queue * q )
2005-04-16 15:20:36 -07:00
{
2005-11-17 14:04:02 +01:00
struct snd_timer_instance * t ;
struct snd_seq_timer * tmr ;
2005-04-16 15:20:36 -07:00
char str [ 32 ] ;
int err ;
tmr = q - > timer ;
2008-08-08 17:09:09 +02:00
if ( snd_BUG_ON ( ! tmr ) )
return - EINVAL ;
2005-04-16 15:20:36 -07:00
if ( tmr - > timeri )
return - EBUSY ;
sprintf ( str , " sequencer queue %i " , q - > queue ) ;
if ( tmr - > type ! = SNDRV_SEQ_TIMER_ALSA ) /* standard ALSA timer */
return - EINVAL ;
if ( tmr - > alsa_id . dev_class ! = SNDRV_TIMER_CLASS_SLAVE )
tmr - > alsa_id . dev_sclass = SNDRV_TIMER_SCLASS_SEQUENCER ;
2019-11-07 20:20:08 +01:00
t = snd_timer_instance_new ( str ) ;
if ( ! t )
return - ENOMEM ;
t - > callback = snd_seq_timer_interrupt ;
t - > callback_data = q ;
t - > flags | = SNDRV_TIMER_IFLG_AUTO ;
err = snd_timer_open ( t , & tmr - > alsa_id , q - > queue ) ;
2005-04-16 15:20:36 -07:00
if ( err < 0 & & tmr - > alsa_id . dev_class ! = SNDRV_TIMER_CLASS_SLAVE ) {
if ( tmr - > alsa_id . dev_class ! = SNDRV_TIMER_CLASS_GLOBAL | |
tmr - > alsa_id . device ! = SNDRV_TIMER_GLOBAL_SYSTEM ) {
2005-11-17 14:04:02 +01:00
struct snd_timer_id tid ;
2005-04-16 15:20:36 -07:00
memset ( & tid , 0 , sizeof ( tid ) ) ;
tid . dev_class = SNDRV_TIMER_CLASS_GLOBAL ;
tid . dev_sclass = SNDRV_TIMER_SCLASS_SEQUENCER ;
tid . card = - 1 ;
tid . device = SNDRV_TIMER_GLOBAL_SYSTEM ;
2019-11-07 20:20:08 +01:00
err = snd_timer_open ( t , & tid , q - > queue ) ;
2005-04-16 15:20:36 -07:00
}
2013-03-08 18:11:17 +01:00
}
if ( err < 0 ) {
2014-02-04 18:24:34 +01:00
pr_err ( " ALSA: seq fatal error: cannot create timer (%i) \n " , err ) ;
2019-11-07 20:20:08 +01:00
snd_timer_instance_free ( t ) ;
2013-03-08 18:11:17 +01:00
return err ;
2005-04-16 15:20:36 -07:00
}
2016-01-30 23:30:25 +01:00
spin_lock_irq ( & tmr - > lock ) ;
2021-06-10 17:20:59 +02:00
if ( tmr - > timeri )
err = - EBUSY ;
else
tmr - > timeri = t ;
2016-01-30 23:30:25 +01:00
spin_unlock_irq ( & tmr - > lock ) ;
2021-06-10 17:20:59 +02:00
if ( err < 0 ) {
snd_timer_close ( t ) ;
snd_timer_instance_free ( t ) ;
return err ;
}
2005-04-16 15:20:36 -07:00
return 0 ;
}
2005-11-17 14:04:02 +01:00
int snd_seq_timer_close ( struct snd_seq_queue * q )
2005-04-16 15:20:36 -07:00
{
2005-11-17 14:04:02 +01:00
struct snd_seq_timer * tmr ;
2016-01-30 23:30:25 +01:00
struct snd_timer_instance * t ;
2005-04-16 15:20:36 -07:00
tmr = q - > timer ;
2008-08-08 17:09:09 +02:00
if ( snd_BUG_ON ( ! tmr ) )
return - EINVAL ;
2016-01-30 23:30:25 +01:00
spin_lock_irq ( & tmr - > lock ) ;
t = tmr - > timeri ;
tmr - > timeri = NULL ;
spin_unlock_irq ( & tmr - > lock ) ;
2019-11-07 20:20:08 +01:00
if ( t ) {
2016-01-30 23:30:25 +01:00
snd_timer_close ( t ) ;
2019-11-07 20:20:08 +01:00
snd_timer_instance_free ( t ) ;
}
2005-04-16 15:20:36 -07:00
return 0 ;
}
2016-01-30 23:30:25 +01:00
static int seq_timer_stop ( struct snd_seq_timer * tmr )
2005-04-16 15:20:36 -07:00
{
if ( ! tmr - > timeri )
return - EINVAL ;
if ( ! tmr - > running )
return 0 ;
tmr - > running = 0 ;
snd_timer_pause ( tmr - > timeri ) ;
return 0 ;
}
2016-01-30 23:30:25 +01:00
int snd_seq_timer_stop ( struct snd_seq_timer * tmr )
{
unsigned long flags ;
int err ;
spin_lock_irqsave ( & tmr - > lock , flags ) ;
err = seq_timer_stop ( tmr ) ;
spin_unlock_irqrestore ( & tmr - > lock , flags ) ;
return err ;
}
2005-11-17 14:04:02 +01:00
static int initialize_timer ( struct snd_seq_timer * tmr )
2005-04-16 15:20:36 -07:00
{
2005-11-17 14:04:02 +01:00
struct snd_timer * t ;
2005-10-19 14:38:30 +02:00
unsigned long freq ;
2005-04-16 15:20:36 -07:00
t = tmr - > timeri - > timer ;
2017-11-30 10:08:28 +01:00
if ( ! t )
2008-08-08 17:09:09 +02:00
return - EINVAL ;
2005-04-16 15:20:36 -07:00
2005-10-19 14:38:30 +02:00
freq = tmr - > preferred_resolution ;
if ( ! freq )
freq = DEFAULT_FREQUENCY ;
else if ( freq < MIN_FREQUENCY )
freq = MIN_FREQUENCY ;
else if ( freq > MAX_FREQUENCY )
freq = MAX_FREQUENCY ;
2005-04-16 15:20:36 -07:00
tmr - > ticks = 1 ;
2005-10-19 14:38:30 +02:00
if ( ! ( t - > hw . flags & SNDRV_TIMER_HW_SLAVE ) ) {
2018-05-17 10:43:16 +02:00
unsigned long r = snd_timer_resolution ( tmr - > timeri ) ;
2005-04-16 15:20:36 -07:00
if ( r ) {
2005-10-19 14:38:30 +02:00
tmr - > ticks = ( unsigned int ) ( 1000000000uL / ( r * freq ) ) ;
2005-04-16 15:20:36 -07:00
if ( ! tmr - > ticks )
tmr - > ticks = 1 ;
}
}
tmr - > initialized = 1 ;
return 0 ;
}
2016-01-30 23:30:25 +01:00
static int seq_timer_start ( struct snd_seq_timer * tmr )
2005-04-16 15:20:36 -07:00
{
if ( ! tmr - > timeri )
return - EINVAL ;
if ( tmr - > running )
2016-01-30 23:30:25 +01:00
seq_timer_stop ( tmr ) ;
seq_timer_reset ( tmr ) ;
2005-04-16 15:20:36 -07:00
if ( initialize_timer ( tmr ) < 0 )
return - EINVAL ;
snd_timer_start ( tmr - > timeri , tmr - > ticks ) ;
tmr - > running = 1 ;
2016-06-17 17:10:32 +02:00
ktime_get_ts64 ( & tmr - > last_update ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
2016-01-30 23:30:25 +01:00
int snd_seq_timer_start ( struct snd_seq_timer * tmr )
{
unsigned long flags ;
int err ;
spin_lock_irqsave ( & tmr - > lock , flags ) ;
err = seq_timer_start ( tmr ) ;
spin_unlock_irqrestore ( & tmr - > lock , flags ) ;
return err ;
}
static int seq_timer_continue ( struct snd_seq_timer * tmr )
2005-04-16 15:20:36 -07:00
{
if ( ! tmr - > timeri )
return - EINVAL ;
if ( tmr - > running )
return - EBUSY ;
if ( ! tmr - > initialized ) {
2016-01-30 23:30:25 +01:00
seq_timer_reset ( tmr ) ;
2005-04-16 15:20:36 -07:00
if ( initialize_timer ( tmr ) < 0 )
return - EINVAL ;
}
snd_timer_start ( tmr - > timeri , tmr - > ticks ) ;
tmr - > running = 1 ;
2016-06-17 17:10:32 +02:00
ktime_get_ts64 ( & tmr - > last_update ) ;
2005-04-16 15:20:36 -07:00
return 0 ;
}
2016-01-30 23:30:25 +01:00
int snd_seq_timer_continue ( struct snd_seq_timer * tmr )
{
unsigned long flags ;
int err ;
spin_lock_irqsave ( & tmr - > lock , flags ) ;
err = seq_timer_continue ( tmr ) ;
spin_unlock_irqrestore ( & tmr - > lock , flags ) ;
return err ;
}
2005-04-16 15:20:36 -07:00
/* return current 'real' time. use timeofday() to get better granularity. */
2020-02-14 12:13:15 +01:00
snd_seq_real_time_t snd_seq_timer_get_cur_time ( struct snd_seq_timer * tmr ,
bool adjust_ktime )
2005-04-16 15:20:36 -07:00
{
snd_seq_real_time_t cur_time ;
2016-01-30 23:30:25 +01:00
unsigned long flags ;
2005-04-16 15:20:36 -07:00
2016-01-30 23:30:25 +01:00
spin_lock_irqsave ( & tmr - > lock , flags ) ;
2005-04-16 15:20:36 -07:00
cur_time = tmr - > cur_time ;
2020-02-14 12:13:15 +01:00
if ( adjust_ktime & & tmr - > running ) {
2016-06-17 17:10:32 +02:00
struct timespec64 tm ;
ktime_get_ts64 ( & tm ) ;
tm = timespec64_sub ( tm , tmr - > last_update ) ;
2016-10-25 15:56:35 +02:00
cur_time . tv_nsec + = tm . tv_nsec ;
cur_time . tv_sec + = tm . tv_sec ;
2005-04-16 15:20:36 -07:00
snd_seq_sanity_real_time ( & cur_time ) ;
}
2016-01-30 23:30:25 +01:00
spin_unlock_irqrestore ( & tmr - > lock , flags ) ;
2005-04-16 15:20:36 -07:00
return cur_time ;
}
/* TODO: use interpolation on tick queue (will only be useful for very
high PPQ values ) */
2005-11-17 14:04:02 +01:00
snd_seq_tick_time_t snd_seq_timer_get_cur_tick ( struct snd_seq_timer * tmr )
2005-04-16 15:20:36 -07:00
{
2020-02-14 12:13:15 +01:00
snd_seq_tick_time_t cur_tick ;
unsigned long flags ;
spin_lock_irqsave ( & tmr - > lock , flags ) ;
cur_tick = tmr - > tick . cur_tick ;
spin_unlock_irqrestore ( & tmr - > lock , flags ) ;
return cur_tick ;
2005-04-16 15:20:36 -07:00
}
2015-05-27 19:45:45 +08:00
# ifdef CONFIG_SND_PROC_FS
2005-04-16 15:20:36 -07:00
/* exported to seq_info.c */
2005-11-17 14:04:02 +01:00
void snd_seq_info_timer_read ( struct snd_info_entry * entry ,
struct snd_info_buffer * buffer )
2005-04-16 15:20:36 -07:00
{
int idx ;
2005-11-17 14:04:02 +01:00
struct snd_seq_queue * q ;
struct snd_seq_timer * tmr ;
struct snd_timer_instance * ti ;
2005-04-16 15:20:36 -07:00
unsigned long resolution ;
for ( idx = 0 ; idx < SNDRV_SEQ_MAX_QUEUES ; idx + + ) {
q = queueptr ( idx ) ;
if ( q = = NULL )
continue ;
2020-01-15 21:37:33 +01:00
mutex_lock ( & q - > timer_mutex ) ;
tmr = q - > timer ;
if ( ! tmr )
goto unlock ;
ti = tmr - > timeri ;
if ( ! ti )
goto unlock ;
2005-04-16 15:20:36 -07:00
snd_iprintf ( buffer , " Timer for queue %i : %s \n " , q - > queue , ti - > timer - > name ) ;
resolution = snd_timer_resolution ( ti ) * tmr - > ticks ;
snd_iprintf ( buffer , " Period time : %lu.%09lu \n " , resolution / 1000000000 , resolution % 1000000000 ) ;
snd_iprintf ( buffer , " Skew : %u / %u \n " , tmr - > skew , tmr - > skew_base ) ;
2020-01-15 21:37:33 +01:00
unlock :
mutex_unlock ( & q - > timer_mutex ) ;
2005-04-16 15:20:36 -07:00
queuefree ( q ) ;
}
}
2015-05-27 19:45:45 +08:00
# endif /* CONFIG_SND_PROC_FS */
2005-12-01 10:43:51 +01:00