11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 21da177e4SLinus Torvalds /* 31da177e4SLinus Torvalds * Timers abstract layer 4c1017a4cSJaroslav Kysela * Copyright (c) by Jaroslav Kysela <perex@perex.cz> 51da177e4SLinus Torvalds */ 61da177e4SLinus Torvalds 71da177e4SLinus Torvalds #include <linux/delay.h> 81da177e4SLinus Torvalds #include <linux/init.h> 91da177e4SLinus Torvalds #include <linux/slab.h> 101da177e4SLinus Torvalds #include <linux/time.h> 111a60d4c5SIngo Molnar #include <linux/mutex.h> 1251990e82SPaul Gortmaker #include <linux/device.h> 1365a77217SPaul Gortmaker #include <linux/module.h> 14543537bdSPaulo Marques #include <linux/string.h> 15174cd4b1SIngo Molnar #include <linux/sched/signal.h> 161da177e4SLinus Torvalds #include <sound/core.h> 171da177e4SLinus Torvalds #include <sound/timer.h> 181da177e4SLinus Torvalds #include <sound/control.h> 191da177e4SLinus Torvalds #include <sound/info.h> 201da177e4SLinus Torvalds #include <sound/minors.h> 211da177e4SLinus Torvalds #include <sound/initval.h> 221da177e4SLinus Torvalds #include <linux/kmod.h> 231da177e4SLinus Torvalds 249f8a7658STakashi Iwai /* internal flags */ 259f8a7658STakashi Iwai #define SNDRV_TIMER_IFLG_PAUSED 0x00010000 26fe1b26c9STakashi Iwai #define SNDRV_TIMER_IFLG_DEAD 0x00020000 279f8a7658STakashi Iwai 288eeaa2f9STakashi Iwai #if IS_ENABLED(CONFIG_SND_HRTIMER) 29109fef9eSClemens Ladisch #define DEFAULT_TIMER_LIMIT 4 301da177e4SLinus Torvalds #else 311da177e4SLinus Torvalds #define DEFAULT_TIMER_LIMIT 1 321da177e4SLinus Torvalds #endif 331da177e4SLinus Torvalds 341da177e4SLinus Torvalds static int timer_limit = DEFAULT_TIMER_LIMIT; 35b751eef1SJaroslav Kysela static int timer_tstamp_monotonic = 1; 36c1017a4cSJaroslav Kysela MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>, Takashi Iwai <tiwai@suse.de>"); 371da177e4SLinus Torvalds MODULE_DESCRIPTION("ALSA timer interface"); 381da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 391da177e4SLinus Torvalds module_param(timer_limit, int, 0444); 401da177e4SLinus Torvalds MODULE_PARM_DESC(timer_limit, "Maximum global timers in system."); 41b751eef1SJaroslav Kysela module_param(timer_tstamp_monotonic, int, 0444); 42b751eef1SJaroslav Kysela MODULE_PARM_DESC(timer_tstamp_monotonic, "Use posix monotonic clock source for timestamps (default)."); 431da177e4SLinus Torvalds 4403cfe6f5SKay Sievers MODULE_ALIAS_CHARDEV(CONFIG_SND_MAJOR, SNDRV_MINOR_TIMER); 4503cfe6f5SKay Sievers MODULE_ALIAS("devname:snd/timer"); 4603cfe6f5SKay Sievers 4753d2f744STakashi Iwai struct snd_timer_user { 4853d2f744STakashi Iwai struct snd_timer_instance *timeri; 491da177e4SLinus Torvalds int tread; /* enhanced read with timestamps and events */ 501da177e4SLinus Torvalds unsigned long ticks; 511da177e4SLinus Torvalds unsigned long overrun; 521da177e4SLinus Torvalds int qhead; 531da177e4SLinus Torvalds int qtail; 541da177e4SLinus Torvalds int qused; 551da177e4SLinus Torvalds int queue_size; 56230323daSTakashi Iwai bool disconnected; 5753d2f744STakashi Iwai struct snd_timer_read *queue; 5853d2f744STakashi Iwai struct snd_timer_tread *tqueue; 591da177e4SLinus Torvalds spinlock_t qlock; 601da177e4SLinus Torvalds unsigned long last_resolution; 611da177e4SLinus Torvalds unsigned int filter; 62fcae40c9SBaolin Wang struct timespec64 tstamp; /* trigger tstamp */ 631da177e4SLinus Torvalds wait_queue_head_t qchange_sleep; 641da177e4SLinus Torvalds struct fasync_struct *fasync; 65af368027STakashi Iwai struct mutex ioctl_lock; 6653d2f744STakashi Iwai }; 671da177e4SLinus Torvalds 681da177e4SLinus Torvalds /* list of timers */ 691da177e4SLinus Torvalds static LIST_HEAD(snd_timer_list); 701da177e4SLinus Torvalds 711da177e4SLinus Torvalds /* list of slave instances */ 721da177e4SLinus Torvalds static LIST_HEAD(snd_timer_slave_list); 731da177e4SLinus Torvalds 741da177e4SLinus Torvalds /* lock for slave active lists */ 751da177e4SLinus Torvalds static DEFINE_SPINLOCK(slave_active_lock); 761da177e4SLinus Torvalds 77fdea53feSTakashi Iwai #define MAX_SLAVE_INSTANCES 1000 78fdea53feSTakashi Iwai static int num_slaves; 79fdea53feSTakashi Iwai 801a60d4c5SIngo Molnar static DEFINE_MUTEX(register_mutex); 811da177e4SLinus Torvalds 8253d2f744STakashi Iwai static int snd_timer_free(struct snd_timer *timer); 8353d2f744STakashi Iwai static int snd_timer_dev_free(struct snd_device *device); 8453d2f744STakashi Iwai static int snd_timer_dev_register(struct snd_device *device); 85c461482cSTakashi Iwai static int snd_timer_dev_disconnect(struct snd_device *device); 861da177e4SLinus Torvalds 8753d2f744STakashi Iwai static void snd_timer_reschedule(struct snd_timer * timer, unsigned long ticks_left); 881da177e4SLinus Torvalds 891da177e4SLinus Torvalds /* 901da177e4SLinus Torvalds * create a timer instance with the given owner string. 911da177e4SLinus Torvalds */ 926a34367eSTakashi Iwai struct snd_timer_instance *snd_timer_instance_new(const char *owner) 931da177e4SLinus Torvalds { 9453d2f744STakashi Iwai struct snd_timer_instance *timeri; 956a34367eSTakashi Iwai 96ca2c0966STakashi Iwai timeri = kzalloc(sizeof(*timeri), GFP_KERNEL); 971da177e4SLinus Torvalds if (timeri == NULL) 981da177e4SLinus Torvalds return NULL; 99543537bdSPaulo Marques timeri->owner = kstrdup(owner, GFP_KERNEL); 1001da177e4SLinus Torvalds if (! timeri->owner) { 1011da177e4SLinus Torvalds kfree(timeri); 1021da177e4SLinus Torvalds return NULL; 1031da177e4SLinus Torvalds } 1041da177e4SLinus Torvalds INIT_LIST_HEAD(&timeri->open_list); 1051da177e4SLinus Torvalds INIT_LIST_HEAD(&timeri->active_list); 1061da177e4SLinus Torvalds INIT_LIST_HEAD(&timeri->ack_list); 1071da177e4SLinus Torvalds INIT_LIST_HEAD(&timeri->slave_list_head); 1081da177e4SLinus Torvalds INIT_LIST_HEAD(&timeri->slave_active_head); 1091da177e4SLinus Torvalds 1101da177e4SLinus Torvalds return timeri; 1111da177e4SLinus Torvalds } 1126a34367eSTakashi Iwai EXPORT_SYMBOL(snd_timer_instance_new); 1136a34367eSTakashi Iwai 1146a34367eSTakashi Iwai void snd_timer_instance_free(struct snd_timer_instance *timeri) 1156a34367eSTakashi Iwai { 1166a34367eSTakashi Iwai if (timeri) { 1176a34367eSTakashi Iwai if (timeri->private_free) 1186a34367eSTakashi Iwai timeri->private_free(timeri); 1196a34367eSTakashi Iwai kfree(timeri->owner); 1206a34367eSTakashi Iwai kfree(timeri); 1216a34367eSTakashi Iwai } 1226a34367eSTakashi Iwai } 1236a34367eSTakashi Iwai EXPORT_SYMBOL(snd_timer_instance_free); 1241da177e4SLinus Torvalds 1251da177e4SLinus Torvalds /* 1261da177e4SLinus Torvalds * find a timer instance from the given timer id 1271da177e4SLinus Torvalds */ 12853d2f744STakashi Iwai static struct snd_timer *snd_timer_find(struct snd_timer_id *tid) 1291da177e4SLinus Torvalds { 13053d2f744STakashi Iwai struct snd_timer *timer = NULL; 1311da177e4SLinus Torvalds 1329244b2c3SJohannes Berg list_for_each_entry(timer, &snd_timer_list, device_list) { 1331da177e4SLinus Torvalds if (timer->tmr_class != tid->dev_class) 1341da177e4SLinus Torvalds continue; 1351da177e4SLinus Torvalds if ((timer->tmr_class == SNDRV_TIMER_CLASS_CARD || 1361da177e4SLinus Torvalds timer->tmr_class == SNDRV_TIMER_CLASS_PCM) && 1371da177e4SLinus Torvalds (timer->card == NULL || 1381da177e4SLinus Torvalds timer->card->number != tid->card)) 1391da177e4SLinus Torvalds continue; 1401da177e4SLinus Torvalds if (timer->tmr_device != tid->device) 1411da177e4SLinus Torvalds continue; 1421da177e4SLinus Torvalds if (timer->tmr_subdevice != tid->subdevice) 1431da177e4SLinus Torvalds continue; 1441da177e4SLinus Torvalds return timer; 1451da177e4SLinus Torvalds } 1461da177e4SLinus Torvalds return NULL; 1471da177e4SLinus Torvalds } 1481da177e4SLinus Torvalds 149ee2da997SJohannes Berg #ifdef CONFIG_MODULES 1501da177e4SLinus Torvalds 15153d2f744STakashi Iwai static void snd_timer_request(struct snd_timer_id *tid) 1521da177e4SLinus Torvalds { 1531da177e4SLinus Torvalds switch (tid->dev_class) { 1541da177e4SLinus Torvalds case SNDRV_TIMER_CLASS_GLOBAL: 1551da177e4SLinus Torvalds if (tid->device < timer_limit) 1561da177e4SLinus Torvalds request_module("snd-timer-%i", tid->device); 1571da177e4SLinus Torvalds break; 1581da177e4SLinus Torvalds case SNDRV_TIMER_CLASS_CARD: 1591da177e4SLinus Torvalds case SNDRV_TIMER_CLASS_PCM: 1601da177e4SLinus Torvalds if (tid->card < snd_ecards_limit) 1611da177e4SLinus Torvalds request_module("snd-card-%i", tid->card); 1621da177e4SLinus Torvalds break; 1631da177e4SLinus Torvalds default: 1641da177e4SLinus Torvalds break; 1651da177e4SLinus Torvalds } 1661da177e4SLinus Torvalds } 1671da177e4SLinus Torvalds 1681da177e4SLinus Torvalds #endif 1691da177e4SLinus Torvalds 170ebfc6de2STakashi Iwai /* move the slave if it belongs to the master; return 1 if match */ 171ebfc6de2STakashi Iwai static int check_matching_master_slave(struct snd_timer_instance *master, 172ebfc6de2STakashi Iwai struct snd_timer_instance *slave) 173ebfc6de2STakashi Iwai { 174ebfc6de2STakashi Iwai if (slave->slave_class != master->slave_class || 175ebfc6de2STakashi Iwai slave->slave_id != master->slave_id) 176ebfc6de2STakashi Iwai return 0; 177ebfc6de2STakashi Iwai if (master->timer->num_instances >= master->timer->max_instances) 178ebfc6de2STakashi Iwai return -EBUSY; 179ebfc6de2STakashi Iwai list_move_tail(&slave->open_list, &master->slave_list_head); 180ebfc6de2STakashi Iwai master->timer->num_instances++; 181ebfc6de2STakashi Iwai spin_lock_irq(&slave_active_lock); 182ebfc6de2STakashi Iwai spin_lock(&master->timer->lock); 183ebfc6de2STakashi Iwai slave->master = master; 184ebfc6de2STakashi Iwai slave->timer = master->timer; 185ebfc6de2STakashi Iwai if (slave->flags & SNDRV_TIMER_IFLG_RUNNING) 186ebfc6de2STakashi Iwai list_add_tail(&slave->active_list, &master->slave_active_head); 187ebfc6de2STakashi Iwai spin_unlock(&master->timer->lock); 188ebfc6de2STakashi Iwai spin_unlock_irq(&slave_active_lock); 189ebfc6de2STakashi Iwai return 1; 190ebfc6de2STakashi Iwai } 191ebfc6de2STakashi Iwai 1921da177e4SLinus Torvalds /* 1931da177e4SLinus Torvalds * look for a master instance matching with the slave id of the given slave. 1941da177e4SLinus Torvalds * when found, relink the open_link of the slave. 1951da177e4SLinus Torvalds * 1961da177e4SLinus Torvalds * call this with register_mutex down. 1971da177e4SLinus Torvalds */ 1989b7d869eSTakashi Iwai static int snd_timer_check_slave(struct snd_timer_instance *slave) 1991da177e4SLinus Torvalds { 20053d2f744STakashi Iwai struct snd_timer *timer; 20153d2f744STakashi Iwai struct snd_timer_instance *master; 202ebfc6de2STakashi Iwai int err = 0; 2031da177e4SLinus Torvalds 2041da177e4SLinus Torvalds /* FIXME: it's really dumb to look up all entries.. */ 2059244b2c3SJohannes Berg list_for_each_entry(timer, &snd_timer_list, device_list) { 2069244b2c3SJohannes Berg list_for_each_entry(master, &timer->open_list_head, open_list) { 207ebfc6de2STakashi Iwai err = check_matching_master_slave(master, slave); 208ebfc6de2STakashi Iwai if (err != 0) /* match found or error */ 209ebfc6de2STakashi Iwai goto out; 2101da177e4SLinus Torvalds } 2111da177e4SLinus Torvalds } 212ebfc6de2STakashi Iwai out: 213ebfc6de2STakashi Iwai return err < 0 ? err : 0; 2141da177e4SLinus Torvalds } 2151da177e4SLinus Torvalds 2161da177e4SLinus Torvalds /* 2171da177e4SLinus Torvalds * look for slave instances matching with the slave id of the given master. 2181da177e4SLinus Torvalds * when found, relink the open_link of slaves. 2191da177e4SLinus Torvalds * 2201da177e4SLinus Torvalds * call this with register_mutex down. 2211da177e4SLinus Torvalds */ 2229b7d869eSTakashi Iwai static int snd_timer_check_master(struct snd_timer_instance *master) 2231da177e4SLinus Torvalds { 2249244b2c3SJohannes Berg struct snd_timer_instance *slave, *tmp; 225ebfc6de2STakashi Iwai int err = 0; 2261da177e4SLinus Torvalds 2271da177e4SLinus Torvalds /* check all pending slaves */ 2289244b2c3SJohannes Berg list_for_each_entry_safe(slave, tmp, &snd_timer_slave_list, open_list) { 229ebfc6de2STakashi Iwai err = check_matching_master_slave(master, slave); 230ebfc6de2STakashi Iwai if (err < 0) 231ebfc6de2STakashi Iwai break; 2321da177e4SLinus Torvalds } 233ebfc6de2STakashi Iwai return err < 0 ? err : 0; 2341da177e4SLinus Torvalds } 2351da177e4SLinus Torvalds 23633bbb8a0STakashi Iwai static void snd_timer_close_locked(struct snd_timer_instance *timeri, 237a3933186STakashi Iwai struct device **card_devp_to_put); 2389b7d869eSTakashi Iwai 2391da177e4SLinus Torvalds /* 2401da177e4SLinus Torvalds * open a timer instance 2411da177e4SLinus Torvalds * when opening a master, the slave id must be here given. 2421da177e4SLinus Torvalds */ 2436a34367eSTakashi Iwai int snd_timer_open(struct snd_timer_instance *timeri, 2446a34367eSTakashi Iwai struct snd_timer_id *tid, 2451da177e4SLinus Torvalds unsigned int slave_id) 2461da177e4SLinus Torvalds { 24753d2f744STakashi Iwai struct snd_timer *timer; 248a3933186STakashi Iwai struct device *card_dev_to_put = NULL; 2499b7d869eSTakashi Iwai int err; 2501da177e4SLinus Torvalds 25141672c0cSTakashi Iwai mutex_lock(®ister_mutex); 2521da177e4SLinus Torvalds if (tid->dev_class == SNDRV_TIMER_CLASS_SLAVE) { 2531da177e4SLinus Torvalds /* open a slave instance */ 2541da177e4SLinus Torvalds if (tid->dev_sclass <= SNDRV_TIMER_SCLASS_NONE || 2551da177e4SLinus Torvalds tid->dev_sclass > SNDRV_TIMER_SCLASS_OSS_SEQUENCER) { 256cf74dcf3STakashi Iwai pr_debug("ALSA: timer: invalid slave class %i\n", 257cf74dcf3STakashi Iwai tid->dev_sclass); 25841672c0cSTakashi Iwai err = -EINVAL; 25941672c0cSTakashi Iwai goto unlock; 2601da177e4SLinus Torvalds } 261fdea53feSTakashi Iwai if (num_slaves >= MAX_SLAVE_INSTANCES) { 262fdea53feSTakashi Iwai err = -EBUSY; 263fdea53feSTakashi Iwai goto unlock; 264fdea53feSTakashi Iwai } 2651da177e4SLinus Torvalds timeri->slave_class = tid->dev_sclass; 2661da177e4SLinus Torvalds timeri->slave_id = tid->device; 2671da177e4SLinus Torvalds timeri->flags |= SNDRV_TIMER_IFLG_SLAVE; 2681da177e4SLinus Torvalds list_add_tail(&timeri->open_list, &snd_timer_slave_list); 269fdea53feSTakashi Iwai num_slaves++; 2709b7d869eSTakashi Iwai err = snd_timer_check_slave(timeri); 2710c4f09ceSTakashi Iwai goto list_added; 2721da177e4SLinus Torvalds } 2731da177e4SLinus Torvalds 2741da177e4SLinus Torvalds /* open a master instance */ 2751da177e4SLinus Torvalds timer = snd_timer_find(tid); 276ee2da997SJohannes Berg #ifdef CONFIG_MODULES 277ee2da997SJohannes Berg if (!timer) { 2781a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 2791da177e4SLinus Torvalds snd_timer_request(tid); 2801a60d4c5SIngo Molnar mutex_lock(®ister_mutex); 2811da177e4SLinus Torvalds timer = snd_timer_find(tid); 2821da177e4SLinus Torvalds } 2831da177e4SLinus Torvalds #endif 2842fd43d11SClemens Ladisch if (!timer) { 28541672c0cSTakashi Iwai err = -ENODEV; 28641672c0cSTakashi Iwai goto unlock; 2872fd43d11SClemens Ladisch } 2881da177e4SLinus Torvalds if (!list_empty(&timer->open_list_head)) { 289e7af6307STakashi Iwai struct snd_timer_instance *t = 290e7af6307STakashi Iwai list_entry(timer->open_list_head.next, 29153d2f744STakashi Iwai struct snd_timer_instance, open_list); 292e7af6307STakashi Iwai if (t->flags & SNDRV_TIMER_IFLG_EXCLUSIVE) { 29341672c0cSTakashi Iwai err = -EBUSY; 29441672c0cSTakashi Iwai goto unlock; 2951da177e4SLinus Torvalds } 2961da177e4SLinus Torvalds } 2979b7d869eSTakashi Iwai if (timer->num_instances >= timer->max_instances) { 29841672c0cSTakashi Iwai err = -EBUSY; 29941672c0cSTakashi Iwai goto unlock; 3009b7d869eSTakashi Iwai } 3016a34367eSTakashi Iwai if (!try_module_get(timer->module)) { 3026a34367eSTakashi Iwai err = -EBUSY; 30341672c0cSTakashi Iwai goto unlock; 3042fd43d11SClemens Ladisch } 305230323daSTakashi Iwai /* take a card refcount for safe disconnection */ 3066a34367eSTakashi Iwai if (timer->card) { 307230323daSTakashi Iwai get_device(&timer->card->card_dev); 3086a34367eSTakashi Iwai card_dev_to_put = &timer->card->card_dev; 3096a34367eSTakashi Iwai } 3108ddc0563SVegard Nossum 3118ddc0563SVegard Nossum if (list_empty(&timer->open_list_head) && timer->hw.open) { 31241672c0cSTakashi Iwai err = timer->hw.open(timer); 3138ddc0563SVegard Nossum if (err) { 3148ddc0563SVegard Nossum module_put(timer->module); 31541672c0cSTakashi Iwai goto unlock; 3168ddc0563SVegard Nossum } 3178ddc0563SVegard Nossum } 3188ddc0563SVegard Nossum 3196a34367eSTakashi Iwai timeri->timer = timer; 3206a34367eSTakashi Iwai timeri->slave_class = tid->dev_sclass; 3216a34367eSTakashi Iwai timeri->slave_id = slave_id; 3226a34367eSTakashi Iwai 3231da177e4SLinus Torvalds list_add_tail(&timeri->open_list, &timer->open_list_head); 3249b7d869eSTakashi Iwai timer->num_instances++; 3259b7d869eSTakashi Iwai err = snd_timer_check_master(timeri); 3260c4f09ceSTakashi Iwai list_added: 3276a34367eSTakashi Iwai if (err < 0) 328a3933186STakashi Iwai snd_timer_close_locked(timeri, &card_dev_to_put); 32941672c0cSTakashi Iwai 33041672c0cSTakashi Iwai unlock: 3311a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 332a3933186STakashi Iwai /* put_device() is called after unlock for avoiding deadlock */ 3336a34367eSTakashi Iwai if (err < 0 && card_dev_to_put) 334a3933186STakashi Iwai put_device(card_dev_to_put); 3359b7d869eSTakashi Iwai return err; 3361da177e4SLinus Torvalds } 33798856392STakashi Iwai EXPORT_SYMBOL(snd_timer_open); 3381da177e4SLinus Torvalds 3391da177e4SLinus Torvalds /* 3401da177e4SLinus Torvalds * close a timer instance 3419b7d869eSTakashi Iwai * call this with register_mutex down. 3421da177e4SLinus Torvalds */ 34333bbb8a0STakashi Iwai static void snd_timer_close_locked(struct snd_timer_instance *timeri, 344a3933186STakashi Iwai struct device **card_devp_to_put) 3451da177e4SLinus Torvalds { 346fe1b26c9STakashi Iwai struct snd_timer *timer = timeri->timer; 3479244b2c3SJohannes Berg struct snd_timer_instance *slave, *tmp; 3481da177e4SLinus Torvalds 349fe1b26c9STakashi Iwai if (timer) { 350fe1b26c9STakashi Iwai spin_lock_irq(&timer->lock); 351fe1b26c9STakashi Iwai timeri->flags |= SNDRV_TIMER_IFLG_DEAD; 352fe1b26c9STakashi Iwai spin_unlock_irq(&timer->lock); 353fe1b26c9STakashi Iwai } 354fe1b26c9STakashi Iwai 3556a34367eSTakashi Iwai if (!list_empty(&timeri->open_list)) { 3566a34367eSTakashi Iwai list_del_init(&timeri->open_list); 357fdea53feSTakashi Iwai if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) 358fdea53feSTakashi Iwai num_slaves--; 3596a34367eSTakashi Iwai } 3609984d1b5STakashi Iwai 3611da177e4SLinus Torvalds /* force to stop the timer */ 3621da177e4SLinus Torvalds snd_timer_stop(timeri); 3631da177e4SLinus Torvalds 3649984d1b5STakashi Iwai if (timer) { 3659b7d869eSTakashi Iwai timer->num_instances--; 3661da177e4SLinus Torvalds /* wait, until the active callback is finished */ 3671da177e4SLinus Torvalds spin_lock_irq(&timer->lock); 3681da177e4SLinus Torvalds while (timeri->flags & SNDRV_TIMER_IFLG_CALLBACK) { 3691da177e4SLinus Torvalds spin_unlock_irq(&timer->lock); 3701da177e4SLinus Torvalds udelay(10); 3711da177e4SLinus Torvalds spin_lock_irq(&timer->lock); 3721da177e4SLinus Torvalds } 3731da177e4SLinus Torvalds spin_unlock_irq(&timer->lock); 3749984d1b5STakashi Iwai 3751da177e4SLinus Torvalds /* remove slave links */ 376b5a663aaSTakashi Iwai spin_lock_irq(&slave_active_lock); 377b5a663aaSTakashi Iwai spin_lock(&timer->lock); 3786a34367eSTakashi Iwai timeri->timer = NULL; 3799244b2c3SJohannes Berg list_for_each_entry_safe(slave, tmp, &timeri->slave_list_head, 3809244b2c3SJohannes Berg open_list) { 3819244b2c3SJohannes Berg list_move_tail(&slave->open_list, &snd_timer_slave_list); 3829b7d869eSTakashi Iwai timer->num_instances--; 3831da177e4SLinus Torvalds slave->master = NULL; 3841da177e4SLinus Torvalds slave->timer = NULL; 385b5a663aaSTakashi Iwai list_del_init(&slave->ack_list); 386b5a663aaSTakashi Iwai list_del_init(&slave->active_list); 3871da177e4SLinus Torvalds } 388b5a663aaSTakashi Iwai spin_unlock(&timer->lock); 389b5a663aaSTakashi Iwai spin_unlock_irq(&slave_active_lock); 3909984d1b5STakashi Iwai 3919984d1b5STakashi Iwai /* slave doesn't need to release timer resources below */ 3929984d1b5STakashi Iwai if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) 3939984d1b5STakashi Iwai timer = NULL; 3941da177e4SLinus Torvalds } 3959984d1b5STakashi Iwai 3969984d1b5STakashi Iwai if (timer) { 3979984d1b5STakashi Iwai if (list_empty(&timer->open_list_head) && timer->hw.close) 3989984d1b5STakashi Iwai timer->hw.close(timer); 3999984d1b5STakashi Iwai /* release a card refcount for safe disconnection */ 4009984d1b5STakashi Iwai if (timer->card) 401a3933186STakashi Iwai *card_devp_to_put = &timer->card->card_dev; 402de24214dSClemens Ladisch module_put(timer->module); 4039984d1b5STakashi Iwai } 4041da177e4SLinus Torvalds } 4059b7d869eSTakashi Iwai 4069b7d869eSTakashi Iwai /* 4079b7d869eSTakashi Iwai * close a timer instance 4089b7d869eSTakashi Iwai */ 40933bbb8a0STakashi Iwai void snd_timer_close(struct snd_timer_instance *timeri) 4109b7d869eSTakashi Iwai { 411a3933186STakashi Iwai struct device *card_dev_to_put = NULL; 4129b7d869eSTakashi Iwai 4139b7d869eSTakashi Iwai if (snd_BUG_ON(!timeri)) 41433bbb8a0STakashi Iwai return; 4159b7d869eSTakashi Iwai 4169b7d869eSTakashi Iwai mutex_lock(®ister_mutex); 41733bbb8a0STakashi Iwai snd_timer_close_locked(timeri, &card_dev_to_put); 4189b7d869eSTakashi Iwai mutex_unlock(®ister_mutex); 419a3933186STakashi Iwai /* put_device() is called after unlock for avoiding deadlock */ 420a3933186STakashi Iwai if (card_dev_to_put) 421a3933186STakashi Iwai put_device(card_dev_to_put); 4229b7d869eSTakashi Iwai } 42398856392STakashi Iwai EXPORT_SYMBOL(snd_timer_close); 4241da177e4SLinus Torvalds 425fdcb5761STakashi Iwai static unsigned long snd_timer_hw_resolution(struct snd_timer *timer) 426fdcb5761STakashi Iwai { 427fdcb5761STakashi Iwai if (timer->hw.c_resolution) 428fdcb5761STakashi Iwai return timer->hw.c_resolution(timer); 429fdcb5761STakashi Iwai else 430fdcb5761STakashi Iwai return timer->hw.resolution; 431fdcb5761STakashi Iwai } 432fdcb5761STakashi Iwai 43353d2f744STakashi Iwai unsigned long snd_timer_resolution(struct snd_timer_instance *timeri) 4341da177e4SLinus Torvalds { 43553d2f744STakashi Iwai struct snd_timer * timer; 4369d4d207dSTakashi Iwai unsigned long ret = 0; 4379d4d207dSTakashi Iwai unsigned long flags; 4381da177e4SLinus Torvalds 4391da177e4SLinus Torvalds if (timeri == NULL) 4401da177e4SLinus Torvalds return 0; 441dd1f7ab8SMarkus Elfring timer = timeri->timer; 4429d4d207dSTakashi Iwai if (timer) { 4439d4d207dSTakashi Iwai spin_lock_irqsave(&timer->lock, flags); 4449d4d207dSTakashi Iwai ret = snd_timer_hw_resolution(timer); 4459d4d207dSTakashi Iwai spin_unlock_irqrestore(&timer->lock, flags); 4469d4d207dSTakashi Iwai } 4479d4d207dSTakashi Iwai return ret; 4481da177e4SLinus Torvalds } 44998856392STakashi Iwai EXPORT_SYMBOL(snd_timer_resolution); 4501da177e4SLinus Torvalds 45153d2f744STakashi Iwai static void snd_timer_notify1(struct snd_timer_instance *ti, int event) 4521da177e4SLinus Torvalds { 4539d4d207dSTakashi Iwai struct snd_timer *timer = ti->timer; 4541da177e4SLinus Torvalds unsigned long resolution = 0; 45553d2f744STakashi Iwai struct snd_timer_instance *ts; 456fcae40c9SBaolin Wang struct timespec64 tstamp; 4571da177e4SLinus Torvalds 458b751eef1SJaroslav Kysela if (timer_tstamp_monotonic) 459fcae40c9SBaolin Wang ktime_get_ts64(&tstamp); 460b751eef1SJaroslav Kysela else 461fcae40c9SBaolin Wang ktime_get_real_ts64(&tstamp); 4627eaa943cSTakashi Iwai if (snd_BUG_ON(event < SNDRV_TIMER_EVENT_START || 4637eaa943cSTakashi Iwai event > SNDRV_TIMER_EVENT_PAUSE)) 4647eaa943cSTakashi Iwai return; 4659d4d207dSTakashi Iwai if (timer && 4669d4d207dSTakashi Iwai (event == SNDRV_TIMER_EVENT_START || 4679d4d207dSTakashi Iwai event == SNDRV_TIMER_EVENT_CONTINUE)) 4689d4d207dSTakashi Iwai resolution = snd_timer_hw_resolution(timer); 4691da177e4SLinus Torvalds if (ti->ccallback) 470b30477d5SJaroslav Kysela ti->ccallback(ti, event, &tstamp, resolution); 4711da177e4SLinus Torvalds if (ti->flags & SNDRV_TIMER_IFLG_SLAVE) 4721da177e4SLinus Torvalds return; 4731da177e4SLinus Torvalds if (timer == NULL) 4741da177e4SLinus Torvalds return; 4751da177e4SLinus Torvalds if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE) 4761da177e4SLinus Torvalds return; 4779244b2c3SJohannes Berg list_for_each_entry(ts, &ti->slave_active_head, active_list) 4781da177e4SLinus Torvalds if (ts->ccallback) 479117159f0STakashi Iwai ts->ccallback(ts, event + 100, &tstamp, resolution); 4801da177e4SLinus Torvalds } 4811da177e4SLinus Torvalds 482f65e0d29STakashi Iwai /* start/continue a master timer */ 483f65e0d29STakashi Iwai static int snd_timer_start1(struct snd_timer_instance *timeri, 484f65e0d29STakashi Iwai bool start, unsigned long ticks) 4851da177e4SLinus Torvalds { 486f65e0d29STakashi Iwai struct snd_timer *timer; 487f65e0d29STakashi Iwai int result; 488f65e0d29STakashi Iwai unsigned long flags; 489f65e0d29STakashi Iwai 490f65e0d29STakashi Iwai timer = timeri->timer; 491f65e0d29STakashi Iwai if (!timer) 492f65e0d29STakashi Iwai return -EINVAL; 493f65e0d29STakashi Iwai 494f65e0d29STakashi Iwai spin_lock_irqsave(&timer->lock, flags); 495fe1b26c9STakashi Iwai if (timeri->flags & SNDRV_TIMER_IFLG_DEAD) { 496fe1b26c9STakashi Iwai result = -EINVAL; 497fe1b26c9STakashi Iwai goto unlock; 498fe1b26c9STakashi Iwai } 499f65e0d29STakashi Iwai if (timer->card && timer->card->shutdown) { 500f65e0d29STakashi Iwai result = -ENODEV; 501f65e0d29STakashi Iwai goto unlock; 502f65e0d29STakashi Iwai } 503f65e0d29STakashi Iwai if (timeri->flags & (SNDRV_TIMER_IFLG_RUNNING | 504f65e0d29STakashi Iwai SNDRV_TIMER_IFLG_START)) { 505f65e0d29STakashi Iwai result = -EBUSY; 506f65e0d29STakashi Iwai goto unlock; 507f65e0d29STakashi Iwai } 508f65e0d29STakashi Iwai 509f65e0d29STakashi Iwai if (start) 510f65e0d29STakashi Iwai timeri->ticks = timeri->cticks = ticks; 511f65e0d29STakashi Iwai else if (!timeri->cticks) 512f65e0d29STakashi Iwai timeri->cticks = 1; 513f65e0d29STakashi Iwai timeri->pticks = 0; 514f65e0d29STakashi Iwai 5155b7c757dSNicolas Kaiser list_move_tail(&timeri->active_list, &timer->active_list_head); 5161da177e4SLinus Torvalds if (timer->running) { 5171da177e4SLinus Torvalds if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE) 5181da177e4SLinus Torvalds goto __start_now; 5191da177e4SLinus Torvalds timer->flags |= SNDRV_TIMER_FLG_RESCHED; 5201da177e4SLinus Torvalds timeri->flags |= SNDRV_TIMER_IFLG_START; 521f65e0d29STakashi Iwai result = 1; /* delayed start */ 5221da177e4SLinus Torvalds } else { 523f65e0d29STakashi Iwai if (start) 524f65e0d29STakashi Iwai timer->sticks = ticks; 5251da177e4SLinus Torvalds timer->hw.start(timer); 5261da177e4SLinus Torvalds __start_now: 5271da177e4SLinus Torvalds timer->running++; 5281da177e4SLinus Torvalds timeri->flags |= SNDRV_TIMER_IFLG_RUNNING; 529f65e0d29STakashi Iwai result = 0; 5301da177e4SLinus Torvalds } 531f65e0d29STakashi Iwai snd_timer_notify1(timeri, start ? SNDRV_TIMER_EVENT_START : 532f65e0d29STakashi Iwai SNDRV_TIMER_EVENT_CONTINUE); 533f65e0d29STakashi Iwai unlock: 534f65e0d29STakashi Iwai spin_unlock_irqrestore(&timer->lock, flags); 535f65e0d29STakashi Iwai return result; 5361da177e4SLinus Torvalds } 5371da177e4SLinus Torvalds 538f65e0d29STakashi Iwai /* start/continue a slave timer */ 539f65e0d29STakashi Iwai static int snd_timer_start_slave(struct snd_timer_instance *timeri, 540f65e0d29STakashi Iwai bool start) 5411da177e4SLinus Torvalds { 5421da177e4SLinus Torvalds unsigned long flags; 543fe1b26c9STakashi Iwai int err; 5441da177e4SLinus Torvalds 5451da177e4SLinus Torvalds spin_lock_irqsave(&slave_active_lock, flags); 546fe1b26c9STakashi Iwai if (timeri->flags & SNDRV_TIMER_IFLG_DEAD) { 547fe1b26c9STakashi Iwai err = -EINVAL; 548fe1b26c9STakashi Iwai goto unlock; 549fe1b26c9STakashi Iwai } 550f784beb7STakashi Iwai if (timeri->flags & SNDRV_TIMER_IFLG_RUNNING) { 551fe1b26c9STakashi Iwai err = -EBUSY; 552fe1b26c9STakashi Iwai goto unlock; 553f784beb7STakashi Iwai } 5541da177e4SLinus Torvalds timeri->flags |= SNDRV_TIMER_IFLG_RUNNING; 555b5a663aaSTakashi Iwai if (timeri->master && timeri->timer) { 556b5a663aaSTakashi Iwai spin_lock(&timeri->timer->lock); 5576b172a85SClemens Ladisch list_add_tail(&timeri->active_list, 5586b172a85SClemens Ladisch &timeri->master->slave_active_head); 559f65e0d29STakashi Iwai snd_timer_notify1(timeri, start ? SNDRV_TIMER_EVENT_START : 560f65e0d29STakashi Iwai SNDRV_TIMER_EVENT_CONTINUE); 561b5a663aaSTakashi Iwai spin_unlock(&timeri->timer->lock); 562b5a663aaSTakashi Iwai } 563fe1b26c9STakashi Iwai err = 1; /* delayed start */ 564fe1b26c9STakashi Iwai unlock: 5651da177e4SLinus Torvalds spin_unlock_irqrestore(&slave_active_lock, flags); 566fe1b26c9STakashi Iwai return err; 5671da177e4SLinus Torvalds } 5681da177e4SLinus Torvalds 569f65e0d29STakashi Iwai /* stop/pause a master timer */ 570f65e0d29STakashi Iwai static int snd_timer_stop1(struct snd_timer_instance *timeri, bool stop) 5711da177e4SLinus Torvalds { 57253d2f744STakashi Iwai struct snd_timer *timer; 573f65e0d29STakashi Iwai int result = 0; 5741da177e4SLinus Torvalds unsigned long flags; 5751da177e4SLinus Torvalds 5761da177e4SLinus Torvalds timer = timeri->timer; 5771da177e4SLinus Torvalds if (!timer) 5781da177e4SLinus Torvalds return -EINVAL; 5791da177e4SLinus Torvalds spin_lock_irqsave(&timer->lock, flags); 580f784beb7STakashi Iwai if (!(timeri->flags & (SNDRV_TIMER_IFLG_RUNNING | 581f784beb7STakashi Iwai SNDRV_TIMER_IFLG_START))) { 582f65e0d29STakashi Iwai result = -EBUSY; 583f65e0d29STakashi Iwai goto unlock; 584f784beb7STakashi Iwai } 5851da177e4SLinus Torvalds list_del_init(&timeri->ack_list); 5861da177e4SLinus Torvalds list_del_init(&timeri->active_list); 587f65e0d29STakashi Iwai if (timer->card && timer->card->shutdown) 588f65e0d29STakashi Iwai goto unlock; 589f65e0d29STakashi Iwai if (stop) { 590f65e0d29STakashi Iwai timeri->cticks = timeri->ticks; 591f65e0d29STakashi Iwai timeri->pticks = 0; 592230323daSTakashi Iwai } 5931da177e4SLinus Torvalds if ((timeri->flags & SNDRV_TIMER_IFLG_RUNNING) && 5941da177e4SLinus Torvalds !(--timer->running)) { 5951da177e4SLinus Torvalds timer->hw.stop(timer); 5961da177e4SLinus Torvalds if (timer->flags & SNDRV_TIMER_FLG_RESCHED) { 5971da177e4SLinus Torvalds timer->flags &= ~SNDRV_TIMER_FLG_RESCHED; 5981da177e4SLinus Torvalds snd_timer_reschedule(timer, 0); 5991da177e4SLinus Torvalds if (timer->flags & SNDRV_TIMER_FLG_CHANGE) { 6001da177e4SLinus Torvalds timer->flags &= ~SNDRV_TIMER_FLG_CHANGE; 6011da177e4SLinus Torvalds timer->hw.start(timer); 6021da177e4SLinus Torvalds } 6031da177e4SLinus Torvalds } 6041da177e4SLinus Torvalds } 605c3b16813STakashi Iwai timeri->flags &= ~(SNDRV_TIMER_IFLG_RUNNING | SNDRV_TIMER_IFLG_START); 6069f8a7658STakashi Iwai if (stop) 6079f8a7658STakashi Iwai timeri->flags &= ~SNDRV_TIMER_IFLG_PAUSED; 6089f8a7658STakashi Iwai else 6099f8a7658STakashi Iwai timeri->flags |= SNDRV_TIMER_IFLG_PAUSED; 610f65e0d29STakashi Iwai snd_timer_notify1(timeri, stop ? SNDRV_TIMER_EVENT_STOP : 6113ae18097SBen Hutchings SNDRV_TIMER_EVENT_PAUSE); 612f65e0d29STakashi Iwai unlock: 6131da177e4SLinus Torvalds spin_unlock_irqrestore(&timer->lock, flags); 614f65e0d29STakashi Iwai return result; 615f65e0d29STakashi Iwai } 616f65e0d29STakashi Iwai 617f65e0d29STakashi Iwai /* stop/pause a slave timer */ 618f65e0d29STakashi Iwai static int snd_timer_stop_slave(struct snd_timer_instance *timeri, bool stop) 619f65e0d29STakashi Iwai { 620f65e0d29STakashi Iwai unsigned long flags; 621f65e0d29STakashi Iwai 622f65e0d29STakashi Iwai spin_lock_irqsave(&slave_active_lock, flags); 623f65e0d29STakashi Iwai if (!(timeri->flags & SNDRV_TIMER_IFLG_RUNNING)) { 624f65e0d29STakashi Iwai spin_unlock_irqrestore(&slave_active_lock, flags); 625f65e0d29STakashi Iwai return -EBUSY; 626f65e0d29STakashi Iwai } 627f65e0d29STakashi Iwai timeri->flags &= ~SNDRV_TIMER_IFLG_RUNNING; 628f65e0d29STakashi Iwai if (timeri->timer) { 629f65e0d29STakashi Iwai spin_lock(&timeri->timer->lock); 630f65e0d29STakashi Iwai list_del_init(&timeri->ack_list); 631f65e0d29STakashi Iwai list_del_init(&timeri->active_list); 632f65e0d29STakashi Iwai snd_timer_notify1(timeri, stop ? SNDRV_TIMER_EVENT_STOP : 6333ae18097SBen Hutchings SNDRV_TIMER_EVENT_PAUSE); 634f65e0d29STakashi Iwai spin_unlock(&timeri->timer->lock); 635f65e0d29STakashi Iwai } 636f65e0d29STakashi Iwai spin_unlock_irqrestore(&slave_active_lock, flags); 6371da177e4SLinus Torvalds return 0; 6381da177e4SLinus Torvalds } 6391da177e4SLinus Torvalds 6401da177e4SLinus Torvalds /* 641f65e0d29STakashi Iwai * start the timer instance 642f65e0d29STakashi Iwai */ 643f65e0d29STakashi Iwai int snd_timer_start(struct snd_timer_instance *timeri, unsigned int ticks) 644f65e0d29STakashi Iwai { 645f65e0d29STakashi Iwai if (timeri == NULL || ticks < 1) 646f65e0d29STakashi Iwai return -EINVAL; 647f65e0d29STakashi Iwai if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) 648f65e0d29STakashi Iwai return snd_timer_start_slave(timeri, true); 649f65e0d29STakashi Iwai else 650f65e0d29STakashi Iwai return snd_timer_start1(timeri, true, ticks); 651f65e0d29STakashi Iwai } 65298856392STakashi Iwai EXPORT_SYMBOL(snd_timer_start); 653f65e0d29STakashi Iwai 654f65e0d29STakashi Iwai /* 6551da177e4SLinus Torvalds * stop the timer instance. 6561da177e4SLinus Torvalds * 6571da177e4SLinus Torvalds * do not call this from the timer callback! 6581da177e4SLinus Torvalds */ 65953d2f744STakashi Iwai int snd_timer_stop(struct snd_timer_instance *timeri) 6601da177e4SLinus Torvalds { 661f65e0d29STakashi Iwai if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) 662f65e0d29STakashi Iwai return snd_timer_stop_slave(timeri, true); 663f65e0d29STakashi Iwai else 664f65e0d29STakashi Iwai return snd_timer_stop1(timeri, true); 6651da177e4SLinus Torvalds } 66698856392STakashi Iwai EXPORT_SYMBOL(snd_timer_stop); 6671da177e4SLinus Torvalds 6681da177e4SLinus Torvalds /* 6691da177e4SLinus Torvalds * start again.. the tick is kept. 6701da177e4SLinus Torvalds */ 67153d2f744STakashi Iwai int snd_timer_continue(struct snd_timer_instance *timeri) 6721da177e4SLinus Torvalds { 6739f8a7658STakashi Iwai /* timer can continue only after pause */ 6749f8a7658STakashi Iwai if (!(timeri->flags & SNDRV_TIMER_IFLG_PAUSED)) 6759f8a7658STakashi Iwai return -EINVAL; 6769f8a7658STakashi Iwai 6771da177e4SLinus Torvalds if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) 678f65e0d29STakashi Iwai return snd_timer_start_slave(timeri, false); 679f65e0d29STakashi Iwai else 680f65e0d29STakashi Iwai return snd_timer_start1(timeri, false, 0); 6811da177e4SLinus Torvalds } 68298856392STakashi Iwai EXPORT_SYMBOL(snd_timer_continue); 6831da177e4SLinus Torvalds 6841da177e4SLinus Torvalds /* 6851da177e4SLinus Torvalds * pause.. remember the ticks left 6861da177e4SLinus Torvalds */ 68753d2f744STakashi Iwai int snd_timer_pause(struct snd_timer_instance * timeri) 6881da177e4SLinus Torvalds { 689f65e0d29STakashi Iwai if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) 690f65e0d29STakashi Iwai return snd_timer_stop_slave(timeri, false); 691f65e0d29STakashi Iwai else 692f65e0d29STakashi Iwai return snd_timer_stop1(timeri, false); 6931da177e4SLinus Torvalds } 69498856392STakashi Iwai EXPORT_SYMBOL(snd_timer_pause); 6951da177e4SLinus Torvalds 6961da177e4SLinus Torvalds /* 6971da177e4SLinus Torvalds * reschedule the timer 6981da177e4SLinus Torvalds * 6991da177e4SLinus Torvalds * start pending instances and check the scheduling ticks. 7001da177e4SLinus Torvalds * when the scheduling ticks is changed set CHANGE flag to reprogram the timer. 7011da177e4SLinus Torvalds */ 70253d2f744STakashi Iwai static void snd_timer_reschedule(struct snd_timer * timer, unsigned long ticks_left) 7031da177e4SLinus Torvalds { 70453d2f744STakashi Iwai struct snd_timer_instance *ti; 7051da177e4SLinus Torvalds unsigned long ticks = ~0UL; 7061da177e4SLinus Torvalds 7079244b2c3SJohannes Berg list_for_each_entry(ti, &timer->active_list_head, active_list) { 7081da177e4SLinus Torvalds if (ti->flags & SNDRV_TIMER_IFLG_START) { 7091da177e4SLinus Torvalds ti->flags &= ~SNDRV_TIMER_IFLG_START; 7101da177e4SLinus Torvalds ti->flags |= SNDRV_TIMER_IFLG_RUNNING; 7111da177e4SLinus Torvalds timer->running++; 7121da177e4SLinus Torvalds } 7131da177e4SLinus Torvalds if (ti->flags & SNDRV_TIMER_IFLG_RUNNING) { 7141da177e4SLinus Torvalds if (ticks > ti->cticks) 7151da177e4SLinus Torvalds ticks = ti->cticks; 7161da177e4SLinus Torvalds } 7171da177e4SLinus Torvalds } 7181da177e4SLinus Torvalds if (ticks == ~0UL) { 7191da177e4SLinus Torvalds timer->flags &= ~SNDRV_TIMER_FLG_RESCHED; 7201da177e4SLinus Torvalds return; 7211da177e4SLinus Torvalds } 7221da177e4SLinus Torvalds if (ticks > timer->hw.ticks) 7231da177e4SLinus Torvalds ticks = timer->hw.ticks; 7241da177e4SLinus Torvalds if (ticks_left != ticks) 7251da177e4SLinus Torvalds timer->flags |= SNDRV_TIMER_FLG_CHANGE; 7261da177e4SLinus Torvalds timer->sticks = ticks; 7271da177e4SLinus Torvalds } 7281da177e4SLinus Torvalds 7298748b850STakashi Iwai /* call callbacks in timer ack list */ 7308748b850STakashi Iwai static void snd_timer_process_callbacks(struct snd_timer *timer, 7318748b850STakashi Iwai struct list_head *head) 7321da177e4SLinus Torvalds { 73353d2f744STakashi Iwai struct snd_timer_instance *ti; 7341da177e4SLinus Torvalds unsigned long resolution, ticks; 7351da177e4SLinus Torvalds 7368748b850STakashi Iwai while (!list_empty(head)) { 7378748b850STakashi Iwai ti = list_first_entry(head, struct snd_timer_instance, 7388748b850STakashi Iwai ack_list); 7391da177e4SLinus Torvalds 7401da177e4SLinus Torvalds /* remove from ack_list and make empty */ 741df55531bSTakashi Iwai list_del_init(&ti->ack_list); 7421da177e4SLinus Torvalds 743fe1b26c9STakashi Iwai if (!(ti->flags & SNDRV_TIMER_IFLG_DEAD)) { 7441da177e4SLinus Torvalds ticks = ti->pticks; 7451da177e4SLinus Torvalds ti->pticks = 0; 7461da177e4SLinus Torvalds resolution = ti->resolution; 7471da177e4SLinus Torvalds ti->flags |= SNDRV_TIMER_IFLG_CALLBACK; 7481da177e4SLinus Torvalds spin_unlock(&timer->lock); 7491da177e4SLinus Torvalds if (ti->callback) 7501da177e4SLinus Torvalds ti->callback(ti, resolution, ticks); 7511da177e4SLinus Torvalds spin_lock(&timer->lock); 7521da177e4SLinus Torvalds ti->flags &= ~SNDRV_TIMER_IFLG_CALLBACK; 7531da177e4SLinus Torvalds } 7541da177e4SLinus Torvalds } 7558748b850STakashi Iwai } 7568748b850STakashi Iwai 7577bb4a8a2STakashi Iwai /* clear pending instances from ack list */ 7587bb4a8a2STakashi Iwai static void snd_timer_clear_callbacks(struct snd_timer *timer, 7597bb4a8a2STakashi Iwai struct list_head *head) 7607bb4a8a2STakashi Iwai { 7617bb4a8a2STakashi Iwai unsigned long flags; 7627bb4a8a2STakashi Iwai 7637bb4a8a2STakashi Iwai spin_lock_irqsave(&timer->lock, flags); 7647bb4a8a2STakashi Iwai while (!list_empty(head)) 7657bb4a8a2STakashi Iwai list_del_init(head->next); 7667bb4a8a2STakashi Iwai spin_unlock_irqrestore(&timer->lock, flags); 7677bb4a8a2STakashi Iwai } 7687bb4a8a2STakashi Iwai 7698748b850STakashi Iwai /* 7708748b850STakashi Iwai * timer tasklet 7718748b850STakashi Iwai * 7728748b850STakashi Iwai */ 7738748b850STakashi Iwai static void snd_timer_tasklet(unsigned long arg) 7748748b850STakashi Iwai { 7758748b850STakashi Iwai struct snd_timer *timer = (struct snd_timer *) arg; 7768748b850STakashi Iwai unsigned long flags; 7778748b850STakashi Iwai 7787bb4a8a2STakashi Iwai if (timer->card && timer->card->shutdown) { 7797bb4a8a2STakashi Iwai snd_timer_clear_callbacks(timer, &timer->sack_list_head); 7808748b850STakashi Iwai return; 7817bb4a8a2STakashi Iwai } 7828748b850STakashi Iwai 7838748b850STakashi Iwai spin_lock_irqsave(&timer->lock, flags); 7848748b850STakashi Iwai snd_timer_process_callbacks(timer, &timer->sack_list_head); 7852999ff5bSTakashi Iwai spin_unlock_irqrestore(&timer->lock, flags); 7861da177e4SLinus Torvalds } 7871da177e4SLinus Torvalds 7881da177e4SLinus Torvalds /* 7891da177e4SLinus Torvalds * timer interrupt 7901da177e4SLinus Torvalds * 7911da177e4SLinus Torvalds * ticks_left is usually equal to timer->sticks. 7921da177e4SLinus Torvalds * 7931da177e4SLinus Torvalds */ 79453d2f744STakashi Iwai void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left) 7951da177e4SLinus Torvalds { 7969244b2c3SJohannes Berg struct snd_timer_instance *ti, *ts, *tmp; 7978748b850STakashi Iwai unsigned long resolution; 7988748b850STakashi Iwai struct list_head *ack_list_head; 799b32425acSTakashi Iwai unsigned long flags; 8001da177e4SLinus Torvalds int use_tasklet = 0; 8011da177e4SLinus Torvalds 8021da177e4SLinus Torvalds if (timer == NULL) 8031da177e4SLinus Torvalds return; 8041da177e4SLinus Torvalds 8057bb4a8a2STakashi Iwai if (timer->card && timer->card->shutdown) { 8067bb4a8a2STakashi Iwai snd_timer_clear_callbacks(timer, &timer->ack_list_head); 807230323daSTakashi Iwai return; 8087bb4a8a2STakashi Iwai } 809230323daSTakashi Iwai 810b32425acSTakashi Iwai spin_lock_irqsave(&timer->lock, flags); 8111da177e4SLinus Torvalds 8121da177e4SLinus Torvalds /* remember the current resolution */ 813fdcb5761STakashi Iwai resolution = snd_timer_hw_resolution(timer); 8141da177e4SLinus Torvalds 8151da177e4SLinus Torvalds /* loop for all active instances 8169244b2c3SJohannes Berg * Here we cannot use list_for_each_entry because the active_list of a 8176b172a85SClemens Ladisch * processed instance is relinked to done_list_head before the callback 8186b172a85SClemens Ladisch * is called. 8191da177e4SLinus Torvalds */ 8209244b2c3SJohannes Berg list_for_each_entry_safe(ti, tmp, &timer->active_list_head, 8219244b2c3SJohannes Berg active_list) { 822fe1b26c9STakashi Iwai if (ti->flags & SNDRV_TIMER_IFLG_DEAD) 823fe1b26c9STakashi Iwai continue; 8241da177e4SLinus Torvalds if (!(ti->flags & SNDRV_TIMER_IFLG_RUNNING)) 8251da177e4SLinus Torvalds continue; 8261da177e4SLinus Torvalds ti->pticks += ticks_left; 8271da177e4SLinus Torvalds ti->resolution = resolution; 8281da177e4SLinus Torvalds if (ti->cticks < ticks_left) 8291da177e4SLinus Torvalds ti->cticks = 0; 8301da177e4SLinus Torvalds else 8311da177e4SLinus Torvalds ti->cticks -= ticks_left; 8321da177e4SLinus Torvalds if (ti->cticks) /* not expired */ 8331da177e4SLinus Torvalds continue; 8341da177e4SLinus Torvalds if (ti->flags & SNDRV_TIMER_IFLG_AUTO) { 8351da177e4SLinus Torvalds ti->cticks = ti->ticks; 8361da177e4SLinus Torvalds } else { 8371da177e4SLinus Torvalds ti->flags &= ~SNDRV_TIMER_IFLG_RUNNING; 838094fd3beSTakashi Iwai --timer->running; 839ee8413b0STakashi Iwai list_del_init(&ti->active_list); 8401da177e4SLinus Torvalds } 8411da177e4SLinus Torvalds if ((timer->hw.flags & SNDRV_TIMER_HW_TASKLET) || 8426b172a85SClemens Ladisch (ti->flags & SNDRV_TIMER_IFLG_FAST)) 8436b172a85SClemens Ladisch ack_list_head = &timer->ack_list_head; 8446b172a85SClemens Ladisch else 8456b172a85SClemens Ladisch ack_list_head = &timer->sack_list_head; 8466b172a85SClemens Ladisch if (list_empty(&ti->ack_list)) 8476b172a85SClemens Ladisch list_add_tail(&ti->ack_list, ack_list_head); 8489244b2c3SJohannes Berg list_for_each_entry(ts, &ti->slave_active_head, active_list) { 8491da177e4SLinus Torvalds ts->pticks = ti->pticks; 8501da177e4SLinus Torvalds ts->resolution = resolution; 8516b172a85SClemens Ladisch if (list_empty(&ts->ack_list)) 8526b172a85SClemens Ladisch list_add_tail(&ts->ack_list, ack_list_head); 8531da177e4SLinus Torvalds } 8541da177e4SLinus Torvalds } 8551da177e4SLinus Torvalds if (timer->flags & SNDRV_TIMER_FLG_RESCHED) 856cd93fe47SClemens Ladisch snd_timer_reschedule(timer, timer->sticks); 8571da177e4SLinus Torvalds if (timer->running) { 8581da177e4SLinus Torvalds if (timer->hw.flags & SNDRV_TIMER_HW_STOP) { 8591da177e4SLinus Torvalds timer->hw.stop(timer); 8601da177e4SLinus Torvalds timer->flags |= SNDRV_TIMER_FLG_CHANGE; 8611da177e4SLinus Torvalds } 8621da177e4SLinus Torvalds if (!(timer->hw.flags & SNDRV_TIMER_HW_AUTO) || 8631da177e4SLinus Torvalds (timer->flags & SNDRV_TIMER_FLG_CHANGE)) { 8641da177e4SLinus Torvalds /* restart timer */ 8651da177e4SLinus Torvalds timer->flags &= ~SNDRV_TIMER_FLG_CHANGE; 8661da177e4SLinus Torvalds timer->hw.start(timer); 8671da177e4SLinus Torvalds } 8681da177e4SLinus Torvalds } else { 8691da177e4SLinus Torvalds timer->hw.stop(timer); 8701da177e4SLinus Torvalds } 8711da177e4SLinus Torvalds 8721da177e4SLinus Torvalds /* now process all fast callbacks */ 8738748b850STakashi Iwai snd_timer_process_callbacks(timer, &timer->ack_list_head); 8741da177e4SLinus Torvalds 8751da177e4SLinus Torvalds /* do we have any slow callbacks? */ 8761da177e4SLinus Torvalds use_tasklet = !list_empty(&timer->sack_list_head); 877b32425acSTakashi Iwai spin_unlock_irqrestore(&timer->lock, flags); 8781da177e4SLinus Torvalds 8791da177e4SLinus Torvalds if (use_tasklet) 8801f04128aSTakashi Iwai tasklet_schedule(&timer->task_queue); 8811da177e4SLinus Torvalds } 88298856392STakashi Iwai EXPORT_SYMBOL(snd_timer_interrupt); 8831da177e4SLinus Torvalds 8841da177e4SLinus Torvalds /* 8851da177e4SLinus Torvalds 8861da177e4SLinus Torvalds */ 8871da177e4SLinus Torvalds 88853d2f744STakashi Iwai int snd_timer_new(struct snd_card *card, char *id, struct snd_timer_id *tid, 88953d2f744STakashi Iwai struct snd_timer **rtimer) 8901da177e4SLinus Torvalds { 89153d2f744STakashi Iwai struct snd_timer *timer; 8921da177e4SLinus Torvalds int err; 89353d2f744STakashi Iwai static struct snd_device_ops ops = { 8941da177e4SLinus Torvalds .dev_free = snd_timer_dev_free, 8951da177e4SLinus Torvalds .dev_register = snd_timer_dev_register, 896c461482cSTakashi Iwai .dev_disconnect = snd_timer_dev_disconnect, 8971da177e4SLinus Torvalds }; 8981da177e4SLinus Torvalds 8997eaa943cSTakashi Iwai if (snd_BUG_ON(!tid)) 9007eaa943cSTakashi Iwai return -EINVAL; 901d10ee9c5SSrikanth K H if (tid->dev_class == SNDRV_TIMER_CLASS_CARD || 902d10ee9c5SSrikanth K H tid->dev_class == SNDRV_TIMER_CLASS_PCM) { 903d10ee9c5SSrikanth K H if (WARN_ON(!card)) 904d10ee9c5SSrikanth K H return -EINVAL; 905d10ee9c5SSrikanth K H } 9067eaa943cSTakashi Iwai if (rtimer) 9071da177e4SLinus Torvalds *rtimer = NULL; 908ca2c0966STakashi Iwai timer = kzalloc(sizeof(*timer), GFP_KERNEL); 909ec0e9937STakashi Iwai if (!timer) 9101da177e4SLinus Torvalds return -ENOMEM; 9111da177e4SLinus Torvalds timer->tmr_class = tid->dev_class; 9121da177e4SLinus Torvalds timer->card = card; 9131da177e4SLinus Torvalds timer->tmr_device = tid->device; 9141da177e4SLinus Torvalds timer->tmr_subdevice = tid->subdevice; 9151da177e4SLinus Torvalds if (id) 9161da177e4SLinus Torvalds strlcpy(timer->id, id, sizeof(timer->id)); 9176b760bb2SVegard Nossum timer->sticks = 1; 9181da177e4SLinus Torvalds INIT_LIST_HEAD(&timer->device_list); 9191da177e4SLinus Torvalds INIT_LIST_HEAD(&timer->open_list_head); 9201da177e4SLinus Torvalds INIT_LIST_HEAD(&timer->active_list_head); 9211da177e4SLinus Torvalds INIT_LIST_HEAD(&timer->ack_list_head); 9221da177e4SLinus Torvalds INIT_LIST_HEAD(&timer->sack_list_head); 9231da177e4SLinus Torvalds spin_lock_init(&timer->lock); 9246b172a85SClemens Ladisch tasklet_init(&timer->task_queue, snd_timer_tasklet, 9256b172a85SClemens Ladisch (unsigned long)timer); 9269b7d869eSTakashi Iwai timer->max_instances = 1000; /* default limit per timer */ 9271da177e4SLinus Torvalds if (card != NULL) { 928de24214dSClemens Ladisch timer->module = card->module; 9296b172a85SClemens Ladisch err = snd_device_new(card, SNDRV_DEV_TIMER, timer, &ops); 9306b172a85SClemens Ladisch if (err < 0) { 9311da177e4SLinus Torvalds snd_timer_free(timer); 9321da177e4SLinus Torvalds return err; 9331da177e4SLinus Torvalds } 9341da177e4SLinus Torvalds } 9357eaa943cSTakashi Iwai if (rtimer) 9361da177e4SLinus Torvalds *rtimer = timer; 9371da177e4SLinus Torvalds return 0; 9381da177e4SLinus Torvalds } 93998856392STakashi Iwai EXPORT_SYMBOL(snd_timer_new); 9401da177e4SLinus Torvalds 94153d2f744STakashi Iwai static int snd_timer_free(struct snd_timer *timer) 9421da177e4SLinus Torvalds { 9437eaa943cSTakashi Iwai if (!timer) 9447eaa943cSTakashi Iwai return 0; 945c461482cSTakashi Iwai 946c461482cSTakashi Iwai mutex_lock(®ister_mutex); 947c461482cSTakashi Iwai if (! list_empty(&timer->open_list_head)) { 948c461482cSTakashi Iwai struct list_head *p, *n; 949c461482cSTakashi Iwai struct snd_timer_instance *ti; 950cf74dcf3STakashi Iwai pr_warn("ALSA: timer %p is busy?\n", timer); 951c461482cSTakashi Iwai list_for_each_safe(p, n, &timer->open_list_head) { 952c461482cSTakashi Iwai list_del_init(p); 953c461482cSTakashi Iwai ti = list_entry(p, struct snd_timer_instance, open_list); 954c461482cSTakashi Iwai ti->timer = NULL; 955c461482cSTakashi Iwai } 956c461482cSTakashi Iwai } 957c461482cSTakashi Iwai list_del(&timer->device_list); 958c461482cSTakashi Iwai mutex_unlock(®ister_mutex); 959c461482cSTakashi Iwai 9601da177e4SLinus Torvalds if (timer->private_free) 9611da177e4SLinus Torvalds timer->private_free(timer); 9621da177e4SLinus Torvalds kfree(timer); 9631da177e4SLinus Torvalds return 0; 9641da177e4SLinus Torvalds } 9651da177e4SLinus Torvalds 96653d2f744STakashi Iwai static int snd_timer_dev_free(struct snd_device *device) 9671da177e4SLinus Torvalds { 96853d2f744STakashi Iwai struct snd_timer *timer = device->device_data; 9691da177e4SLinus Torvalds return snd_timer_free(timer); 9701da177e4SLinus Torvalds } 9711da177e4SLinus Torvalds 97253d2f744STakashi Iwai static int snd_timer_dev_register(struct snd_device *dev) 9731da177e4SLinus Torvalds { 97453d2f744STakashi Iwai struct snd_timer *timer = dev->device_data; 97553d2f744STakashi Iwai struct snd_timer *timer1; 9761da177e4SLinus Torvalds 9777eaa943cSTakashi Iwai if (snd_BUG_ON(!timer || !timer->hw.start || !timer->hw.stop)) 9787eaa943cSTakashi Iwai return -ENXIO; 9791da177e4SLinus Torvalds if (!(timer->hw.flags & SNDRV_TIMER_HW_SLAVE) && 9801da177e4SLinus Torvalds !timer->hw.resolution && timer->hw.c_resolution == NULL) 9811da177e4SLinus Torvalds return -EINVAL; 9821da177e4SLinus Torvalds 9831a60d4c5SIngo Molnar mutex_lock(®ister_mutex); 9849244b2c3SJohannes Berg list_for_each_entry(timer1, &snd_timer_list, device_list) { 9851da177e4SLinus Torvalds if (timer1->tmr_class > timer->tmr_class) 9861da177e4SLinus Torvalds break; 9871da177e4SLinus Torvalds if (timer1->tmr_class < timer->tmr_class) 9881da177e4SLinus Torvalds continue; 9891da177e4SLinus Torvalds if (timer1->card && timer->card) { 9901da177e4SLinus Torvalds if (timer1->card->number > timer->card->number) 9911da177e4SLinus Torvalds break; 9921da177e4SLinus Torvalds if (timer1->card->number < timer->card->number) 9931da177e4SLinus Torvalds continue; 9941da177e4SLinus Torvalds } 9951da177e4SLinus Torvalds if (timer1->tmr_device > timer->tmr_device) 9961da177e4SLinus Torvalds break; 9971da177e4SLinus Torvalds if (timer1->tmr_device < timer->tmr_device) 9981da177e4SLinus Torvalds continue; 9991da177e4SLinus Torvalds if (timer1->tmr_subdevice > timer->tmr_subdevice) 10001da177e4SLinus Torvalds break; 10011da177e4SLinus Torvalds if (timer1->tmr_subdevice < timer->tmr_subdevice) 10021da177e4SLinus Torvalds continue; 10031da177e4SLinus Torvalds /* conflicts.. */ 10041a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 10051da177e4SLinus Torvalds return -EBUSY; 10061da177e4SLinus Torvalds } 10079244b2c3SJohannes Berg list_add_tail(&timer->device_list, &timer1->device_list); 10081a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 10091da177e4SLinus Torvalds return 0; 10101da177e4SLinus Torvalds } 10111da177e4SLinus Torvalds 1012c461482cSTakashi Iwai static int snd_timer_dev_disconnect(struct snd_device *device) 10131da177e4SLinus Torvalds { 101453d2f744STakashi Iwai struct snd_timer *timer = device->device_data; 1015230323daSTakashi Iwai struct snd_timer_instance *ti; 1016230323daSTakashi Iwai 1017c461482cSTakashi Iwai mutex_lock(®ister_mutex); 1018c461482cSTakashi Iwai list_del_init(&timer->device_list); 1019230323daSTakashi Iwai /* wake up pending sleepers */ 1020230323daSTakashi Iwai list_for_each_entry(ti, &timer->open_list_head, open_list) { 102140ed9444STakashi Iwai if (ti->disconnect) 102240ed9444STakashi Iwai ti->disconnect(ti); 1023230323daSTakashi Iwai } 1024c461482cSTakashi Iwai mutex_unlock(®ister_mutex); 1025c461482cSTakashi Iwai return 0; 10261da177e4SLinus Torvalds } 10271da177e4SLinus Torvalds 1028fcae40c9SBaolin Wang void snd_timer_notify(struct snd_timer *timer, int event, struct timespec64 *tstamp) 10291da177e4SLinus Torvalds { 10301da177e4SLinus Torvalds unsigned long flags; 10311da177e4SLinus Torvalds unsigned long resolution = 0; 103253d2f744STakashi Iwai struct snd_timer_instance *ti, *ts; 10331da177e4SLinus Torvalds 1034230323daSTakashi Iwai if (timer->card && timer->card->shutdown) 1035230323daSTakashi Iwai return; 10367c22f1aaSTakashi Iwai if (! (timer->hw.flags & SNDRV_TIMER_HW_SLAVE)) 10377c22f1aaSTakashi Iwai return; 10387eaa943cSTakashi Iwai if (snd_BUG_ON(event < SNDRV_TIMER_EVENT_MSTART || 10397eaa943cSTakashi Iwai event > SNDRV_TIMER_EVENT_MRESUME)) 10407eaa943cSTakashi Iwai return; 10411da177e4SLinus Torvalds spin_lock_irqsave(&timer->lock, flags); 1042a501dfa3SJaroslav Kysela if (event == SNDRV_TIMER_EVENT_MSTART || 1043a501dfa3SJaroslav Kysela event == SNDRV_TIMER_EVENT_MCONTINUE || 1044fdcb5761STakashi Iwai event == SNDRV_TIMER_EVENT_MRESUME) 1045fdcb5761STakashi Iwai resolution = snd_timer_hw_resolution(timer); 10469244b2c3SJohannes Berg list_for_each_entry(ti, &timer->active_list_head, active_list) { 10471da177e4SLinus Torvalds if (ti->ccallback) 10481da177e4SLinus Torvalds ti->ccallback(ti, event, tstamp, resolution); 10499244b2c3SJohannes Berg list_for_each_entry(ts, &ti->slave_active_head, active_list) 10501da177e4SLinus Torvalds if (ts->ccallback) 10511da177e4SLinus Torvalds ts->ccallback(ts, event, tstamp, resolution); 10521da177e4SLinus Torvalds } 10531da177e4SLinus Torvalds spin_unlock_irqrestore(&timer->lock, flags); 10541da177e4SLinus Torvalds } 105598856392STakashi Iwai EXPORT_SYMBOL(snd_timer_notify); 10561da177e4SLinus Torvalds 10571da177e4SLinus Torvalds /* 10581da177e4SLinus Torvalds * exported functions for global timers 10591da177e4SLinus Torvalds */ 106053d2f744STakashi Iwai int snd_timer_global_new(char *id, int device, struct snd_timer **rtimer) 10611da177e4SLinus Torvalds { 106253d2f744STakashi Iwai struct snd_timer_id tid; 10631da177e4SLinus Torvalds 10641da177e4SLinus Torvalds tid.dev_class = SNDRV_TIMER_CLASS_GLOBAL; 10651da177e4SLinus Torvalds tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE; 10661da177e4SLinus Torvalds tid.card = -1; 10671da177e4SLinus Torvalds tid.device = device; 10681da177e4SLinus Torvalds tid.subdevice = 0; 10691da177e4SLinus Torvalds return snd_timer_new(NULL, id, &tid, rtimer); 10701da177e4SLinus Torvalds } 107198856392STakashi Iwai EXPORT_SYMBOL(snd_timer_global_new); 10721da177e4SLinus Torvalds 107353d2f744STakashi Iwai int snd_timer_global_free(struct snd_timer *timer) 10741da177e4SLinus Torvalds { 10751da177e4SLinus Torvalds return snd_timer_free(timer); 10761da177e4SLinus Torvalds } 107798856392STakashi Iwai EXPORT_SYMBOL(snd_timer_global_free); 10781da177e4SLinus Torvalds 107953d2f744STakashi Iwai int snd_timer_global_register(struct snd_timer *timer) 10801da177e4SLinus Torvalds { 108153d2f744STakashi Iwai struct snd_device dev; 10821da177e4SLinus Torvalds 10831da177e4SLinus Torvalds memset(&dev, 0, sizeof(dev)); 10841da177e4SLinus Torvalds dev.device_data = timer; 10851da177e4SLinus Torvalds return snd_timer_dev_register(&dev); 10861da177e4SLinus Torvalds } 108798856392STakashi Iwai EXPORT_SYMBOL(snd_timer_global_register); 10881da177e4SLinus Torvalds 10891da177e4SLinus Torvalds /* 10901da177e4SLinus Torvalds * System timer 10911da177e4SLinus Torvalds */ 10921da177e4SLinus Torvalds 10931da177e4SLinus Torvalds struct snd_timer_system_private { 10941da177e4SLinus Torvalds struct timer_list tlist; 109538e9a80fSKees Cook struct snd_timer *snd_timer; 10961da177e4SLinus Torvalds unsigned long last_expires; 10971da177e4SLinus Torvalds unsigned long last_jiffies; 10981da177e4SLinus Torvalds unsigned long correction; 10991da177e4SLinus Torvalds }; 11001da177e4SLinus Torvalds 110138e9a80fSKees Cook static void snd_timer_s_function(struct timer_list *t) 11021da177e4SLinus Torvalds { 110338e9a80fSKees Cook struct snd_timer_system_private *priv = from_timer(priv, t, 110438e9a80fSKees Cook tlist); 110538e9a80fSKees Cook struct snd_timer *timer = priv->snd_timer; 11061da177e4SLinus Torvalds unsigned long jiff = jiffies; 11071da177e4SLinus Torvalds if (time_after(jiff, priv->last_expires)) 11086ed5eff0SClemens Ladisch priv->correction += (long)jiff - (long)priv->last_expires; 11091da177e4SLinus Torvalds snd_timer_interrupt(timer, (long)jiff - (long)priv->last_jiffies); 11101da177e4SLinus Torvalds } 11111da177e4SLinus Torvalds 111253d2f744STakashi Iwai static int snd_timer_s_start(struct snd_timer * timer) 11131da177e4SLinus Torvalds { 11141da177e4SLinus Torvalds struct snd_timer_system_private *priv; 11151da177e4SLinus Torvalds unsigned long njiff; 11161da177e4SLinus Torvalds 11171da177e4SLinus Torvalds priv = (struct snd_timer_system_private *) timer->private_data; 11181da177e4SLinus Torvalds njiff = (priv->last_jiffies = jiffies); 11191da177e4SLinus Torvalds if (priv->correction > timer->sticks - 1) { 11201da177e4SLinus Torvalds priv->correction -= timer->sticks - 1; 11211da177e4SLinus Torvalds njiff++; 11221da177e4SLinus Torvalds } else { 11231da177e4SLinus Torvalds njiff += timer->sticks - priv->correction; 112417f48ec3SClemens Ladisch priv->correction = 0; 11251da177e4SLinus Torvalds } 11264a07083eSTakashi Iwai priv->last_expires = njiff; 11274a07083eSTakashi Iwai mod_timer(&priv->tlist, njiff); 11281da177e4SLinus Torvalds return 0; 11291da177e4SLinus Torvalds } 11301da177e4SLinus Torvalds 113153d2f744STakashi Iwai static int snd_timer_s_stop(struct snd_timer * timer) 11321da177e4SLinus Torvalds { 11331da177e4SLinus Torvalds struct snd_timer_system_private *priv; 11341da177e4SLinus Torvalds unsigned long jiff; 11351da177e4SLinus Torvalds 11361da177e4SLinus Torvalds priv = (struct snd_timer_system_private *) timer->private_data; 11371da177e4SLinus Torvalds del_timer(&priv->tlist); 11381da177e4SLinus Torvalds jiff = jiffies; 11391da177e4SLinus Torvalds if (time_before(jiff, priv->last_expires)) 11401da177e4SLinus Torvalds timer->sticks = priv->last_expires - jiff; 11411da177e4SLinus Torvalds else 11421da177e4SLinus Torvalds timer->sticks = 1; 1143de2696d8SClemens Ladisch priv->correction = 0; 11441da177e4SLinus Torvalds return 0; 11451da177e4SLinus Torvalds } 11461da177e4SLinus Torvalds 1147f146357fSTakashi Iwai static int snd_timer_s_close(struct snd_timer *timer) 1148f146357fSTakashi Iwai { 1149f146357fSTakashi Iwai struct snd_timer_system_private *priv; 1150f146357fSTakashi Iwai 1151f146357fSTakashi Iwai priv = (struct snd_timer_system_private *)timer->private_data; 1152f146357fSTakashi Iwai del_timer_sync(&priv->tlist); 1153f146357fSTakashi Iwai return 0; 1154f146357fSTakashi Iwai } 1155f146357fSTakashi Iwai 115653d2f744STakashi Iwai static struct snd_timer_hardware snd_timer_system = 11571da177e4SLinus Torvalds { 11581da177e4SLinus Torvalds .flags = SNDRV_TIMER_HW_FIRST | SNDRV_TIMER_HW_TASKLET, 11591da177e4SLinus Torvalds .resolution = 1000000000L / HZ, 11601da177e4SLinus Torvalds .ticks = 10000000L, 1161f146357fSTakashi Iwai .close = snd_timer_s_close, 11621da177e4SLinus Torvalds .start = snd_timer_s_start, 11631da177e4SLinus Torvalds .stop = snd_timer_s_stop 11641da177e4SLinus Torvalds }; 11651da177e4SLinus Torvalds 116653d2f744STakashi Iwai static void snd_timer_free_system(struct snd_timer *timer) 11671da177e4SLinus Torvalds { 11681da177e4SLinus Torvalds kfree(timer->private_data); 11691da177e4SLinus Torvalds } 11701da177e4SLinus Torvalds 11711da177e4SLinus Torvalds static int snd_timer_register_system(void) 11721da177e4SLinus Torvalds { 117353d2f744STakashi Iwai struct snd_timer *timer; 11741da177e4SLinus Torvalds struct snd_timer_system_private *priv; 11751da177e4SLinus Torvalds int err; 11761da177e4SLinus Torvalds 11776b172a85SClemens Ladisch err = snd_timer_global_new("system", SNDRV_TIMER_GLOBAL_SYSTEM, &timer); 11786b172a85SClemens Ladisch if (err < 0) 11791da177e4SLinus Torvalds return err; 11801da177e4SLinus Torvalds strcpy(timer->name, "system timer"); 11811da177e4SLinus Torvalds timer->hw = snd_timer_system; 1182ca2c0966STakashi Iwai priv = kzalloc(sizeof(*priv), GFP_KERNEL); 11831da177e4SLinus Torvalds if (priv == NULL) { 11841da177e4SLinus Torvalds snd_timer_free(timer); 11851da177e4SLinus Torvalds return -ENOMEM; 11861da177e4SLinus Torvalds } 118738e9a80fSKees Cook priv->snd_timer = timer; 118838e9a80fSKees Cook timer_setup(&priv->tlist, snd_timer_s_function, 0); 11891da177e4SLinus Torvalds timer->private_data = priv; 11901da177e4SLinus Torvalds timer->private_free = snd_timer_free_system; 11911da177e4SLinus Torvalds return snd_timer_global_register(timer); 11921da177e4SLinus Torvalds } 11931da177e4SLinus Torvalds 1194cd6a6503SJie Yang #ifdef CONFIG_SND_PROC_FS 11951da177e4SLinus Torvalds /* 11961da177e4SLinus Torvalds * Info interface 11971da177e4SLinus Torvalds */ 11981da177e4SLinus Torvalds 119953d2f744STakashi Iwai static void snd_timer_proc_read(struct snd_info_entry *entry, 120053d2f744STakashi Iwai struct snd_info_buffer *buffer) 12011da177e4SLinus Torvalds { 120253d2f744STakashi Iwai struct snd_timer *timer; 120353d2f744STakashi Iwai struct snd_timer_instance *ti; 12041da177e4SLinus Torvalds 12051a60d4c5SIngo Molnar mutex_lock(®ister_mutex); 12069244b2c3SJohannes Berg list_for_each_entry(timer, &snd_timer_list, device_list) { 1207230323daSTakashi Iwai if (timer->card && timer->card->shutdown) 1208230323daSTakashi Iwai continue; 12091da177e4SLinus Torvalds switch (timer->tmr_class) { 12101da177e4SLinus Torvalds case SNDRV_TIMER_CLASS_GLOBAL: 12111da177e4SLinus Torvalds snd_iprintf(buffer, "G%i: ", timer->tmr_device); 12121da177e4SLinus Torvalds break; 12131da177e4SLinus Torvalds case SNDRV_TIMER_CLASS_CARD: 12146b172a85SClemens Ladisch snd_iprintf(buffer, "C%i-%i: ", 12156b172a85SClemens Ladisch timer->card->number, timer->tmr_device); 12161da177e4SLinus Torvalds break; 12171da177e4SLinus Torvalds case SNDRV_TIMER_CLASS_PCM: 12186b172a85SClemens Ladisch snd_iprintf(buffer, "P%i-%i-%i: ", timer->card->number, 12196b172a85SClemens Ladisch timer->tmr_device, timer->tmr_subdevice); 12201da177e4SLinus Torvalds break; 12211da177e4SLinus Torvalds default: 12226b172a85SClemens Ladisch snd_iprintf(buffer, "?%i-%i-%i-%i: ", timer->tmr_class, 12236b172a85SClemens Ladisch timer->card ? timer->card->number : -1, 12246b172a85SClemens Ladisch timer->tmr_device, timer->tmr_subdevice); 12251da177e4SLinus Torvalds } 12261da177e4SLinus Torvalds snd_iprintf(buffer, "%s :", timer->name); 12271da177e4SLinus Torvalds if (timer->hw.resolution) 12286b172a85SClemens Ladisch snd_iprintf(buffer, " %lu.%03luus (%lu ticks)", 12296b172a85SClemens Ladisch timer->hw.resolution / 1000, 12306b172a85SClemens Ladisch timer->hw.resolution % 1000, 12316b172a85SClemens Ladisch timer->hw.ticks); 12321da177e4SLinus Torvalds if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE) 12331da177e4SLinus Torvalds snd_iprintf(buffer, " SLAVE"); 12341da177e4SLinus Torvalds snd_iprintf(buffer, "\n"); 12359244b2c3SJohannes Berg list_for_each_entry(ti, &timer->open_list_head, open_list) 12366b172a85SClemens Ladisch snd_iprintf(buffer, " Client %s : %s\n", 12371da177e4SLinus Torvalds ti->owner ? ti->owner : "unknown", 12386b172a85SClemens Ladisch ti->flags & (SNDRV_TIMER_IFLG_START | 12396b172a85SClemens Ladisch SNDRV_TIMER_IFLG_RUNNING) 12406b172a85SClemens Ladisch ? "running" : "stopped"); 12411da177e4SLinus Torvalds } 12421a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 12431da177e4SLinus Torvalds } 12441da177e4SLinus Torvalds 12456581f4e7STakashi Iwai static struct snd_info_entry *snd_timer_proc_entry; 1246e28563ccSTakashi Iwai 1247e28563ccSTakashi Iwai static void __init snd_timer_proc_init(void) 1248e28563ccSTakashi Iwai { 1249e28563ccSTakashi Iwai struct snd_info_entry *entry; 1250e28563ccSTakashi Iwai 1251e28563ccSTakashi Iwai entry = snd_info_create_module_entry(THIS_MODULE, "timers", NULL); 1252e28563ccSTakashi Iwai if (entry != NULL) { 1253e28563ccSTakashi Iwai entry->c.text.read = snd_timer_proc_read; 1254e28563ccSTakashi Iwai if (snd_info_register(entry) < 0) { 1255e28563ccSTakashi Iwai snd_info_free_entry(entry); 1256e28563ccSTakashi Iwai entry = NULL; 1257e28563ccSTakashi Iwai } 1258e28563ccSTakashi Iwai } 1259e28563ccSTakashi Iwai snd_timer_proc_entry = entry; 1260e28563ccSTakashi Iwai } 1261e28563ccSTakashi Iwai 1262e28563ccSTakashi Iwai static void __exit snd_timer_proc_done(void) 1263e28563ccSTakashi Iwai { 1264746d4a02STakashi Iwai snd_info_free_entry(snd_timer_proc_entry); 1265e28563ccSTakashi Iwai } 1266cd6a6503SJie Yang #else /* !CONFIG_SND_PROC_FS */ 1267e28563ccSTakashi Iwai #define snd_timer_proc_init() 1268e28563ccSTakashi Iwai #define snd_timer_proc_done() 1269e28563ccSTakashi Iwai #endif 1270e28563ccSTakashi Iwai 12711da177e4SLinus Torvalds /* 12721da177e4SLinus Torvalds * USER SPACE interface 12731da177e4SLinus Torvalds */ 12741da177e4SLinus Torvalds 127553d2f744STakashi Iwai static void snd_timer_user_interrupt(struct snd_timer_instance *timeri, 12761da177e4SLinus Torvalds unsigned long resolution, 12771da177e4SLinus Torvalds unsigned long ticks) 12781da177e4SLinus Torvalds { 127953d2f744STakashi Iwai struct snd_timer_user *tu = timeri->callback_data; 128053d2f744STakashi Iwai struct snd_timer_read *r; 12811da177e4SLinus Torvalds int prev; 12821da177e4SLinus Torvalds 12831da177e4SLinus Torvalds spin_lock(&tu->qlock); 12841da177e4SLinus Torvalds if (tu->qused > 0) { 12851da177e4SLinus Torvalds prev = tu->qtail == 0 ? tu->queue_size - 1 : tu->qtail - 1; 12861da177e4SLinus Torvalds r = &tu->queue[prev]; 12871da177e4SLinus Torvalds if (r->resolution == resolution) { 12881da177e4SLinus Torvalds r->ticks += ticks; 12891da177e4SLinus Torvalds goto __wake; 12901da177e4SLinus Torvalds } 12911da177e4SLinus Torvalds } 12921da177e4SLinus Torvalds if (tu->qused >= tu->queue_size) { 12931da177e4SLinus Torvalds tu->overrun++; 12941da177e4SLinus Torvalds } else { 12951da177e4SLinus Torvalds r = &tu->queue[tu->qtail++]; 12961da177e4SLinus Torvalds tu->qtail %= tu->queue_size; 12971da177e4SLinus Torvalds r->resolution = resolution; 12981da177e4SLinus Torvalds r->ticks = ticks; 12991da177e4SLinus Torvalds tu->qused++; 13001da177e4SLinus Torvalds } 13011da177e4SLinus Torvalds __wake: 13021da177e4SLinus Torvalds spin_unlock(&tu->qlock); 13031da177e4SLinus Torvalds kill_fasync(&tu->fasync, SIGIO, POLL_IN); 13041da177e4SLinus Torvalds wake_up(&tu->qchange_sleep); 13051da177e4SLinus Torvalds } 13061da177e4SLinus Torvalds 130753d2f744STakashi Iwai static void snd_timer_user_append_to_tqueue(struct snd_timer_user *tu, 130853d2f744STakashi Iwai struct snd_timer_tread *tread) 13091da177e4SLinus Torvalds { 13101da177e4SLinus Torvalds if (tu->qused >= tu->queue_size) { 13111da177e4SLinus Torvalds tu->overrun++; 13121da177e4SLinus Torvalds } else { 13131da177e4SLinus Torvalds memcpy(&tu->tqueue[tu->qtail++], tread, sizeof(*tread)); 13141da177e4SLinus Torvalds tu->qtail %= tu->queue_size; 13151da177e4SLinus Torvalds tu->qused++; 13161da177e4SLinus Torvalds } 13171da177e4SLinus Torvalds } 13181da177e4SLinus Torvalds 131953d2f744STakashi Iwai static void snd_timer_user_ccallback(struct snd_timer_instance *timeri, 132053d2f744STakashi Iwai int event, 1321fcae40c9SBaolin Wang struct timespec64 *tstamp, 13221da177e4SLinus Torvalds unsigned long resolution) 13231da177e4SLinus Torvalds { 132453d2f744STakashi Iwai struct snd_timer_user *tu = timeri->callback_data; 132553d2f744STakashi Iwai struct snd_timer_tread r1; 1326bfe70783SDan Carpenter unsigned long flags; 13271da177e4SLinus Torvalds 13286b172a85SClemens Ladisch if (event >= SNDRV_TIMER_EVENT_START && 13296b172a85SClemens Ladisch event <= SNDRV_TIMER_EVENT_PAUSE) 13301da177e4SLinus Torvalds tu->tstamp = *tstamp; 13311da177e4SLinus Torvalds if ((tu->filter & (1 << event)) == 0 || !tu->tread) 13321da177e4SLinus Torvalds return; 13339a47e9cfSKangjie Lu memset(&r1, 0, sizeof(r1)); 13341da177e4SLinus Torvalds r1.event = event; 1335fcae40c9SBaolin Wang r1.tstamp = timespec64_to_timespec(*tstamp); 13361da177e4SLinus Torvalds r1.val = resolution; 1337bfe70783SDan Carpenter spin_lock_irqsave(&tu->qlock, flags); 13381da177e4SLinus Torvalds snd_timer_user_append_to_tqueue(tu, &r1); 1339bfe70783SDan Carpenter spin_unlock_irqrestore(&tu->qlock, flags); 13401da177e4SLinus Torvalds kill_fasync(&tu->fasync, SIGIO, POLL_IN); 13411da177e4SLinus Torvalds wake_up(&tu->qchange_sleep); 13421da177e4SLinus Torvalds } 13431da177e4SLinus Torvalds 134440ed9444STakashi Iwai static void snd_timer_user_disconnect(struct snd_timer_instance *timeri) 134540ed9444STakashi Iwai { 134640ed9444STakashi Iwai struct snd_timer_user *tu = timeri->callback_data; 134740ed9444STakashi Iwai 134840ed9444STakashi Iwai tu->disconnected = true; 134940ed9444STakashi Iwai wake_up(&tu->qchange_sleep); 135040ed9444STakashi Iwai } 135140ed9444STakashi Iwai 135253d2f744STakashi Iwai static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri, 13531da177e4SLinus Torvalds unsigned long resolution, 13541da177e4SLinus Torvalds unsigned long ticks) 13551da177e4SLinus Torvalds { 135653d2f744STakashi Iwai struct snd_timer_user *tu = timeri->callback_data; 135753d2f744STakashi Iwai struct snd_timer_tread *r, r1; 1358fcae40c9SBaolin Wang struct timespec64 tstamp; 13591da177e4SLinus Torvalds int prev, append = 0; 13601da177e4SLinus Torvalds 1361a8c006aaSDan Carpenter memset(&r1, 0, sizeof(r1)); 136207799e75STakashi Iwai memset(&tstamp, 0, sizeof(tstamp)); 13631da177e4SLinus Torvalds spin_lock(&tu->qlock); 13646b172a85SClemens Ladisch if ((tu->filter & ((1 << SNDRV_TIMER_EVENT_RESOLUTION) | 13656b172a85SClemens Ladisch (1 << SNDRV_TIMER_EVENT_TICK))) == 0) { 13661da177e4SLinus Torvalds spin_unlock(&tu->qlock); 13671da177e4SLinus Torvalds return; 13681da177e4SLinus Torvalds } 1369b751eef1SJaroslav Kysela if (tu->last_resolution != resolution || ticks > 0) { 1370b751eef1SJaroslav Kysela if (timer_tstamp_monotonic) 1371fcae40c9SBaolin Wang ktime_get_ts64(&tstamp); 1372b751eef1SJaroslav Kysela else 1373fcae40c9SBaolin Wang ktime_get_real_ts64(&tstamp); 1374b751eef1SJaroslav Kysela } 13756b172a85SClemens Ladisch if ((tu->filter & (1 << SNDRV_TIMER_EVENT_RESOLUTION)) && 13766b172a85SClemens Ladisch tu->last_resolution != resolution) { 13771da177e4SLinus Torvalds r1.event = SNDRV_TIMER_EVENT_RESOLUTION; 1378fcae40c9SBaolin Wang r1.tstamp = timespec64_to_timespec(tstamp); 13791da177e4SLinus Torvalds r1.val = resolution; 13801da177e4SLinus Torvalds snd_timer_user_append_to_tqueue(tu, &r1); 13811da177e4SLinus Torvalds tu->last_resolution = resolution; 13821da177e4SLinus Torvalds append++; 13831da177e4SLinus Torvalds } 13841da177e4SLinus Torvalds if ((tu->filter & (1 << SNDRV_TIMER_EVENT_TICK)) == 0) 13851da177e4SLinus Torvalds goto __wake; 13861da177e4SLinus Torvalds if (ticks == 0) 13871da177e4SLinus Torvalds goto __wake; 13881da177e4SLinus Torvalds if (tu->qused > 0) { 13891da177e4SLinus Torvalds prev = tu->qtail == 0 ? tu->queue_size - 1 : tu->qtail - 1; 13901da177e4SLinus Torvalds r = &tu->tqueue[prev]; 13911da177e4SLinus Torvalds if (r->event == SNDRV_TIMER_EVENT_TICK) { 1392fcae40c9SBaolin Wang r->tstamp = timespec64_to_timespec(tstamp); 13931da177e4SLinus Torvalds r->val += ticks; 13941da177e4SLinus Torvalds append++; 13951da177e4SLinus Torvalds goto __wake; 13961da177e4SLinus Torvalds } 13971da177e4SLinus Torvalds } 13981da177e4SLinus Torvalds r1.event = SNDRV_TIMER_EVENT_TICK; 1399fcae40c9SBaolin Wang r1.tstamp = timespec64_to_timespec(tstamp); 14001da177e4SLinus Torvalds r1.val = ticks; 14011da177e4SLinus Torvalds snd_timer_user_append_to_tqueue(tu, &r1); 14021da177e4SLinus Torvalds append++; 14031da177e4SLinus Torvalds __wake: 14041da177e4SLinus Torvalds spin_unlock(&tu->qlock); 14051da177e4SLinus Torvalds if (append == 0) 14061da177e4SLinus Torvalds return; 14071da177e4SLinus Torvalds kill_fasync(&tu->fasync, SIGIO, POLL_IN); 14081da177e4SLinus Torvalds wake_up(&tu->qchange_sleep); 14091da177e4SLinus Torvalds } 14101da177e4SLinus Torvalds 1411890e2cb5STakashi Iwai static int realloc_user_queue(struct snd_timer_user *tu, int size) 1412890e2cb5STakashi Iwai { 1413890e2cb5STakashi Iwai struct snd_timer_read *queue = NULL; 1414890e2cb5STakashi Iwai struct snd_timer_tread *tqueue = NULL; 1415890e2cb5STakashi Iwai 1416890e2cb5STakashi Iwai if (tu->tread) { 1417890e2cb5STakashi Iwai tqueue = kcalloc(size, sizeof(*tqueue), GFP_KERNEL); 1418890e2cb5STakashi Iwai if (!tqueue) 1419890e2cb5STakashi Iwai return -ENOMEM; 1420890e2cb5STakashi Iwai } else { 1421890e2cb5STakashi Iwai queue = kcalloc(size, sizeof(*queue), GFP_KERNEL); 1422890e2cb5STakashi Iwai if (!queue) 1423890e2cb5STakashi Iwai return -ENOMEM; 1424890e2cb5STakashi Iwai } 1425890e2cb5STakashi Iwai 1426890e2cb5STakashi Iwai spin_lock_irq(&tu->qlock); 1427890e2cb5STakashi Iwai kfree(tu->queue); 1428890e2cb5STakashi Iwai kfree(tu->tqueue); 1429890e2cb5STakashi Iwai tu->queue_size = size; 1430890e2cb5STakashi Iwai tu->queue = queue; 1431890e2cb5STakashi Iwai tu->tqueue = tqueue; 1432890e2cb5STakashi Iwai tu->qhead = tu->qtail = tu->qused = 0; 1433890e2cb5STakashi Iwai spin_unlock_irq(&tu->qlock); 1434890e2cb5STakashi Iwai 1435890e2cb5STakashi Iwai return 0; 1436890e2cb5STakashi Iwai } 1437890e2cb5STakashi Iwai 14381da177e4SLinus Torvalds static int snd_timer_user_open(struct inode *inode, struct file *file) 14391da177e4SLinus Torvalds { 144053d2f744STakashi Iwai struct snd_timer_user *tu; 144102f4865fSTakashi Iwai int err; 144202f4865fSTakashi Iwai 1443c5bf68feSKirill Smelkov err = stream_open(inode, file); 144402f4865fSTakashi Iwai if (err < 0) 144502f4865fSTakashi Iwai return err; 14461da177e4SLinus Torvalds 1447ca2c0966STakashi Iwai tu = kzalloc(sizeof(*tu), GFP_KERNEL); 14481da177e4SLinus Torvalds if (tu == NULL) 14491da177e4SLinus Torvalds return -ENOMEM; 14501da177e4SLinus Torvalds spin_lock_init(&tu->qlock); 14511da177e4SLinus Torvalds init_waitqueue_head(&tu->qchange_sleep); 1452af368027STakashi Iwai mutex_init(&tu->ioctl_lock); 14531da177e4SLinus Torvalds tu->ticks = 1; 1454890e2cb5STakashi Iwai if (realloc_user_queue(tu, 128) < 0) { 14551da177e4SLinus Torvalds kfree(tu); 14561da177e4SLinus Torvalds return -ENOMEM; 14571da177e4SLinus Torvalds } 14581da177e4SLinus Torvalds file->private_data = tu; 14591da177e4SLinus Torvalds return 0; 14601da177e4SLinus Torvalds } 14611da177e4SLinus Torvalds 14621da177e4SLinus Torvalds static int snd_timer_user_release(struct inode *inode, struct file *file) 14631da177e4SLinus Torvalds { 146453d2f744STakashi Iwai struct snd_timer_user *tu; 14651da177e4SLinus Torvalds 14661da177e4SLinus Torvalds if (file->private_data) { 14671da177e4SLinus Torvalds tu = file->private_data; 14681da177e4SLinus Torvalds file->private_data = NULL; 1469af368027STakashi Iwai mutex_lock(&tu->ioctl_lock); 14706a34367eSTakashi Iwai if (tu->timeri) { 14711da177e4SLinus Torvalds snd_timer_close(tu->timeri); 14726a34367eSTakashi Iwai snd_timer_instance_free(tu->timeri); 14736a34367eSTakashi Iwai } 1474af368027STakashi Iwai mutex_unlock(&tu->ioctl_lock); 14751da177e4SLinus Torvalds kfree(tu->queue); 14761da177e4SLinus Torvalds kfree(tu->tqueue); 14771da177e4SLinus Torvalds kfree(tu); 14781da177e4SLinus Torvalds } 14791da177e4SLinus Torvalds return 0; 14801da177e4SLinus Torvalds } 14811da177e4SLinus Torvalds 148253d2f744STakashi Iwai static void snd_timer_user_zero_id(struct snd_timer_id *id) 14831da177e4SLinus Torvalds { 14841da177e4SLinus Torvalds id->dev_class = SNDRV_TIMER_CLASS_NONE; 14851da177e4SLinus Torvalds id->dev_sclass = SNDRV_TIMER_SCLASS_NONE; 14861da177e4SLinus Torvalds id->card = -1; 14871da177e4SLinus Torvalds id->device = -1; 14881da177e4SLinus Torvalds id->subdevice = -1; 14891da177e4SLinus Torvalds } 14901da177e4SLinus Torvalds 149153d2f744STakashi Iwai static void snd_timer_user_copy_id(struct snd_timer_id *id, struct snd_timer *timer) 14921da177e4SLinus Torvalds { 14931da177e4SLinus Torvalds id->dev_class = timer->tmr_class; 14941da177e4SLinus Torvalds id->dev_sclass = SNDRV_TIMER_SCLASS_NONE; 14951da177e4SLinus Torvalds id->card = timer->card ? timer->card->number : -1; 14961da177e4SLinus Torvalds id->device = timer->tmr_device; 14971da177e4SLinus Torvalds id->subdevice = timer->tmr_subdevice; 14981da177e4SLinus Torvalds } 14991da177e4SLinus Torvalds 150053d2f744STakashi Iwai static int snd_timer_user_next_device(struct snd_timer_id __user *_tid) 15011da177e4SLinus Torvalds { 150253d2f744STakashi Iwai struct snd_timer_id id; 150353d2f744STakashi Iwai struct snd_timer *timer; 15041da177e4SLinus Torvalds struct list_head *p; 15051da177e4SLinus Torvalds 15061da177e4SLinus Torvalds if (copy_from_user(&id, _tid, sizeof(id))) 15071da177e4SLinus Torvalds return -EFAULT; 15081a60d4c5SIngo Molnar mutex_lock(®ister_mutex); 15091da177e4SLinus Torvalds if (id.dev_class < 0) { /* first item */ 15101da177e4SLinus Torvalds if (list_empty(&snd_timer_list)) 15111da177e4SLinus Torvalds snd_timer_user_zero_id(&id); 15121da177e4SLinus Torvalds else { 15139dfba380SClemens Ladisch timer = list_entry(snd_timer_list.next, 151453d2f744STakashi Iwai struct snd_timer, device_list); 15151da177e4SLinus Torvalds snd_timer_user_copy_id(&id, timer); 15161da177e4SLinus Torvalds } 15171da177e4SLinus Torvalds } else { 15181da177e4SLinus Torvalds switch (id.dev_class) { 15191da177e4SLinus Torvalds case SNDRV_TIMER_CLASS_GLOBAL: 15201da177e4SLinus Torvalds id.device = id.device < 0 ? 0 : id.device + 1; 15211da177e4SLinus Torvalds list_for_each(p, &snd_timer_list) { 152253d2f744STakashi Iwai timer = list_entry(p, struct snd_timer, device_list); 15231da177e4SLinus Torvalds if (timer->tmr_class > SNDRV_TIMER_CLASS_GLOBAL) { 15241da177e4SLinus Torvalds snd_timer_user_copy_id(&id, timer); 15251da177e4SLinus Torvalds break; 15261da177e4SLinus Torvalds } 15271da177e4SLinus Torvalds if (timer->tmr_device >= id.device) { 15281da177e4SLinus Torvalds snd_timer_user_copy_id(&id, timer); 15291da177e4SLinus Torvalds break; 15301da177e4SLinus Torvalds } 15311da177e4SLinus Torvalds } 15321da177e4SLinus Torvalds if (p == &snd_timer_list) 15331da177e4SLinus Torvalds snd_timer_user_zero_id(&id); 15341da177e4SLinus Torvalds break; 15351da177e4SLinus Torvalds case SNDRV_TIMER_CLASS_CARD: 15361da177e4SLinus Torvalds case SNDRV_TIMER_CLASS_PCM: 15371da177e4SLinus Torvalds if (id.card < 0) { 15381da177e4SLinus Torvalds id.card = 0; 15391da177e4SLinus Torvalds } else { 15401da177e4SLinus Torvalds if (id.device < 0) { 15411da177e4SLinus Torvalds id.device = 0; 15421da177e4SLinus Torvalds } else { 1543e8ed6820SDan Carpenter if (id.subdevice < 0) 15446b172a85SClemens Ladisch id.subdevice = 0; 1545b41f794fSTakashi Iwai else if (id.subdevice < INT_MAX) 15466b172a85SClemens Ladisch id.subdevice++; 15476b172a85SClemens Ladisch } 15481da177e4SLinus Torvalds } 15491da177e4SLinus Torvalds list_for_each(p, &snd_timer_list) { 155053d2f744STakashi Iwai timer = list_entry(p, struct snd_timer, device_list); 15511da177e4SLinus Torvalds if (timer->tmr_class > id.dev_class) { 15521da177e4SLinus Torvalds snd_timer_user_copy_id(&id, timer); 15531da177e4SLinus Torvalds break; 15541da177e4SLinus Torvalds } 15551da177e4SLinus Torvalds if (timer->tmr_class < id.dev_class) 15561da177e4SLinus Torvalds continue; 15571da177e4SLinus Torvalds if (timer->card->number > id.card) { 15581da177e4SLinus Torvalds snd_timer_user_copy_id(&id, timer); 15591da177e4SLinus Torvalds break; 15601da177e4SLinus Torvalds } 15611da177e4SLinus Torvalds if (timer->card->number < id.card) 15621da177e4SLinus Torvalds continue; 15631da177e4SLinus Torvalds if (timer->tmr_device > id.device) { 15641da177e4SLinus Torvalds snd_timer_user_copy_id(&id, timer); 15651da177e4SLinus Torvalds break; 15661da177e4SLinus Torvalds } 15671da177e4SLinus Torvalds if (timer->tmr_device < id.device) 15681da177e4SLinus Torvalds continue; 15691da177e4SLinus Torvalds if (timer->tmr_subdevice > id.subdevice) { 15701da177e4SLinus Torvalds snd_timer_user_copy_id(&id, timer); 15711da177e4SLinus Torvalds break; 15721da177e4SLinus Torvalds } 15731da177e4SLinus Torvalds if (timer->tmr_subdevice < id.subdevice) 15741da177e4SLinus Torvalds continue; 15751da177e4SLinus Torvalds snd_timer_user_copy_id(&id, timer); 15761da177e4SLinus Torvalds break; 15771da177e4SLinus Torvalds } 15781da177e4SLinus Torvalds if (p == &snd_timer_list) 15791da177e4SLinus Torvalds snd_timer_user_zero_id(&id); 15801da177e4SLinus Torvalds break; 15811da177e4SLinus Torvalds default: 15821da177e4SLinus Torvalds snd_timer_user_zero_id(&id); 15831da177e4SLinus Torvalds } 15841da177e4SLinus Torvalds } 15851a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 15861da177e4SLinus Torvalds if (copy_to_user(_tid, &id, sizeof(*_tid))) 15871da177e4SLinus Torvalds return -EFAULT; 15881da177e4SLinus Torvalds return 0; 15891da177e4SLinus Torvalds } 15901da177e4SLinus Torvalds 15916b172a85SClemens Ladisch static int snd_timer_user_ginfo(struct file *file, 159253d2f744STakashi Iwai struct snd_timer_ginfo __user *_ginfo) 15931da177e4SLinus Torvalds { 159453d2f744STakashi Iwai struct snd_timer_ginfo *ginfo; 159553d2f744STakashi Iwai struct snd_timer_id tid; 159653d2f744STakashi Iwai struct snd_timer *t; 15971da177e4SLinus Torvalds struct list_head *p; 15981da177e4SLinus Torvalds int err = 0; 15991da177e4SLinus Torvalds 1600ef44a1ecSLi Zefan ginfo = memdup_user(_ginfo, sizeof(*ginfo)); 1601ef44a1ecSLi Zefan if (IS_ERR(ginfo)) 1602ef44a1ecSLi Zefan return PTR_ERR(ginfo); 1603ef44a1ecSLi Zefan 16041da177e4SLinus Torvalds tid = ginfo->tid; 16051da177e4SLinus Torvalds memset(ginfo, 0, sizeof(*ginfo)); 16061da177e4SLinus Torvalds ginfo->tid = tid; 16071a60d4c5SIngo Molnar mutex_lock(®ister_mutex); 16081da177e4SLinus Torvalds t = snd_timer_find(&tid); 16091da177e4SLinus Torvalds if (t != NULL) { 16101da177e4SLinus Torvalds ginfo->card = t->card ? t->card->number : -1; 16111da177e4SLinus Torvalds if (t->hw.flags & SNDRV_TIMER_HW_SLAVE) 16121da177e4SLinus Torvalds ginfo->flags |= SNDRV_TIMER_FLG_SLAVE; 16131da177e4SLinus Torvalds strlcpy(ginfo->id, t->id, sizeof(ginfo->id)); 16141da177e4SLinus Torvalds strlcpy(ginfo->name, t->name, sizeof(ginfo->name)); 16151da177e4SLinus Torvalds ginfo->resolution = t->hw.resolution; 16161da177e4SLinus Torvalds if (t->hw.resolution_min > 0) { 16171da177e4SLinus Torvalds ginfo->resolution_min = t->hw.resolution_min; 16181da177e4SLinus Torvalds ginfo->resolution_max = t->hw.resolution_max; 16191da177e4SLinus Torvalds } 16201da177e4SLinus Torvalds list_for_each(p, &t->open_list_head) { 16211da177e4SLinus Torvalds ginfo->clients++; 16221da177e4SLinus Torvalds } 16231da177e4SLinus Torvalds } else { 16241da177e4SLinus Torvalds err = -ENODEV; 16251da177e4SLinus Torvalds } 16261a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 16271da177e4SLinus Torvalds if (err >= 0 && copy_to_user(_ginfo, ginfo, sizeof(*ginfo))) 16281da177e4SLinus Torvalds err = -EFAULT; 16291da177e4SLinus Torvalds kfree(ginfo); 16301da177e4SLinus Torvalds return err; 16311da177e4SLinus Torvalds } 16321da177e4SLinus Torvalds 163391d2178eSTakashi Sakamoto static int timer_set_gparams(struct snd_timer_gparams *gparams) 16341da177e4SLinus Torvalds { 163553d2f744STakashi Iwai struct snd_timer *t; 16361da177e4SLinus Torvalds int err; 16371da177e4SLinus Torvalds 16381a60d4c5SIngo Molnar mutex_lock(®ister_mutex); 163991d2178eSTakashi Sakamoto t = snd_timer_find(&gparams->tid); 16406b172a85SClemens Ladisch if (!t) { 16411da177e4SLinus Torvalds err = -ENODEV; 16426b172a85SClemens Ladisch goto _error; 16431da177e4SLinus Torvalds } 16446b172a85SClemens Ladisch if (!list_empty(&t->open_list_head)) { 16456b172a85SClemens Ladisch err = -EBUSY; 16466b172a85SClemens Ladisch goto _error; 16476b172a85SClemens Ladisch } 16486b172a85SClemens Ladisch if (!t->hw.set_period) { 16496b172a85SClemens Ladisch err = -ENOSYS; 16506b172a85SClemens Ladisch goto _error; 16516b172a85SClemens Ladisch } 165291d2178eSTakashi Sakamoto err = t->hw.set_period(t, gparams->period_num, gparams->period_den); 16536b172a85SClemens Ladisch _error: 16541a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 16551da177e4SLinus Torvalds return err; 16561da177e4SLinus Torvalds } 16571da177e4SLinus Torvalds 165891d2178eSTakashi Sakamoto static int snd_timer_user_gparams(struct file *file, 165991d2178eSTakashi Sakamoto struct snd_timer_gparams __user *_gparams) 166091d2178eSTakashi Sakamoto { 166191d2178eSTakashi Sakamoto struct snd_timer_gparams gparams; 166291d2178eSTakashi Sakamoto 166391d2178eSTakashi Sakamoto if (copy_from_user(&gparams, _gparams, sizeof(gparams))) 166491d2178eSTakashi Sakamoto return -EFAULT; 166591d2178eSTakashi Sakamoto return timer_set_gparams(&gparams); 166691d2178eSTakashi Sakamoto } 166791d2178eSTakashi Sakamoto 16686b172a85SClemens Ladisch static int snd_timer_user_gstatus(struct file *file, 166953d2f744STakashi Iwai struct snd_timer_gstatus __user *_gstatus) 16701da177e4SLinus Torvalds { 167153d2f744STakashi Iwai struct snd_timer_gstatus gstatus; 167253d2f744STakashi Iwai struct snd_timer_id tid; 167353d2f744STakashi Iwai struct snd_timer *t; 16741da177e4SLinus Torvalds int err = 0; 16751da177e4SLinus Torvalds 16761da177e4SLinus Torvalds if (copy_from_user(&gstatus, _gstatus, sizeof(gstatus))) 16771da177e4SLinus Torvalds return -EFAULT; 16781da177e4SLinus Torvalds tid = gstatus.tid; 16791da177e4SLinus Torvalds memset(&gstatus, 0, sizeof(gstatus)); 16801da177e4SLinus Torvalds gstatus.tid = tid; 16811a60d4c5SIngo Molnar mutex_lock(®ister_mutex); 16821da177e4SLinus Torvalds t = snd_timer_find(&tid); 16831da177e4SLinus Torvalds if (t != NULL) { 16849d4d207dSTakashi Iwai spin_lock_irq(&t->lock); 1685fdcb5761STakashi Iwai gstatus.resolution = snd_timer_hw_resolution(t); 16861da177e4SLinus Torvalds if (t->hw.precise_resolution) { 16876b172a85SClemens Ladisch t->hw.precise_resolution(t, &gstatus.resolution_num, 16886b172a85SClemens Ladisch &gstatus.resolution_den); 16891da177e4SLinus Torvalds } else { 16901da177e4SLinus Torvalds gstatus.resolution_num = gstatus.resolution; 16911da177e4SLinus Torvalds gstatus.resolution_den = 1000000000uL; 16921da177e4SLinus Torvalds } 16939d4d207dSTakashi Iwai spin_unlock_irq(&t->lock); 16941da177e4SLinus Torvalds } else { 16951da177e4SLinus Torvalds err = -ENODEV; 16961da177e4SLinus Torvalds } 16971a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 16981da177e4SLinus Torvalds if (err >= 0 && copy_to_user(_gstatus, &gstatus, sizeof(gstatus))) 16991da177e4SLinus Torvalds err = -EFAULT; 17001da177e4SLinus Torvalds return err; 17011da177e4SLinus Torvalds } 17021da177e4SLinus Torvalds 17036b172a85SClemens Ladisch static int snd_timer_user_tselect(struct file *file, 170453d2f744STakashi Iwai struct snd_timer_select __user *_tselect) 17051da177e4SLinus Torvalds { 170653d2f744STakashi Iwai struct snd_timer_user *tu; 170753d2f744STakashi Iwai struct snd_timer_select tselect; 17081da177e4SLinus Torvalds char str[32]; 1709c1935b4dSJaroslav Kysela int err = 0; 17101da177e4SLinus Torvalds 17111da177e4SLinus Torvalds tu = file->private_data; 1712c1935b4dSJaroslav Kysela if (tu->timeri) { 17131da177e4SLinus Torvalds snd_timer_close(tu->timeri); 17146a34367eSTakashi Iwai snd_timer_instance_free(tu->timeri); 1715c1935b4dSJaroslav Kysela tu->timeri = NULL; 1716c1935b4dSJaroslav Kysela } 1717c1935b4dSJaroslav Kysela if (copy_from_user(&tselect, _tselect, sizeof(tselect))) { 1718c1935b4dSJaroslav Kysela err = -EFAULT; 1719c1935b4dSJaroslav Kysela goto __err; 1720c1935b4dSJaroslav Kysela } 17211da177e4SLinus Torvalds sprintf(str, "application %i", current->pid); 17221da177e4SLinus Torvalds if (tselect.id.dev_class != SNDRV_TIMER_CLASS_SLAVE) 17231da177e4SLinus Torvalds tselect.id.dev_sclass = SNDRV_TIMER_SCLASS_APPLICATION; 17246a34367eSTakashi Iwai tu->timeri = snd_timer_instance_new(str); 17256a34367eSTakashi Iwai if (!tu->timeri) { 17266a34367eSTakashi Iwai err = -ENOMEM; 1727c1935b4dSJaroslav Kysela goto __err; 17286a34367eSTakashi Iwai } 17291da177e4SLinus Torvalds 17301da177e4SLinus Torvalds tu->timeri->flags |= SNDRV_TIMER_IFLG_FAST; 17316b172a85SClemens Ladisch tu->timeri->callback = tu->tread 17326b172a85SClemens Ladisch ? snd_timer_user_tinterrupt : snd_timer_user_interrupt; 17331da177e4SLinus Torvalds tu->timeri->ccallback = snd_timer_user_ccallback; 17341da177e4SLinus Torvalds tu->timeri->callback_data = (void *)tu; 173540ed9444STakashi Iwai tu->timeri->disconnect = snd_timer_user_disconnect; 1736c1935b4dSJaroslav Kysela 17376a34367eSTakashi Iwai err = snd_timer_open(tu->timeri, &tselect.id, current->pid); 17386a34367eSTakashi Iwai if (err < 0) { 17396a34367eSTakashi Iwai snd_timer_instance_free(tu->timeri); 17406a34367eSTakashi Iwai tu->timeri = NULL; 17416a34367eSTakashi Iwai } 17426a34367eSTakashi Iwai 1743c1935b4dSJaroslav Kysela __err: 1744c1935b4dSJaroslav Kysela return err; 17451da177e4SLinus Torvalds } 17461da177e4SLinus Torvalds 17476b172a85SClemens Ladisch static int snd_timer_user_info(struct file *file, 174853d2f744STakashi Iwai struct snd_timer_info __user *_info) 17491da177e4SLinus Torvalds { 175053d2f744STakashi Iwai struct snd_timer_user *tu; 175153d2f744STakashi Iwai struct snd_timer_info *info; 175253d2f744STakashi Iwai struct snd_timer *t; 17531da177e4SLinus Torvalds int err = 0; 17541da177e4SLinus Torvalds 17551da177e4SLinus Torvalds tu = file->private_data; 17567c64ec34SClemens Ladisch if (!tu->timeri) 17577c64ec34SClemens Ladisch return -EBADFD; 17581da177e4SLinus Torvalds t = tu->timeri->timer; 17597c64ec34SClemens Ladisch if (!t) 17607c64ec34SClemens Ladisch return -EBADFD; 17611da177e4SLinus Torvalds 1762ca2c0966STakashi Iwai info = kzalloc(sizeof(*info), GFP_KERNEL); 17631da177e4SLinus Torvalds if (! info) 17641da177e4SLinus Torvalds return -ENOMEM; 17651da177e4SLinus Torvalds info->card = t->card ? t->card->number : -1; 17661da177e4SLinus Torvalds if (t->hw.flags & SNDRV_TIMER_HW_SLAVE) 17671da177e4SLinus Torvalds info->flags |= SNDRV_TIMER_FLG_SLAVE; 17681da177e4SLinus Torvalds strlcpy(info->id, t->id, sizeof(info->id)); 17691da177e4SLinus Torvalds strlcpy(info->name, t->name, sizeof(info->name)); 17701da177e4SLinus Torvalds info->resolution = t->hw.resolution; 17711da177e4SLinus Torvalds if (copy_to_user(_info, info, sizeof(*_info))) 17721da177e4SLinus Torvalds err = -EFAULT; 17731da177e4SLinus Torvalds kfree(info); 17741da177e4SLinus Torvalds return err; 17751da177e4SLinus Torvalds } 17761da177e4SLinus Torvalds 17776b172a85SClemens Ladisch static int snd_timer_user_params(struct file *file, 177853d2f744STakashi Iwai struct snd_timer_params __user *_params) 17791da177e4SLinus Torvalds { 178053d2f744STakashi Iwai struct snd_timer_user *tu; 178153d2f744STakashi Iwai struct snd_timer_params params; 178253d2f744STakashi Iwai struct snd_timer *t; 17831da177e4SLinus Torvalds int err; 17841da177e4SLinus Torvalds 17851da177e4SLinus Torvalds tu = file->private_data; 17867c64ec34SClemens Ladisch if (!tu->timeri) 17877c64ec34SClemens Ladisch return -EBADFD; 17881da177e4SLinus Torvalds t = tu->timeri->timer; 17897c64ec34SClemens Ladisch if (!t) 17907c64ec34SClemens Ladisch return -EBADFD; 17911da177e4SLinus Torvalds if (copy_from_user(¶ms, _params, sizeof(params))) 17921da177e4SLinus Torvalds return -EFAULT; 179371321eb3STakashi Iwai if (!(t->hw.flags & SNDRV_TIMER_HW_SLAVE)) { 179471321eb3STakashi Iwai u64 resolution; 179571321eb3STakashi Iwai 179671321eb3STakashi Iwai if (params.ticks < 1) { 17971da177e4SLinus Torvalds err = -EINVAL; 17981da177e4SLinus Torvalds goto _end; 17991da177e4SLinus Torvalds } 180071321eb3STakashi Iwai 180171321eb3STakashi Iwai /* Don't allow resolution less than 1ms */ 180271321eb3STakashi Iwai resolution = snd_timer_resolution(tu->timeri); 180371321eb3STakashi Iwai resolution *= params.ticks; 180471321eb3STakashi Iwai if (resolution < 1000000) { 180571321eb3STakashi Iwai err = -EINVAL; 180671321eb3STakashi Iwai goto _end; 180771321eb3STakashi Iwai } 180871321eb3STakashi Iwai } 18096b172a85SClemens Ladisch if (params.queue_size > 0 && 18106b172a85SClemens Ladisch (params.queue_size < 32 || params.queue_size > 1024)) { 18111da177e4SLinus Torvalds err = -EINVAL; 18121da177e4SLinus Torvalds goto _end; 18131da177e4SLinus Torvalds } 18141da177e4SLinus Torvalds if (params.filter & ~((1<<SNDRV_TIMER_EVENT_RESOLUTION)| 18151da177e4SLinus Torvalds (1<<SNDRV_TIMER_EVENT_TICK)| 18161da177e4SLinus Torvalds (1<<SNDRV_TIMER_EVENT_START)| 18171da177e4SLinus Torvalds (1<<SNDRV_TIMER_EVENT_STOP)| 18181da177e4SLinus Torvalds (1<<SNDRV_TIMER_EVENT_CONTINUE)| 18191da177e4SLinus Torvalds (1<<SNDRV_TIMER_EVENT_PAUSE)| 1820a501dfa3SJaroslav Kysela (1<<SNDRV_TIMER_EVENT_SUSPEND)| 1821a501dfa3SJaroslav Kysela (1<<SNDRV_TIMER_EVENT_RESUME)| 18221da177e4SLinus Torvalds (1<<SNDRV_TIMER_EVENT_MSTART)| 18231da177e4SLinus Torvalds (1<<SNDRV_TIMER_EVENT_MSTOP)| 18241da177e4SLinus Torvalds (1<<SNDRV_TIMER_EVENT_MCONTINUE)| 182565d11d95SJaroslav Kysela (1<<SNDRV_TIMER_EVENT_MPAUSE)| 182665d11d95SJaroslav Kysela (1<<SNDRV_TIMER_EVENT_MSUSPEND)| 1827a501dfa3SJaroslav Kysela (1<<SNDRV_TIMER_EVENT_MRESUME))) { 18281da177e4SLinus Torvalds err = -EINVAL; 18291da177e4SLinus Torvalds goto _end; 18301da177e4SLinus Torvalds } 18311da177e4SLinus Torvalds snd_timer_stop(tu->timeri); 18321da177e4SLinus Torvalds spin_lock_irq(&t->lock); 18331da177e4SLinus Torvalds tu->timeri->flags &= ~(SNDRV_TIMER_IFLG_AUTO| 18341da177e4SLinus Torvalds SNDRV_TIMER_IFLG_EXCLUSIVE| 18351da177e4SLinus Torvalds SNDRV_TIMER_IFLG_EARLY_EVENT); 18361da177e4SLinus Torvalds if (params.flags & SNDRV_TIMER_PSFLG_AUTO) 18371da177e4SLinus Torvalds tu->timeri->flags |= SNDRV_TIMER_IFLG_AUTO; 18381da177e4SLinus Torvalds if (params.flags & SNDRV_TIMER_PSFLG_EXCLUSIVE) 18391da177e4SLinus Torvalds tu->timeri->flags |= SNDRV_TIMER_IFLG_EXCLUSIVE; 18401da177e4SLinus Torvalds if (params.flags & SNDRV_TIMER_PSFLG_EARLY_EVENT) 18411da177e4SLinus Torvalds tu->timeri->flags |= SNDRV_TIMER_IFLG_EARLY_EVENT; 18421da177e4SLinus Torvalds spin_unlock_irq(&t->lock); 18436b172a85SClemens Ladisch if (params.queue_size > 0 && 18446b172a85SClemens Ladisch (unsigned int)tu->queue_size != params.queue_size) { 1845890e2cb5STakashi Iwai err = realloc_user_queue(tu, params.queue_size); 1846890e2cb5STakashi Iwai if (err < 0) 1847890e2cb5STakashi Iwai goto _end; 18481da177e4SLinus Torvalds } 1849d7f910bfSTakashi Iwai spin_lock_irq(&tu->qlock); 18501da177e4SLinus Torvalds tu->qhead = tu->qtail = tu->qused = 0; 18511da177e4SLinus Torvalds if (tu->timeri->flags & SNDRV_TIMER_IFLG_EARLY_EVENT) { 18521da177e4SLinus Torvalds if (tu->tread) { 185353d2f744STakashi Iwai struct snd_timer_tread tread; 1854cec8f96eSKangjie Lu memset(&tread, 0, sizeof(tread)); 18551da177e4SLinus Torvalds tread.event = SNDRV_TIMER_EVENT_EARLY; 18561da177e4SLinus Torvalds tread.tstamp.tv_sec = 0; 18571da177e4SLinus Torvalds tread.tstamp.tv_nsec = 0; 18581da177e4SLinus Torvalds tread.val = 0; 18591da177e4SLinus Torvalds snd_timer_user_append_to_tqueue(tu, &tread); 18601da177e4SLinus Torvalds } else { 186153d2f744STakashi Iwai struct snd_timer_read *r = &tu->queue[0]; 18621da177e4SLinus Torvalds r->resolution = 0; 18631da177e4SLinus Torvalds r->ticks = 0; 18641da177e4SLinus Torvalds tu->qused++; 18651da177e4SLinus Torvalds tu->qtail++; 18661da177e4SLinus Torvalds } 18671da177e4SLinus Torvalds } 18681da177e4SLinus Torvalds tu->filter = params.filter; 18691da177e4SLinus Torvalds tu->ticks = params.ticks; 1870d7f910bfSTakashi Iwai spin_unlock_irq(&tu->qlock); 18711da177e4SLinus Torvalds err = 0; 18721da177e4SLinus Torvalds _end: 18731da177e4SLinus Torvalds if (copy_to_user(_params, ¶ms, sizeof(params))) 18741da177e4SLinus Torvalds return -EFAULT; 18751da177e4SLinus Torvalds return err; 18761da177e4SLinus Torvalds } 18771da177e4SLinus Torvalds 18786b172a85SClemens Ladisch static int snd_timer_user_status(struct file *file, 187953d2f744STakashi Iwai struct snd_timer_status __user *_status) 18801da177e4SLinus Torvalds { 188153d2f744STakashi Iwai struct snd_timer_user *tu; 188253d2f744STakashi Iwai struct snd_timer_status status; 18831da177e4SLinus Torvalds 18841da177e4SLinus Torvalds tu = file->private_data; 18857c64ec34SClemens Ladisch if (!tu->timeri) 18867c64ec34SClemens Ladisch return -EBADFD; 18871da177e4SLinus Torvalds memset(&status, 0, sizeof(status)); 1888fcae40c9SBaolin Wang status.tstamp = timespec64_to_timespec(tu->tstamp); 18891da177e4SLinus Torvalds status.resolution = snd_timer_resolution(tu->timeri); 18901da177e4SLinus Torvalds status.lost = tu->timeri->lost; 18911da177e4SLinus Torvalds status.overrun = tu->overrun; 18921da177e4SLinus Torvalds spin_lock_irq(&tu->qlock); 18931da177e4SLinus Torvalds status.queue = tu->qused; 18941da177e4SLinus Torvalds spin_unlock_irq(&tu->qlock); 18951da177e4SLinus Torvalds if (copy_to_user(_status, &status, sizeof(status))) 18961da177e4SLinus Torvalds return -EFAULT; 18971da177e4SLinus Torvalds return 0; 18981da177e4SLinus Torvalds } 18991da177e4SLinus Torvalds 19001da177e4SLinus Torvalds static int snd_timer_user_start(struct file *file) 19011da177e4SLinus Torvalds { 19021da177e4SLinus Torvalds int err; 190353d2f744STakashi Iwai struct snd_timer_user *tu; 19041da177e4SLinus Torvalds 19051da177e4SLinus Torvalds tu = file->private_data; 19067c64ec34SClemens Ladisch if (!tu->timeri) 19077c64ec34SClemens Ladisch return -EBADFD; 19081da177e4SLinus Torvalds snd_timer_stop(tu->timeri); 19091da177e4SLinus Torvalds tu->timeri->lost = 0; 19101da177e4SLinus Torvalds tu->last_resolution = 0; 19115d704b0dSTakashi Iwai err = snd_timer_start(tu->timeri, tu->ticks); 19125d704b0dSTakashi Iwai if (err < 0) 19135d704b0dSTakashi Iwai return err; 19145d704b0dSTakashi Iwai return 0; 19151da177e4SLinus Torvalds } 19161da177e4SLinus Torvalds 19171da177e4SLinus Torvalds static int snd_timer_user_stop(struct file *file) 19181da177e4SLinus Torvalds { 19191da177e4SLinus Torvalds int err; 192053d2f744STakashi Iwai struct snd_timer_user *tu; 19211da177e4SLinus Torvalds 19221da177e4SLinus Torvalds tu = file->private_data; 19237c64ec34SClemens Ladisch if (!tu->timeri) 19247c64ec34SClemens Ladisch return -EBADFD; 19255d704b0dSTakashi Iwai err = snd_timer_stop(tu->timeri); 19265d704b0dSTakashi Iwai if (err < 0) 19275d704b0dSTakashi Iwai return err; 19285d704b0dSTakashi Iwai return 0; 19291da177e4SLinus Torvalds } 19301da177e4SLinus Torvalds 19311da177e4SLinus Torvalds static int snd_timer_user_continue(struct file *file) 19321da177e4SLinus Torvalds { 19331da177e4SLinus Torvalds int err; 193453d2f744STakashi Iwai struct snd_timer_user *tu; 19351da177e4SLinus Torvalds 19361da177e4SLinus Torvalds tu = file->private_data; 19377c64ec34SClemens Ladisch if (!tu->timeri) 19387c64ec34SClemens Ladisch return -EBADFD; 19399f8a7658STakashi Iwai /* start timer instead of continue if it's not used before */ 19409f8a7658STakashi Iwai if (!(tu->timeri->flags & SNDRV_TIMER_IFLG_PAUSED)) 19419f8a7658STakashi Iwai return snd_timer_user_start(file); 19421da177e4SLinus Torvalds tu->timeri->lost = 0; 19435d704b0dSTakashi Iwai err = snd_timer_continue(tu->timeri); 19445d704b0dSTakashi Iwai if (err < 0) 19455d704b0dSTakashi Iwai return err; 19465d704b0dSTakashi Iwai return 0; 19471da177e4SLinus Torvalds } 19481da177e4SLinus Torvalds 194915790a6bSTakashi Iwai static int snd_timer_user_pause(struct file *file) 195015790a6bSTakashi Iwai { 195115790a6bSTakashi Iwai int err; 195253d2f744STakashi Iwai struct snd_timer_user *tu; 195315790a6bSTakashi Iwai 195415790a6bSTakashi Iwai tu = file->private_data; 19557c64ec34SClemens Ladisch if (!tu->timeri) 19567c64ec34SClemens Ladisch return -EBADFD; 19575d704b0dSTakashi Iwai err = snd_timer_pause(tu->timeri); 19585d704b0dSTakashi Iwai if (err < 0) 19595d704b0dSTakashi Iwai return err; 19605d704b0dSTakashi Iwai return 0; 196115790a6bSTakashi Iwai } 196215790a6bSTakashi Iwai 19638c50b37cSTakashi Iwai enum { 19648c50b37cSTakashi Iwai SNDRV_TIMER_IOCTL_START_OLD = _IO('T', 0x20), 19658c50b37cSTakashi Iwai SNDRV_TIMER_IOCTL_STOP_OLD = _IO('T', 0x21), 19668c50b37cSTakashi Iwai SNDRV_TIMER_IOCTL_CONTINUE_OLD = _IO('T', 0x22), 19678c50b37cSTakashi Iwai SNDRV_TIMER_IOCTL_PAUSE_OLD = _IO('T', 0x23), 19688c50b37cSTakashi Iwai }; 19698c50b37cSTakashi Iwai 1970af368027STakashi Iwai static long __snd_timer_user_ioctl(struct file *file, unsigned int cmd, 19716b172a85SClemens Ladisch unsigned long arg) 19721da177e4SLinus Torvalds { 197353d2f744STakashi Iwai struct snd_timer_user *tu; 19741da177e4SLinus Torvalds void __user *argp = (void __user *)arg; 19751da177e4SLinus Torvalds int __user *p = argp; 19761da177e4SLinus Torvalds 19771da177e4SLinus Torvalds tu = file->private_data; 19781da177e4SLinus Torvalds switch (cmd) { 19791da177e4SLinus Torvalds case SNDRV_TIMER_IOCTL_PVERSION: 19801da177e4SLinus Torvalds return put_user(SNDRV_TIMER_VERSION, p) ? -EFAULT : 0; 19811da177e4SLinus Torvalds case SNDRV_TIMER_IOCTL_NEXT_DEVICE: 19821da177e4SLinus Torvalds return snd_timer_user_next_device(argp); 19831da177e4SLinus Torvalds case SNDRV_TIMER_IOCTL_TREAD: 19841da177e4SLinus Torvalds { 1985890e2cb5STakashi Iwai int xarg, old_tread; 19861da177e4SLinus Torvalds 1987af368027STakashi Iwai if (tu->timeri) /* too late */ 19881da177e4SLinus Torvalds return -EBUSY; 1989af368027STakashi Iwai if (get_user(xarg, p)) 19901da177e4SLinus Torvalds return -EFAULT; 1991890e2cb5STakashi Iwai old_tread = tu->tread; 19921da177e4SLinus Torvalds tu->tread = xarg ? 1 : 0; 1993890e2cb5STakashi Iwai if (tu->tread != old_tread && 1994890e2cb5STakashi Iwai realloc_user_queue(tu, tu->queue_size) < 0) { 1995890e2cb5STakashi Iwai tu->tread = old_tread; 1996890e2cb5STakashi Iwai return -ENOMEM; 1997890e2cb5STakashi Iwai } 19981da177e4SLinus Torvalds return 0; 19991da177e4SLinus Torvalds } 20001da177e4SLinus Torvalds case SNDRV_TIMER_IOCTL_GINFO: 20011da177e4SLinus Torvalds return snd_timer_user_ginfo(file, argp); 20021da177e4SLinus Torvalds case SNDRV_TIMER_IOCTL_GPARAMS: 20031da177e4SLinus Torvalds return snd_timer_user_gparams(file, argp); 20041da177e4SLinus Torvalds case SNDRV_TIMER_IOCTL_GSTATUS: 20051da177e4SLinus Torvalds return snd_timer_user_gstatus(file, argp); 20061da177e4SLinus Torvalds case SNDRV_TIMER_IOCTL_SELECT: 20071da177e4SLinus Torvalds return snd_timer_user_tselect(file, argp); 20081da177e4SLinus Torvalds case SNDRV_TIMER_IOCTL_INFO: 20091da177e4SLinus Torvalds return snd_timer_user_info(file, argp); 20101da177e4SLinus Torvalds case SNDRV_TIMER_IOCTL_PARAMS: 20111da177e4SLinus Torvalds return snd_timer_user_params(file, argp); 20121da177e4SLinus Torvalds case SNDRV_TIMER_IOCTL_STATUS: 20131da177e4SLinus Torvalds return snd_timer_user_status(file, argp); 20141da177e4SLinus Torvalds case SNDRV_TIMER_IOCTL_START: 20158c50b37cSTakashi Iwai case SNDRV_TIMER_IOCTL_START_OLD: 20161da177e4SLinus Torvalds return snd_timer_user_start(file); 20171da177e4SLinus Torvalds case SNDRV_TIMER_IOCTL_STOP: 20188c50b37cSTakashi Iwai case SNDRV_TIMER_IOCTL_STOP_OLD: 20191da177e4SLinus Torvalds return snd_timer_user_stop(file); 20201da177e4SLinus Torvalds case SNDRV_TIMER_IOCTL_CONTINUE: 20218c50b37cSTakashi Iwai case SNDRV_TIMER_IOCTL_CONTINUE_OLD: 20221da177e4SLinus Torvalds return snd_timer_user_continue(file); 202315790a6bSTakashi Iwai case SNDRV_TIMER_IOCTL_PAUSE: 20248c50b37cSTakashi Iwai case SNDRV_TIMER_IOCTL_PAUSE_OLD: 202515790a6bSTakashi Iwai return snd_timer_user_pause(file); 20261da177e4SLinus Torvalds } 20271da177e4SLinus Torvalds return -ENOTTY; 20281da177e4SLinus Torvalds } 20291da177e4SLinus Torvalds 2030af368027STakashi Iwai static long snd_timer_user_ioctl(struct file *file, unsigned int cmd, 2031af368027STakashi Iwai unsigned long arg) 2032af368027STakashi Iwai { 2033af368027STakashi Iwai struct snd_timer_user *tu = file->private_data; 2034af368027STakashi Iwai long ret; 2035af368027STakashi Iwai 2036af368027STakashi Iwai mutex_lock(&tu->ioctl_lock); 2037af368027STakashi Iwai ret = __snd_timer_user_ioctl(file, cmd, arg); 2038af368027STakashi Iwai mutex_unlock(&tu->ioctl_lock); 2039af368027STakashi Iwai return ret; 2040af368027STakashi Iwai } 2041af368027STakashi Iwai 20421da177e4SLinus Torvalds static int snd_timer_user_fasync(int fd, struct file * file, int on) 20431da177e4SLinus Torvalds { 204453d2f744STakashi Iwai struct snd_timer_user *tu; 20451da177e4SLinus Torvalds 20461da177e4SLinus Torvalds tu = file->private_data; 204760aa4924SJonathan Corbet return fasync_helper(fd, file, on, &tu->fasync); 20481da177e4SLinus Torvalds } 20491da177e4SLinus Torvalds 20506b172a85SClemens Ladisch static ssize_t snd_timer_user_read(struct file *file, char __user *buffer, 20516b172a85SClemens Ladisch size_t count, loff_t *offset) 20521da177e4SLinus Torvalds { 205353d2f744STakashi Iwai struct snd_timer_user *tu; 20541da177e4SLinus Torvalds long result = 0, unit; 20554dff5c7bSTakashi Iwai int qhead; 20561da177e4SLinus Torvalds int err = 0; 20571da177e4SLinus Torvalds 20581da177e4SLinus Torvalds tu = file->private_data; 205953d2f744STakashi Iwai unit = tu->tread ? sizeof(struct snd_timer_tread) : sizeof(struct snd_timer_read); 2060d11662f4STakashi Iwai mutex_lock(&tu->ioctl_lock); 20611da177e4SLinus Torvalds spin_lock_irq(&tu->qlock); 20621da177e4SLinus Torvalds while ((long)count - result >= unit) { 20631da177e4SLinus Torvalds while (!tu->qused) { 2064ac6424b9SIngo Molnar wait_queue_entry_t wait; 20651da177e4SLinus Torvalds 20661da177e4SLinus Torvalds if ((file->f_flags & O_NONBLOCK) != 0 || result > 0) { 20671da177e4SLinus Torvalds err = -EAGAIN; 20684dff5c7bSTakashi Iwai goto _error; 20691da177e4SLinus Torvalds } 20701da177e4SLinus Torvalds 20711da177e4SLinus Torvalds set_current_state(TASK_INTERRUPTIBLE); 20721da177e4SLinus Torvalds init_waitqueue_entry(&wait, current); 20731da177e4SLinus Torvalds add_wait_queue(&tu->qchange_sleep, &wait); 20741da177e4SLinus Torvalds 20751da177e4SLinus Torvalds spin_unlock_irq(&tu->qlock); 2076d11662f4STakashi Iwai mutex_unlock(&tu->ioctl_lock); 20771da177e4SLinus Torvalds schedule(); 2078d11662f4STakashi Iwai mutex_lock(&tu->ioctl_lock); 20791da177e4SLinus Torvalds spin_lock_irq(&tu->qlock); 20801da177e4SLinus Torvalds 20811da177e4SLinus Torvalds remove_wait_queue(&tu->qchange_sleep, &wait); 20821da177e4SLinus Torvalds 2083230323daSTakashi Iwai if (tu->disconnected) { 2084230323daSTakashi Iwai err = -ENODEV; 20854dff5c7bSTakashi Iwai goto _error; 2086230323daSTakashi Iwai } 20871da177e4SLinus Torvalds if (signal_pending(current)) { 20881da177e4SLinus Torvalds err = -ERESTARTSYS; 20894dff5c7bSTakashi Iwai goto _error; 20901da177e4SLinus Torvalds } 20911da177e4SLinus Torvalds } 20921da177e4SLinus Torvalds 20934dff5c7bSTakashi Iwai qhead = tu->qhead++; 20944dff5c7bSTakashi Iwai tu->qhead %= tu->queue_size; 20953fa6993fSTakashi Iwai tu->qused--; 20961da177e4SLinus Torvalds spin_unlock_irq(&tu->qlock); 20971da177e4SLinus Torvalds 20981da177e4SLinus Torvalds if (tu->tread) { 20994dff5c7bSTakashi Iwai if (copy_to_user(buffer, &tu->tqueue[qhead], 21004dff5c7bSTakashi Iwai sizeof(struct snd_timer_tread))) 21011da177e4SLinus Torvalds err = -EFAULT; 21021da177e4SLinus Torvalds } else { 21034dff5c7bSTakashi Iwai if (copy_to_user(buffer, &tu->queue[qhead], 21044dff5c7bSTakashi Iwai sizeof(struct snd_timer_read))) 21051da177e4SLinus Torvalds err = -EFAULT; 21061da177e4SLinus Torvalds } 21071da177e4SLinus Torvalds 21081da177e4SLinus Torvalds spin_lock_irq(&tu->qlock); 21094dff5c7bSTakashi Iwai if (err < 0) 21104dff5c7bSTakashi Iwai goto _error; 21114dff5c7bSTakashi Iwai result += unit; 21124dff5c7bSTakashi Iwai buffer += unit; 21131da177e4SLinus Torvalds } 21141da177e4SLinus Torvalds _error: 21154dff5c7bSTakashi Iwai spin_unlock_irq(&tu->qlock); 2116d11662f4STakashi Iwai mutex_unlock(&tu->ioctl_lock); 21171da177e4SLinus Torvalds return result > 0 ? result : err; 21181da177e4SLinus Torvalds } 21191da177e4SLinus Torvalds 2120680ef72aSAl Viro static __poll_t snd_timer_user_poll(struct file *file, poll_table * wait) 21211da177e4SLinus Torvalds { 2122680ef72aSAl Viro __poll_t mask; 212353d2f744STakashi Iwai struct snd_timer_user *tu; 21241da177e4SLinus Torvalds 21251da177e4SLinus Torvalds tu = file->private_data; 21261da177e4SLinus Torvalds 21271da177e4SLinus Torvalds poll_wait(file, &tu->qchange_sleep, wait); 21281da177e4SLinus Torvalds 21291da177e4SLinus Torvalds mask = 0; 2130d7f910bfSTakashi Iwai spin_lock_irq(&tu->qlock); 21311da177e4SLinus Torvalds if (tu->qused) 2132a9a08845SLinus Torvalds mask |= EPOLLIN | EPOLLRDNORM; 2133230323daSTakashi Iwai if (tu->disconnected) 2134a9a08845SLinus Torvalds mask |= EPOLLERR; 2135d7f910bfSTakashi Iwai spin_unlock_irq(&tu->qlock); 21361da177e4SLinus Torvalds 21371da177e4SLinus Torvalds return mask; 21381da177e4SLinus Torvalds } 21391da177e4SLinus Torvalds 21401da177e4SLinus Torvalds #ifdef CONFIG_COMPAT 21411da177e4SLinus Torvalds #include "timer_compat.c" 21421da177e4SLinus Torvalds #else 21431da177e4SLinus Torvalds #define snd_timer_user_ioctl_compat NULL 21441da177e4SLinus Torvalds #endif 21451da177e4SLinus Torvalds 21469c2e08c5SArjan van de Ven static const struct file_operations snd_timer_f_ops = 21471da177e4SLinus Torvalds { 21481da177e4SLinus Torvalds .owner = THIS_MODULE, 21491da177e4SLinus Torvalds .read = snd_timer_user_read, 21501da177e4SLinus Torvalds .open = snd_timer_user_open, 21511da177e4SLinus Torvalds .release = snd_timer_user_release, 215202f4865fSTakashi Iwai .llseek = no_llseek, 21531da177e4SLinus Torvalds .poll = snd_timer_user_poll, 21541da177e4SLinus Torvalds .unlocked_ioctl = snd_timer_user_ioctl, 21551da177e4SLinus Torvalds .compat_ioctl = snd_timer_user_ioctl_compat, 21561da177e4SLinus Torvalds .fasync = snd_timer_user_fasync, 21571da177e4SLinus Torvalds }; 21581da177e4SLinus Torvalds 21597c35860dSTakashi Iwai /* unregister the system timer */ 21607c35860dSTakashi Iwai static void snd_timer_free_all(void) 21617c35860dSTakashi Iwai { 21627c35860dSTakashi Iwai struct snd_timer *timer, *n; 21637c35860dSTakashi Iwai 21647c35860dSTakashi Iwai list_for_each_entry_safe(timer, n, &snd_timer_list, device_list) 21657c35860dSTakashi Iwai snd_timer_free(timer); 21667c35860dSTakashi Iwai } 21677c35860dSTakashi Iwai 216889da061fSTakashi Iwai static struct device timer_dev; 216989da061fSTakashi Iwai 21701da177e4SLinus Torvalds /* 21711da177e4SLinus Torvalds * ENTRY functions 21721da177e4SLinus Torvalds */ 21731da177e4SLinus Torvalds 21741da177e4SLinus Torvalds static int __init alsa_timer_init(void) 21751da177e4SLinus Torvalds { 21761da177e4SLinus Torvalds int err; 21771da177e4SLinus Torvalds 217889da061fSTakashi Iwai snd_device_initialize(&timer_dev, NULL); 217989da061fSTakashi Iwai dev_set_name(&timer_dev, "timer"); 218089da061fSTakashi Iwai 21811da177e4SLinus Torvalds #ifdef SNDRV_OSS_INFO_DEV_TIMERS 21826b172a85SClemens Ladisch snd_oss_info_register(SNDRV_OSS_INFO_DEV_TIMERS, SNDRV_CARDS - 1, 21836b172a85SClemens Ladisch "system timer"); 21841da177e4SLinus Torvalds #endif 2185e28563ccSTakashi Iwai 21867c35860dSTakashi Iwai err = snd_timer_register_system(); 21877c35860dSTakashi Iwai if (err < 0) { 2188cf74dcf3STakashi Iwai pr_err("ALSA: unable to register system timer (%i)\n", err); 21891ae0e4ceSMarkus Elfring goto put_timer; 21907c35860dSTakashi Iwai } 21917c35860dSTakashi Iwai 219240a4b263STakashi Iwai err = snd_register_device(SNDRV_DEVICE_TYPE_TIMER, NULL, 0, 219340a4b263STakashi Iwai &snd_timer_f_ops, NULL, &timer_dev); 21947c35860dSTakashi Iwai if (err < 0) { 2195cf74dcf3STakashi Iwai pr_err("ALSA: unable to register timer device (%i)\n", err); 21967c35860dSTakashi Iwai snd_timer_free_all(); 21971ae0e4ceSMarkus Elfring goto put_timer; 21987c35860dSTakashi Iwai } 21997c35860dSTakashi Iwai 2200e28563ccSTakashi Iwai snd_timer_proc_init(); 22011da177e4SLinus Torvalds return 0; 22021ae0e4ceSMarkus Elfring 22031ae0e4ceSMarkus Elfring put_timer: 22041ae0e4ceSMarkus Elfring put_device(&timer_dev); 22051ae0e4ceSMarkus Elfring return err; 22061da177e4SLinus Torvalds } 22071da177e4SLinus Torvalds 22081da177e4SLinus Torvalds static void __exit alsa_timer_exit(void) 22091da177e4SLinus Torvalds { 221040a4b263STakashi Iwai snd_unregister_device(&timer_dev); 22117c35860dSTakashi Iwai snd_timer_free_all(); 221289da061fSTakashi Iwai put_device(&timer_dev); 2213e28563ccSTakashi Iwai snd_timer_proc_done(); 22141da177e4SLinus Torvalds #ifdef SNDRV_OSS_INFO_DEV_TIMERS 22151da177e4SLinus Torvalds snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_TIMERS, SNDRV_CARDS - 1); 22161da177e4SLinus Torvalds #endif 22171da177e4SLinus Torvalds } 22181da177e4SLinus Torvalds 22191da177e4SLinus Torvalds module_init(alsa_timer_init) 22201da177e4SLinus Torvalds module_exit(alsa_timer_exit) 2221