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; 621da177e4SLinus Torvalds struct timespec 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 771a60d4c5SIngo Molnar static DEFINE_MUTEX(register_mutex); 781da177e4SLinus Torvalds 7953d2f744STakashi Iwai static int snd_timer_free(struct snd_timer *timer); 8053d2f744STakashi Iwai static int snd_timer_dev_free(struct snd_device *device); 8153d2f744STakashi Iwai static int snd_timer_dev_register(struct snd_device *device); 82c461482cSTakashi Iwai static int snd_timer_dev_disconnect(struct snd_device *device); 831da177e4SLinus Torvalds 8453d2f744STakashi Iwai static void snd_timer_reschedule(struct snd_timer * timer, unsigned long ticks_left); 851da177e4SLinus Torvalds 861da177e4SLinus Torvalds /* 871da177e4SLinus Torvalds * create a timer instance with the given owner string. 881da177e4SLinus Torvalds * when timer is not NULL, increments the module counter 891da177e4SLinus Torvalds */ 9053d2f744STakashi Iwai static struct snd_timer_instance *snd_timer_instance_new(char *owner, 9153d2f744STakashi Iwai struct snd_timer *timer) 921da177e4SLinus Torvalds { 9353d2f744STakashi Iwai struct snd_timer_instance *timeri; 94ca2c0966STakashi Iwai timeri = kzalloc(sizeof(*timeri), GFP_KERNEL); 951da177e4SLinus Torvalds if (timeri == NULL) 961da177e4SLinus Torvalds return NULL; 97543537bdSPaulo Marques timeri->owner = kstrdup(owner, GFP_KERNEL); 981da177e4SLinus Torvalds if (! timeri->owner) { 991da177e4SLinus Torvalds kfree(timeri); 1001da177e4SLinus Torvalds return NULL; 1011da177e4SLinus Torvalds } 1021da177e4SLinus Torvalds INIT_LIST_HEAD(&timeri->open_list); 1031da177e4SLinus Torvalds INIT_LIST_HEAD(&timeri->active_list); 1041da177e4SLinus Torvalds INIT_LIST_HEAD(&timeri->ack_list); 1051da177e4SLinus Torvalds INIT_LIST_HEAD(&timeri->slave_list_head); 1061da177e4SLinus Torvalds INIT_LIST_HEAD(&timeri->slave_active_head); 1071da177e4SLinus Torvalds 1081da177e4SLinus Torvalds timeri->timer = timer; 109de24214dSClemens Ladisch if (timer && !try_module_get(timer->module)) { 1101da177e4SLinus Torvalds kfree(timeri->owner); 1111da177e4SLinus Torvalds kfree(timeri); 1121da177e4SLinus Torvalds return NULL; 1131da177e4SLinus Torvalds } 1141da177e4SLinus Torvalds 1151da177e4SLinus Torvalds return timeri; 1161da177e4SLinus Torvalds } 1171da177e4SLinus Torvalds 1181da177e4SLinus Torvalds /* 1191da177e4SLinus Torvalds * find a timer instance from the given timer id 1201da177e4SLinus Torvalds */ 12153d2f744STakashi Iwai static struct snd_timer *snd_timer_find(struct snd_timer_id *tid) 1221da177e4SLinus Torvalds { 12353d2f744STakashi Iwai struct snd_timer *timer = NULL; 1241da177e4SLinus Torvalds 1259244b2c3SJohannes Berg list_for_each_entry(timer, &snd_timer_list, device_list) { 1261da177e4SLinus Torvalds if (timer->tmr_class != tid->dev_class) 1271da177e4SLinus Torvalds continue; 1281da177e4SLinus Torvalds if ((timer->tmr_class == SNDRV_TIMER_CLASS_CARD || 1291da177e4SLinus Torvalds timer->tmr_class == SNDRV_TIMER_CLASS_PCM) && 1301da177e4SLinus Torvalds (timer->card == NULL || 1311da177e4SLinus Torvalds timer->card->number != tid->card)) 1321da177e4SLinus Torvalds continue; 1331da177e4SLinus Torvalds if (timer->tmr_device != tid->device) 1341da177e4SLinus Torvalds continue; 1351da177e4SLinus Torvalds if (timer->tmr_subdevice != tid->subdevice) 1361da177e4SLinus Torvalds continue; 1371da177e4SLinus Torvalds return timer; 1381da177e4SLinus Torvalds } 1391da177e4SLinus Torvalds return NULL; 1401da177e4SLinus Torvalds } 1411da177e4SLinus Torvalds 142ee2da997SJohannes Berg #ifdef CONFIG_MODULES 1431da177e4SLinus Torvalds 14453d2f744STakashi Iwai static void snd_timer_request(struct snd_timer_id *tid) 1451da177e4SLinus Torvalds { 1461da177e4SLinus Torvalds switch (tid->dev_class) { 1471da177e4SLinus Torvalds case SNDRV_TIMER_CLASS_GLOBAL: 1481da177e4SLinus Torvalds if (tid->device < timer_limit) 1491da177e4SLinus Torvalds request_module("snd-timer-%i", tid->device); 1501da177e4SLinus Torvalds break; 1511da177e4SLinus Torvalds case SNDRV_TIMER_CLASS_CARD: 1521da177e4SLinus Torvalds case SNDRV_TIMER_CLASS_PCM: 1531da177e4SLinus Torvalds if (tid->card < snd_ecards_limit) 1541da177e4SLinus Torvalds request_module("snd-card-%i", tid->card); 1551da177e4SLinus Torvalds break; 1561da177e4SLinus Torvalds default: 1571da177e4SLinus Torvalds break; 1581da177e4SLinus Torvalds } 1591da177e4SLinus Torvalds } 1601da177e4SLinus Torvalds 1611da177e4SLinus Torvalds #endif 1621da177e4SLinus Torvalds 1631da177e4SLinus Torvalds /* 1641da177e4SLinus Torvalds * look for a master instance matching with the slave id of the given slave. 1651da177e4SLinus Torvalds * when found, relink the open_link of the slave. 1661da177e4SLinus Torvalds * 1671da177e4SLinus Torvalds * call this with register_mutex down. 1681da177e4SLinus Torvalds */ 1699b7d869eSTakashi Iwai static int snd_timer_check_slave(struct snd_timer_instance *slave) 1701da177e4SLinus Torvalds { 17153d2f744STakashi Iwai struct snd_timer *timer; 17253d2f744STakashi Iwai struct snd_timer_instance *master; 1731da177e4SLinus Torvalds 1741da177e4SLinus Torvalds /* FIXME: it's really dumb to look up all entries.. */ 1759244b2c3SJohannes Berg list_for_each_entry(timer, &snd_timer_list, device_list) { 1769244b2c3SJohannes Berg list_for_each_entry(master, &timer->open_list_head, open_list) { 1771da177e4SLinus Torvalds if (slave->slave_class == master->slave_class && 1781da177e4SLinus Torvalds slave->slave_id == master->slave_id) { 1799b7d869eSTakashi Iwai if (master->timer->num_instances >= 1809b7d869eSTakashi Iwai master->timer->max_instances) 1819b7d869eSTakashi Iwai return -EBUSY; 1825b7c757dSNicolas Kaiser list_move_tail(&slave->open_list, 1836b172a85SClemens Ladisch &master->slave_list_head); 1849b7d869eSTakashi Iwai master->timer->num_instances++; 1851da177e4SLinus Torvalds spin_lock_irq(&slave_active_lock); 1861da177e4SLinus Torvalds slave->master = master; 1871da177e4SLinus Torvalds slave->timer = master->timer; 1881da177e4SLinus Torvalds spin_unlock_irq(&slave_active_lock); 1899b7d869eSTakashi Iwai return 0; 1901da177e4SLinus Torvalds } 1911da177e4SLinus Torvalds } 1921da177e4SLinus Torvalds } 1939b7d869eSTakashi Iwai return 0; 1941da177e4SLinus Torvalds } 1951da177e4SLinus Torvalds 1961da177e4SLinus Torvalds /* 1971da177e4SLinus Torvalds * look for slave instances matching with the slave id of the given master. 1981da177e4SLinus Torvalds * when found, relink the open_link of slaves. 1991da177e4SLinus Torvalds * 2001da177e4SLinus Torvalds * call this with register_mutex down. 2011da177e4SLinus Torvalds */ 2029b7d869eSTakashi Iwai static int snd_timer_check_master(struct snd_timer_instance *master) 2031da177e4SLinus Torvalds { 2049244b2c3SJohannes Berg struct snd_timer_instance *slave, *tmp; 2051da177e4SLinus Torvalds 2061da177e4SLinus Torvalds /* check all pending slaves */ 2079244b2c3SJohannes Berg list_for_each_entry_safe(slave, tmp, &snd_timer_slave_list, open_list) { 2081da177e4SLinus Torvalds if (slave->slave_class == master->slave_class && 2091da177e4SLinus Torvalds slave->slave_id == master->slave_id) { 2109b7d869eSTakashi Iwai if (master->timer->num_instances >= 2119b7d869eSTakashi Iwai master->timer->max_instances) 2129b7d869eSTakashi Iwai return -EBUSY; 2139244b2c3SJohannes Berg list_move_tail(&slave->open_list, &master->slave_list_head); 2149b7d869eSTakashi Iwai master->timer->num_instances++; 2151da177e4SLinus Torvalds spin_lock_irq(&slave_active_lock); 216b5a663aaSTakashi Iwai spin_lock(&master->timer->lock); 2171da177e4SLinus Torvalds slave->master = master; 2181da177e4SLinus Torvalds slave->timer = master->timer; 2191da177e4SLinus Torvalds if (slave->flags & SNDRV_TIMER_IFLG_RUNNING) 2206b172a85SClemens Ladisch list_add_tail(&slave->active_list, 2216b172a85SClemens Ladisch &master->slave_active_head); 222b5a663aaSTakashi Iwai spin_unlock(&master->timer->lock); 2231da177e4SLinus Torvalds spin_unlock_irq(&slave_active_lock); 2241da177e4SLinus Torvalds } 2251da177e4SLinus Torvalds } 2269b7d869eSTakashi Iwai return 0; 2271da177e4SLinus Torvalds } 2281da177e4SLinus Torvalds 2299b7d869eSTakashi Iwai static int snd_timer_close_locked(struct snd_timer_instance *timeri); 2309b7d869eSTakashi Iwai 2311da177e4SLinus Torvalds /* 2321da177e4SLinus Torvalds * open a timer instance 2331da177e4SLinus Torvalds * when opening a master, the slave id must be here given. 2341da177e4SLinus Torvalds */ 23553d2f744STakashi Iwai int snd_timer_open(struct snd_timer_instance **ti, 23653d2f744STakashi Iwai char *owner, struct snd_timer_id *tid, 2371da177e4SLinus Torvalds unsigned int slave_id) 2381da177e4SLinus Torvalds { 23953d2f744STakashi Iwai struct snd_timer *timer; 24053d2f744STakashi Iwai struct snd_timer_instance *timeri = NULL; 2419b7d869eSTakashi Iwai int err; 2421da177e4SLinus Torvalds 24341672c0cSTakashi Iwai mutex_lock(®ister_mutex); 2441da177e4SLinus Torvalds if (tid->dev_class == SNDRV_TIMER_CLASS_SLAVE) { 2451da177e4SLinus Torvalds /* open a slave instance */ 2461da177e4SLinus Torvalds if (tid->dev_sclass <= SNDRV_TIMER_SCLASS_NONE || 2471da177e4SLinus Torvalds tid->dev_sclass > SNDRV_TIMER_SCLASS_OSS_SEQUENCER) { 248cf74dcf3STakashi Iwai pr_debug("ALSA: timer: invalid slave class %i\n", 249cf74dcf3STakashi Iwai tid->dev_sclass); 25041672c0cSTakashi Iwai err = -EINVAL; 25141672c0cSTakashi Iwai goto unlock; 2521da177e4SLinus Torvalds } 2531da177e4SLinus Torvalds timeri = snd_timer_instance_new(owner, NULL); 2542fd43d11SClemens Ladisch if (!timeri) { 25541672c0cSTakashi Iwai err = -ENOMEM; 25641672c0cSTakashi Iwai goto unlock; 2572fd43d11SClemens Ladisch } 2581da177e4SLinus Torvalds timeri->slave_class = tid->dev_sclass; 2591da177e4SLinus Torvalds timeri->slave_id = tid->device; 2601da177e4SLinus Torvalds timeri->flags |= SNDRV_TIMER_IFLG_SLAVE; 2611da177e4SLinus Torvalds list_add_tail(&timeri->open_list, &snd_timer_slave_list); 2629b7d869eSTakashi Iwai err = snd_timer_check_slave(timeri); 2639b7d869eSTakashi Iwai if (err < 0) { 2649b7d869eSTakashi Iwai snd_timer_close_locked(timeri); 2659b7d869eSTakashi Iwai timeri = NULL; 2669b7d869eSTakashi Iwai } 26741672c0cSTakashi Iwai goto unlock; 2681da177e4SLinus Torvalds } 2691da177e4SLinus Torvalds 2701da177e4SLinus Torvalds /* open a master instance */ 2711da177e4SLinus Torvalds timer = snd_timer_find(tid); 272ee2da997SJohannes Berg #ifdef CONFIG_MODULES 273ee2da997SJohannes Berg if (!timer) { 2741a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 2751da177e4SLinus Torvalds snd_timer_request(tid); 2761a60d4c5SIngo Molnar mutex_lock(®ister_mutex); 2771da177e4SLinus Torvalds timer = snd_timer_find(tid); 2781da177e4SLinus Torvalds } 2791da177e4SLinus Torvalds #endif 2802fd43d11SClemens Ladisch if (!timer) { 28141672c0cSTakashi Iwai err = -ENODEV; 28241672c0cSTakashi Iwai goto unlock; 2832fd43d11SClemens Ladisch } 2841da177e4SLinus Torvalds if (!list_empty(&timer->open_list_head)) { 2852fd43d11SClemens Ladisch timeri = list_entry(timer->open_list_head.next, 28653d2f744STakashi Iwai struct snd_timer_instance, open_list); 2871da177e4SLinus Torvalds if (timeri->flags & SNDRV_TIMER_IFLG_EXCLUSIVE) { 28841672c0cSTakashi Iwai err = -EBUSY; 28941672c0cSTakashi Iwai timeri = NULL; 29041672c0cSTakashi Iwai goto unlock; 2911da177e4SLinus Torvalds } 2921da177e4SLinus Torvalds } 2939b7d869eSTakashi Iwai if (timer->num_instances >= timer->max_instances) { 29441672c0cSTakashi Iwai err = -EBUSY; 29541672c0cSTakashi Iwai goto unlock; 2969b7d869eSTakashi Iwai } 2971da177e4SLinus Torvalds timeri = snd_timer_instance_new(owner, timer); 2982fd43d11SClemens Ladisch if (!timeri) { 29941672c0cSTakashi Iwai err = -ENOMEM; 30041672c0cSTakashi Iwai goto unlock; 3012fd43d11SClemens Ladisch } 302230323daSTakashi Iwai /* take a card refcount for safe disconnection */ 303230323daSTakashi Iwai if (timer->card) 304230323daSTakashi Iwai get_device(&timer->card->card_dev); 3051da177e4SLinus Torvalds timeri->slave_class = tid->dev_sclass; 3061da177e4SLinus Torvalds timeri->slave_id = slave_id; 3078ddc0563SVegard Nossum 3088ddc0563SVegard Nossum if (list_empty(&timer->open_list_head) && timer->hw.open) { 30941672c0cSTakashi Iwai err = timer->hw.open(timer); 3108ddc0563SVegard Nossum if (err) { 3118ddc0563SVegard Nossum kfree(timeri->owner); 3128ddc0563SVegard Nossum kfree(timeri); 31341672c0cSTakashi Iwai timeri = NULL; 3148ddc0563SVegard Nossum 3158ddc0563SVegard Nossum if (timer->card) 3168ddc0563SVegard Nossum put_device(&timer->card->card_dev); 3178ddc0563SVegard Nossum module_put(timer->module); 31841672c0cSTakashi Iwai goto unlock; 3198ddc0563SVegard Nossum } 3208ddc0563SVegard Nossum } 3218ddc0563SVegard Nossum 3221da177e4SLinus Torvalds list_add_tail(&timeri->open_list, &timer->open_list_head); 3239b7d869eSTakashi Iwai timer->num_instances++; 3249b7d869eSTakashi Iwai err = snd_timer_check_master(timeri); 3259b7d869eSTakashi Iwai if (err < 0) { 3269b7d869eSTakashi Iwai snd_timer_close_locked(timeri); 3279b7d869eSTakashi Iwai timeri = NULL; 3289b7d869eSTakashi Iwai } 32941672c0cSTakashi Iwai 33041672c0cSTakashi Iwai unlock: 3311a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 3321da177e4SLinus Torvalds *ti = timeri; 3339b7d869eSTakashi Iwai return err; 3341da177e4SLinus Torvalds } 33598856392STakashi Iwai EXPORT_SYMBOL(snd_timer_open); 3361da177e4SLinus Torvalds 3371da177e4SLinus Torvalds /* 3381da177e4SLinus Torvalds * close a timer instance 3399b7d869eSTakashi Iwai * call this with register_mutex down. 3401da177e4SLinus Torvalds */ 3419b7d869eSTakashi Iwai static int snd_timer_close_locked(struct snd_timer_instance *timeri) 3421da177e4SLinus Torvalds { 343fe1b26c9STakashi Iwai struct snd_timer *timer = timeri->timer; 3449244b2c3SJohannes Berg struct snd_timer_instance *slave, *tmp; 3451da177e4SLinus Torvalds 346fe1b26c9STakashi Iwai if (timer) { 347fe1b26c9STakashi Iwai spin_lock_irq(&timer->lock); 348fe1b26c9STakashi Iwai timeri->flags |= SNDRV_TIMER_IFLG_DEAD; 349fe1b26c9STakashi Iwai spin_unlock_irq(&timer->lock); 350fe1b26c9STakashi Iwai } 351fe1b26c9STakashi Iwai 3529984d1b5STakashi Iwai list_del(&timeri->open_list); 3539984d1b5STakashi Iwai 3541da177e4SLinus Torvalds /* force to stop the timer */ 3551da177e4SLinus Torvalds snd_timer_stop(timeri); 3561da177e4SLinus Torvalds 3579984d1b5STakashi Iwai if (timer) { 3589b7d869eSTakashi Iwai timer->num_instances--; 3591da177e4SLinus Torvalds /* wait, until the active callback is finished */ 3601da177e4SLinus Torvalds spin_lock_irq(&timer->lock); 3611da177e4SLinus Torvalds while (timeri->flags & SNDRV_TIMER_IFLG_CALLBACK) { 3621da177e4SLinus Torvalds spin_unlock_irq(&timer->lock); 3631da177e4SLinus Torvalds udelay(10); 3641da177e4SLinus Torvalds spin_lock_irq(&timer->lock); 3651da177e4SLinus Torvalds } 3661da177e4SLinus Torvalds spin_unlock_irq(&timer->lock); 3679984d1b5STakashi Iwai 3681da177e4SLinus Torvalds /* remove slave links */ 369b5a663aaSTakashi Iwai spin_lock_irq(&slave_active_lock); 370b5a663aaSTakashi Iwai spin_lock(&timer->lock); 3719244b2c3SJohannes Berg list_for_each_entry_safe(slave, tmp, &timeri->slave_list_head, 3729244b2c3SJohannes Berg open_list) { 3739244b2c3SJohannes Berg list_move_tail(&slave->open_list, &snd_timer_slave_list); 3749b7d869eSTakashi Iwai timer->num_instances--; 3751da177e4SLinus Torvalds slave->master = NULL; 3761da177e4SLinus Torvalds slave->timer = NULL; 377b5a663aaSTakashi Iwai list_del_init(&slave->ack_list); 378b5a663aaSTakashi Iwai list_del_init(&slave->active_list); 3791da177e4SLinus Torvalds } 380b5a663aaSTakashi Iwai spin_unlock(&timer->lock); 381b5a663aaSTakashi Iwai spin_unlock_irq(&slave_active_lock); 3829984d1b5STakashi Iwai 3839984d1b5STakashi Iwai /* slave doesn't need to release timer resources below */ 3849984d1b5STakashi Iwai if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) 3859984d1b5STakashi Iwai timer = NULL; 3861da177e4SLinus Torvalds } 3879984d1b5STakashi Iwai 3881da177e4SLinus Torvalds if (timeri->private_free) 3891da177e4SLinus Torvalds timeri->private_free(timeri); 3901da177e4SLinus Torvalds kfree(timeri->owner); 3911da177e4SLinus Torvalds kfree(timeri); 3929984d1b5STakashi Iwai 3939984d1b5STakashi Iwai if (timer) { 3949984d1b5STakashi Iwai if (list_empty(&timer->open_list_head) && timer->hw.close) 3959984d1b5STakashi Iwai timer->hw.close(timer); 3969984d1b5STakashi Iwai /* release a card refcount for safe disconnection */ 3979984d1b5STakashi Iwai if (timer->card) 3989984d1b5STakashi Iwai put_device(&timer->card->card_dev); 399de24214dSClemens Ladisch module_put(timer->module); 4009984d1b5STakashi Iwai } 4019984d1b5STakashi Iwai 4021da177e4SLinus Torvalds return 0; 4031da177e4SLinus Torvalds } 4049b7d869eSTakashi Iwai 4059b7d869eSTakashi Iwai /* 4069b7d869eSTakashi Iwai * close a timer instance 4079b7d869eSTakashi Iwai */ 4089b7d869eSTakashi Iwai int snd_timer_close(struct snd_timer_instance *timeri) 4099b7d869eSTakashi Iwai { 4109b7d869eSTakashi Iwai int err; 4119b7d869eSTakashi Iwai 4129b7d869eSTakashi Iwai if (snd_BUG_ON(!timeri)) 4139b7d869eSTakashi Iwai return -ENXIO; 4149b7d869eSTakashi Iwai 4159b7d869eSTakashi Iwai mutex_lock(®ister_mutex); 4169b7d869eSTakashi Iwai err = snd_timer_close_locked(timeri); 4179b7d869eSTakashi Iwai mutex_unlock(®ister_mutex); 4189b7d869eSTakashi Iwai return err; 4199b7d869eSTakashi Iwai } 42098856392STakashi Iwai EXPORT_SYMBOL(snd_timer_close); 4211da177e4SLinus Torvalds 422fdcb5761STakashi Iwai static unsigned long snd_timer_hw_resolution(struct snd_timer *timer) 423fdcb5761STakashi Iwai { 424fdcb5761STakashi Iwai if (timer->hw.c_resolution) 425fdcb5761STakashi Iwai return timer->hw.c_resolution(timer); 426fdcb5761STakashi Iwai else 427fdcb5761STakashi Iwai return timer->hw.resolution; 428fdcb5761STakashi Iwai } 429fdcb5761STakashi Iwai 43053d2f744STakashi Iwai unsigned long snd_timer_resolution(struct snd_timer_instance *timeri) 4311da177e4SLinus Torvalds { 43253d2f744STakashi Iwai struct snd_timer * timer; 4339d4d207dSTakashi Iwai unsigned long ret = 0; 4349d4d207dSTakashi Iwai unsigned long flags; 4351da177e4SLinus Torvalds 4361da177e4SLinus Torvalds if (timeri == NULL) 4371da177e4SLinus Torvalds return 0; 438dd1f7ab8SMarkus Elfring timer = timeri->timer; 4399d4d207dSTakashi Iwai if (timer) { 4409d4d207dSTakashi Iwai spin_lock_irqsave(&timer->lock, flags); 4419d4d207dSTakashi Iwai ret = snd_timer_hw_resolution(timer); 4429d4d207dSTakashi Iwai spin_unlock_irqrestore(&timer->lock, flags); 4439d4d207dSTakashi Iwai } 4449d4d207dSTakashi Iwai return ret; 4451da177e4SLinus Torvalds } 44698856392STakashi Iwai EXPORT_SYMBOL(snd_timer_resolution); 4471da177e4SLinus Torvalds 44853d2f744STakashi Iwai static void snd_timer_notify1(struct snd_timer_instance *ti, int event) 4491da177e4SLinus Torvalds { 4509d4d207dSTakashi Iwai struct snd_timer *timer = ti->timer; 4511da177e4SLinus Torvalds unsigned long resolution = 0; 45253d2f744STakashi Iwai struct snd_timer_instance *ts; 4531da177e4SLinus Torvalds struct timespec tstamp; 4541da177e4SLinus Torvalds 455b751eef1SJaroslav Kysela if (timer_tstamp_monotonic) 45626204e04SThomas Gleixner ktime_get_ts(&tstamp); 457b751eef1SJaroslav Kysela else 45807799e75STakashi Iwai getnstimeofday(&tstamp); 4597eaa943cSTakashi Iwai if (snd_BUG_ON(event < SNDRV_TIMER_EVENT_START || 4607eaa943cSTakashi Iwai event > SNDRV_TIMER_EVENT_PAUSE)) 4617eaa943cSTakashi Iwai return; 4629d4d207dSTakashi Iwai if (timer && 4639d4d207dSTakashi Iwai (event == SNDRV_TIMER_EVENT_START || 4649d4d207dSTakashi Iwai event == SNDRV_TIMER_EVENT_CONTINUE)) 4659d4d207dSTakashi Iwai resolution = snd_timer_hw_resolution(timer); 4661da177e4SLinus Torvalds if (ti->ccallback) 467b30477d5SJaroslav Kysela ti->ccallback(ti, event, &tstamp, resolution); 4681da177e4SLinus Torvalds if (ti->flags & SNDRV_TIMER_IFLG_SLAVE) 4691da177e4SLinus Torvalds return; 4701da177e4SLinus Torvalds if (timer == NULL) 4711da177e4SLinus Torvalds return; 4721da177e4SLinus Torvalds if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE) 4731da177e4SLinus Torvalds return; 4749244b2c3SJohannes Berg list_for_each_entry(ts, &ti->slave_active_head, active_list) 4751da177e4SLinus Torvalds if (ts->ccallback) 476117159f0STakashi Iwai ts->ccallback(ts, event + 100, &tstamp, resolution); 4771da177e4SLinus Torvalds } 4781da177e4SLinus Torvalds 479f65e0d29STakashi Iwai /* start/continue a master timer */ 480f65e0d29STakashi Iwai static int snd_timer_start1(struct snd_timer_instance *timeri, 481f65e0d29STakashi Iwai bool start, unsigned long ticks) 4821da177e4SLinus Torvalds { 483f65e0d29STakashi Iwai struct snd_timer *timer; 484f65e0d29STakashi Iwai int result; 485f65e0d29STakashi Iwai unsigned long flags; 486f65e0d29STakashi Iwai 487f65e0d29STakashi Iwai timer = timeri->timer; 488f65e0d29STakashi Iwai if (!timer) 489f65e0d29STakashi Iwai return -EINVAL; 490f65e0d29STakashi Iwai 491f65e0d29STakashi Iwai spin_lock_irqsave(&timer->lock, flags); 492fe1b26c9STakashi Iwai if (timeri->flags & SNDRV_TIMER_IFLG_DEAD) { 493fe1b26c9STakashi Iwai result = -EINVAL; 494fe1b26c9STakashi Iwai goto unlock; 495fe1b26c9STakashi Iwai } 496f65e0d29STakashi Iwai if (timer->card && timer->card->shutdown) { 497f65e0d29STakashi Iwai result = -ENODEV; 498f65e0d29STakashi Iwai goto unlock; 499f65e0d29STakashi Iwai } 500f65e0d29STakashi Iwai if (timeri->flags & (SNDRV_TIMER_IFLG_RUNNING | 501f65e0d29STakashi Iwai SNDRV_TIMER_IFLG_START)) { 502f65e0d29STakashi Iwai result = -EBUSY; 503f65e0d29STakashi Iwai goto unlock; 504f65e0d29STakashi Iwai } 505f65e0d29STakashi Iwai 506f65e0d29STakashi Iwai if (start) 507f65e0d29STakashi Iwai timeri->ticks = timeri->cticks = ticks; 508f65e0d29STakashi Iwai else if (!timeri->cticks) 509f65e0d29STakashi Iwai timeri->cticks = 1; 510f65e0d29STakashi Iwai timeri->pticks = 0; 511f65e0d29STakashi Iwai 5125b7c757dSNicolas Kaiser list_move_tail(&timeri->active_list, &timer->active_list_head); 5131da177e4SLinus Torvalds if (timer->running) { 5141da177e4SLinus Torvalds if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE) 5151da177e4SLinus Torvalds goto __start_now; 5161da177e4SLinus Torvalds timer->flags |= SNDRV_TIMER_FLG_RESCHED; 5171da177e4SLinus Torvalds timeri->flags |= SNDRV_TIMER_IFLG_START; 518f65e0d29STakashi Iwai result = 1; /* delayed start */ 5191da177e4SLinus Torvalds } else { 520f65e0d29STakashi Iwai if (start) 521f65e0d29STakashi Iwai timer->sticks = ticks; 5221da177e4SLinus Torvalds timer->hw.start(timer); 5231da177e4SLinus Torvalds __start_now: 5241da177e4SLinus Torvalds timer->running++; 5251da177e4SLinus Torvalds timeri->flags |= SNDRV_TIMER_IFLG_RUNNING; 526f65e0d29STakashi Iwai result = 0; 5271da177e4SLinus Torvalds } 528f65e0d29STakashi Iwai snd_timer_notify1(timeri, start ? SNDRV_TIMER_EVENT_START : 529f65e0d29STakashi Iwai SNDRV_TIMER_EVENT_CONTINUE); 530f65e0d29STakashi Iwai unlock: 531f65e0d29STakashi Iwai spin_unlock_irqrestore(&timer->lock, flags); 532f65e0d29STakashi Iwai return result; 5331da177e4SLinus Torvalds } 5341da177e4SLinus Torvalds 535f65e0d29STakashi Iwai /* start/continue a slave timer */ 536f65e0d29STakashi Iwai static int snd_timer_start_slave(struct snd_timer_instance *timeri, 537f65e0d29STakashi Iwai bool start) 5381da177e4SLinus Torvalds { 5391da177e4SLinus Torvalds unsigned long flags; 540fe1b26c9STakashi Iwai int err; 5411da177e4SLinus Torvalds 5421da177e4SLinus Torvalds spin_lock_irqsave(&slave_active_lock, flags); 543fe1b26c9STakashi Iwai if (timeri->flags & SNDRV_TIMER_IFLG_DEAD) { 544fe1b26c9STakashi Iwai err = -EINVAL; 545fe1b26c9STakashi Iwai goto unlock; 546fe1b26c9STakashi Iwai } 547f784beb7STakashi Iwai if (timeri->flags & SNDRV_TIMER_IFLG_RUNNING) { 548fe1b26c9STakashi Iwai err = -EBUSY; 549fe1b26c9STakashi Iwai goto unlock; 550f784beb7STakashi Iwai } 5511da177e4SLinus Torvalds timeri->flags |= SNDRV_TIMER_IFLG_RUNNING; 552b5a663aaSTakashi Iwai if (timeri->master && timeri->timer) { 553b5a663aaSTakashi Iwai spin_lock(&timeri->timer->lock); 5546b172a85SClemens Ladisch list_add_tail(&timeri->active_list, 5556b172a85SClemens Ladisch &timeri->master->slave_active_head); 556f65e0d29STakashi Iwai snd_timer_notify1(timeri, start ? SNDRV_TIMER_EVENT_START : 557f65e0d29STakashi Iwai SNDRV_TIMER_EVENT_CONTINUE); 558b5a663aaSTakashi Iwai spin_unlock(&timeri->timer->lock); 559b5a663aaSTakashi Iwai } 560fe1b26c9STakashi Iwai err = 1; /* delayed start */ 561fe1b26c9STakashi Iwai unlock: 5621da177e4SLinus Torvalds spin_unlock_irqrestore(&slave_active_lock, flags); 563fe1b26c9STakashi Iwai return err; 5641da177e4SLinus Torvalds } 5651da177e4SLinus Torvalds 566f65e0d29STakashi Iwai /* stop/pause a master timer */ 567f65e0d29STakashi Iwai static int snd_timer_stop1(struct snd_timer_instance *timeri, bool stop) 5681da177e4SLinus Torvalds { 56953d2f744STakashi Iwai struct snd_timer *timer; 570f65e0d29STakashi Iwai int result = 0; 5711da177e4SLinus Torvalds unsigned long flags; 5721da177e4SLinus Torvalds 5731da177e4SLinus Torvalds timer = timeri->timer; 5741da177e4SLinus Torvalds if (!timer) 5751da177e4SLinus Torvalds return -EINVAL; 5761da177e4SLinus Torvalds spin_lock_irqsave(&timer->lock, flags); 577f784beb7STakashi Iwai if (!(timeri->flags & (SNDRV_TIMER_IFLG_RUNNING | 578f784beb7STakashi Iwai SNDRV_TIMER_IFLG_START))) { 579f65e0d29STakashi Iwai result = -EBUSY; 580f65e0d29STakashi Iwai goto unlock; 581f784beb7STakashi Iwai } 5821da177e4SLinus Torvalds list_del_init(&timeri->ack_list); 5831da177e4SLinus Torvalds list_del_init(&timeri->active_list); 584f65e0d29STakashi Iwai if (timer->card && timer->card->shutdown) 585f65e0d29STakashi Iwai goto unlock; 586f65e0d29STakashi Iwai if (stop) { 587f65e0d29STakashi Iwai timeri->cticks = timeri->ticks; 588f65e0d29STakashi Iwai timeri->pticks = 0; 589230323daSTakashi Iwai } 5901da177e4SLinus Torvalds if ((timeri->flags & SNDRV_TIMER_IFLG_RUNNING) && 5911da177e4SLinus Torvalds !(--timer->running)) { 5921da177e4SLinus Torvalds timer->hw.stop(timer); 5931da177e4SLinus Torvalds if (timer->flags & SNDRV_TIMER_FLG_RESCHED) { 5941da177e4SLinus Torvalds timer->flags &= ~SNDRV_TIMER_FLG_RESCHED; 5951da177e4SLinus Torvalds snd_timer_reschedule(timer, 0); 5961da177e4SLinus Torvalds if (timer->flags & SNDRV_TIMER_FLG_CHANGE) { 5971da177e4SLinus Torvalds timer->flags &= ~SNDRV_TIMER_FLG_CHANGE; 5981da177e4SLinus Torvalds timer->hw.start(timer); 5991da177e4SLinus Torvalds } 6001da177e4SLinus Torvalds } 6011da177e4SLinus Torvalds } 602c3b16813STakashi Iwai timeri->flags &= ~(SNDRV_TIMER_IFLG_RUNNING | SNDRV_TIMER_IFLG_START); 6039f8a7658STakashi Iwai if (stop) 6049f8a7658STakashi Iwai timeri->flags &= ~SNDRV_TIMER_IFLG_PAUSED; 6059f8a7658STakashi Iwai else 6069f8a7658STakashi Iwai timeri->flags |= SNDRV_TIMER_IFLG_PAUSED; 607f65e0d29STakashi Iwai snd_timer_notify1(timeri, stop ? SNDRV_TIMER_EVENT_STOP : 6083ae18097SBen Hutchings SNDRV_TIMER_EVENT_PAUSE); 609f65e0d29STakashi Iwai unlock: 6101da177e4SLinus Torvalds spin_unlock_irqrestore(&timer->lock, flags); 611f65e0d29STakashi Iwai return result; 612f65e0d29STakashi Iwai } 613f65e0d29STakashi Iwai 614f65e0d29STakashi Iwai /* stop/pause a slave timer */ 615f65e0d29STakashi Iwai static int snd_timer_stop_slave(struct snd_timer_instance *timeri, bool stop) 616f65e0d29STakashi Iwai { 617f65e0d29STakashi Iwai unsigned long flags; 618f65e0d29STakashi Iwai 619f65e0d29STakashi Iwai spin_lock_irqsave(&slave_active_lock, flags); 620f65e0d29STakashi Iwai if (!(timeri->flags & SNDRV_TIMER_IFLG_RUNNING)) { 621f65e0d29STakashi Iwai spin_unlock_irqrestore(&slave_active_lock, flags); 622f65e0d29STakashi Iwai return -EBUSY; 623f65e0d29STakashi Iwai } 624f65e0d29STakashi Iwai timeri->flags &= ~SNDRV_TIMER_IFLG_RUNNING; 625f65e0d29STakashi Iwai if (timeri->timer) { 626f65e0d29STakashi Iwai spin_lock(&timeri->timer->lock); 627f65e0d29STakashi Iwai list_del_init(&timeri->ack_list); 628f65e0d29STakashi Iwai list_del_init(&timeri->active_list); 629f65e0d29STakashi Iwai snd_timer_notify1(timeri, stop ? SNDRV_TIMER_EVENT_STOP : 6303ae18097SBen Hutchings SNDRV_TIMER_EVENT_PAUSE); 631f65e0d29STakashi Iwai spin_unlock(&timeri->timer->lock); 632f65e0d29STakashi Iwai } 633f65e0d29STakashi Iwai spin_unlock_irqrestore(&slave_active_lock, flags); 6341da177e4SLinus Torvalds return 0; 6351da177e4SLinus Torvalds } 6361da177e4SLinus Torvalds 6371da177e4SLinus Torvalds /* 638f65e0d29STakashi Iwai * start the timer instance 639f65e0d29STakashi Iwai */ 640f65e0d29STakashi Iwai int snd_timer_start(struct snd_timer_instance *timeri, unsigned int ticks) 641f65e0d29STakashi Iwai { 642f65e0d29STakashi Iwai if (timeri == NULL || ticks < 1) 643f65e0d29STakashi Iwai return -EINVAL; 644f65e0d29STakashi Iwai if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) 645f65e0d29STakashi Iwai return snd_timer_start_slave(timeri, true); 646f65e0d29STakashi Iwai else 647f65e0d29STakashi Iwai return snd_timer_start1(timeri, true, ticks); 648f65e0d29STakashi Iwai } 64998856392STakashi Iwai EXPORT_SYMBOL(snd_timer_start); 650f65e0d29STakashi Iwai 651f65e0d29STakashi Iwai /* 6521da177e4SLinus Torvalds * stop the timer instance. 6531da177e4SLinus Torvalds * 6541da177e4SLinus Torvalds * do not call this from the timer callback! 6551da177e4SLinus Torvalds */ 65653d2f744STakashi Iwai int snd_timer_stop(struct snd_timer_instance *timeri) 6571da177e4SLinus Torvalds { 658f65e0d29STakashi Iwai if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) 659f65e0d29STakashi Iwai return snd_timer_stop_slave(timeri, true); 660f65e0d29STakashi Iwai else 661f65e0d29STakashi Iwai return snd_timer_stop1(timeri, true); 6621da177e4SLinus Torvalds } 66398856392STakashi Iwai EXPORT_SYMBOL(snd_timer_stop); 6641da177e4SLinus Torvalds 6651da177e4SLinus Torvalds /* 6661da177e4SLinus Torvalds * start again.. the tick is kept. 6671da177e4SLinus Torvalds */ 66853d2f744STakashi Iwai int snd_timer_continue(struct snd_timer_instance *timeri) 6691da177e4SLinus Torvalds { 6709f8a7658STakashi Iwai /* timer can continue only after pause */ 6719f8a7658STakashi Iwai if (!(timeri->flags & SNDRV_TIMER_IFLG_PAUSED)) 6729f8a7658STakashi Iwai return -EINVAL; 6739f8a7658STakashi Iwai 6741da177e4SLinus Torvalds if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) 675f65e0d29STakashi Iwai return snd_timer_start_slave(timeri, false); 676f65e0d29STakashi Iwai else 677f65e0d29STakashi Iwai return snd_timer_start1(timeri, false, 0); 6781da177e4SLinus Torvalds } 67998856392STakashi Iwai EXPORT_SYMBOL(snd_timer_continue); 6801da177e4SLinus Torvalds 6811da177e4SLinus Torvalds /* 6821da177e4SLinus Torvalds * pause.. remember the ticks left 6831da177e4SLinus Torvalds */ 68453d2f744STakashi Iwai int snd_timer_pause(struct snd_timer_instance * timeri) 6851da177e4SLinus Torvalds { 686f65e0d29STakashi Iwai if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) 687f65e0d29STakashi Iwai return snd_timer_stop_slave(timeri, false); 688f65e0d29STakashi Iwai else 689f65e0d29STakashi Iwai return snd_timer_stop1(timeri, false); 6901da177e4SLinus Torvalds } 69198856392STakashi Iwai EXPORT_SYMBOL(snd_timer_pause); 6921da177e4SLinus Torvalds 6931da177e4SLinus Torvalds /* 6941da177e4SLinus Torvalds * reschedule the timer 6951da177e4SLinus Torvalds * 6961da177e4SLinus Torvalds * start pending instances and check the scheduling ticks. 6971da177e4SLinus Torvalds * when the scheduling ticks is changed set CHANGE flag to reprogram the timer. 6981da177e4SLinus Torvalds */ 69953d2f744STakashi Iwai static void snd_timer_reschedule(struct snd_timer * timer, unsigned long ticks_left) 7001da177e4SLinus Torvalds { 70153d2f744STakashi Iwai struct snd_timer_instance *ti; 7021da177e4SLinus Torvalds unsigned long ticks = ~0UL; 7031da177e4SLinus Torvalds 7049244b2c3SJohannes Berg list_for_each_entry(ti, &timer->active_list_head, active_list) { 7051da177e4SLinus Torvalds if (ti->flags & SNDRV_TIMER_IFLG_START) { 7061da177e4SLinus Torvalds ti->flags &= ~SNDRV_TIMER_IFLG_START; 7071da177e4SLinus Torvalds ti->flags |= SNDRV_TIMER_IFLG_RUNNING; 7081da177e4SLinus Torvalds timer->running++; 7091da177e4SLinus Torvalds } 7101da177e4SLinus Torvalds if (ti->flags & SNDRV_TIMER_IFLG_RUNNING) { 7111da177e4SLinus Torvalds if (ticks > ti->cticks) 7121da177e4SLinus Torvalds ticks = ti->cticks; 7131da177e4SLinus Torvalds } 7141da177e4SLinus Torvalds } 7151da177e4SLinus Torvalds if (ticks == ~0UL) { 7161da177e4SLinus Torvalds timer->flags &= ~SNDRV_TIMER_FLG_RESCHED; 7171da177e4SLinus Torvalds return; 7181da177e4SLinus Torvalds } 7191da177e4SLinus Torvalds if (ticks > timer->hw.ticks) 7201da177e4SLinus Torvalds ticks = timer->hw.ticks; 7211da177e4SLinus Torvalds if (ticks_left != ticks) 7221da177e4SLinus Torvalds timer->flags |= SNDRV_TIMER_FLG_CHANGE; 7231da177e4SLinus Torvalds timer->sticks = ticks; 7241da177e4SLinus Torvalds } 7251da177e4SLinus Torvalds 7268748b850STakashi Iwai /* call callbacks in timer ack list */ 7278748b850STakashi Iwai static void snd_timer_process_callbacks(struct snd_timer *timer, 7288748b850STakashi Iwai struct list_head *head) 7291da177e4SLinus Torvalds { 73053d2f744STakashi Iwai struct snd_timer_instance *ti; 7311da177e4SLinus Torvalds unsigned long resolution, ticks; 7321da177e4SLinus Torvalds 7338748b850STakashi Iwai while (!list_empty(head)) { 7348748b850STakashi Iwai ti = list_first_entry(head, struct snd_timer_instance, 7358748b850STakashi Iwai ack_list); 7361da177e4SLinus Torvalds 7371da177e4SLinus Torvalds /* remove from ack_list and make empty */ 738df55531bSTakashi Iwai list_del_init(&ti->ack_list); 7391da177e4SLinus Torvalds 740fe1b26c9STakashi Iwai if (!(ti->flags & SNDRV_TIMER_IFLG_DEAD)) { 7411da177e4SLinus Torvalds ticks = ti->pticks; 7421da177e4SLinus Torvalds ti->pticks = 0; 7431da177e4SLinus Torvalds resolution = ti->resolution; 7441da177e4SLinus Torvalds ti->flags |= SNDRV_TIMER_IFLG_CALLBACK; 7451da177e4SLinus Torvalds spin_unlock(&timer->lock); 7461da177e4SLinus Torvalds if (ti->callback) 7471da177e4SLinus Torvalds ti->callback(ti, resolution, ticks); 7481da177e4SLinus Torvalds spin_lock(&timer->lock); 7491da177e4SLinus Torvalds ti->flags &= ~SNDRV_TIMER_IFLG_CALLBACK; 7501da177e4SLinus Torvalds } 7511da177e4SLinus Torvalds } 7528748b850STakashi Iwai } 7538748b850STakashi Iwai 7547bb4a8a2STakashi Iwai /* clear pending instances from ack list */ 7557bb4a8a2STakashi Iwai static void snd_timer_clear_callbacks(struct snd_timer *timer, 7567bb4a8a2STakashi Iwai struct list_head *head) 7577bb4a8a2STakashi Iwai { 7587bb4a8a2STakashi Iwai unsigned long flags; 7597bb4a8a2STakashi Iwai 7607bb4a8a2STakashi Iwai spin_lock_irqsave(&timer->lock, flags); 7617bb4a8a2STakashi Iwai while (!list_empty(head)) 7627bb4a8a2STakashi Iwai list_del_init(head->next); 7637bb4a8a2STakashi Iwai spin_unlock_irqrestore(&timer->lock, flags); 7647bb4a8a2STakashi Iwai } 7657bb4a8a2STakashi Iwai 7668748b850STakashi Iwai /* 7678748b850STakashi Iwai * timer tasklet 7688748b850STakashi Iwai * 7698748b850STakashi Iwai */ 7708748b850STakashi Iwai static void snd_timer_tasklet(unsigned long arg) 7718748b850STakashi Iwai { 7728748b850STakashi Iwai struct snd_timer *timer = (struct snd_timer *) arg; 7738748b850STakashi Iwai unsigned long flags; 7748748b850STakashi Iwai 7757bb4a8a2STakashi Iwai if (timer->card && timer->card->shutdown) { 7767bb4a8a2STakashi Iwai snd_timer_clear_callbacks(timer, &timer->sack_list_head); 7778748b850STakashi Iwai return; 7787bb4a8a2STakashi Iwai } 7798748b850STakashi Iwai 7808748b850STakashi Iwai spin_lock_irqsave(&timer->lock, flags); 7818748b850STakashi Iwai snd_timer_process_callbacks(timer, &timer->sack_list_head); 7822999ff5bSTakashi Iwai spin_unlock_irqrestore(&timer->lock, flags); 7831da177e4SLinus Torvalds } 7841da177e4SLinus Torvalds 7851da177e4SLinus Torvalds /* 7861da177e4SLinus Torvalds * timer interrupt 7871da177e4SLinus Torvalds * 7881da177e4SLinus Torvalds * ticks_left is usually equal to timer->sticks. 7891da177e4SLinus Torvalds * 7901da177e4SLinus Torvalds */ 79153d2f744STakashi Iwai void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left) 7921da177e4SLinus Torvalds { 7939244b2c3SJohannes Berg struct snd_timer_instance *ti, *ts, *tmp; 7948748b850STakashi Iwai unsigned long resolution; 7958748b850STakashi Iwai struct list_head *ack_list_head; 796b32425acSTakashi Iwai unsigned long flags; 7971da177e4SLinus Torvalds int use_tasklet = 0; 7981da177e4SLinus Torvalds 7991da177e4SLinus Torvalds if (timer == NULL) 8001da177e4SLinus Torvalds return; 8011da177e4SLinus Torvalds 8027bb4a8a2STakashi Iwai if (timer->card && timer->card->shutdown) { 8037bb4a8a2STakashi Iwai snd_timer_clear_callbacks(timer, &timer->ack_list_head); 804230323daSTakashi Iwai return; 8057bb4a8a2STakashi Iwai } 806230323daSTakashi Iwai 807b32425acSTakashi Iwai spin_lock_irqsave(&timer->lock, flags); 8081da177e4SLinus Torvalds 8091da177e4SLinus Torvalds /* remember the current resolution */ 810fdcb5761STakashi Iwai resolution = snd_timer_hw_resolution(timer); 8111da177e4SLinus Torvalds 8121da177e4SLinus Torvalds /* loop for all active instances 8139244b2c3SJohannes Berg * Here we cannot use list_for_each_entry because the active_list of a 8146b172a85SClemens Ladisch * processed instance is relinked to done_list_head before the callback 8156b172a85SClemens Ladisch * is called. 8161da177e4SLinus Torvalds */ 8179244b2c3SJohannes Berg list_for_each_entry_safe(ti, tmp, &timer->active_list_head, 8189244b2c3SJohannes Berg active_list) { 819fe1b26c9STakashi Iwai if (ti->flags & SNDRV_TIMER_IFLG_DEAD) 820fe1b26c9STakashi Iwai continue; 8211da177e4SLinus Torvalds if (!(ti->flags & SNDRV_TIMER_IFLG_RUNNING)) 8221da177e4SLinus Torvalds continue; 8231da177e4SLinus Torvalds ti->pticks += ticks_left; 8241da177e4SLinus Torvalds ti->resolution = resolution; 8251da177e4SLinus Torvalds if (ti->cticks < ticks_left) 8261da177e4SLinus Torvalds ti->cticks = 0; 8271da177e4SLinus Torvalds else 8281da177e4SLinus Torvalds ti->cticks -= ticks_left; 8291da177e4SLinus Torvalds if (ti->cticks) /* not expired */ 8301da177e4SLinus Torvalds continue; 8311da177e4SLinus Torvalds if (ti->flags & SNDRV_TIMER_IFLG_AUTO) { 8321da177e4SLinus Torvalds ti->cticks = ti->ticks; 8331da177e4SLinus Torvalds } else { 8341da177e4SLinus Torvalds ti->flags &= ~SNDRV_TIMER_IFLG_RUNNING; 835094fd3beSTakashi Iwai --timer->running; 836ee8413b0STakashi Iwai list_del_init(&ti->active_list); 8371da177e4SLinus Torvalds } 8381da177e4SLinus Torvalds if ((timer->hw.flags & SNDRV_TIMER_HW_TASKLET) || 8396b172a85SClemens Ladisch (ti->flags & SNDRV_TIMER_IFLG_FAST)) 8406b172a85SClemens Ladisch ack_list_head = &timer->ack_list_head; 8416b172a85SClemens Ladisch else 8426b172a85SClemens Ladisch ack_list_head = &timer->sack_list_head; 8436b172a85SClemens Ladisch if (list_empty(&ti->ack_list)) 8446b172a85SClemens Ladisch list_add_tail(&ti->ack_list, ack_list_head); 8459244b2c3SJohannes Berg list_for_each_entry(ts, &ti->slave_active_head, active_list) { 8461da177e4SLinus Torvalds ts->pticks = ti->pticks; 8471da177e4SLinus Torvalds ts->resolution = resolution; 8486b172a85SClemens Ladisch if (list_empty(&ts->ack_list)) 8496b172a85SClemens Ladisch list_add_tail(&ts->ack_list, ack_list_head); 8501da177e4SLinus Torvalds } 8511da177e4SLinus Torvalds } 8521da177e4SLinus Torvalds if (timer->flags & SNDRV_TIMER_FLG_RESCHED) 853cd93fe47SClemens Ladisch snd_timer_reschedule(timer, timer->sticks); 8541da177e4SLinus Torvalds if (timer->running) { 8551da177e4SLinus Torvalds if (timer->hw.flags & SNDRV_TIMER_HW_STOP) { 8561da177e4SLinus Torvalds timer->hw.stop(timer); 8571da177e4SLinus Torvalds timer->flags |= SNDRV_TIMER_FLG_CHANGE; 8581da177e4SLinus Torvalds } 8591da177e4SLinus Torvalds if (!(timer->hw.flags & SNDRV_TIMER_HW_AUTO) || 8601da177e4SLinus Torvalds (timer->flags & SNDRV_TIMER_FLG_CHANGE)) { 8611da177e4SLinus Torvalds /* restart timer */ 8621da177e4SLinus Torvalds timer->flags &= ~SNDRV_TIMER_FLG_CHANGE; 8631da177e4SLinus Torvalds timer->hw.start(timer); 8641da177e4SLinus Torvalds } 8651da177e4SLinus Torvalds } else { 8661da177e4SLinus Torvalds timer->hw.stop(timer); 8671da177e4SLinus Torvalds } 8681da177e4SLinus Torvalds 8691da177e4SLinus Torvalds /* now process all fast callbacks */ 8708748b850STakashi Iwai snd_timer_process_callbacks(timer, &timer->ack_list_head); 8711da177e4SLinus Torvalds 8721da177e4SLinus Torvalds /* do we have any slow callbacks? */ 8731da177e4SLinus Torvalds use_tasklet = !list_empty(&timer->sack_list_head); 874b32425acSTakashi Iwai spin_unlock_irqrestore(&timer->lock, flags); 8751da177e4SLinus Torvalds 8761da177e4SLinus Torvalds if (use_tasklet) 8771f04128aSTakashi Iwai tasklet_schedule(&timer->task_queue); 8781da177e4SLinus Torvalds } 87998856392STakashi Iwai EXPORT_SYMBOL(snd_timer_interrupt); 8801da177e4SLinus Torvalds 8811da177e4SLinus Torvalds /* 8821da177e4SLinus Torvalds 8831da177e4SLinus Torvalds */ 8841da177e4SLinus Torvalds 88553d2f744STakashi Iwai int snd_timer_new(struct snd_card *card, char *id, struct snd_timer_id *tid, 88653d2f744STakashi Iwai struct snd_timer **rtimer) 8871da177e4SLinus Torvalds { 88853d2f744STakashi Iwai struct snd_timer *timer; 8891da177e4SLinus Torvalds int err; 89053d2f744STakashi Iwai static struct snd_device_ops ops = { 8911da177e4SLinus Torvalds .dev_free = snd_timer_dev_free, 8921da177e4SLinus Torvalds .dev_register = snd_timer_dev_register, 893c461482cSTakashi Iwai .dev_disconnect = snd_timer_dev_disconnect, 8941da177e4SLinus Torvalds }; 8951da177e4SLinus Torvalds 8967eaa943cSTakashi Iwai if (snd_BUG_ON(!tid)) 8977eaa943cSTakashi Iwai return -EINVAL; 898d10ee9c5SSrikanth K H if (tid->dev_class == SNDRV_TIMER_CLASS_CARD || 899d10ee9c5SSrikanth K H tid->dev_class == SNDRV_TIMER_CLASS_PCM) { 900d10ee9c5SSrikanth K H if (WARN_ON(!card)) 901d10ee9c5SSrikanth K H return -EINVAL; 902d10ee9c5SSrikanth K H } 9037eaa943cSTakashi Iwai if (rtimer) 9041da177e4SLinus Torvalds *rtimer = NULL; 905ca2c0966STakashi Iwai timer = kzalloc(sizeof(*timer), GFP_KERNEL); 906ec0e9937STakashi Iwai if (!timer) 9071da177e4SLinus Torvalds return -ENOMEM; 9081da177e4SLinus Torvalds timer->tmr_class = tid->dev_class; 9091da177e4SLinus Torvalds timer->card = card; 9101da177e4SLinus Torvalds timer->tmr_device = tid->device; 9111da177e4SLinus Torvalds timer->tmr_subdevice = tid->subdevice; 9121da177e4SLinus Torvalds if (id) 9131da177e4SLinus Torvalds strlcpy(timer->id, id, sizeof(timer->id)); 9146b760bb2SVegard Nossum timer->sticks = 1; 9151da177e4SLinus Torvalds INIT_LIST_HEAD(&timer->device_list); 9161da177e4SLinus Torvalds INIT_LIST_HEAD(&timer->open_list_head); 9171da177e4SLinus Torvalds INIT_LIST_HEAD(&timer->active_list_head); 9181da177e4SLinus Torvalds INIT_LIST_HEAD(&timer->ack_list_head); 9191da177e4SLinus Torvalds INIT_LIST_HEAD(&timer->sack_list_head); 9201da177e4SLinus Torvalds spin_lock_init(&timer->lock); 9216b172a85SClemens Ladisch tasklet_init(&timer->task_queue, snd_timer_tasklet, 9226b172a85SClemens Ladisch (unsigned long)timer); 9239b7d869eSTakashi Iwai timer->max_instances = 1000; /* default limit per timer */ 9241da177e4SLinus Torvalds if (card != NULL) { 925de24214dSClemens Ladisch timer->module = card->module; 9266b172a85SClemens Ladisch err = snd_device_new(card, SNDRV_DEV_TIMER, timer, &ops); 9276b172a85SClemens Ladisch if (err < 0) { 9281da177e4SLinus Torvalds snd_timer_free(timer); 9291da177e4SLinus Torvalds return err; 9301da177e4SLinus Torvalds } 9311da177e4SLinus Torvalds } 9327eaa943cSTakashi Iwai if (rtimer) 9331da177e4SLinus Torvalds *rtimer = timer; 9341da177e4SLinus Torvalds return 0; 9351da177e4SLinus Torvalds } 93698856392STakashi Iwai EXPORT_SYMBOL(snd_timer_new); 9371da177e4SLinus Torvalds 93853d2f744STakashi Iwai static int snd_timer_free(struct snd_timer *timer) 9391da177e4SLinus Torvalds { 9407eaa943cSTakashi Iwai if (!timer) 9417eaa943cSTakashi Iwai return 0; 942c461482cSTakashi Iwai 943c461482cSTakashi Iwai mutex_lock(®ister_mutex); 944c461482cSTakashi Iwai if (! list_empty(&timer->open_list_head)) { 945c461482cSTakashi Iwai struct list_head *p, *n; 946c461482cSTakashi Iwai struct snd_timer_instance *ti; 947cf74dcf3STakashi Iwai pr_warn("ALSA: timer %p is busy?\n", timer); 948c461482cSTakashi Iwai list_for_each_safe(p, n, &timer->open_list_head) { 949c461482cSTakashi Iwai list_del_init(p); 950c461482cSTakashi Iwai ti = list_entry(p, struct snd_timer_instance, open_list); 951c461482cSTakashi Iwai ti->timer = NULL; 952c461482cSTakashi Iwai } 953c461482cSTakashi Iwai } 954c461482cSTakashi Iwai list_del(&timer->device_list); 955c461482cSTakashi Iwai mutex_unlock(®ister_mutex); 956c461482cSTakashi Iwai 9571da177e4SLinus Torvalds if (timer->private_free) 9581da177e4SLinus Torvalds timer->private_free(timer); 9591da177e4SLinus Torvalds kfree(timer); 9601da177e4SLinus Torvalds return 0; 9611da177e4SLinus Torvalds } 9621da177e4SLinus Torvalds 96353d2f744STakashi Iwai static int snd_timer_dev_free(struct snd_device *device) 9641da177e4SLinus Torvalds { 96553d2f744STakashi Iwai struct snd_timer *timer = device->device_data; 9661da177e4SLinus Torvalds return snd_timer_free(timer); 9671da177e4SLinus Torvalds } 9681da177e4SLinus Torvalds 96953d2f744STakashi Iwai static int snd_timer_dev_register(struct snd_device *dev) 9701da177e4SLinus Torvalds { 97153d2f744STakashi Iwai struct snd_timer *timer = dev->device_data; 97253d2f744STakashi Iwai struct snd_timer *timer1; 9731da177e4SLinus Torvalds 9747eaa943cSTakashi Iwai if (snd_BUG_ON(!timer || !timer->hw.start || !timer->hw.stop)) 9757eaa943cSTakashi Iwai return -ENXIO; 9761da177e4SLinus Torvalds if (!(timer->hw.flags & SNDRV_TIMER_HW_SLAVE) && 9771da177e4SLinus Torvalds !timer->hw.resolution && timer->hw.c_resolution == NULL) 9781da177e4SLinus Torvalds return -EINVAL; 9791da177e4SLinus Torvalds 9801a60d4c5SIngo Molnar mutex_lock(®ister_mutex); 9819244b2c3SJohannes Berg list_for_each_entry(timer1, &snd_timer_list, device_list) { 9821da177e4SLinus Torvalds if (timer1->tmr_class > timer->tmr_class) 9831da177e4SLinus Torvalds break; 9841da177e4SLinus Torvalds if (timer1->tmr_class < timer->tmr_class) 9851da177e4SLinus Torvalds continue; 9861da177e4SLinus Torvalds if (timer1->card && timer->card) { 9871da177e4SLinus Torvalds if (timer1->card->number > timer->card->number) 9881da177e4SLinus Torvalds break; 9891da177e4SLinus Torvalds if (timer1->card->number < timer->card->number) 9901da177e4SLinus Torvalds continue; 9911da177e4SLinus Torvalds } 9921da177e4SLinus Torvalds if (timer1->tmr_device > timer->tmr_device) 9931da177e4SLinus Torvalds break; 9941da177e4SLinus Torvalds if (timer1->tmr_device < timer->tmr_device) 9951da177e4SLinus Torvalds continue; 9961da177e4SLinus Torvalds if (timer1->tmr_subdevice > timer->tmr_subdevice) 9971da177e4SLinus Torvalds break; 9981da177e4SLinus Torvalds if (timer1->tmr_subdevice < timer->tmr_subdevice) 9991da177e4SLinus Torvalds continue; 10001da177e4SLinus Torvalds /* conflicts.. */ 10011a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 10021da177e4SLinus Torvalds return -EBUSY; 10031da177e4SLinus Torvalds } 10049244b2c3SJohannes Berg list_add_tail(&timer->device_list, &timer1->device_list); 10051a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 10061da177e4SLinus Torvalds return 0; 10071da177e4SLinus Torvalds } 10081da177e4SLinus Torvalds 1009c461482cSTakashi Iwai static int snd_timer_dev_disconnect(struct snd_device *device) 10101da177e4SLinus Torvalds { 101153d2f744STakashi Iwai struct snd_timer *timer = device->device_data; 1012230323daSTakashi Iwai struct snd_timer_instance *ti; 1013230323daSTakashi Iwai 1014c461482cSTakashi Iwai mutex_lock(®ister_mutex); 1015c461482cSTakashi Iwai list_del_init(&timer->device_list); 1016230323daSTakashi Iwai /* wake up pending sleepers */ 1017230323daSTakashi Iwai list_for_each_entry(ti, &timer->open_list_head, open_list) { 101840ed9444STakashi Iwai if (ti->disconnect) 101940ed9444STakashi Iwai ti->disconnect(ti); 1020230323daSTakashi Iwai } 1021c461482cSTakashi Iwai mutex_unlock(®ister_mutex); 1022c461482cSTakashi Iwai return 0; 10231da177e4SLinus Torvalds } 10241da177e4SLinus Torvalds 102553d2f744STakashi Iwai void snd_timer_notify(struct snd_timer *timer, int event, struct timespec *tstamp) 10261da177e4SLinus Torvalds { 10271da177e4SLinus Torvalds unsigned long flags; 10281da177e4SLinus Torvalds unsigned long resolution = 0; 102953d2f744STakashi Iwai struct snd_timer_instance *ti, *ts; 10301da177e4SLinus Torvalds 1031230323daSTakashi Iwai if (timer->card && timer->card->shutdown) 1032230323daSTakashi Iwai return; 10337c22f1aaSTakashi Iwai if (! (timer->hw.flags & SNDRV_TIMER_HW_SLAVE)) 10347c22f1aaSTakashi Iwai return; 10357eaa943cSTakashi Iwai if (snd_BUG_ON(event < SNDRV_TIMER_EVENT_MSTART || 10367eaa943cSTakashi Iwai event > SNDRV_TIMER_EVENT_MRESUME)) 10377eaa943cSTakashi Iwai return; 10381da177e4SLinus Torvalds spin_lock_irqsave(&timer->lock, flags); 1039a501dfa3SJaroslav Kysela if (event == SNDRV_TIMER_EVENT_MSTART || 1040a501dfa3SJaroslav Kysela event == SNDRV_TIMER_EVENT_MCONTINUE || 1041fdcb5761STakashi Iwai event == SNDRV_TIMER_EVENT_MRESUME) 1042fdcb5761STakashi Iwai resolution = snd_timer_hw_resolution(timer); 10439244b2c3SJohannes Berg list_for_each_entry(ti, &timer->active_list_head, active_list) { 10441da177e4SLinus Torvalds if (ti->ccallback) 10451da177e4SLinus Torvalds ti->ccallback(ti, event, tstamp, resolution); 10469244b2c3SJohannes Berg list_for_each_entry(ts, &ti->slave_active_head, active_list) 10471da177e4SLinus Torvalds if (ts->ccallback) 10481da177e4SLinus Torvalds ts->ccallback(ts, event, tstamp, resolution); 10491da177e4SLinus Torvalds } 10501da177e4SLinus Torvalds spin_unlock_irqrestore(&timer->lock, flags); 10511da177e4SLinus Torvalds } 105298856392STakashi Iwai EXPORT_SYMBOL(snd_timer_notify); 10531da177e4SLinus Torvalds 10541da177e4SLinus Torvalds /* 10551da177e4SLinus Torvalds * exported functions for global timers 10561da177e4SLinus Torvalds */ 105753d2f744STakashi Iwai int snd_timer_global_new(char *id, int device, struct snd_timer **rtimer) 10581da177e4SLinus Torvalds { 105953d2f744STakashi Iwai struct snd_timer_id tid; 10601da177e4SLinus Torvalds 10611da177e4SLinus Torvalds tid.dev_class = SNDRV_TIMER_CLASS_GLOBAL; 10621da177e4SLinus Torvalds tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE; 10631da177e4SLinus Torvalds tid.card = -1; 10641da177e4SLinus Torvalds tid.device = device; 10651da177e4SLinus Torvalds tid.subdevice = 0; 10661da177e4SLinus Torvalds return snd_timer_new(NULL, id, &tid, rtimer); 10671da177e4SLinus Torvalds } 106898856392STakashi Iwai EXPORT_SYMBOL(snd_timer_global_new); 10691da177e4SLinus Torvalds 107053d2f744STakashi Iwai int snd_timer_global_free(struct snd_timer *timer) 10711da177e4SLinus Torvalds { 10721da177e4SLinus Torvalds return snd_timer_free(timer); 10731da177e4SLinus Torvalds } 107498856392STakashi Iwai EXPORT_SYMBOL(snd_timer_global_free); 10751da177e4SLinus Torvalds 107653d2f744STakashi Iwai int snd_timer_global_register(struct snd_timer *timer) 10771da177e4SLinus Torvalds { 107853d2f744STakashi Iwai struct snd_device dev; 10791da177e4SLinus Torvalds 10801da177e4SLinus Torvalds memset(&dev, 0, sizeof(dev)); 10811da177e4SLinus Torvalds dev.device_data = timer; 10821da177e4SLinus Torvalds return snd_timer_dev_register(&dev); 10831da177e4SLinus Torvalds } 108498856392STakashi Iwai EXPORT_SYMBOL(snd_timer_global_register); 10851da177e4SLinus Torvalds 10861da177e4SLinus Torvalds /* 10871da177e4SLinus Torvalds * System timer 10881da177e4SLinus Torvalds */ 10891da177e4SLinus Torvalds 10901da177e4SLinus Torvalds struct snd_timer_system_private { 10911da177e4SLinus Torvalds struct timer_list tlist; 109238e9a80fSKees Cook struct snd_timer *snd_timer; 10931da177e4SLinus Torvalds unsigned long last_expires; 10941da177e4SLinus Torvalds unsigned long last_jiffies; 10951da177e4SLinus Torvalds unsigned long correction; 10961da177e4SLinus Torvalds }; 10971da177e4SLinus Torvalds 109838e9a80fSKees Cook static void snd_timer_s_function(struct timer_list *t) 10991da177e4SLinus Torvalds { 110038e9a80fSKees Cook struct snd_timer_system_private *priv = from_timer(priv, t, 110138e9a80fSKees Cook tlist); 110238e9a80fSKees Cook struct snd_timer *timer = priv->snd_timer; 11031da177e4SLinus Torvalds unsigned long jiff = jiffies; 11041da177e4SLinus Torvalds if (time_after(jiff, priv->last_expires)) 11056ed5eff0SClemens Ladisch priv->correction += (long)jiff - (long)priv->last_expires; 11061da177e4SLinus Torvalds snd_timer_interrupt(timer, (long)jiff - (long)priv->last_jiffies); 11071da177e4SLinus Torvalds } 11081da177e4SLinus Torvalds 110953d2f744STakashi Iwai static int snd_timer_s_start(struct snd_timer * timer) 11101da177e4SLinus Torvalds { 11111da177e4SLinus Torvalds struct snd_timer_system_private *priv; 11121da177e4SLinus Torvalds unsigned long njiff; 11131da177e4SLinus Torvalds 11141da177e4SLinus Torvalds priv = (struct snd_timer_system_private *) timer->private_data; 11151da177e4SLinus Torvalds njiff = (priv->last_jiffies = jiffies); 11161da177e4SLinus Torvalds if (priv->correction > timer->sticks - 1) { 11171da177e4SLinus Torvalds priv->correction -= timer->sticks - 1; 11181da177e4SLinus Torvalds njiff++; 11191da177e4SLinus Torvalds } else { 11201da177e4SLinus Torvalds njiff += timer->sticks - priv->correction; 112117f48ec3SClemens Ladisch priv->correction = 0; 11221da177e4SLinus Torvalds } 11234a07083eSTakashi Iwai priv->last_expires = njiff; 11244a07083eSTakashi Iwai mod_timer(&priv->tlist, njiff); 11251da177e4SLinus Torvalds return 0; 11261da177e4SLinus Torvalds } 11271da177e4SLinus Torvalds 112853d2f744STakashi Iwai static int snd_timer_s_stop(struct snd_timer * timer) 11291da177e4SLinus Torvalds { 11301da177e4SLinus Torvalds struct snd_timer_system_private *priv; 11311da177e4SLinus Torvalds unsigned long jiff; 11321da177e4SLinus Torvalds 11331da177e4SLinus Torvalds priv = (struct snd_timer_system_private *) timer->private_data; 11341da177e4SLinus Torvalds del_timer(&priv->tlist); 11351da177e4SLinus Torvalds jiff = jiffies; 11361da177e4SLinus Torvalds if (time_before(jiff, priv->last_expires)) 11371da177e4SLinus Torvalds timer->sticks = priv->last_expires - jiff; 11381da177e4SLinus Torvalds else 11391da177e4SLinus Torvalds timer->sticks = 1; 1140de2696d8SClemens Ladisch priv->correction = 0; 11411da177e4SLinus Torvalds return 0; 11421da177e4SLinus Torvalds } 11431da177e4SLinus Torvalds 1144f146357fSTakashi Iwai static int snd_timer_s_close(struct snd_timer *timer) 1145f146357fSTakashi Iwai { 1146f146357fSTakashi Iwai struct snd_timer_system_private *priv; 1147f146357fSTakashi Iwai 1148f146357fSTakashi Iwai priv = (struct snd_timer_system_private *)timer->private_data; 1149f146357fSTakashi Iwai del_timer_sync(&priv->tlist); 1150f146357fSTakashi Iwai return 0; 1151f146357fSTakashi Iwai } 1152f146357fSTakashi Iwai 115353d2f744STakashi Iwai static struct snd_timer_hardware snd_timer_system = 11541da177e4SLinus Torvalds { 11551da177e4SLinus Torvalds .flags = SNDRV_TIMER_HW_FIRST | SNDRV_TIMER_HW_TASKLET, 11561da177e4SLinus Torvalds .resolution = 1000000000L / HZ, 11571da177e4SLinus Torvalds .ticks = 10000000L, 1158f146357fSTakashi Iwai .close = snd_timer_s_close, 11591da177e4SLinus Torvalds .start = snd_timer_s_start, 11601da177e4SLinus Torvalds .stop = snd_timer_s_stop 11611da177e4SLinus Torvalds }; 11621da177e4SLinus Torvalds 116353d2f744STakashi Iwai static void snd_timer_free_system(struct snd_timer *timer) 11641da177e4SLinus Torvalds { 11651da177e4SLinus Torvalds kfree(timer->private_data); 11661da177e4SLinus Torvalds } 11671da177e4SLinus Torvalds 11681da177e4SLinus Torvalds static int snd_timer_register_system(void) 11691da177e4SLinus Torvalds { 117053d2f744STakashi Iwai struct snd_timer *timer; 11711da177e4SLinus Torvalds struct snd_timer_system_private *priv; 11721da177e4SLinus Torvalds int err; 11731da177e4SLinus Torvalds 11746b172a85SClemens Ladisch err = snd_timer_global_new("system", SNDRV_TIMER_GLOBAL_SYSTEM, &timer); 11756b172a85SClemens Ladisch if (err < 0) 11761da177e4SLinus Torvalds return err; 11771da177e4SLinus Torvalds strcpy(timer->name, "system timer"); 11781da177e4SLinus Torvalds timer->hw = snd_timer_system; 1179ca2c0966STakashi Iwai priv = kzalloc(sizeof(*priv), GFP_KERNEL); 11801da177e4SLinus Torvalds if (priv == NULL) { 11811da177e4SLinus Torvalds snd_timer_free(timer); 11821da177e4SLinus Torvalds return -ENOMEM; 11831da177e4SLinus Torvalds } 118438e9a80fSKees Cook priv->snd_timer = timer; 118538e9a80fSKees Cook timer_setup(&priv->tlist, snd_timer_s_function, 0); 11861da177e4SLinus Torvalds timer->private_data = priv; 11871da177e4SLinus Torvalds timer->private_free = snd_timer_free_system; 11881da177e4SLinus Torvalds return snd_timer_global_register(timer); 11891da177e4SLinus Torvalds } 11901da177e4SLinus Torvalds 1191cd6a6503SJie Yang #ifdef CONFIG_SND_PROC_FS 11921da177e4SLinus Torvalds /* 11931da177e4SLinus Torvalds * Info interface 11941da177e4SLinus Torvalds */ 11951da177e4SLinus Torvalds 119653d2f744STakashi Iwai static void snd_timer_proc_read(struct snd_info_entry *entry, 119753d2f744STakashi Iwai struct snd_info_buffer *buffer) 11981da177e4SLinus Torvalds { 119953d2f744STakashi Iwai struct snd_timer *timer; 120053d2f744STakashi Iwai struct snd_timer_instance *ti; 12011da177e4SLinus Torvalds 12021a60d4c5SIngo Molnar mutex_lock(®ister_mutex); 12039244b2c3SJohannes Berg list_for_each_entry(timer, &snd_timer_list, device_list) { 1204230323daSTakashi Iwai if (timer->card && timer->card->shutdown) 1205230323daSTakashi Iwai continue; 12061da177e4SLinus Torvalds switch (timer->tmr_class) { 12071da177e4SLinus Torvalds case SNDRV_TIMER_CLASS_GLOBAL: 12081da177e4SLinus Torvalds snd_iprintf(buffer, "G%i: ", timer->tmr_device); 12091da177e4SLinus Torvalds break; 12101da177e4SLinus Torvalds case SNDRV_TIMER_CLASS_CARD: 12116b172a85SClemens Ladisch snd_iprintf(buffer, "C%i-%i: ", 12126b172a85SClemens Ladisch timer->card->number, timer->tmr_device); 12131da177e4SLinus Torvalds break; 12141da177e4SLinus Torvalds case SNDRV_TIMER_CLASS_PCM: 12156b172a85SClemens Ladisch snd_iprintf(buffer, "P%i-%i-%i: ", timer->card->number, 12166b172a85SClemens Ladisch timer->tmr_device, timer->tmr_subdevice); 12171da177e4SLinus Torvalds break; 12181da177e4SLinus Torvalds default: 12196b172a85SClemens Ladisch snd_iprintf(buffer, "?%i-%i-%i-%i: ", timer->tmr_class, 12206b172a85SClemens Ladisch timer->card ? timer->card->number : -1, 12216b172a85SClemens Ladisch timer->tmr_device, timer->tmr_subdevice); 12221da177e4SLinus Torvalds } 12231da177e4SLinus Torvalds snd_iprintf(buffer, "%s :", timer->name); 12241da177e4SLinus Torvalds if (timer->hw.resolution) 12256b172a85SClemens Ladisch snd_iprintf(buffer, " %lu.%03luus (%lu ticks)", 12266b172a85SClemens Ladisch timer->hw.resolution / 1000, 12276b172a85SClemens Ladisch timer->hw.resolution % 1000, 12286b172a85SClemens Ladisch timer->hw.ticks); 12291da177e4SLinus Torvalds if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE) 12301da177e4SLinus Torvalds snd_iprintf(buffer, " SLAVE"); 12311da177e4SLinus Torvalds snd_iprintf(buffer, "\n"); 12329244b2c3SJohannes Berg list_for_each_entry(ti, &timer->open_list_head, open_list) 12336b172a85SClemens Ladisch snd_iprintf(buffer, " Client %s : %s\n", 12341da177e4SLinus Torvalds ti->owner ? ti->owner : "unknown", 12356b172a85SClemens Ladisch ti->flags & (SNDRV_TIMER_IFLG_START | 12366b172a85SClemens Ladisch SNDRV_TIMER_IFLG_RUNNING) 12376b172a85SClemens Ladisch ? "running" : "stopped"); 12381da177e4SLinus Torvalds } 12391a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 12401da177e4SLinus Torvalds } 12411da177e4SLinus Torvalds 12426581f4e7STakashi Iwai static struct snd_info_entry *snd_timer_proc_entry; 1243e28563ccSTakashi Iwai 1244e28563ccSTakashi Iwai static void __init snd_timer_proc_init(void) 1245e28563ccSTakashi Iwai { 1246e28563ccSTakashi Iwai struct snd_info_entry *entry; 1247e28563ccSTakashi Iwai 1248e28563ccSTakashi Iwai entry = snd_info_create_module_entry(THIS_MODULE, "timers", NULL); 1249e28563ccSTakashi Iwai if (entry != NULL) { 1250e28563ccSTakashi Iwai entry->c.text.read = snd_timer_proc_read; 1251e28563ccSTakashi Iwai if (snd_info_register(entry) < 0) { 1252e28563ccSTakashi Iwai snd_info_free_entry(entry); 1253e28563ccSTakashi Iwai entry = NULL; 1254e28563ccSTakashi Iwai } 1255e28563ccSTakashi Iwai } 1256e28563ccSTakashi Iwai snd_timer_proc_entry = entry; 1257e28563ccSTakashi Iwai } 1258e28563ccSTakashi Iwai 1259e28563ccSTakashi Iwai static void __exit snd_timer_proc_done(void) 1260e28563ccSTakashi Iwai { 1261746d4a02STakashi Iwai snd_info_free_entry(snd_timer_proc_entry); 1262e28563ccSTakashi Iwai } 1263cd6a6503SJie Yang #else /* !CONFIG_SND_PROC_FS */ 1264e28563ccSTakashi Iwai #define snd_timer_proc_init() 1265e28563ccSTakashi Iwai #define snd_timer_proc_done() 1266e28563ccSTakashi Iwai #endif 1267e28563ccSTakashi Iwai 12681da177e4SLinus Torvalds /* 12691da177e4SLinus Torvalds * USER SPACE interface 12701da177e4SLinus Torvalds */ 12711da177e4SLinus Torvalds 127253d2f744STakashi Iwai static void snd_timer_user_interrupt(struct snd_timer_instance *timeri, 12731da177e4SLinus Torvalds unsigned long resolution, 12741da177e4SLinus Torvalds unsigned long ticks) 12751da177e4SLinus Torvalds { 127653d2f744STakashi Iwai struct snd_timer_user *tu = timeri->callback_data; 127753d2f744STakashi Iwai struct snd_timer_read *r; 12781da177e4SLinus Torvalds int prev; 12791da177e4SLinus Torvalds 12801da177e4SLinus Torvalds spin_lock(&tu->qlock); 12811da177e4SLinus Torvalds if (tu->qused > 0) { 12821da177e4SLinus Torvalds prev = tu->qtail == 0 ? tu->queue_size - 1 : tu->qtail - 1; 12831da177e4SLinus Torvalds r = &tu->queue[prev]; 12841da177e4SLinus Torvalds if (r->resolution == resolution) { 12851da177e4SLinus Torvalds r->ticks += ticks; 12861da177e4SLinus Torvalds goto __wake; 12871da177e4SLinus Torvalds } 12881da177e4SLinus Torvalds } 12891da177e4SLinus Torvalds if (tu->qused >= tu->queue_size) { 12901da177e4SLinus Torvalds tu->overrun++; 12911da177e4SLinus Torvalds } else { 12921da177e4SLinus Torvalds r = &tu->queue[tu->qtail++]; 12931da177e4SLinus Torvalds tu->qtail %= tu->queue_size; 12941da177e4SLinus Torvalds r->resolution = resolution; 12951da177e4SLinus Torvalds r->ticks = ticks; 12961da177e4SLinus Torvalds tu->qused++; 12971da177e4SLinus Torvalds } 12981da177e4SLinus Torvalds __wake: 12991da177e4SLinus Torvalds spin_unlock(&tu->qlock); 13001da177e4SLinus Torvalds kill_fasync(&tu->fasync, SIGIO, POLL_IN); 13011da177e4SLinus Torvalds wake_up(&tu->qchange_sleep); 13021da177e4SLinus Torvalds } 13031da177e4SLinus Torvalds 130453d2f744STakashi Iwai static void snd_timer_user_append_to_tqueue(struct snd_timer_user *tu, 130553d2f744STakashi Iwai struct snd_timer_tread *tread) 13061da177e4SLinus Torvalds { 13071da177e4SLinus Torvalds if (tu->qused >= tu->queue_size) { 13081da177e4SLinus Torvalds tu->overrun++; 13091da177e4SLinus Torvalds } else { 13101da177e4SLinus Torvalds memcpy(&tu->tqueue[tu->qtail++], tread, sizeof(*tread)); 13111da177e4SLinus Torvalds tu->qtail %= tu->queue_size; 13121da177e4SLinus Torvalds tu->qused++; 13131da177e4SLinus Torvalds } 13141da177e4SLinus Torvalds } 13151da177e4SLinus Torvalds 131653d2f744STakashi Iwai static void snd_timer_user_ccallback(struct snd_timer_instance *timeri, 131753d2f744STakashi Iwai int event, 13181da177e4SLinus Torvalds struct timespec *tstamp, 13191da177e4SLinus Torvalds unsigned long resolution) 13201da177e4SLinus Torvalds { 132153d2f744STakashi Iwai struct snd_timer_user *tu = timeri->callback_data; 132253d2f744STakashi Iwai struct snd_timer_tread r1; 1323bfe70783SDan Carpenter unsigned long flags; 13241da177e4SLinus Torvalds 13256b172a85SClemens Ladisch if (event >= SNDRV_TIMER_EVENT_START && 13266b172a85SClemens Ladisch event <= SNDRV_TIMER_EVENT_PAUSE) 13271da177e4SLinus Torvalds tu->tstamp = *tstamp; 13281da177e4SLinus Torvalds if ((tu->filter & (1 << event)) == 0 || !tu->tread) 13291da177e4SLinus Torvalds return; 13309a47e9cfSKangjie Lu memset(&r1, 0, sizeof(r1)); 13311da177e4SLinus Torvalds r1.event = event; 13321da177e4SLinus Torvalds r1.tstamp = *tstamp; 13331da177e4SLinus Torvalds r1.val = resolution; 1334bfe70783SDan Carpenter spin_lock_irqsave(&tu->qlock, flags); 13351da177e4SLinus Torvalds snd_timer_user_append_to_tqueue(tu, &r1); 1336bfe70783SDan Carpenter spin_unlock_irqrestore(&tu->qlock, flags); 13371da177e4SLinus Torvalds kill_fasync(&tu->fasync, SIGIO, POLL_IN); 13381da177e4SLinus Torvalds wake_up(&tu->qchange_sleep); 13391da177e4SLinus Torvalds } 13401da177e4SLinus Torvalds 134140ed9444STakashi Iwai static void snd_timer_user_disconnect(struct snd_timer_instance *timeri) 134240ed9444STakashi Iwai { 134340ed9444STakashi Iwai struct snd_timer_user *tu = timeri->callback_data; 134440ed9444STakashi Iwai 134540ed9444STakashi Iwai tu->disconnected = true; 134640ed9444STakashi Iwai wake_up(&tu->qchange_sleep); 134740ed9444STakashi Iwai } 134840ed9444STakashi Iwai 134953d2f744STakashi Iwai static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri, 13501da177e4SLinus Torvalds unsigned long resolution, 13511da177e4SLinus Torvalds unsigned long ticks) 13521da177e4SLinus Torvalds { 135353d2f744STakashi Iwai struct snd_timer_user *tu = timeri->callback_data; 135453d2f744STakashi Iwai struct snd_timer_tread *r, r1; 13551da177e4SLinus Torvalds struct timespec tstamp; 13561da177e4SLinus Torvalds int prev, append = 0; 13571da177e4SLinus Torvalds 1358a8c006aaSDan Carpenter memset(&r1, 0, sizeof(r1)); 135907799e75STakashi Iwai memset(&tstamp, 0, sizeof(tstamp)); 13601da177e4SLinus Torvalds spin_lock(&tu->qlock); 13616b172a85SClemens Ladisch if ((tu->filter & ((1 << SNDRV_TIMER_EVENT_RESOLUTION) | 13626b172a85SClemens Ladisch (1 << SNDRV_TIMER_EVENT_TICK))) == 0) { 13631da177e4SLinus Torvalds spin_unlock(&tu->qlock); 13641da177e4SLinus Torvalds return; 13651da177e4SLinus Torvalds } 1366b751eef1SJaroslav Kysela if (tu->last_resolution != resolution || ticks > 0) { 1367b751eef1SJaroslav Kysela if (timer_tstamp_monotonic) 136826204e04SThomas Gleixner ktime_get_ts(&tstamp); 1369b751eef1SJaroslav Kysela else 137007799e75STakashi Iwai getnstimeofday(&tstamp); 1371b751eef1SJaroslav Kysela } 13726b172a85SClemens Ladisch if ((tu->filter & (1 << SNDRV_TIMER_EVENT_RESOLUTION)) && 13736b172a85SClemens Ladisch tu->last_resolution != resolution) { 13741da177e4SLinus Torvalds r1.event = SNDRV_TIMER_EVENT_RESOLUTION; 13751da177e4SLinus Torvalds r1.tstamp = tstamp; 13761da177e4SLinus Torvalds r1.val = resolution; 13771da177e4SLinus Torvalds snd_timer_user_append_to_tqueue(tu, &r1); 13781da177e4SLinus Torvalds tu->last_resolution = resolution; 13791da177e4SLinus Torvalds append++; 13801da177e4SLinus Torvalds } 13811da177e4SLinus Torvalds if ((tu->filter & (1 << SNDRV_TIMER_EVENT_TICK)) == 0) 13821da177e4SLinus Torvalds goto __wake; 13831da177e4SLinus Torvalds if (ticks == 0) 13841da177e4SLinus Torvalds goto __wake; 13851da177e4SLinus Torvalds if (tu->qused > 0) { 13861da177e4SLinus Torvalds prev = tu->qtail == 0 ? tu->queue_size - 1 : tu->qtail - 1; 13871da177e4SLinus Torvalds r = &tu->tqueue[prev]; 13881da177e4SLinus Torvalds if (r->event == SNDRV_TIMER_EVENT_TICK) { 13891da177e4SLinus Torvalds r->tstamp = tstamp; 13901da177e4SLinus Torvalds r->val += ticks; 13911da177e4SLinus Torvalds append++; 13921da177e4SLinus Torvalds goto __wake; 13931da177e4SLinus Torvalds } 13941da177e4SLinus Torvalds } 13951da177e4SLinus Torvalds r1.event = SNDRV_TIMER_EVENT_TICK; 13961da177e4SLinus Torvalds r1.tstamp = tstamp; 13971da177e4SLinus Torvalds r1.val = ticks; 13981da177e4SLinus Torvalds snd_timer_user_append_to_tqueue(tu, &r1); 13991da177e4SLinus Torvalds append++; 14001da177e4SLinus Torvalds __wake: 14011da177e4SLinus Torvalds spin_unlock(&tu->qlock); 14021da177e4SLinus Torvalds if (append == 0) 14031da177e4SLinus Torvalds return; 14041da177e4SLinus Torvalds kill_fasync(&tu->fasync, SIGIO, POLL_IN); 14051da177e4SLinus Torvalds wake_up(&tu->qchange_sleep); 14061da177e4SLinus Torvalds } 14071da177e4SLinus Torvalds 1408890e2cb5STakashi Iwai static int realloc_user_queue(struct snd_timer_user *tu, int size) 1409890e2cb5STakashi Iwai { 1410890e2cb5STakashi Iwai struct snd_timer_read *queue = NULL; 1411890e2cb5STakashi Iwai struct snd_timer_tread *tqueue = NULL; 1412890e2cb5STakashi Iwai 1413890e2cb5STakashi Iwai if (tu->tread) { 1414890e2cb5STakashi Iwai tqueue = kcalloc(size, sizeof(*tqueue), GFP_KERNEL); 1415890e2cb5STakashi Iwai if (!tqueue) 1416890e2cb5STakashi Iwai return -ENOMEM; 1417890e2cb5STakashi Iwai } else { 1418890e2cb5STakashi Iwai queue = kcalloc(size, sizeof(*queue), GFP_KERNEL); 1419890e2cb5STakashi Iwai if (!queue) 1420890e2cb5STakashi Iwai return -ENOMEM; 1421890e2cb5STakashi Iwai } 1422890e2cb5STakashi Iwai 1423890e2cb5STakashi Iwai spin_lock_irq(&tu->qlock); 1424890e2cb5STakashi Iwai kfree(tu->queue); 1425890e2cb5STakashi Iwai kfree(tu->tqueue); 1426890e2cb5STakashi Iwai tu->queue_size = size; 1427890e2cb5STakashi Iwai tu->queue = queue; 1428890e2cb5STakashi Iwai tu->tqueue = tqueue; 1429890e2cb5STakashi Iwai tu->qhead = tu->qtail = tu->qused = 0; 1430890e2cb5STakashi Iwai spin_unlock_irq(&tu->qlock); 1431890e2cb5STakashi Iwai 1432890e2cb5STakashi Iwai return 0; 1433890e2cb5STakashi Iwai } 1434890e2cb5STakashi Iwai 14351da177e4SLinus Torvalds static int snd_timer_user_open(struct inode *inode, struct file *file) 14361da177e4SLinus Torvalds { 143753d2f744STakashi Iwai struct snd_timer_user *tu; 143802f4865fSTakashi Iwai int err; 143902f4865fSTakashi Iwai 1440c5bf68feSKirill Smelkov err = stream_open(inode, file); 144102f4865fSTakashi Iwai if (err < 0) 144202f4865fSTakashi Iwai return err; 14431da177e4SLinus Torvalds 1444ca2c0966STakashi Iwai tu = kzalloc(sizeof(*tu), GFP_KERNEL); 14451da177e4SLinus Torvalds if (tu == NULL) 14461da177e4SLinus Torvalds return -ENOMEM; 14471da177e4SLinus Torvalds spin_lock_init(&tu->qlock); 14481da177e4SLinus Torvalds init_waitqueue_head(&tu->qchange_sleep); 1449af368027STakashi Iwai mutex_init(&tu->ioctl_lock); 14501da177e4SLinus Torvalds tu->ticks = 1; 1451890e2cb5STakashi Iwai if (realloc_user_queue(tu, 128) < 0) { 14521da177e4SLinus Torvalds kfree(tu); 14531da177e4SLinus Torvalds return -ENOMEM; 14541da177e4SLinus Torvalds } 14551da177e4SLinus Torvalds file->private_data = tu; 14561da177e4SLinus Torvalds return 0; 14571da177e4SLinus Torvalds } 14581da177e4SLinus Torvalds 14591da177e4SLinus Torvalds static int snd_timer_user_release(struct inode *inode, struct file *file) 14601da177e4SLinus Torvalds { 146153d2f744STakashi Iwai struct snd_timer_user *tu; 14621da177e4SLinus Torvalds 14631da177e4SLinus Torvalds if (file->private_data) { 14641da177e4SLinus Torvalds tu = file->private_data; 14651da177e4SLinus Torvalds file->private_data = NULL; 1466af368027STakashi Iwai mutex_lock(&tu->ioctl_lock); 14671da177e4SLinus Torvalds if (tu->timeri) 14681da177e4SLinus Torvalds snd_timer_close(tu->timeri); 1469af368027STakashi Iwai mutex_unlock(&tu->ioctl_lock); 14701da177e4SLinus Torvalds kfree(tu->queue); 14711da177e4SLinus Torvalds kfree(tu->tqueue); 14721da177e4SLinus Torvalds kfree(tu); 14731da177e4SLinus Torvalds } 14741da177e4SLinus Torvalds return 0; 14751da177e4SLinus Torvalds } 14761da177e4SLinus Torvalds 147753d2f744STakashi Iwai static void snd_timer_user_zero_id(struct snd_timer_id *id) 14781da177e4SLinus Torvalds { 14791da177e4SLinus Torvalds id->dev_class = SNDRV_TIMER_CLASS_NONE; 14801da177e4SLinus Torvalds id->dev_sclass = SNDRV_TIMER_SCLASS_NONE; 14811da177e4SLinus Torvalds id->card = -1; 14821da177e4SLinus Torvalds id->device = -1; 14831da177e4SLinus Torvalds id->subdevice = -1; 14841da177e4SLinus Torvalds } 14851da177e4SLinus Torvalds 148653d2f744STakashi Iwai static void snd_timer_user_copy_id(struct snd_timer_id *id, struct snd_timer *timer) 14871da177e4SLinus Torvalds { 14881da177e4SLinus Torvalds id->dev_class = timer->tmr_class; 14891da177e4SLinus Torvalds id->dev_sclass = SNDRV_TIMER_SCLASS_NONE; 14901da177e4SLinus Torvalds id->card = timer->card ? timer->card->number : -1; 14911da177e4SLinus Torvalds id->device = timer->tmr_device; 14921da177e4SLinus Torvalds id->subdevice = timer->tmr_subdevice; 14931da177e4SLinus Torvalds } 14941da177e4SLinus Torvalds 149553d2f744STakashi Iwai static int snd_timer_user_next_device(struct snd_timer_id __user *_tid) 14961da177e4SLinus Torvalds { 149753d2f744STakashi Iwai struct snd_timer_id id; 149853d2f744STakashi Iwai struct snd_timer *timer; 14991da177e4SLinus Torvalds struct list_head *p; 15001da177e4SLinus Torvalds 15011da177e4SLinus Torvalds if (copy_from_user(&id, _tid, sizeof(id))) 15021da177e4SLinus Torvalds return -EFAULT; 15031a60d4c5SIngo Molnar mutex_lock(®ister_mutex); 15041da177e4SLinus Torvalds if (id.dev_class < 0) { /* first item */ 15051da177e4SLinus Torvalds if (list_empty(&snd_timer_list)) 15061da177e4SLinus Torvalds snd_timer_user_zero_id(&id); 15071da177e4SLinus Torvalds else { 15089dfba380SClemens Ladisch timer = list_entry(snd_timer_list.next, 150953d2f744STakashi Iwai struct snd_timer, device_list); 15101da177e4SLinus Torvalds snd_timer_user_copy_id(&id, timer); 15111da177e4SLinus Torvalds } 15121da177e4SLinus Torvalds } else { 15131da177e4SLinus Torvalds switch (id.dev_class) { 15141da177e4SLinus Torvalds case SNDRV_TIMER_CLASS_GLOBAL: 15151da177e4SLinus Torvalds id.device = id.device < 0 ? 0 : id.device + 1; 15161da177e4SLinus Torvalds list_for_each(p, &snd_timer_list) { 151753d2f744STakashi Iwai timer = list_entry(p, struct snd_timer, device_list); 15181da177e4SLinus Torvalds if (timer->tmr_class > SNDRV_TIMER_CLASS_GLOBAL) { 15191da177e4SLinus Torvalds snd_timer_user_copy_id(&id, timer); 15201da177e4SLinus Torvalds break; 15211da177e4SLinus Torvalds } 15221da177e4SLinus Torvalds if (timer->tmr_device >= id.device) { 15231da177e4SLinus Torvalds snd_timer_user_copy_id(&id, timer); 15241da177e4SLinus Torvalds break; 15251da177e4SLinus Torvalds } 15261da177e4SLinus Torvalds } 15271da177e4SLinus Torvalds if (p == &snd_timer_list) 15281da177e4SLinus Torvalds snd_timer_user_zero_id(&id); 15291da177e4SLinus Torvalds break; 15301da177e4SLinus Torvalds case SNDRV_TIMER_CLASS_CARD: 15311da177e4SLinus Torvalds case SNDRV_TIMER_CLASS_PCM: 15321da177e4SLinus Torvalds if (id.card < 0) { 15331da177e4SLinus Torvalds id.card = 0; 15341da177e4SLinus Torvalds } else { 15351da177e4SLinus Torvalds if (id.device < 0) { 15361da177e4SLinus Torvalds id.device = 0; 15371da177e4SLinus Torvalds } else { 1538e8ed6820SDan Carpenter if (id.subdevice < 0) 15396b172a85SClemens Ladisch id.subdevice = 0; 1540b41f794fSTakashi Iwai else if (id.subdevice < INT_MAX) 15416b172a85SClemens Ladisch id.subdevice++; 15426b172a85SClemens Ladisch } 15431da177e4SLinus Torvalds } 15441da177e4SLinus Torvalds list_for_each(p, &snd_timer_list) { 154553d2f744STakashi Iwai timer = list_entry(p, struct snd_timer, device_list); 15461da177e4SLinus Torvalds if (timer->tmr_class > id.dev_class) { 15471da177e4SLinus Torvalds snd_timer_user_copy_id(&id, timer); 15481da177e4SLinus Torvalds break; 15491da177e4SLinus Torvalds } 15501da177e4SLinus Torvalds if (timer->tmr_class < id.dev_class) 15511da177e4SLinus Torvalds continue; 15521da177e4SLinus Torvalds if (timer->card->number > id.card) { 15531da177e4SLinus Torvalds snd_timer_user_copy_id(&id, timer); 15541da177e4SLinus Torvalds break; 15551da177e4SLinus Torvalds } 15561da177e4SLinus Torvalds if (timer->card->number < id.card) 15571da177e4SLinus Torvalds continue; 15581da177e4SLinus Torvalds if (timer->tmr_device > id.device) { 15591da177e4SLinus Torvalds snd_timer_user_copy_id(&id, timer); 15601da177e4SLinus Torvalds break; 15611da177e4SLinus Torvalds } 15621da177e4SLinus Torvalds if (timer->tmr_device < id.device) 15631da177e4SLinus Torvalds continue; 15641da177e4SLinus Torvalds if (timer->tmr_subdevice > id.subdevice) { 15651da177e4SLinus Torvalds snd_timer_user_copy_id(&id, timer); 15661da177e4SLinus Torvalds break; 15671da177e4SLinus Torvalds } 15681da177e4SLinus Torvalds if (timer->tmr_subdevice < id.subdevice) 15691da177e4SLinus Torvalds continue; 15701da177e4SLinus Torvalds snd_timer_user_copy_id(&id, timer); 15711da177e4SLinus Torvalds break; 15721da177e4SLinus Torvalds } 15731da177e4SLinus Torvalds if (p == &snd_timer_list) 15741da177e4SLinus Torvalds snd_timer_user_zero_id(&id); 15751da177e4SLinus Torvalds break; 15761da177e4SLinus Torvalds default: 15771da177e4SLinus Torvalds snd_timer_user_zero_id(&id); 15781da177e4SLinus Torvalds } 15791da177e4SLinus Torvalds } 15801a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 15811da177e4SLinus Torvalds if (copy_to_user(_tid, &id, sizeof(*_tid))) 15821da177e4SLinus Torvalds return -EFAULT; 15831da177e4SLinus Torvalds return 0; 15841da177e4SLinus Torvalds } 15851da177e4SLinus Torvalds 15866b172a85SClemens Ladisch static int snd_timer_user_ginfo(struct file *file, 158753d2f744STakashi Iwai struct snd_timer_ginfo __user *_ginfo) 15881da177e4SLinus Torvalds { 158953d2f744STakashi Iwai struct snd_timer_ginfo *ginfo; 159053d2f744STakashi Iwai struct snd_timer_id tid; 159153d2f744STakashi Iwai struct snd_timer *t; 15921da177e4SLinus Torvalds struct list_head *p; 15931da177e4SLinus Torvalds int err = 0; 15941da177e4SLinus Torvalds 1595ef44a1ecSLi Zefan ginfo = memdup_user(_ginfo, sizeof(*ginfo)); 1596ef44a1ecSLi Zefan if (IS_ERR(ginfo)) 1597ef44a1ecSLi Zefan return PTR_ERR(ginfo); 1598ef44a1ecSLi Zefan 15991da177e4SLinus Torvalds tid = ginfo->tid; 16001da177e4SLinus Torvalds memset(ginfo, 0, sizeof(*ginfo)); 16011da177e4SLinus Torvalds ginfo->tid = tid; 16021a60d4c5SIngo Molnar mutex_lock(®ister_mutex); 16031da177e4SLinus Torvalds t = snd_timer_find(&tid); 16041da177e4SLinus Torvalds if (t != NULL) { 16051da177e4SLinus Torvalds ginfo->card = t->card ? t->card->number : -1; 16061da177e4SLinus Torvalds if (t->hw.flags & SNDRV_TIMER_HW_SLAVE) 16071da177e4SLinus Torvalds ginfo->flags |= SNDRV_TIMER_FLG_SLAVE; 16081da177e4SLinus Torvalds strlcpy(ginfo->id, t->id, sizeof(ginfo->id)); 16091da177e4SLinus Torvalds strlcpy(ginfo->name, t->name, sizeof(ginfo->name)); 16101da177e4SLinus Torvalds ginfo->resolution = t->hw.resolution; 16111da177e4SLinus Torvalds if (t->hw.resolution_min > 0) { 16121da177e4SLinus Torvalds ginfo->resolution_min = t->hw.resolution_min; 16131da177e4SLinus Torvalds ginfo->resolution_max = t->hw.resolution_max; 16141da177e4SLinus Torvalds } 16151da177e4SLinus Torvalds list_for_each(p, &t->open_list_head) { 16161da177e4SLinus Torvalds ginfo->clients++; 16171da177e4SLinus Torvalds } 16181da177e4SLinus Torvalds } else { 16191da177e4SLinus Torvalds err = -ENODEV; 16201da177e4SLinus Torvalds } 16211a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 16221da177e4SLinus Torvalds if (err >= 0 && copy_to_user(_ginfo, ginfo, sizeof(*ginfo))) 16231da177e4SLinus Torvalds err = -EFAULT; 16241da177e4SLinus Torvalds kfree(ginfo); 16251da177e4SLinus Torvalds return err; 16261da177e4SLinus Torvalds } 16271da177e4SLinus Torvalds 162891d2178eSTakashi Sakamoto static int timer_set_gparams(struct snd_timer_gparams *gparams) 16291da177e4SLinus Torvalds { 163053d2f744STakashi Iwai struct snd_timer *t; 16311da177e4SLinus Torvalds int err; 16321da177e4SLinus Torvalds 16331a60d4c5SIngo Molnar mutex_lock(®ister_mutex); 163491d2178eSTakashi Sakamoto t = snd_timer_find(&gparams->tid); 16356b172a85SClemens Ladisch if (!t) { 16361da177e4SLinus Torvalds err = -ENODEV; 16376b172a85SClemens Ladisch goto _error; 16381da177e4SLinus Torvalds } 16396b172a85SClemens Ladisch if (!list_empty(&t->open_list_head)) { 16406b172a85SClemens Ladisch err = -EBUSY; 16416b172a85SClemens Ladisch goto _error; 16426b172a85SClemens Ladisch } 16436b172a85SClemens Ladisch if (!t->hw.set_period) { 16446b172a85SClemens Ladisch err = -ENOSYS; 16456b172a85SClemens Ladisch goto _error; 16466b172a85SClemens Ladisch } 164791d2178eSTakashi Sakamoto err = t->hw.set_period(t, gparams->period_num, gparams->period_den); 16486b172a85SClemens Ladisch _error: 16491a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 16501da177e4SLinus Torvalds return err; 16511da177e4SLinus Torvalds } 16521da177e4SLinus Torvalds 165391d2178eSTakashi Sakamoto static int snd_timer_user_gparams(struct file *file, 165491d2178eSTakashi Sakamoto struct snd_timer_gparams __user *_gparams) 165591d2178eSTakashi Sakamoto { 165691d2178eSTakashi Sakamoto struct snd_timer_gparams gparams; 165791d2178eSTakashi Sakamoto 165891d2178eSTakashi Sakamoto if (copy_from_user(&gparams, _gparams, sizeof(gparams))) 165991d2178eSTakashi Sakamoto return -EFAULT; 166091d2178eSTakashi Sakamoto return timer_set_gparams(&gparams); 166191d2178eSTakashi Sakamoto } 166291d2178eSTakashi Sakamoto 16636b172a85SClemens Ladisch static int snd_timer_user_gstatus(struct file *file, 166453d2f744STakashi Iwai struct snd_timer_gstatus __user *_gstatus) 16651da177e4SLinus Torvalds { 166653d2f744STakashi Iwai struct snd_timer_gstatus gstatus; 166753d2f744STakashi Iwai struct snd_timer_id tid; 166853d2f744STakashi Iwai struct snd_timer *t; 16691da177e4SLinus Torvalds int err = 0; 16701da177e4SLinus Torvalds 16711da177e4SLinus Torvalds if (copy_from_user(&gstatus, _gstatus, sizeof(gstatus))) 16721da177e4SLinus Torvalds return -EFAULT; 16731da177e4SLinus Torvalds tid = gstatus.tid; 16741da177e4SLinus Torvalds memset(&gstatus, 0, sizeof(gstatus)); 16751da177e4SLinus Torvalds gstatus.tid = tid; 16761a60d4c5SIngo Molnar mutex_lock(®ister_mutex); 16771da177e4SLinus Torvalds t = snd_timer_find(&tid); 16781da177e4SLinus Torvalds if (t != NULL) { 16799d4d207dSTakashi Iwai spin_lock_irq(&t->lock); 1680fdcb5761STakashi Iwai gstatus.resolution = snd_timer_hw_resolution(t); 16811da177e4SLinus Torvalds if (t->hw.precise_resolution) { 16826b172a85SClemens Ladisch t->hw.precise_resolution(t, &gstatus.resolution_num, 16836b172a85SClemens Ladisch &gstatus.resolution_den); 16841da177e4SLinus Torvalds } else { 16851da177e4SLinus Torvalds gstatus.resolution_num = gstatus.resolution; 16861da177e4SLinus Torvalds gstatus.resolution_den = 1000000000uL; 16871da177e4SLinus Torvalds } 16889d4d207dSTakashi Iwai spin_unlock_irq(&t->lock); 16891da177e4SLinus Torvalds } else { 16901da177e4SLinus Torvalds err = -ENODEV; 16911da177e4SLinus Torvalds } 16921a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 16931da177e4SLinus Torvalds if (err >= 0 && copy_to_user(_gstatus, &gstatus, sizeof(gstatus))) 16941da177e4SLinus Torvalds err = -EFAULT; 16951da177e4SLinus Torvalds return err; 16961da177e4SLinus Torvalds } 16971da177e4SLinus Torvalds 16986b172a85SClemens Ladisch static int snd_timer_user_tselect(struct file *file, 169953d2f744STakashi Iwai struct snd_timer_select __user *_tselect) 17001da177e4SLinus Torvalds { 170153d2f744STakashi Iwai struct snd_timer_user *tu; 170253d2f744STakashi Iwai struct snd_timer_select tselect; 17031da177e4SLinus Torvalds char str[32]; 1704c1935b4dSJaroslav Kysela int err = 0; 17051da177e4SLinus Torvalds 17061da177e4SLinus Torvalds tu = file->private_data; 1707c1935b4dSJaroslav Kysela if (tu->timeri) { 17081da177e4SLinus Torvalds snd_timer_close(tu->timeri); 1709c1935b4dSJaroslav Kysela tu->timeri = NULL; 1710c1935b4dSJaroslav Kysela } 1711c1935b4dSJaroslav Kysela if (copy_from_user(&tselect, _tselect, sizeof(tselect))) { 1712c1935b4dSJaroslav Kysela err = -EFAULT; 1713c1935b4dSJaroslav Kysela goto __err; 1714c1935b4dSJaroslav Kysela } 17151da177e4SLinus Torvalds sprintf(str, "application %i", current->pid); 17161da177e4SLinus Torvalds if (tselect.id.dev_class != SNDRV_TIMER_CLASS_SLAVE) 17171da177e4SLinus Torvalds tselect.id.dev_sclass = SNDRV_TIMER_SCLASS_APPLICATION; 17186b172a85SClemens Ladisch err = snd_timer_open(&tu->timeri, str, &tselect.id, current->pid); 17196b172a85SClemens Ladisch if (err < 0) 1720c1935b4dSJaroslav Kysela goto __err; 17211da177e4SLinus Torvalds 17221da177e4SLinus Torvalds tu->timeri->flags |= SNDRV_TIMER_IFLG_FAST; 17236b172a85SClemens Ladisch tu->timeri->callback = tu->tread 17246b172a85SClemens Ladisch ? snd_timer_user_tinterrupt : snd_timer_user_interrupt; 17251da177e4SLinus Torvalds tu->timeri->ccallback = snd_timer_user_ccallback; 17261da177e4SLinus Torvalds tu->timeri->callback_data = (void *)tu; 172740ed9444STakashi Iwai tu->timeri->disconnect = snd_timer_user_disconnect; 1728c1935b4dSJaroslav Kysela 1729c1935b4dSJaroslav Kysela __err: 1730c1935b4dSJaroslav Kysela return err; 17311da177e4SLinus Torvalds } 17321da177e4SLinus Torvalds 17336b172a85SClemens Ladisch static int snd_timer_user_info(struct file *file, 173453d2f744STakashi Iwai struct snd_timer_info __user *_info) 17351da177e4SLinus Torvalds { 173653d2f744STakashi Iwai struct snd_timer_user *tu; 173753d2f744STakashi Iwai struct snd_timer_info *info; 173853d2f744STakashi Iwai struct snd_timer *t; 17391da177e4SLinus Torvalds int err = 0; 17401da177e4SLinus Torvalds 17411da177e4SLinus Torvalds tu = file->private_data; 17427c64ec34SClemens Ladisch if (!tu->timeri) 17437c64ec34SClemens Ladisch return -EBADFD; 17441da177e4SLinus Torvalds t = tu->timeri->timer; 17457c64ec34SClemens Ladisch if (!t) 17467c64ec34SClemens Ladisch return -EBADFD; 17471da177e4SLinus Torvalds 1748ca2c0966STakashi Iwai info = kzalloc(sizeof(*info), GFP_KERNEL); 17491da177e4SLinus Torvalds if (! info) 17501da177e4SLinus Torvalds return -ENOMEM; 17511da177e4SLinus Torvalds info->card = t->card ? t->card->number : -1; 17521da177e4SLinus Torvalds if (t->hw.flags & SNDRV_TIMER_HW_SLAVE) 17531da177e4SLinus Torvalds info->flags |= SNDRV_TIMER_FLG_SLAVE; 17541da177e4SLinus Torvalds strlcpy(info->id, t->id, sizeof(info->id)); 17551da177e4SLinus Torvalds strlcpy(info->name, t->name, sizeof(info->name)); 17561da177e4SLinus Torvalds info->resolution = t->hw.resolution; 17571da177e4SLinus Torvalds if (copy_to_user(_info, info, sizeof(*_info))) 17581da177e4SLinus Torvalds err = -EFAULT; 17591da177e4SLinus Torvalds kfree(info); 17601da177e4SLinus Torvalds return err; 17611da177e4SLinus Torvalds } 17621da177e4SLinus Torvalds 17636b172a85SClemens Ladisch static int snd_timer_user_params(struct file *file, 176453d2f744STakashi Iwai struct snd_timer_params __user *_params) 17651da177e4SLinus Torvalds { 176653d2f744STakashi Iwai struct snd_timer_user *tu; 176753d2f744STakashi Iwai struct snd_timer_params params; 176853d2f744STakashi Iwai struct snd_timer *t; 17691da177e4SLinus Torvalds int err; 17701da177e4SLinus Torvalds 17711da177e4SLinus Torvalds tu = file->private_data; 17727c64ec34SClemens Ladisch if (!tu->timeri) 17737c64ec34SClemens Ladisch return -EBADFD; 17741da177e4SLinus Torvalds t = tu->timeri->timer; 17757c64ec34SClemens Ladisch if (!t) 17767c64ec34SClemens Ladisch return -EBADFD; 17771da177e4SLinus Torvalds if (copy_from_user(¶ms, _params, sizeof(params))) 17781da177e4SLinus Torvalds return -EFAULT; 177971321eb3STakashi Iwai if (!(t->hw.flags & SNDRV_TIMER_HW_SLAVE)) { 178071321eb3STakashi Iwai u64 resolution; 178171321eb3STakashi Iwai 178271321eb3STakashi Iwai if (params.ticks < 1) { 17831da177e4SLinus Torvalds err = -EINVAL; 17841da177e4SLinus Torvalds goto _end; 17851da177e4SLinus Torvalds } 178671321eb3STakashi Iwai 178771321eb3STakashi Iwai /* Don't allow resolution less than 1ms */ 178871321eb3STakashi Iwai resolution = snd_timer_resolution(tu->timeri); 178971321eb3STakashi Iwai resolution *= params.ticks; 179071321eb3STakashi Iwai if (resolution < 1000000) { 179171321eb3STakashi Iwai err = -EINVAL; 179271321eb3STakashi Iwai goto _end; 179371321eb3STakashi Iwai } 179471321eb3STakashi Iwai } 17956b172a85SClemens Ladisch if (params.queue_size > 0 && 17966b172a85SClemens Ladisch (params.queue_size < 32 || params.queue_size > 1024)) { 17971da177e4SLinus Torvalds err = -EINVAL; 17981da177e4SLinus Torvalds goto _end; 17991da177e4SLinus Torvalds } 18001da177e4SLinus Torvalds if (params.filter & ~((1<<SNDRV_TIMER_EVENT_RESOLUTION)| 18011da177e4SLinus Torvalds (1<<SNDRV_TIMER_EVENT_TICK)| 18021da177e4SLinus Torvalds (1<<SNDRV_TIMER_EVENT_START)| 18031da177e4SLinus Torvalds (1<<SNDRV_TIMER_EVENT_STOP)| 18041da177e4SLinus Torvalds (1<<SNDRV_TIMER_EVENT_CONTINUE)| 18051da177e4SLinus Torvalds (1<<SNDRV_TIMER_EVENT_PAUSE)| 1806a501dfa3SJaroslav Kysela (1<<SNDRV_TIMER_EVENT_SUSPEND)| 1807a501dfa3SJaroslav Kysela (1<<SNDRV_TIMER_EVENT_RESUME)| 18081da177e4SLinus Torvalds (1<<SNDRV_TIMER_EVENT_MSTART)| 18091da177e4SLinus Torvalds (1<<SNDRV_TIMER_EVENT_MSTOP)| 18101da177e4SLinus Torvalds (1<<SNDRV_TIMER_EVENT_MCONTINUE)| 181165d11d95SJaroslav Kysela (1<<SNDRV_TIMER_EVENT_MPAUSE)| 181265d11d95SJaroslav Kysela (1<<SNDRV_TIMER_EVENT_MSUSPEND)| 1813a501dfa3SJaroslav Kysela (1<<SNDRV_TIMER_EVENT_MRESUME))) { 18141da177e4SLinus Torvalds err = -EINVAL; 18151da177e4SLinus Torvalds goto _end; 18161da177e4SLinus Torvalds } 18171da177e4SLinus Torvalds snd_timer_stop(tu->timeri); 18181da177e4SLinus Torvalds spin_lock_irq(&t->lock); 18191da177e4SLinus Torvalds tu->timeri->flags &= ~(SNDRV_TIMER_IFLG_AUTO| 18201da177e4SLinus Torvalds SNDRV_TIMER_IFLG_EXCLUSIVE| 18211da177e4SLinus Torvalds SNDRV_TIMER_IFLG_EARLY_EVENT); 18221da177e4SLinus Torvalds if (params.flags & SNDRV_TIMER_PSFLG_AUTO) 18231da177e4SLinus Torvalds tu->timeri->flags |= SNDRV_TIMER_IFLG_AUTO; 18241da177e4SLinus Torvalds if (params.flags & SNDRV_TIMER_PSFLG_EXCLUSIVE) 18251da177e4SLinus Torvalds tu->timeri->flags |= SNDRV_TIMER_IFLG_EXCLUSIVE; 18261da177e4SLinus Torvalds if (params.flags & SNDRV_TIMER_PSFLG_EARLY_EVENT) 18271da177e4SLinus Torvalds tu->timeri->flags |= SNDRV_TIMER_IFLG_EARLY_EVENT; 18281da177e4SLinus Torvalds spin_unlock_irq(&t->lock); 18296b172a85SClemens Ladisch if (params.queue_size > 0 && 18306b172a85SClemens Ladisch (unsigned int)tu->queue_size != params.queue_size) { 1831890e2cb5STakashi Iwai err = realloc_user_queue(tu, params.queue_size); 1832890e2cb5STakashi Iwai if (err < 0) 1833890e2cb5STakashi Iwai goto _end; 18341da177e4SLinus Torvalds } 1835d7f910bfSTakashi Iwai spin_lock_irq(&tu->qlock); 18361da177e4SLinus Torvalds tu->qhead = tu->qtail = tu->qused = 0; 18371da177e4SLinus Torvalds if (tu->timeri->flags & SNDRV_TIMER_IFLG_EARLY_EVENT) { 18381da177e4SLinus Torvalds if (tu->tread) { 183953d2f744STakashi Iwai struct snd_timer_tread tread; 1840cec8f96eSKangjie Lu memset(&tread, 0, sizeof(tread)); 18411da177e4SLinus Torvalds tread.event = SNDRV_TIMER_EVENT_EARLY; 18421da177e4SLinus Torvalds tread.tstamp.tv_sec = 0; 18431da177e4SLinus Torvalds tread.tstamp.tv_nsec = 0; 18441da177e4SLinus Torvalds tread.val = 0; 18451da177e4SLinus Torvalds snd_timer_user_append_to_tqueue(tu, &tread); 18461da177e4SLinus Torvalds } else { 184753d2f744STakashi Iwai struct snd_timer_read *r = &tu->queue[0]; 18481da177e4SLinus Torvalds r->resolution = 0; 18491da177e4SLinus Torvalds r->ticks = 0; 18501da177e4SLinus Torvalds tu->qused++; 18511da177e4SLinus Torvalds tu->qtail++; 18521da177e4SLinus Torvalds } 18531da177e4SLinus Torvalds } 18541da177e4SLinus Torvalds tu->filter = params.filter; 18551da177e4SLinus Torvalds tu->ticks = params.ticks; 1856d7f910bfSTakashi Iwai spin_unlock_irq(&tu->qlock); 18571da177e4SLinus Torvalds err = 0; 18581da177e4SLinus Torvalds _end: 18591da177e4SLinus Torvalds if (copy_to_user(_params, ¶ms, sizeof(params))) 18601da177e4SLinus Torvalds return -EFAULT; 18611da177e4SLinus Torvalds return err; 18621da177e4SLinus Torvalds } 18631da177e4SLinus Torvalds 18646b172a85SClemens Ladisch static int snd_timer_user_status(struct file *file, 186553d2f744STakashi Iwai struct snd_timer_status __user *_status) 18661da177e4SLinus Torvalds { 186753d2f744STakashi Iwai struct snd_timer_user *tu; 186853d2f744STakashi Iwai struct snd_timer_status status; 18691da177e4SLinus Torvalds 18701da177e4SLinus Torvalds tu = file->private_data; 18717c64ec34SClemens Ladisch if (!tu->timeri) 18727c64ec34SClemens Ladisch return -EBADFD; 18731da177e4SLinus Torvalds memset(&status, 0, sizeof(status)); 18741da177e4SLinus Torvalds status.tstamp = tu->tstamp; 18751da177e4SLinus Torvalds status.resolution = snd_timer_resolution(tu->timeri); 18761da177e4SLinus Torvalds status.lost = tu->timeri->lost; 18771da177e4SLinus Torvalds status.overrun = tu->overrun; 18781da177e4SLinus Torvalds spin_lock_irq(&tu->qlock); 18791da177e4SLinus Torvalds status.queue = tu->qused; 18801da177e4SLinus Torvalds spin_unlock_irq(&tu->qlock); 18811da177e4SLinus Torvalds if (copy_to_user(_status, &status, sizeof(status))) 18821da177e4SLinus Torvalds return -EFAULT; 18831da177e4SLinus Torvalds return 0; 18841da177e4SLinus Torvalds } 18851da177e4SLinus Torvalds 18861da177e4SLinus Torvalds static int snd_timer_user_start(struct file *file) 18871da177e4SLinus Torvalds { 18881da177e4SLinus Torvalds int err; 188953d2f744STakashi Iwai struct snd_timer_user *tu; 18901da177e4SLinus Torvalds 18911da177e4SLinus Torvalds tu = file->private_data; 18927c64ec34SClemens Ladisch if (!tu->timeri) 18937c64ec34SClemens Ladisch return -EBADFD; 18941da177e4SLinus Torvalds snd_timer_stop(tu->timeri); 18951da177e4SLinus Torvalds tu->timeri->lost = 0; 18961da177e4SLinus Torvalds tu->last_resolution = 0; 18975d704b0dSTakashi Iwai err = snd_timer_start(tu->timeri, tu->ticks); 18985d704b0dSTakashi Iwai if (err < 0) 18995d704b0dSTakashi Iwai return err; 19005d704b0dSTakashi Iwai return 0; 19011da177e4SLinus Torvalds } 19021da177e4SLinus Torvalds 19031da177e4SLinus Torvalds static int snd_timer_user_stop(struct file *file) 19041da177e4SLinus Torvalds { 19051da177e4SLinus Torvalds int err; 190653d2f744STakashi Iwai struct snd_timer_user *tu; 19071da177e4SLinus Torvalds 19081da177e4SLinus Torvalds tu = file->private_data; 19097c64ec34SClemens Ladisch if (!tu->timeri) 19107c64ec34SClemens Ladisch return -EBADFD; 19115d704b0dSTakashi Iwai err = snd_timer_stop(tu->timeri); 19125d704b0dSTakashi Iwai if (err < 0) 19135d704b0dSTakashi Iwai return err; 19145d704b0dSTakashi Iwai return 0; 19151da177e4SLinus Torvalds } 19161da177e4SLinus Torvalds 19171da177e4SLinus Torvalds static int snd_timer_user_continue(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; 19259f8a7658STakashi Iwai /* start timer instead of continue if it's not used before */ 19269f8a7658STakashi Iwai if (!(tu->timeri->flags & SNDRV_TIMER_IFLG_PAUSED)) 19279f8a7658STakashi Iwai return snd_timer_user_start(file); 19281da177e4SLinus Torvalds tu->timeri->lost = 0; 19295d704b0dSTakashi Iwai err = snd_timer_continue(tu->timeri); 19305d704b0dSTakashi Iwai if (err < 0) 19315d704b0dSTakashi Iwai return err; 19325d704b0dSTakashi Iwai return 0; 19331da177e4SLinus Torvalds } 19341da177e4SLinus Torvalds 193515790a6bSTakashi Iwai static int snd_timer_user_pause(struct file *file) 193615790a6bSTakashi Iwai { 193715790a6bSTakashi Iwai int err; 193853d2f744STakashi Iwai struct snd_timer_user *tu; 193915790a6bSTakashi Iwai 194015790a6bSTakashi Iwai tu = file->private_data; 19417c64ec34SClemens Ladisch if (!tu->timeri) 19427c64ec34SClemens Ladisch return -EBADFD; 19435d704b0dSTakashi Iwai err = snd_timer_pause(tu->timeri); 19445d704b0dSTakashi Iwai if (err < 0) 19455d704b0dSTakashi Iwai return err; 19465d704b0dSTakashi Iwai return 0; 194715790a6bSTakashi Iwai } 194815790a6bSTakashi Iwai 19498c50b37cSTakashi Iwai enum { 19508c50b37cSTakashi Iwai SNDRV_TIMER_IOCTL_START_OLD = _IO('T', 0x20), 19518c50b37cSTakashi Iwai SNDRV_TIMER_IOCTL_STOP_OLD = _IO('T', 0x21), 19528c50b37cSTakashi Iwai SNDRV_TIMER_IOCTL_CONTINUE_OLD = _IO('T', 0x22), 19538c50b37cSTakashi Iwai SNDRV_TIMER_IOCTL_PAUSE_OLD = _IO('T', 0x23), 19548c50b37cSTakashi Iwai }; 19558c50b37cSTakashi Iwai 1956af368027STakashi Iwai static long __snd_timer_user_ioctl(struct file *file, unsigned int cmd, 19576b172a85SClemens Ladisch unsigned long arg) 19581da177e4SLinus Torvalds { 195953d2f744STakashi Iwai struct snd_timer_user *tu; 19601da177e4SLinus Torvalds void __user *argp = (void __user *)arg; 19611da177e4SLinus Torvalds int __user *p = argp; 19621da177e4SLinus Torvalds 19631da177e4SLinus Torvalds tu = file->private_data; 19641da177e4SLinus Torvalds switch (cmd) { 19651da177e4SLinus Torvalds case SNDRV_TIMER_IOCTL_PVERSION: 19661da177e4SLinus Torvalds return put_user(SNDRV_TIMER_VERSION, p) ? -EFAULT : 0; 19671da177e4SLinus Torvalds case SNDRV_TIMER_IOCTL_NEXT_DEVICE: 19681da177e4SLinus Torvalds return snd_timer_user_next_device(argp); 19691da177e4SLinus Torvalds case SNDRV_TIMER_IOCTL_TREAD: 19701da177e4SLinus Torvalds { 1971890e2cb5STakashi Iwai int xarg, old_tread; 19721da177e4SLinus Torvalds 1973af368027STakashi Iwai if (tu->timeri) /* too late */ 19741da177e4SLinus Torvalds return -EBUSY; 1975af368027STakashi Iwai if (get_user(xarg, p)) 19761da177e4SLinus Torvalds return -EFAULT; 1977890e2cb5STakashi Iwai old_tread = tu->tread; 19781da177e4SLinus Torvalds tu->tread = xarg ? 1 : 0; 1979890e2cb5STakashi Iwai if (tu->tread != old_tread && 1980890e2cb5STakashi Iwai realloc_user_queue(tu, tu->queue_size) < 0) { 1981890e2cb5STakashi Iwai tu->tread = old_tread; 1982890e2cb5STakashi Iwai return -ENOMEM; 1983890e2cb5STakashi Iwai } 19841da177e4SLinus Torvalds return 0; 19851da177e4SLinus Torvalds } 19861da177e4SLinus Torvalds case SNDRV_TIMER_IOCTL_GINFO: 19871da177e4SLinus Torvalds return snd_timer_user_ginfo(file, argp); 19881da177e4SLinus Torvalds case SNDRV_TIMER_IOCTL_GPARAMS: 19891da177e4SLinus Torvalds return snd_timer_user_gparams(file, argp); 19901da177e4SLinus Torvalds case SNDRV_TIMER_IOCTL_GSTATUS: 19911da177e4SLinus Torvalds return snd_timer_user_gstatus(file, argp); 19921da177e4SLinus Torvalds case SNDRV_TIMER_IOCTL_SELECT: 19931da177e4SLinus Torvalds return snd_timer_user_tselect(file, argp); 19941da177e4SLinus Torvalds case SNDRV_TIMER_IOCTL_INFO: 19951da177e4SLinus Torvalds return snd_timer_user_info(file, argp); 19961da177e4SLinus Torvalds case SNDRV_TIMER_IOCTL_PARAMS: 19971da177e4SLinus Torvalds return snd_timer_user_params(file, argp); 19981da177e4SLinus Torvalds case SNDRV_TIMER_IOCTL_STATUS: 19991da177e4SLinus Torvalds return snd_timer_user_status(file, argp); 20001da177e4SLinus Torvalds case SNDRV_TIMER_IOCTL_START: 20018c50b37cSTakashi Iwai case SNDRV_TIMER_IOCTL_START_OLD: 20021da177e4SLinus Torvalds return snd_timer_user_start(file); 20031da177e4SLinus Torvalds case SNDRV_TIMER_IOCTL_STOP: 20048c50b37cSTakashi Iwai case SNDRV_TIMER_IOCTL_STOP_OLD: 20051da177e4SLinus Torvalds return snd_timer_user_stop(file); 20061da177e4SLinus Torvalds case SNDRV_TIMER_IOCTL_CONTINUE: 20078c50b37cSTakashi Iwai case SNDRV_TIMER_IOCTL_CONTINUE_OLD: 20081da177e4SLinus Torvalds return snd_timer_user_continue(file); 200915790a6bSTakashi Iwai case SNDRV_TIMER_IOCTL_PAUSE: 20108c50b37cSTakashi Iwai case SNDRV_TIMER_IOCTL_PAUSE_OLD: 201115790a6bSTakashi Iwai return snd_timer_user_pause(file); 20121da177e4SLinus Torvalds } 20131da177e4SLinus Torvalds return -ENOTTY; 20141da177e4SLinus Torvalds } 20151da177e4SLinus Torvalds 2016af368027STakashi Iwai static long snd_timer_user_ioctl(struct file *file, unsigned int cmd, 2017af368027STakashi Iwai unsigned long arg) 2018af368027STakashi Iwai { 2019af368027STakashi Iwai struct snd_timer_user *tu = file->private_data; 2020af368027STakashi Iwai long ret; 2021af368027STakashi Iwai 2022af368027STakashi Iwai mutex_lock(&tu->ioctl_lock); 2023af368027STakashi Iwai ret = __snd_timer_user_ioctl(file, cmd, arg); 2024af368027STakashi Iwai mutex_unlock(&tu->ioctl_lock); 2025af368027STakashi Iwai return ret; 2026af368027STakashi Iwai } 2027af368027STakashi Iwai 20281da177e4SLinus Torvalds static int snd_timer_user_fasync(int fd, struct file * file, int on) 20291da177e4SLinus Torvalds { 203053d2f744STakashi Iwai struct snd_timer_user *tu; 20311da177e4SLinus Torvalds 20321da177e4SLinus Torvalds tu = file->private_data; 203360aa4924SJonathan Corbet return fasync_helper(fd, file, on, &tu->fasync); 20341da177e4SLinus Torvalds } 20351da177e4SLinus Torvalds 20366b172a85SClemens Ladisch static ssize_t snd_timer_user_read(struct file *file, char __user *buffer, 20376b172a85SClemens Ladisch size_t count, loff_t *offset) 20381da177e4SLinus Torvalds { 203953d2f744STakashi Iwai struct snd_timer_user *tu; 20401da177e4SLinus Torvalds long result = 0, unit; 20414dff5c7bSTakashi Iwai int qhead; 20421da177e4SLinus Torvalds int err = 0; 20431da177e4SLinus Torvalds 20441da177e4SLinus Torvalds tu = file->private_data; 204553d2f744STakashi Iwai unit = tu->tread ? sizeof(struct snd_timer_tread) : sizeof(struct snd_timer_read); 2046d11662f4STakashi Iwai mutex_lock(&tu->ioctl_lock); 20471da177e4SLinus Torvalds spin_lock_irq(&tu->qlock); 20481da177e4SLinus Torvalds while ((long)count - result >= unit) { 20491da177e4SLinus Torvalds while (!tu->qused) { 2050ac6424b9SIngo Molnar wait_queue_entry_t wait; 20511da177e4SLinus Torvalds 20521da177e4SLinus Torvalds if ((file->f_flags & O_NONBLOCK) != 0 || result > 0) { 20531da177e4SLinus Torvalds err = -EAGAIN; 20544dff5c7bSTakashi Iwai goto _error; 20551da177e4SLinus Torvalds } 20561da177e4SLinus Torvalds 20571da177e4SLinus Torvalds set_current_state(TASK_INTERRUPTIBLE); 20581da177e4SLinus Torvalds init_waitqueue_entry(&wait, current); 20591da177e4SLinus Torvalds add_wait_queue(&tu->qchange_sleep, &wait); 20601da177e4SLinus Torvalds 20611da177e4SLinus Torvalds spin_unlock_irq(&tu->qlock); 2062d11662f4STakashi Iwai mutex_unlock(&tu->ioctl_lock); 20631da177e4SLinus Torvalds schedule(); 2064d11662f4STakashi Iwai mutex_lock(&tu->ioctl_lock); 20651da177e4SLinus Torvalds spin_lock_irq(&tu->qlock); 20661da177e4SLinus Torvalds 20671da177e4SLinus Torvalds remove_wait_queue(&tu->qchange_sleep, &wait); 20681da177e4SLinus Torvalds 2069230323daSTakashi Iwai if (tu->disconnected) { 2070230323daSTakashi Iwai err = -ENODEV; 20714dff5c7bSTakashi Iwai goto _error; 2072230323daSTakashi Iwai } 20731da177e4SLinus Torvalds if (signal_pending(current)) { 20741da177e4SLinus Torvalds err = -ERESTARTSYS; 20754dff5c7bSTakashi Iwai goto _error; 20761da177e4SLinus Torvalds } 20771da177e4SLinus Torvalds } 20781da177e4SLinus Torvalds 20794dff5c7bSTakashi Iwai qhead = tu->qhead++; 20804dff5c7bSTakashi Iwai tu->qhead %= tu->queue_size; 20813fa6993fSTakashi Iwai tu->qused--; 20821da177e4SLinus Torvalds spin_unlock_irq(&tu->qlock); 20831da177e4SLinus Torvalds 20841da177e4SLinus Torvalds if (tu->tread) { 20854dff5c7bSTakashi Iwai if (copy_to_user(buffer, &tu->tqueue[qhead], 20864dff5c7bSTakashi Iwai sizeof(struct snd_timer_tread))) 20871da177e4SLinus Torvalds err = -EFAULT; 20881da177e4SLinus Torvalds } else { 20894dff5c7bSTakashi Iwai if (copy_to_user(buffer, &tu->queue[qhead], 20904dff5c7bSTakashi Iwai sizeof(struct snd_timer_read))) 20911da177e4SLinus Torvalds err = -EFAULT; 20921da177e4SLinus Torvalds } 20931da177e4SLinus Torvalds 20941da177e4SLinus Torvalds spin_lock_irq(&tu->qlock); 20954dff5c7bSTakashi Iwai if (err < 0) 20964dff5c7bSTakashi Iwai goto _error; 20974dff5c7bSTakashi Iwai result += unit; 20984dff5c7bSTakashi Iwai buffer += unit; 20991da177e4SLinus Torvalds } 21001da177e4SLinus Torvalds _error: 21014dff5c7bSTakashi Iwai spin_unlock_irq(&tu->qlock); 2102d11662f4STakashi Iwai mutex_unlock(&tu->ioctl_lock); 21031da177e4SLinus Torvalds return result > 0 ? result : err; 21041da177e4SLinus Torvalds } 21051da177e4SLinus Torvalds 2106680ef72aSAl Viro static __poll_t snd_timer_user_poll(struct file *file, poll_table * wait) 21071da177e4SLinus Torvalds { 2108680ef72aSAl Viro __poll_t mask; 210953d2f744STakashi Iwai struct snd_timer_user *tu; 21101da177e4SLinus Torvalds 21111da177e4SLinus Torvalds tu = file->private_data; 21121da177e4SLinus Torvalds 21131da177e4SLinus Torvalds poll_wait(file, &tu->qchange_sleep, wait); 21141da177e4SLinus Torvalds 21151da177e4SLinus Torvalds mask = 0; 2116d7f910bfSTakashi Iwai spin_lock_irq(&tu->qlock); 21171da177e4SLinus Torvalds if (tu->qused) 2118a9a08845SLinus Torvalds mask |= EPOLLIN | EPOLLRDNORM; 2119230323daSTakashi Iwai if (tu->disconnected) 2120a9a08845SLinus Torvalds mask |= EPOLLERR; 2121d7f910bfSTakashi Iwai spin_unlock_irq(&tu->qlock); 21221da177e4SLinus Torvalds 21231da177e4SLinus Torvalds return mask; 21241da177e4SLinus Torvalds } 21251da177e4SLinus Torvalds 21261da177e4SLinus Torvalds #ifdef CONFIG_COMPAT 21271da177e4SLinus Torvalds #include "timer_compat.c" 21281da177e4SLinus Torvalds #else 21291da177e4SLinus Torvalds #define snd_timer_user_ioctl_compat NULL 21301da177e4SLinus Torvalds #endif 21311da177e4SLinus Torvalds 21329c2e08c5SArjan van de Ven static const struct file_operations snd_timer_f_ops = 21331da177e4SLinus Torvalds { 21341da177e4SLinus Torvalds .owner = THIS_MODULE, 21351da177e4SLinus Torvalds .read = snd_timer_user_read, 21361da177e4SLinus Torvalds .open = snd_timer_user_open, 21371da177e4SLinus Torvalds .release = snd_timer_user_release, 213802f4865fSTakashi Iwai .llseek = no_llseek, 21391da177e4SLinus Torvalds .poll = snd_timer_user_poll, 21401da177e4SLinus Torvalds .unlocked_ioctl = snd_timer_user_ioctl, 21411da177e4SLinus Torvalds .compat_ioctl = snd_timer_user_ioctl_compat, 21421da177e4SLinus Torvalds .fasync = snd_timer_user_fasync, 21431da177e4SLinus Torvalds }; 21441da177e4SLinus Torvalds 21457c35860dSTakashi Iwai /* unregister the system timer */ 21467c35860dSTakashi Iwai static void snd_timer_free_all(void) 21477c35860dSTakashi Iwai { 21487c35860dSTakashi Iwai struct snd_timer *timer, *n; 21497c35860dSTakashi Iwai 21507c35860dSTakashi Iwai list_for_each_entry_safe(timer, n, &snd_timer_list, device_list) 21517c35860dSTakashi Iwai snd_timer_free(timer); 21527c35860dSTakashi Iwai } 21537c35860dSTakashi Iwai 215489da061fSTakashi Iwai static struct device timer_dev; 215589da061fSTakashi Iwai 21561da177e4SLinus Torvalds /* 21571da177e4SLinus Torvalds * ENTRY functions 21581da177e4SLinus Torvalds */ 21591da177e4SLinus Torvalds 21601da177e4SLinus Torvalds static int __init alsa_timer_init(void) 21611da177e4SLinus Torvalds { 21621da177e4SLinus Torvalds int err; 21631da177e4SLinus Torvalds 216489da061fSTakashi Iwai snd_device_initialize(&timer_dev, NULL); 216589da061fSTakashi Iwai dev_set_name(&timer_dev, "timer"); 216689da061fSTakashi Iwai 21671da177e4SLinus Torvalds #ifdef SNDRV_OSS_INFO_DEV_TIMERS 21686b172a85SClemens Ladisch snd_oss_info_register(SNDRV_OSS_INFO_DEV_TIMERS, SNDRV_CARDS - 1, 21696b172a85SClemens Ladisch "system timer"); 21701da177e4SLinus Torvalds #endif 2171e28563ccSTakashi Iwai 21727c35860dSTakashi Iwai err = snd_timer_register_system(); 21737c35860dSTakashi Iwai if (err < 0) { 2174cf74dcf3STakashi Iwai pr_err("ALSA: unable to register system timer (%i)\n", err); 21751ae0e4ceSMarkus Elfring goto put_timer; 21767c35860dSTakashi Iwai } 21777c35860dSTakashi Iwai 217840a4b263STakashi Iwai err = snd_register_device(SNDRV_DEVICE_TYPE_TIMER, NULL, 0, 217940a4b263STakashi Iwai &snd_timer_f_ops, NULL, &timer_dev); 21807c35860dSTakashi Iwai if (err < 0) { 2181cf74dcf3STakashi Iwai pr_err("ALSA: unable to register timer device (%i)\n", err); 21827c35860dSTakashi Iwai snd_timer_free_all(); 21831ae0e4ceSMarkus Elfring goto put_timer; 21847c35860dSTakashi Iwai } 21857c35860dSTakashi Iwai 2186e28563ccSTakashi Iwai snd_timer_proc_init(); 21871da177e4SLinus Torvalds return 0; 21881ae0e4ceSMarkus Elfring 21891ae0e4ceSMarkus Elfring put_timer: 21901ae0e4ceSMarkus Elfring put_device(&timer_dev); 21911ae0e4ceSMarkus Elfring return err; 21921da177e4SLinus Torvalds } 21931da177e4SLinus Torvalds 21941da177e4SLinus Torvalds static void __exit alsa_timer_exit(void) 21951da177e4SLinus Torvalds { 219640a4b263STakashi Iwai snd_unregister_device(&timer_dev); 21977c35860dSTakashi Iwai snd_timer_free_all(); 219889da061fSTakashi Iwai put_device(&timer_dev); 2199e28563ccSTakashi Iwai snd_timer_proc_done(); 22001da177e4SLinus Torvalds #ifdef SNDRV_OSS_INFO_DEV_TIMERS 22011da177e4SLinus Torvalds snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_TIMERS, SNDRV_CARDS - 1); 22021da177e4SLinus Torvalds #endif 22031da177e4SLinus Torvalds } 22041da177e4SLinus Torvalds 22051da177e4SLinus Torvalds module_init(alsa_timer_init) 22061da177e4SLinus Torvalds module_exit(alsa_timer_exit) 2207