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 4707094ae6SBaolin Wang enum timer_tread_format { 4807094ae6SBaolin Wang TREAD_FORMAT_NONE = 0, 4907094ae6SBaolin Wang TREAD_FORMAT_TIME64, 5007094ae6SBaolin Wang TREAD_FORMAT_TIME32, 5107094ae6SBaolin Wang }; 5207094ae6SBaolin Wang 5307094ae6SBaolin Wang struct snd_timer_tread32 { 5407094ae6SBaolin Wang int event; 5507094ae6SBaolin Wang s32 tstamp_sec; 5607094ae6SBaolin Wang s32 tstamp_nsec; 5707094ae6SBaolin Wang unsigned int val; 5807094ae6SBaolin Wang }; 5907094ae6SBaolin Wang 6007094ae6SBaolin Wang struct snd_timer_tread64 { 6107094ae6SBaolin Wang int event; 6207094ae6SBaolin Wang u8 pad1[4]; 6307094ae6SBaolin Wang s64 tstamp_sec; 6407094ae6SBaolin Wang s64 tstamp_nsec; 6507094ae6SBaolin Wang unsigned int val; 6607094ae6SBaolin Wang u8 pad2[4]; 6707094ae6SBaolin Wang }; 6807094ae6SBaolin Wang 6953d2f744STakashi Iwai struct snd_timer_user { 7053d2f744STakashi Iwai struct snd_timer_instance *timeri; 711da177e4SLinus Torvalds int tread; /* enhanced read with timestamps and events */ 721da177e4SLinus Torvalds unsigned long ticks; 731da177e4SLinus Torvalds unsigned long overrun; 741da177e4SLinus Torvalds int qhead; 751da177e4SLinus Torvalds int qtail; 761da177e4SLinus Torvalds int qused; 771da177e4SLinus Torvalds int queue_size; 78230323daSTakashi Iwai bool disconnected; 7953d2f744STakashi Iwai struct snd_timer_read *queue; 8007094ae6SBaolin Wang struct snd_timer_tread64 *tqueue; 811da177e4SLinus Torvalds spinlock_t qlock; 821da177e4SLinus Torvalds unsigned long last_resolution; 831da177e4SLinus Torvalds unsigned int filter; 84fcae40c9SBaolin Wang struct timespec64 tstamp; /* trigger tstamp */ 851da177e4SLinus Torvalds wait_queue_head_t qchange_sleep; 861da177e4SLinus Torvalds struct fasync_struct *fasync; 87af368027STakashi Iwai struct mutex ioctl_lock; 8853d2f744STakashi Iwai }; 891da177e4SLinus Torvalds 90a07804ccSBaolin Wang struct snd_timer_status32 { 91a07804ccSBaolin Wang s32 tstamp_sec; /* Timestamp - last update */ 92a07804ccSBaolin Wang s32 tstamp_nsec; 93a07804ccSBaolin Wang unsigned int resolution; /* current period resolution in ns */ 94a07804ccSBaolin Wang unsigned int lost; /* counter of master tick lost */ 95a07804ccSBaolin Wang unsigned int overrun; /* count of read queue overruns */ 96a07804ccSBaolin Wang unsigned int queue; /* used queue size */ 97a07804ccSBaolin Wang unsigned char reserved[64]; /* reserved */ 98a07804ccSBaolin Wang }; 99a07804ccSBaolin Wang 100a07804ccSBaolin Wang #define SNDRV_TIMER_IOCTL_STATUS32 _IOR('T', 0x14, struct snd_timer_status32) 101a07804ccSBaolin Wang 102a07804ccSBaolin Wang struct snd_timer_status64 { 103a07804ccSBaolin Wang s64 tstamp_sec; /* Timestamp - last update */ 104a07804ccSBaolin Wang s64 tstamp_nsec; 105a07804ccSBaolin Wang unsigned int resolution; /* current period resolution in ns */ 106a07804ccSBaolin Wang unsigned int lost; /* counter of master tick lost */ 107a07804ccSBaolin Wang unsigned int overrun; /* count of read queue overruns */ 108a07804ccSBaolin Wang unsigned int queue; /* used queue size */ 109a07804ccSBaolin Wang unsigned char reserved[64]; /* reserved */ 110a07804ccSBaolin Wang }; 111a07804ccSBaolin Wang 112a07804ccSBaolin Wang #define SNDRV_TIMER_IOCTL_STATUS64 _IOR('T', 0x14, struct snd_timer_status64) 113a07804ccSBaolin Wang 1141da177e4SLinus Torvalds /* list of timers */ 1151da177e4SLinus Torvalds static LIST_HEAD(snd_timer_list); 1161da177e4SLinus Torvalds 1171da177e4SLinus Torvalds /* list of slave instances */ 1181da177e4SLinus Torvalds static LIST_HEAD(snd_timer_slave_list); 1191da177e4SLinus Torvalds 1201da177e4SLinus Torvalds /* lock for slave active lists */ 1211da177e4SLinus Torvalds static DEFINE_SPINLOCK(slave_active_lock); 1221da177e4SLinus Torvalds 123fdea53feSTakashi Iwai #define MAX_SLAVE_INSTANCES 1000 124fdea53feSTakashi Iwai static int num_slaves; 125fdea53feSTakashi Iwai 1261a60d4c5SIngo Molnar static DEFINE_MUTEX(register_mutex); 1271da177e4SLinus Torvalds 12853d2f744STakashi Iwai static int snd_timer_free(struct snd_timer *timer); 12953d2f744STakashi Iwai static int snd_timer_dev_free(struct snd_device *device); 13053d2f744STakashi Iwai static int snd_timer_dev_register(struct snd_device *device); 131c461482cSTakashi Iwai static int snd_timer_dev_disconnect(struct snd_device *device); 1321da177e4SLinus Torvalds 13353d2f744STakashi Iwai static void snd_timer_reschedule(struct snd_timer * timer, unsigned long ticks_left); 1341da177e4SLinus Torvalds 1351da177e4SLinus Torvalds /* 1361da177e4SLinus Torvalds * create a timer instance with the given owner string. 1371da177e4SLinus Torvalds */ 1386a34367eSTakashi Iwai struct snd_timer_instance *snd_timer_instance_new(const char *owner) 1391da177e4SLinus Torvalds { 14053d2f744STakashi Iwai struct snd_timer_instance *timeri; 1416a34367eSTakashi Iwai 142ca2c0966STakashi Iwai timeri = kzalloc(sizeof(*timeri), GFP_KERNEL); 1431da177e4SLinus Torvalds if (timeri == NULL) 1441da177e4SLinus Torvalds return NULL; 145543537bdSPaulo Marques timeri->owner = kstrdup(owner, GFP_KERNEL); 1461da177e4SLinus Torvalds if (! timeri->owner) { 1471da177e4SLinus Torvalds kfree(timeri); 1481da177e4SLinus Torvalds return NULL; 1491da177e4SLinus Torvalds } 1501da177e4SLinus Torvalds INIT_LIST_HEAD(&timeri->open_list); 1511da177e4SLinus Torvalds INIT_LIST_HEAD(&timeri->active_list); 1521da177e4SLinus Torvalds INIT_LIST_HEAD(&timeri->ack_list); 1531da177e4SLinus Torvalds INIT_LIST_HEAD(&timeri->slave_list_head); 1541da177e4SLinus Torvalds INIT_LIST_HEAD(&timeri->slave_active_head); 1551da177e4SLinus Torvalds 1561da177e4SLinus Torvalds return timeri; 1571da177e4SLinus Torvalds } 1586a34367eSTakashi Iwai EXPORT_SYMBOL(snd_timer_instance_new); 1596a34367eSTakashi Iwai 1606a34367eSTakashi Iwai void snd_timer_instance_free(struct snd_timer_instance *timeri) 1616a34367eSTakashi Iwai { 1626a34367eSTakashi Iwai if (timeri) { 1636a34367eSTakashi Iwai if (timeri->private_free) 1646a34367eSTakashi Iwai timeri->private_free(timeri); 1656a34367eSTakashi Iwai kfree(timeri->owner); 1666a34367eSTakashi Iwai kfree(timeri); 1676a34367eSTakashi Iwai } 1686a34367eSTakashi Iwai } 1696a34367eSTakashi Iwai EXPORT_SYMBOL(snd_timer_instance_free); 1701da177e4SLinus Torvalds 1711da177e4SLinus Torvalds /* 1721da177e4SLinus Torvalds * find a timer instance from the given timer id 1731da177e4SLinus Torvalds */ 17453d2f744STakashi Iwai static struct snd_timer *snd_timer_find(struct snd_timer_id *tid) 1751da177e4SLinus Torvalds { 17653d2f744STakashi Iwai struct snd_timer *timer = NULL; 1771da177e4SLinus Torvalds 1789244b2c3SJohannes Berg list_for_each_entry(timer, &snd_timer_list, device_list) { 1791da177e4SLinus Torvalds if (timer->tmr_class != tid->dev_class) 1801da177e4SLinus Torvalds continue; 1811da177e4SLinus Torvalds if ((timer->tmr_class == SNDRV_TIMER_CLASS_CARD || 1821da177e4SLinus Torvalds timer->tmr_class == SNDRV_TIMER_CLASS_PCM) && 1831da177e4SLinus Torvalds (timer->card == NULL || 1841da177e4SLinus Torvalds timer->card->number != tid->card)) 1851da177e4SLinus Torvalds continue; 1861da177e4SLinus Torvalds if (timer->tmr_device != tid->device) 1871da177e4SLinus Torvalds continue; 1881da177e4SLinus Torvalds if (timer->tmr_subdevice != tid->subdevice) 1891da177e4SLinus Torvalds continue; 1901da177e4SLinus Torvalds return timer; 1911da177e4SLinus Torvalds } 1921da177e4SLinus Torvalds return NULL; 1931da177e4SLinus Torvalds } 1941da177e4SLinus Torvalds 195ee2da997SJohannes Berg #ifdef CONFIG_MODULES 1961da177e4SLinus Torvalds 19753d2f744STakashi Iwai static void snd_timer_request(struct snd_timer_id *tid) 1981da177e4SLinus Torvalds { 1991da177e4SLinus Torvalds switch (tid->dev_class) { 2001da177e4SLinus Torvalds case SNDRV_TIMER_CLASS_GLOBAL: 2011da177e4SLinus Torvalds if (tid->device < timer_limit) 2021da177e4SLinus Torvalds request_module("snd-timer-%i", tid->device); 2031da177e4SLinus Torvalds break; 2041da177e4SLinus Torvalds case SNDRV_TIMER_CLASS_CARD: 2051da177e4SLinus Torvalds case SNDRV_TIMER_CLASS_PCM: 2061da177e4SLinus Torvalds if (tid->card < snd_ecards_limit) 2071da177e4SLinus Torvalds request_module("snd-card-%i", tid->card); 2081da177e4SLinus Torvalds break; 2091da177e4SLinus Torvalds default: 2101da177e4SLinus Torvalds break; 2111da177e4SLinus Torvalds } 2121da177e4SLinus Torvalds } 2131da177e4SLinus Torvalds 2141da177e4SLinus Torvalds #endif 2151da177e4SLinus Torvalds 216ebfc6de2STakashi Iwai /* move the slave if it belongs to the master; return 1 if match */ 217ebfc6de2STakashi Iwai static int check_matching_master_slave(struct snd_timer_instance *master, 218ebfc6de2STakashi Iwai struct snd_timer_instance *slave) 219ebfc6de2STakashi Iwai { 220ebfc6de2STakashi Iwai if (slave->slave_class != master->slave_class || 221ebfc6de2STakashi Iwai slave->slave_id != master->slave_id) 222ebfc6de2STakashi Iwai return 0; 223ebfc6de2STakashi Iwai if (master->timer->num_instances >= master->timer->max_instances) 224ebfc6de2STakashi Iwai return -EBUSY; 225ebfc6de2STakashi Iwai list_move_tail(&slave->open_list, &master->slave_list_head); 226ebfc6de2STakashi Iwai master->timer->num_instances++; 227ebfc6de2STakashi Iwai spin_lock_irq(&slave_active_lock); 228ebfc6de2STakashi Iwai spin_lock(&master->timer->lock); 229ebfc6de2STakashi Iwai slave->master = master; 230ebfc6de2STakashi Iwai slave->timer = master->timer; 231ebfc6de2STakashi Iwai if (slave->flags & SNDRV_TIMER_IFLG_RUNNING) 232ebfc6de2STakashi Iwai list_add_tail(&slave->active_list, &master->slave_active_head); 233ebfc6de2STakashi Iwai spin_unlock(&master->timer->lock); 234ebfc6de2STakashi Iwai spin_unlock_irq(&slave_active_lock); 235ebfc6de2STakashi Iwai return 1; 236ebfc6de2STakashi Iwai } 237ebfc6de2STakashi Iwai 2381da177e4SLinus Torvalds /* 2391da177e4SLinus Torvalds * look for a master instance matching with the slave id of the given slave. 2401da177e4SLinus Torvalds * when found, relink the open_link of the slave. 2411da177e4SLinus Torvalds * 2421da177e4SLinus Torvalds * call this with register_mutex down. 2431da177e4SLinus Torvalds */ 2449b7d869eSTakashi Iwai static int snd_timer_check_slave(struct snd_timer_instance *slave) 2451da177e4SLinus Torvalds { 24653d2f744STakashi Iwai struct snd_timer *timer; 24753d2f744STakashi Iwai struct snd_timer_instance *master; 248ebfc6de2STakashi Iwai int err = 0; 2491da177e4SLinus Torvalds 2501da177e4SLinus Torvalds /* FIXME: it's really dumb to look up all entries.. */ 2519244b2c3SJohannes Berg list_for_each_entry(timer, &snd_timer_list, device_list) { 2529244b2c3SJohannes Berg list_for_each_entry(master, &timer->open_list_head, open_list) { 253ebfc6de2STakashi Iwai err = check_matching_master_slave(master, slave); 254ebfc6de2STakashi Iwai if (err != 0) /* match found or error */ 255ebfc6de2STakashi Iwai goto out; 2561da177e4SLinus Torvalds } 2571da177e4SLinus Torvalds } 258ebfc6de2STakashi Iwai out: 259ebfc6de2STakashi Iwai return err < 0 ? err : 0; 2601da177e4SLinus Torvalds } 2611da177e4SLinus Torvalds 2621da177e4SLinus Torvalds /* 2631da177e4SLinus Torvalds * look for slave instances matching with the slave id of the given master. 2641da177e4SLinus Torvalds * when found, relink the open_link of slaves. 2651da177e4SLinus Torvalds * 2661da177e4SLinus Torvalds * call this with register_mutex down. 2671da177e4SLinus Torvalds */ 2689b7d869eSTakashi Iwai static int snd_timer_check_master(struct snd_timer_instance *master) 2691da177e4SLinus Torvalds { 2709244b2c3SJohannes Berg struct snd_timer_instance *slave, *tmp; 271ebfc6de2STakashi Iwai int err = 0; 2721da177e4SLinus Torvalds 2731da177e4SLinus Torvalds /* check all pending slaves */ 2749244b2c3SJohannes Berg list_for_each_entry_safe(slave, tmp, &snd_timer_slave_list, open_list) { 275ebfc6de2STakashi Iwai err = check_matching_master_slave(master, slave); 276ebfc6de2STakashi Iwai if (err < 0) 277ebfc6de2STakashi Iwai break; 2781da177e4SLinus Torvalds } 279ebfc6de2STakashi Iwai return err < 0 ? err : 0; 2801da177e4SLinus Torvalds } 2811da177e4SLinus Torvalds 28233bbb8a0STakashi Iwai static void snd_timer_close_locked(struct snd_timer_instance *timeri, 283a3933186STakashi Iwai struct device **card_devp_to_put); 2849b7d869eSTakashi Iwai 2851da177e4SLinus Torvalds /* 2861da177e4SLinus Torvalds * open a timer instance 2871da177e4SLinus Torvalds * when opening a master, the slave id must be here given. 2881da177e4SLinus Torvalds */ 2896a34367eSTakashi Iwai int snd_timer_open(struct snd_timer_instance *timeri, 2906a34367eSTakashi Iwai struct snd_timer_id *tid, 2911da177e4SLinus Torvalds unsigned int slave_id) 2921da177e4SLinus Torvalds { 29353d2f744STakashi Iwai struct snd_timer *timer; 294a3933186STakashi Iwai struct device *card_dev_to_put = NULL; 2959b7d869eSTakashi Iwai int err; 2961da177e4SLinus Torvalds 29741672c0cSTakashi Iwai mutex_lock(®ister_mutex); 2981da177e4SLinus Torvalds if (tid->dev_class == SNDRV_TIMER_CLASS_SLAVE) { 2991da177e4SLinus Torvalds /* open a slave instance */ 3001da177e4SLinus Torvalds if (tid->dev_sclass <= SNDRV_TIMER_SCLASS_NONE || 3011da177e4SLinus Torvalds tid->dev_sclass > SNDRV_TIMER_SCLASS_OSS_SEQUENCER) { 302cf74dcf3STakashi Iwai pr_debug("ALSA: timer: invalid slave class %i\n", 303cf74dcf3STakashi Iwai tid->dev_sclass); 30441672c0cSTakashi Iwai err = -EINVAL; 30541672c0cSTakashi Iwai goto unlock; 3061da177e4SLinus Torvalds } 307fdea53feSTakashi Iwai if (num_slaves >= MAX_SLAVE_INSTANCES) { 308fdea53feSTakashi Iwai err = -EBUSY; 309fdea53feSTakashi Iwai goto unlock; 310fdea53feSTakashi Iwai } 3111da177e4SLinus Torvalds timeri->slave_class = tid->dev_sclass; 3121da177e4SLinus Torvalds timeri->slave_id = tid->device; 3131da177e4SLinus Torvalds timeri->flags |= SNDRV_TIMER_IFLG_SLAVE; 3141da177e4SLinus Torvalds list_add_tail(&timeri->open_list, &snd_timer_slave_list); 315fdea53feSTakashi Iwai num_slaves++; 3169b7d869eSTakashi Iwai err = snd_timer_check_slave(timeri); 3170c4f09ceSTakashi Iwai goto list_added; 3181da177e4SLinus Torvalds } 3191da177e4SLinus Torvalds 3201da177e4SLinus Torvalds /* open a master instance */ 3211da177e4SLinus Torvalds timer = snd_timer_find(tid); 322ee2da997SJohannes Berg #ifdef CONFIG_MODULES 323ee2da997SJohannes Berg if (!timer) { 3241a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 3251da177e4SLinus Torvalds snd_timer_request(tid); 3261a60d4c5SIngo Molnar mutex_lock(®ister_mutex); 3271da177e4SLinus Torvalds timer = snd_timer_find(tid); 3281da177e4SLinus Torvalds } 3291da177e4SLinus Torvalds #endif 3302fd43d11SClemens Ladisch if (!timer) { 33141672c0cSTakashi Iwai err = -ENODEV; 33241672c0cSTakashi Iwai goto unlock; 3332fd43d11SClemens Ladisch } 3341da177e4SLinus Torvalds if (!list_empty(&timer->open_list_head)) { 335e7af6307STakashi Iwai struct snd_timer_instance *t = 336e7af6307STakashi Iwai list_entry(timer->open_list_head.next, 33753d2f744STakashi Iwai struct snd_timer_instance, open_list); 338e7af6307STakashi Iwai if (t->flags & SNDRV_TIMER_IFLG_EXCLUSIVE) { 33941672c0cSTakashi Iwai err = -EBUSY; 34041672c0cSTakashi Iwai goto unlock; 3411da177e4SLinus Torvalds } 3421da177e4SLinus Torvalds } 3439b7d869eSTakashi Iwai if (timer->num_instances >= timer->max_instances) { 34441672c0cSTakashi Iwai err = -EBUSY; 34541672c0cSTakashi Iwai goto unlock; 3469b7d869eSTakashi Iwai } 3476a34367eSTakashi Iwai if (!try_module_get(timer->module)) { 3486a34367eSTakashi Iwai err = -EBUSY; 34941672c0cSTakashi Iwai goto unlock; 3502fd43d11SClemens Ladisch } 351230323daSTakashi Iwai /* take a card refcount for safe disconnection */ 3526a34367eSTakashi Iwai if (timer->card) { 353230323daSTakashi Iwai get_device(&timer->card->card_dev); 3546a34367eSTakashi Iwai card_dev_to_put = &timer->card->card_dev; 3556a34367eSTakashi Iwai } 3568ddc0563SVegard Nossum 3578ddc0563SVegard Nossum if (list_empty(&timer->open_list_head) && timer->hw.open) { 35841672c0cSTakashi Iwai err = timer->hw.open(timer); 3598ddc0563SVegard Nossum if (err) { 3608ddc0563SVegard Nossum module_put(timer->module); 36141672c0cSTakashi Iwai goto unlock; 3628ddc0563SVegard Nossum } 3638ddc0563SVegard Nossum } 3648ddc0563SVegard Nossum 3656a34367eSTakashi Iwai timeri->timer = timer; 3666a34367eSTakashi Iwai timeri->slave_class = tid->dev_sclass; 3676a34367eSTakashi Iwai timeri->slave_id = slave_id; 3686a34367eSTakashi Iwai 3691da177e4SLinus Torvalds list_add_tail(&timeri->open_list, &timer->open_list_head); 3709b7d869eSTakashi Iwai timer->num_instances++; 3719b7d869eSTakashi Iwai err = snd_timer_check_master(timeri); 3720c4f09ceSTakashi Iwai list_added: 3736a34367eSTakashi Iwai if (err < 0) 374a3933186STakashi Iwai snd_timer_close_locked(timeri, &card_dev_to_put); 37541672c0cSTakashi Iwai 37641672c0cSTakashi Iwai unlock: 3771a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 378a3933186STakashi Iwai /* put_device() is called after unlock for avoiding deadlock */ 3796a34367eSTakashi Iwai if (err < 0 && card_dev_to_put) 380a3933186STakashi Iwai put_device(card_dev_to_put); 3819b7d869eSTakashi Iwai return err; 3821da177e4SLinus Torvalds } 38398856392STakashi Iwai EXPORT_SYMBOL(snd_timer_open); 3841da177e4SLinus Torvalds 3851da177e4SLinus Torvalds /* 3861da177e4SLinus Torvalds * close a timer instance 3879b7d869eSTakashi Iwai * call this with register_mutex down. 3881da177e4SLinus Torvalds */ 38933bbb8a0STakashi Iwai static void snd_timer_close_locked(struct snd_timer_instance *timeri, 390a3933186STakashi Iwai struct device **card_devp_to_put) 3911da177e4SLinus Torvalds { 392fe1b26c9STakashi Iwai struct snd_timer *timer = timeri->timer; 3939244b2c3SJohannes Berg struct snd_timer_instance *slave, *tmp; 3941da177e4SLinus Torvalds 395fe1b26c9STakashi Iwai if (timer) { 396fe1b26c9STakashi Iwai spin_lock_irq(&timer->lock); 397fe1b26c9STakashi Iwai timeri->flags |= SNDRV_TIMER_IFLG_DEAD; 398fe1b26c9STakashi Iwai spin_unlock_irq(&timer->lock); 399fe1b26c9STakashi Iwai } 400fe1b26c9STakashi Iwai 4016a34367eSTakashi Iwai if (!list_empty(&timeri->open_list)) { 4026a34367eSTakashi Iwai list_del_init(&timeri->open_list); 403fdea53feSTakashi Iwai if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) 404fdea53feSTakashi Iwai num_slaves--; 4056a34367eSTakashi Iwai } 4069984d1b5STakashi Iwai 4071da177e4SLinus Torvalds /* force to stop the timer */ 4081da177e4SLinus Torvalds snd_timer_stop(timeri); 4091da177e4SLinus Torvalds 4109984d1b5STakashi Iwai if (timer) { 4119b7d869eSTakashi Iwai timer->num_instances--; 4121da177e4SLinus Torvalds /* wait, until the active callback is finished */ 4131da177e4SLinus Torvalds spin_lock_irq(&timer->lock); 4141da177e4SLinus Torvalds while (timeri->flags & SNDRV_TIMER_IFLG_CALLBACK) { 4151da177e4SLinus Torvalds spin_unlock_irq(&timer->lock); 4161da177e4SLinus Torvalds udelay(10); 4171da177e4SLinus Torvalds spin_lock_irq(&timer->lock); 4181da177e4SLinus Torvalds } 4191da177e4SLinus Torvalds spin_unlock_irq(&timer->lock); 4209984d1b5STakashi Iwai 4211da177e4SLinus Torvalds /* remove slave links */ 422b5a663aaSTakashi Iwai spin_lock_irq(&slave_active_lock); 423b5a663aaSTakashi Iwai spin_lock(&timer->lock); 4246a34367eSTakashi Iwai timeri->timer = NULL; 4259244b2c3SJohannes Berg list_for_each_entry_safe(slave, tmp, &timeri->slave_list_head, 4269244b2c3SJohannes Berg open_list) { 4279244b2c3SJohannes Berg list_move_tail(&slave->open_list, &snd_timer_slave_list); 4289b7d869eSTakashi Iwai timer->num_instances--; 4291da177e4SLinus Torvalds slave->master = NULL; 4301da177e4SLinus Torvalds slave->timer = NULL; 431b5a663aaSTakashi Iwai list_del_init(&slave->ack_list); 432b5a663aaSTakashi Iwai list_del_init(&slave->active_list); 4331da177e4SLinus Torvalds } 434b5a663aaSTakashi Iwai spin_unlock(&timer->lock); 435b5a663aaSTakashi Iwai spin_unlock_irq(&slave_active_lock); 4369984d1b5STakashi Iwai 4379984d1b5STakashi Iwai /* slave doesn't need to release timer resources below */ 4389984d1b5STakashi Iwai if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) 4399984d1b5STakashi Iwai timer = NULL; 4401da177e4SLinus Torvalds } 4419984d1b5STakashi Iwai 4429984d1b5STakashi Iwai if (timer) { 4439984d1b5STakashi Iwai if (list_empty(&timer->open_list_head) && timer->hw.close) 4449984d1b5STakashi Iwai timer->hw.close(timer); 4459984d1b5STakashi Iwai /* release a card refcount for safe disconnection */ 4469984d1b5STakashi Iwai if (timer->card) 447a3933186STakashi Iwai *card_devp_to_put = &timer->card->card_dev; 448de24214dSClemens Ladisch module_put(timer->module); 4499984d1b5STakashi Iwai } 4501da177e4SLinus Torvalds } 4519b7d869eSTakashi Iwai 4529b7d869eSTakashi Iwai /* 4539b7d869eSTakashi Iwai * close a timer instance 4549b7d869eSTakashi Iwai */ 45533bbb8a0STakashi Iwai void snd_timer_close(struct snd_timer_instance *timeri) 4569b7d869eSTakashi Iwai { 457a3933186STakashi Iwai struct device *card_dev_to_put = NULL; 4589b7d869eSTakashi Iwai 4599b7d869eSTakashi Iwai if (snd_BUG_ON(!timeri)) 46033bbb8a0STakashi Iwai return; 4619b7d869eSTakashi Iwai 4629b7d869eSTakashi Iwai mutex_lock(®ister_mutex); 46333bbb8a0STakashi Iwai snd_timer_close_locked(timeri, &card_dev_to_put); 4649b7d869eSTakashi Iwai mutex_unlock(®ister_mutex); 465a3933186STakashi Iwai /* put_device() is called after unlock for avoiding deadlock */ 466a3933186STakashi Iwai if (card_dev_to_put) 467a3933186STakashi Iwai put_device(card_dev_to_put); 4689b7d869eSTakashi Iwai } 46998856392STakashi Iwai EXPORT_SYMBOL(snd_timer_close); 4701da177e4SLinus Torvalds 471fdcb5761STakashi Iwai static unsigned long snd_timer_hw_resolution(struct snd_timer *timer) 472fdcb5761STakashi Iwai { 473fdcb5761STakashi Iwai if (timer->hw.c_resolution) 474fdcb5761STakashi Iwai return timer->hw.c_resolution(timer); 475fdcb5761STakashi Iwai else 476fdcb5761STakashi Iwai return timer->hw.resolution; 477fdcb5761STakashi Iwai } 478fdcb5761STakashi Iwai 47953d2f744STakashi Iwai unsigned long snd_timer_resolution(struct snd_timer_instance *timeri) 4801da177e4SLinus Torvalds { 48153d2f744STakashi Iwai struct snd_timer * timer; 4829d4d207dSTakashi Iwai unsigned long ret = 0; 4839d4d207dSTakashi Iwai unsigned long flags; 4841da177e4SLinus Torvalds 4851da177e4SLinus Torvalds if (timeri == NULL) 4861da177e4SLinus Torvalds return 0; 487dd1f7ab8SMarkus Elfring timer = timeri->timer; 4889d4d207dSTakashi Iwai if (timer) { 4899d4d207dSTakashi Iwai spin_lock_irqsave(&timer->lock, flags); 4909d4d207dSTakashi Iwai ret = snd_timer_hw_resolution(timer); 4919d4d207dSTakashi Iwai spin_unlock_irqrestore(&timer->lock, flags); 4929d4d207dSTakashi Iwai } 4939d4d207dSTakashi Iwai return ret; 4941da177e4SLinus Torvalds } 49598856392STakashi Iwai EXPORT_SYMBOL(snd_timer_resolution); 4961da177e4SLinus Torvalds 49753d2f744STakashi Iwai static void snd_timer_notify1(struct snd_timer_instance *ti, int event) 4981da177e4SLinus Torvalds { 4999d4d207dSTakashi Iwai struct snd_timer *timer = ti->timer; 5001da177e4SLinus Torvalds unsigned long resolution = 0; 50153d2f744STakashi Iwai struct snd_timer_instance *ts; 502fcae40c9SBaolin Wang struct timespec64 tstamp; 5031da177e4SLinus Torvalds 504b751eef1SJaroslav Kysela if (timer_tstamp_monotonic) 505fcae40c9SBaolin Wang ktime_get_ts64(&tstamp); 506b751eef1SJaroslav Kysela else 507fcae40c9SBaolin Wang ktime_get_real_ts64(&tstamp); 5087eaa943cSTakashi Iwai if (snd_BUG_ON(event < SNDRV_TIMER_EVENT_START || 5097eaa943cSTakashi Iwai event > SNDRV_TIMER_EVENT_PAUSE)) 5107eaa943cSTakashi Iwai return; 5119d4d207dSTakashi Iwai if (timer && 5129d4d207dSTakashi Iwai (event == SNDRV_TIMER_EVENT_START || 5139d4d207dSTakashi Iwai event == SNDRV_TIMER_EVENT_CONTINUE)) 5149d4d207dSTakashi Iwai resolution = snd_timer_hw_resolution(timer); 5151da177e4SLinus Torvalds if (ti->ccallback) 516b30477d5SJaroslav Kysela ti->ccallback(ti, event, &tstamp, resolution); 5171da177e4SLinus Torvalds if (ti->flags & SNDRV_TIMER_IFLG_SLAVE) 5181da177e4SLinus Torvalds return; 5191da177e4SLinus Torvalds if (timer == NULL) 5201da177e4SLinus Torvalds return; 5211da177e4SLinus Torvalds if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE) 5221da177e4SLinus Torvalds return; 5239244b2c3SJohannes Berg list_for_each_entry(ts, &ti->slave_active_head, active_list) 5241da177e4SLinus Torvalds if (ts->ccallback) 525117159f0STakashi Iwai ts->ccallback(ts, event + 100, &tstamp, resolution); 5261da177e4SLinus Torvalds } 5271da177e4SLinus Torvalds 528f65e0d29STakashi Iwai /* start/continue a master timer */ 529f65e0d29STakashi Iwai static int snd_timer_start1(struct snd_timer_instance *timeri, 530f65e0d29STakashi Iwai bool start, unsigned long ticks) 5311da177e4SLinus Torvalds { 532f65e0d29STakashi Iwai struct snd_timer *timer; 533f65e0d29STakashi Iwai int result; 534f65e0d29STakashi Iwai unsigned long flags; 535f65e0d29STakashi Iwai 536f65e0d29STakashi Iwai timer = timeri->timer; 537f65e0d29STakashi Iwai if (!timer) 538f65e0d29STakashi Iwai return -EINVAL; 539f65e0d29STakashi Iwai 540f65e0d29STakashi Iwai spin_lock_irqsave(&timer->lock, flags); 541fe1b26c9STakashi Iwai if (timeri->flags & SNDRV_TIMER_IFLG_DEAD) { 542fe1b26c9STakashi Iwai result = -EINVAL; 543fe1b26c9STakashi Iwai goto unlock; 544fe1b26c9STakashi Iwai } 545f65e0d29STakashi Iwai if (timer->card && timer->card->shutdown) { 546f65e0d29STakashi Iwai result = -ENODEV; 547f65e0d29STakashi Iwai goto unlock; 548f65e0d29STakashi Iwai } 549f65e0d29STakashi Iwai if (timeri->flags & (SNDRV_TIMER_IFLG_RUNNING | 550f65e0d29STakashi Iwai SNDRV_TIMER_IFLG_START)) { 551f65e0d29STakashi Iwai result = -EBUSY; 552f65e0d29STakashi Iwai goto unlock; 553f65e0d29STakashi Iwai } 554f65e0d29STakashi Iwai 555f65e0d29STakashi Iwai if (start) 556f65e0d29STakashi Iwai timeri->ticks = timeri->cticks = ticks; 557f65e0d29STakashi Iwai else if (!timeri->cticks) 558f65e0d29STakashi Iwai timeri->cticks = 1; 559f65e0d29STakashi Iwai timeri->pticks = 0; 560f65e0d29STakashi Iwai 5615b7c757dSNicolas Kaiser list_move_tail(&timeri->active_list, &timer->active_list_head); 5621da177e4SLinus Torvalds if (timer->running) { 5631da177e4SLinus Torvalds if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE) 5641da177e4SLinus Torvalds goto __start_now; 5651da177e4SLinus Torvalds timer->flags |= SNDRV_TIMER_FLG_RESCHED; 5661da177e4SLinus Torvalds timeri->flags |= SNDRV_TIMER_IFLG_START; 567f65e0d29STakashi Iwai result = 1; /* delayed start */ 5681da177e4SLinus Torvalds } else { 569f65e0d29STakashi Iwai if (start) 570f65e0d29STakashi Iwai timer->sticks = ticks; 5711da177e4SLinus Torvalds timer->hw.start(timer); 5721da177e4SLinus Torvalds __start_now: 5731da177e4SLinus Torvalds timer->running++; 5741da177e4SLinus Torvalds timeri->flags |= SNDRV_TIMER_IFLG_RUNNING; 575f65e0d29STakashi Iwai result = 0; 5761da177e4SLinus Torvalds } 577f65e0d29STakashi Iwai snd_timer_notify1(timeri, start ? SNDRV_TIMER_EVENT_START : 578f65e0d29STakashi Iwai SNDRV_TIMER_EVENT_CONTINUE); 579f65e0d29STakashi Iwai unlock: 580f65e0d29STakashi Iwai spin_unlock_irqrestore(&timer->lock, flags); 581f65e0d29STakashi Iwai return result; 5821da177e4SLinus Torvalds } 5831da177e4SLinus Torvalds 584f65e0d29STakashi Iwai /* start/continue a slave timer */ 585f65e0d29STakashi Iwai static int snd_timer_start_slave(struct snd_timer_instance *timeri, 586f65e0d29STakashi Iwai bool start) 5871da177e4SLinus Torvalds { 5881da177e4SLinus Torvalds unsigned long flags; 589fe1b26c9STakashi Iwai int err; 5901da177e4SLinus Torvalds 5911da177e4SLinus Torvalds spin_lock_irqsave(&slave_active_lock, flags); 592fe1b26c9STakashi Iwai if (timeri->flags & SNDRV_TIMER_IFLG_DEAD) { 593fe1b26c9STakashi Iwai err = -EINVAL; 594fe1b26c9STakashi Iwai goto unlock; 595fe1b26c9STakashi Iwai } 596f784beb7STakashi Iwai if (timeri->flags & SNDRV_TIMER_IFLG_RUNNING) { 597fe1b26c9STakashi Iwai err = -EBUSY; 598fe1b26c9STakashi Iwai goto unlock; 599f784beb7STakashi Iwai } 6001da177e4SLinus Torvalds timeri->flags |= SNDRV_TIMER_IFLG_RUNNING; 601b5a663aaSTakashi Iwai if (timeri->master && timeri->timer) { 602b5a663aaSTakashi Iwai spin_lock(&timeri->timer->lock); 6036b172a85SClemens Ladisch list_add_tail(&timeri->active_list, 6046b172a85SClemens Ladisch &timeri->master->slave_active_head); 605f65e0d29STakashi Iwai snd_timer_notify1(timeri, start ? SNDRV_TIMER_EVENT_START : 606f65e0d29STakashi Iwai SNDRV_TIMER_EVENT_CONTINUE); 607b5a663aaSTakashi Iwai spin_unlock(&timeri->timer->lock); 608b5a663aaSTakashi Iwai } 609fe1b26c9STakashi Iwai err = 1; /* delayed start */ 610fe1b26c9STakashi Iwai unlock: 6111da177e4SLinus Torvalds spin_unlock_irqrestore(&slave_active_lock, flags); 612fe1b26c9STakashi Iwai return err; 6131da177e4SLinus Torvalds } 6141da177e4SLinus Torvalds 615f65e0d29STakashi Iwai /* stop/pause a master timer */ 616f65e0d29STakashi Iwai static int snd_timer_stop1(struct snd_timer_instance *timeri, bool stop) 6171da177e4SLinus Torvalds { 61853d2f744STakashi Iwai struct snd_timer *timer; 619f65e0d29STakashi Iwai int result = 0; 6201da177e4SLinus Torvalds unsigned long flags; 6211da177e4SLinus Torvalds 6221da177e4SLinus Torvalds timer = timeri->timer; 6231da177e4SLinus Torvalds if (!timer) 6241da177e4SLinus Torvalds return -EINVAL; 6251da177e4SLinus Torvalds spin_lock_irqsave(&timer->lock, flags); 626f784beb7STakashi Iwai if (!(timeri->flags & (SNDRV_TIMER_IFLG_RUNNING | 627f784beb7STakashi Iwai SNDRV_TIMER_IFLG_START))) { 628f65e0d29STakashi Iwai result = -EBUSY; 629f65e0d29STakashi Iwai goto unlock; 630f784beb7STakashi Iwai } 6311da177e4SLinus Torvalds list_del_init(&timeri->ack_list); 6321da177e4SLinus Torvalds list_del_init(&timeri->active_list); 633f65e0d29STakashi Iwai if (timer->card && timer->card->shutdown) 634f65e0d29STakashi Iwai goto unlock; 635f65e0d29STakashi Iwai if (stop) { 636f65e0d29STakashi Iwai timeri->cticks = timeri->ticks; 637f65e0d29STakashi Iwai timeri->pticks = 0; 638230323daSTakashi Iwai } 6391da177e4SLinus Torvalds if ((timeri->flags & SNDRV_TIMER_IFLG_RUNNING) && 6401da177e4SLinus Torvalds !(--timer->running)) { 6411da177e4SLinus Torvalds timer->hw.stop(timer); 6421da177e4SLinus Torvalds if (timer->flags & SNDRV_TIMER_FLG_RESCHED) { 6431da177e4SLinus Torvalds timer->flags &= ~SNDRV_TIMER_FLG_RESCHED; 6441da177e4SLinus Torvalds snd_timer_reschedule(timer, 0); 6451da177e4SLinus Torvalds if (timer->flags & SNDRV_TIMER_FLG_CHANGE) { 6461da177e4SLinus Torvalds timer->flags &= ~SNDRV_TIMER_FLG_CHANGE; 6471da177e4SLinus Torvalds timer->hw.start(timer); 6481da177e4SLinus Torvalds } 6491da177e4SLinus Torvalds } 6501da177e4SLinus Torvalds } 651c3b16813STakashi Iwai timeri->flags &= ~(SNDRV_TIMER_IFLG_RUNNING | SNDRV_TIMER_IFLG_START); 6529f8a7658STakashi Iwai if (stop) 6539f8a7658STakashi Iwai timeri->flags &= ~SNDRV_TIMER_IFLG_PAUSED; 6549f8a7658STakashi Iwai else 6559f8a7658STakashi Iwai timeri->flags |= SNDRV_TIMER_IFLG_PAUSED; 656f65e0d29STakashi Iwai snd_timer_notify1(timeri, stop ? SNDRV_TIMER_EVENT_STOP : 6573ae18097SBen Hutchings SNDRV_TIMER_EVENT_PAUSE); 658f65e0d29STakashi Iwai unlock: 6591da177e4SLinus Torvalds spin_unlock_irqrestore(&timer->lock, flags); 660f65e0d29STakashi Iwai return result; 661f65e0d29STakashi Iwai } 662f65e0d29STakashi Iwai 663f65e0d29STakashi Iwai /* stop/pause a slave timer */ 664f65e0d29STakashi Iwai static int snd_timer_stop_slave(struct snd_timer_instance *timeri, bool stop) 665f65e0d29STakashi Iwai { 666f65e0d29STakashi Iwai unsigned long flags; 667f65e0d29STakashi Iwai 668f65e0d29STakashi Iwai spin_lock_irqsave(&slave_active_lock, flags); 669f65e0d29STakashi Iwai if (!(timeri->flags & SNDRV_TIMER_IFLG_RUNNING)) { 670f65e0d29STakashi Iwai spin_unlock_irqrestore(&slave_active_lock, flags); 671f65e0d29STakashi Iwai return -EBUSY; 672f65e0d29STakashi Iwai } 673f65e0d29STakashi Iwai timeri->flags &= ~SNDRV_TIMER_IFLG_RUNNING; 674f65e0d29STakashi Iwai if (timeri->timer) { 675f65e0d29STakashi Iwai spin_lock(&timeri->timer->lock); 676f65e0d29STakashi Iwai list_del_init(&timeri->ack_list); 677f65e0d29STakashi Iwai list_del_init(&timeri->active_list); 678f65e0d29STakashi Iwai snd_timer_notify1(timeri, stop ? SNDRV_TIMER_EVENT_STOP : 6793ae18097SBen Hutchings SNDRV_TIMER_EVENT_PAUSE); 680f65e0d29STakashi Iwai spin_unlock(&timeri->timer->lock); 681f65e0d29STakashi Iwai } 682f65e0d29STakashi Iwai spin_unlock_irqrestore(&slave_active_lock, flags); 6831da177e4SLinus Torvalds return 0; 6841da177e4SLinus Torvalds } 6851da177e4SLinus Torvalds 6861da177e4SLinus Torvalds /* 687f65e0d29STakashi Iwai * start the timer instance 688f65e0d29STakashi Iwai */ 689f65e0d29STakashi Iwai int snd_timer_start(struct snd_timer_instance *timeri, unsigned int ticks) 690f65e0d29STakashi Iwai { 691f65e0d29STakashi Iwai if (timeri == NULL || ticks < 1) 692f65e0d29STakashi Iwai return -EINVAL; 693f65e0d29STakashi Iwai if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) 694f65e0d29STakashi Iwai return snd_timer_start_slave(timeri, true); 695f65e0d29STakashi Iwai else 696f65e0d29STakashi Iwai return snd_timer_start1(timeri, true, ticks); 697f65e0d29STakashi Iwai } 69898856392STakashi Iwai EXPORT_SYMBOL(snd_timer_start); 699f65e0d29STakashi Iwai 700f65e0d29STakashi Iwai /* 7011da177e4SLinus Torvalds * stop the timer instance. 7021da177e4SLinus Torvalds * 7031da177e4SLinus Torvalds * do not call this from the timer callback! 7041da177e4SLinus Torvalds */ 70553d2f744STakashi Iwai int snd_timer_stop(struct snd_timer_instance *timeri) 7061da177e4SLinus Torvalds { 707f65e0d29STakashi Iwai if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) 708f65e0d29STakashi Iwai return snd_timer_stop_slave(timeri, true); 709f65e0d29STakashi Iwai else 710f65e0d29STakashi Iwai return snd_timer_stop1(timeri, true); 7111da177e4SLinus Torvalds } 71298856392STakashi Iwai EXPORT_SYMBOL(snd_timer_stop); 7131da177e4SLinus Torvalds 7141da177e4SLinus Torvalds /* 7151da177e4SLinus Torvalds * start again.. the tick is kept. 7161da177e4SLinus Torvalds */ 71753d2f744STakashi Iwai int snd_timer_continue(struct snd_timer_instance *timeri) 7181da177e4SLinus Torvalds { 7199f8a7658STakashi Iwai /* timer can continue only after pause */ 7209f8a7658STakashi Iwai if (!(timeri->flags & SNDRV_TIMER_IFLG_PAUSED)) 7219f8a7658STakashi Iwai return -EINVAL; 7229f8a7658STakashi Iwai 7231da177e4SLinus Torvalds if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) 724f65e0d29STakashi Iwai return snd_timer_start_slave(timeri, false); 725f65e0d29STakashi Iwai else 726f65e0d29STakashi Iwai return snd_timer_start1(timeri, false, 0); 7271da177e4SLinus Torvalds } 72898856392STakashi Iwai EXPORT_SYMBOL(snd_timer_continue); 7291da177e4SLinus Torvalds 7301da177e4SLinus Torvalds /* 7311da177e4SLinus Torvalds * pause.. remember the ticks left 7321da177e4SLinus Torvalds */ 73353d2f744STakashi Iwai int snd_timer_pause(struct snd_timer_instance * timeri) 7341da177e4SLinus Torvalds { 735f65e0d29STakashi Iwai if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) 736f65e0d29STakashi Iwai return snd_timer_stop_slave(timeri, false); 737f65e0d29STakashi Iwai else 738f65e0d29STakashi Iwai return snd_timer_stop1(timeri, false); 7391da177e4SLinus Torvalds } 74098856392STakashi Iwai EXPORT_SYMBOL(snd_timer_pause); 7411da177e4SLinus Torvalds 7421da177e4SLinus Torvalds /* 7431da177e4SLinus Torvalds * reschedule the timer 7441da177e4SLinus Torvalds * 7451da177e4SLinus Torvalds * start pending instances and check the scheduling ticks. 7461da177e4SLinus Torvalds * when the scheduling ticks is changed set CHANGE flag to reprogram the timer. 7471da177e4SLinus Torvalds */ 74853d2f744STakashi Iwai static void snd_timer_reschedule(struct snd_timer * timer, unsigned long ticks_left) 7491da177e4SLinus Torvalds { 75053d2f744STakashi Iwai struct snd_timer_instance *ti; 7511da177e4SLinus Torvalds unsigned long ticks = ~0UL; 7521da177e4SLinus Torvalds 7539244b2c3SJohannes Berg list_for_each_entry(ti, &timer->active_list_head, active_list) { 7541da177e4SLinus Torvalds if (ti->flags & SNDRV_TIMER_IFLG_START) { 7551da177e4SLinus Torvalds ti->flags &= ~SNDRV_TIMER_IFLG_START; 7561da177e4SLinus Torvalds ti->flags |= SNDRV_TIMER_IFLG_RUNNING; 7571da177e4SLinus Torvalds timer->running++; 7581da177e4SLinus Torvalds } 7591da177e4SLinus Torvalds if (ti->flags & SNDRV_TIMER_IFLG_RUNNING) { 7601da177e4SLinus Torvalds if (ticks > ti->cticks) 7611da177e4SLinus Torvalds ticks = ti->cticks; 7621da177e4SLinus Torvalds } 7631da177e4SLinus Torvalds } 7641da177e4SLinus Torvalds if (ticks == ~0UL) { 7651da177e4SLinus Torvalds timer->flags &= ~SNDRV_TIMER_FLG_RESCHED; 7661da177e4SLinus Torvalds return; 7671da177e4SLinus Torvalds } 7681da177e4SLinus Torvalds if (ticks > timer->hw.ticks) 7691da177e4SLinus Torvalds ticks = timer->hw.ticks; 7701da177e4SLinus Torvalds if (ticks_left != ticks) 7711da177e4SLinus Torvalds timer->flags |= SNDRV_TIMER_FLG_CHANGE; 7721da177e4SLinus Torvalds timer->sticks = ticks; 7731da177e4SLinus Torvalds } 7741da177e4SLinus Torvalds 7758748b850STakashi Iwai /* call callbacks in timer ack list */ 7768748b850STakashi Iwai static void snd_timer_process_callbacks(struct snd_timer *timer, 7778748b850STakashi Iwai struct list_head *head) 7781da177e4SLinus Torvalds { 77953d2f744STakashi Iwai struct snd_timer_instance *ti; 7801da177e4SLinus Torvalds unsigned long resolution, ticks; 7811da177e4SLinus Torvalds 7828748b850STakashi Iwai while (!list_empty(head)) { 7838748b850STakashi Iwai ti = list_first_entry(head, struct snd_timer_instance, 7848748b850STakashi Iwai ack_list); 7851da177e4SLinus Torvalds 7861da177e4SLinus Torvalds /* remove from ack_list and make empty */ 787df55531bSTakashi Iwai list_del_init(&ti->ack_list); 7881da177e4SLinus Torvalds 789fe1b26c9STakashi Iwai if (!(ti->flags & SNDRV_TIMER_IFLG_DEAD)) { 7901da177e4SLinus Torvalds ticks = ti->pticks; 7911da177e4SLinus Torvalds ti->pticks = 0; 7921da177e4SLinus Torvalds resolution = ti->resolution; 7931da177e4SLinus Torvalds ti->flags |= SNDRV_TIMER_IFLG_CALLBACK; 7941da177e4SLinus Torvalds spin_unlock(&timer->lock); 7951da177e4SLinus Torvalds if (ti->callback) 7961da177e4SLinus Torvalds ti->callback(ti, resolution, ticks); 7971da177e4SLinus Torvalds spin_lock(&timer->lock); 7981da177e4SLinus Torvalds ti->flags &= ~SNDRV_TIMER_IFLG_CALLBACK; 7991da177e4SLinus Torvalds } 8001da177e4SLinus Torvalds } 8018748b850STakashi Iwai } 8028748b850STakashi Iwai 8037bb4a8a2STakashi Iwai /* clear pending instances from ack list */ 8047bb4a8a2STakashi Iwai static void snd_timer_clear_callbacks(struct snd_timer *timer, 8057bb4a8a2STakashi Iwai struct list_head *head) 8067bb4a8a2STakashi Iwai { 8077bb4a8a2STakashi Iwai unsigned long flags; 8087bb4a8a2STakashi Iwai 8097bb4a8a2STakashi Iwai spin_lock_irqsave(&timer->lock, flags); 8107bb4a8a2STakashi Iwai while (!list_empty(head)) 8117bb4a8a2STakashi Iwai list_del_init(head->next); 8127bb4a8a2STakashi Iwai spin_unlock_irqrestore(&timer->lock, flags); 8137bb4a8a2STakashi Iwai } 8147bb4a8a2STakashi Iwai 8158748b850STakashi Iwai /* 8168748b850STakashi Iwai * timer tasklet 8178748b850STakashi Iwai * 8188748b850STakashi Iwai */ 8198748b850STakashi Iwai static void snd_timer_tasklet(unsigned long arg) 8208748b850STakashi Iwai { 8218748b850STakashi Iwai struct snd_timer *timer = (struct snd_timer *) arg; 8228748b850STakashi Iwai unsigned long flags; 8238748b850STakashi Iwai 8247bb4a8a2STakashi Iwai if (timer->card && timer->card->shutdown) { 8257bb4a8a2STakashi Iwai snd_timer_clear_callbacks(timer, &timer->sack_list_head); 8268748b850STakashi Iwai return; 8277bb4a8a2STakashi Iwai } 8288748b850STakashi Iwai 8298748b850STakashi Iwai spin_lock_irqsave(&timer->lock, flags); 8308748b850STakashi Iwai snd_timer_process_callbacks(timer, &timer->sack_list_head); 8312999ff5bSTakashi Iwai spin_unlock_irqrestore(&timer->lock, flags); 8321da177e4SLinus Torvalds } 8331da177e4SLinus Torvalds 8341da177e4SLinus Torvalds /* 8351da177e4SLinus Torvalds * timer interrupt 8361da177e4SLinus Torvalds * 8371da177e4SLinus Torvalds * ticks_left is usually equal to timer->sticks. 8381da177e4SLinus Torvalds * 8391da177e4SLinus Torvalds */ 84053d2f744STakashi Iwai void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left) 8411da177e4SLinus Torvalds { 8429244b2c3SJohannes Berg struct snd_timer_instance *ti, *ts, *tmp; 8438748b850STakashi Iwai unsigned long resolution; 8448748b850STakashi Iwai struct list_head *ack_list_head; 845b32425acSTakashi Iwai unsigned long flags; 8461da177e4SLinus Torvalds int use_tasklet = 0; 8471da177e4SLinus Torvalds 8481da177e4SLinus Torvalds if (timer == NULL) 8491da177e4SLinus Torvalds return; 8501da177e4SLinus Torvalds 8517bb4a8a2STakashi Iwai if (timer->card && timer->card->shutdown) { 8527bb4a8a2STakashi Iwai snd_timer_clear_callbacks(timer, &timer->ack_list_head); 853230323daSTakashi Iwai return; 8547bb4a8a2STakashi Iwai } 855230323daSTakashi Iwai 856b32425acSTakashi Iwai spin_lock_irqsave(&timer->lock, flags); 8571da177e4SLinus Torvalds 8581da177e4SLinus Torvalds /* remember the current resolution */ 859fdcb5761STakashi Iwai resolution = snd_timer_hw_resolution(timer); 8601da177e4SLinus Torvalds 8611da177e4SLinus Torvalds /* loop for all active instances 8629244b2c3SJohannes Berg * Here we cannot use list_for_each_entry because the active_list of a 8636b172a85SClemens Ladisch * processed instance is relinked to done_list_head before the callback 8646b172a85SClemens Ladisch * is called. 8651da177e4SLinus Torvalds */ 8669244b2c3SJohannes Berg list_for_each_entry_safe(ti, tmp, &timer->active_list_head, 8679244b2c3SJohannes Berg active_list) { 868fe1b26c9STakashi Iwai if (ti->flags & SNDRV_TIMER_IFLG_DEAD) 869fe1b26c9STakashi Iwai continue; 8701da177e4SLinus Torvalds if (!(ti->flags & SNDRV_TIMER_IFLG_RUNNING)) 8711da177e4SLinus Torvalds continue; 8721da177e4SLinus Torvalds ti->pticks += ticks_left; 8731da177e4SLinus Torvalds ti->resolution = resolution; 8741da177e4SLinus Torvalds if (ti->cticks < ticks_left) 8751da177e4SLinus Torvalds ti->cticks = 0; 8761da177e4SLinus Torvalds else 8771da177e4SLinus Torvalds ti->cticks -= ticks_left; 8781da177e4SLinus Torvalds if (ti->cticks) /* not expired */ 8791da177e4SLinus Torvalds continue; 8801da177e4SLinus Torvalds if (ti->flags & SNDRV_TIMER_IFLG_AUTO) { 8811da177e4SLinus Torvalds ti->cticks = ti->ticks; 8821da177e4SLinus Torvalds } else { 8831da177e4SLinus Torvalds ti->flags &= ~SNDRV_TIMER_IFLG_RUNNING; 884094fd3beSTakashi Iwai --timer->running; 885ee8413b0STakashi Iwai list_del_init(&ti->active_list); 8861da177e4SLinus Torvalds } 8871da177e4SLinus Torvalds if ((timer->hw.flags & SNDRV_TIMER_HW_TASKLET) || 8886b172a85SClemens Ladisch (ti->flags & SNDRV_TIMER_IFLG_FAST)) 8896b172a85SClemens Ladisch ack_list_head = &timer->ack_list_head; 8906b172a85SClemens Ladisch else 8916b172a85SClemens Ladisch ack_list_head = &timer->sack_list_head; 8926b172a85SClemens Ladisch if (list_empty(&ti->ack_list)) 8936b172a85SClemens Ladisch list_add_tail(&ti->ack_list, ack_list_head); 8949244b2c3SJohannes Berg list_for_each_entry(ts, &ti->slave_active_head, active_list) { 8951da177e4SLinus Torvalds ts->pticks = ti->pticks; 8961da177e4SLinus Torvalds ts->resolution = resolution; 8976b172a85SClemens Ladisch if (list_empty(&ts->ack_list)) 8986b172a85SClemens Ladisch list_add_tail(&ts->ack_list, ack_list_head); 8991da177e4SLinus Torvalds } 9001da177e4SLinus Torvalds } 9011da177e4SLinus Torvalds if (timer->flags & SNDRV_TIMER_FLG_RESCHED) 902cd93fe47SClemens Ladisch snd_timer_reschedule(timer, timer->sticks); 9031da177e4SLinus Torvalds if (timer->running) { 9041da177e4SLinus Torvalds if (timer->hw.flags & SNDRV_TIMER_HW_STOP) { 9051da177e4SLinus Torvalds timer->hw.stop(timer); 9061da177e4SLinus Torvalds timer->flags |= SNDRV_TIMER_FLG_CHANGE; 9071da177e4SLinus Torvalds } 9081da177e4SLinus Torvalds if (!(timer->hw.flags & SNDRV_TIMER_HW_AUTO) || 9091da177e4SLinus Torvalds (timer->flags & SNDRV_TIMER_FLG_CHANGE)) { 9101da177e4SLinus Torvalds /* restart timer */ 9111da177e4SLinus Torvalds timer->flags &= ~SNDRV_TIMER_FLG_CHANGE; 9121da177e4SLinus Torvalds timer->hw.start(timer); 9131da177e4SLinus Torvalds } 9141da177e4SLinus Torvalds } else { 9151da177e4SLinus Torvalds timer->hw.stop(timer); 9161da177e4SLinus Torvalds } 9171da177e4SLinus Torvalds 9181da177e4SLinus Torvalds /* now process all fast callbacks */ 9198748b850STakashi Iwai snd_timer_process_callbacks(timer, &timer->ack_list_head); 9201da177e4SLinus Torvalds 9211da177e4SLinus Torvalds /* do we have any slow callbacks? */ 9221da177e4SLinus Torvalds use_tasklet = !list_empty(&timer->sack_list_head); 923b32425acSTakashi Iwai spin_unlock_irqrestore(&timer->lock, flags); 9241da177e4SLinus Torvalds 9251da177e4SLinus Torvalds if (use_tasklet) 9261f04128aSTakashi Iwai tasklet_schedule(&timer->task_queue); 9271da177e4SLinus Torvalds } 92898856392STakashi Iwai EXPORT_SYMBOL(snd_timer_interrupt); 9291da177e4SLinus Torvalds 9301da177e4SLinus Torvalds /* 9311da177e4SLinus Torvalds 9321da177e4SLinus Torvalds */ 9331da177e4SLinus Torvalds 93453d2f744STakashi Iwai int snd_timer_new(struct snd_card *card, char *id, struct snd_timer_id *tid, 93553d2f744STakashi Iwai struct snd_timer **rtimer) 9361da177e4SLinus Torvalds { 93753d2f744STakashi Iwai struct snd_timer *timer; 9381da177e4SLinus Torvalds int err; 939*f15ee210STakashi Iwai static const struct snd_device_ops ops = { 9401da177e4SLinus Torvalds .dev_free = snd_timer_dev_free, 9411da177e4SLinus Torvalds .dev_register = snd_timer_dev_register, 942c461482cSTakashi Iwai .dev_disconnect = snd_timer_dev_disconnect, 9431da177e4SLinus Torvalds }; 9441da177e4SLinus Torvalds 9457eaa943cSTakashi Iwai if (snd_BUG_ON(!tid)) 9467eaa943cSTakashi Iwai return -EINVAL; 947d10ee9c5SSrikanth K H if (tid->dev_class == SNDRV_TIMER_CLASS_CARD || 948d10ee9c5SSrikanth K H tid->dev_class == SNDRV_TIMER_CLASS_PCM) { 949d10ee9c5SSrikanth K H if (WARN_ON(!card)) 950d10ee9c5SSrikanth K H return -EINVAL; 951d10ee9c5SSrikanth K H } 9527eaa943cSTakashi Iwai if (rtimer) 9531da177e4SLinus Torvalds *rtimer = NULL; 954ca2c0966STakashi Iwai timer = kzalloc(sizeof(*timer), GFP_KERNEL); 955ec0e9937STakashi Iwai if (!timer) 9561da177e4SLinus Torvalds return -ENOMEM; 9571da177e4SLinus Torvalds timer->tmr_class = tid->dev_class; 9581da177e4SLinus Torvalds timer->card = card; 9591da177e4SLinus Torvalds timer->tmr_device = tid->device; 9601da177e4SLinus Torvalds timer->tmr_subdevice = tid->subdevice; 9611da177e4SLinus Torvalds if (id) 9621da177e4SLinus Torvalds strlcpy(timer->id, id, sizeof(timer->id)); 9636b760bb2SVegard Nossum timer->sticks = 1; 9641da177e4SLinus Torvalds INIT_LIST_HEAD(&timer->device_list); 9651da177e4SLinus Torvalds INIT_LIST_HEAD(&timer->open_list_head); 9661da177e4SLinus Torvalds INIT_LIST_HEAD(&timer->active_list_head); 9671da177e4SLinus Torvalds INIT_LIST_HEAD(&timer->ack_list_head); 9681da177e4SLinus Torvalds INIT_LIST_HEAD(&timer->sack_list_head); 9691da177e4SLinus Torvalds spin_lock_init(&timer->lock); 9706b172a85SClemens Ladisch tasklet_init(&timer->task_queue, snd_timer_tasklet, 9716b172a85SClemens Ladisch (unsigned long)timer); 9729b7d869eSTakashi Iwai timer->max_instances = 1000; /* default limit per timer */ 9731da177e4SLinus Torvalds if (card != NULL) { 974de24214dSClemens Ladisch timer->module = card->module; 9756b172a85SClemens Ladisch err = snd_device_new(card, SNDRV_DEV_TIMER, timer, &ops); 9766b172a85SClemens Ladisch if (err < 0) { 9771da177e4SLinus Torvalds snd_timer_free(timer); 9781da177e4SLinus Torvalds return err; 9791da177e4SLinus Torvalds } 9801da177e4SLinus Torvalds } 9817eaa943cSTakashi Iwai if (rtimer) 9821da177e4SLinus Torvalds *rtimer = timer; 9831da177e4SLinus Torvalds return 0; 9841da177e4SLinus Torvalds } 98598856392STakashi Iwai EXPORT_SYMBOL(snd_timer_new); 9861da177e4SLinus Torvalds 98753d2f744STakashi Iwai static int snd_timer_free(struct snd_timer *timer) 9881da177e4SLinus Torvalds { 9897eaa943cSTakashi Iwai if (!timer) 9907eaa943cSTakashi Iwai return 0; 991c461482cSTakashi Iwai 992c461482cSTakashi Iwai mutex_lock(®ister_mutex); 993c461482cSTakashi Iwai if (! list_empty(&timer->open_list_head)) { 994c461482cSTakashi Iwai struct list_head *p, *n; 995c461482cSTakashi Iwai struct snd_timer_instance *ti; 996cf74dcf3STakashi Iwai pr_warn("ALSA: timer %p is busy?\n", timer); 997c461482cSTakashi Iwai list_for_each_safe(p, n, &timer->open_list_head) { 998c461482cSTakashi Iwai list_del_init(p); 999c461482cSTakashi Iwai ti = list_entry(p, struct snd_timer_instance, open_list); 1000c461482cSTakashi Iwai ti->timer = NULL; 1001c461482cSTakashi Iwai } 1002c461482cSTakashi Iwai } 1003c461482cSTakashi Iwai list_del(&timer->device_list); 1004c461482cSTakashi Iwai mutex_unlock(®ister_mutex); 1005c461482cSTakashi Iwai 10061da177e4SLinus Torvalds if (timer->private_free) 10071da177e4SLinus Torvalds timer->private_free(timer); 10081da177e4SLinus Torvalds kfree(timer); 10091da177e4SLinus Torvalds return 0; 10101da177e4SLinus Torvalds } 10111da177e4SLinus Torvalds 101253d2f744STakashi Iwai static int snd_timer_dev_free(struct snd_device *device) 10131da177e4SLinus Torvalds { 101453d2f744STakashi Iwai struct snd_timer *timer = device->device_data; 10151da177e4SLinus Torvalds return snd_timer_free(timer); 10161da177e4SLinus Torvalds } 10171da177e4SLinus Torvalds 101853d2f744STakashi Iwai static int snd_timer_dev_register(struct snd_device *dev) 10191da177e4SLinus Torvalds { 102053d2f744STakashi Iwai struct snd_timer *timer = dev->device_data; 102153d2f744STakashi Iwai struct snd_timer *timer1; 10221da177e4SLinus Torvalds 10237eaa943cSTakashi Iwai if (snd_BUG_ON(!timer || !timer->hw.start || !timer->hw.stop)) 10247eaa943cSTakashi Iwai return -ENXIO; 10251da177e4SLinus Torvalds if (!(timer->hw.flags & SNDRV_TIMER_HW_SLAVE) && 10261da177e4SLinus Torvalds !timer->hw.resolution && timer->hw.c_resolution == NULL) 10271da177e4SLinus Torvalds return -EINVAL; 10281da177e4SLinus Torvalds 10291a60d4c5SIngo Molnar mutex_lock(®ister_mutex); 10309244b2c3SJohannes Berg list_for_each_entry(timer1, &snd_timer_list, device_list) { 10311da177e4SLinus Torvalds if (timer1->tmr_class > timer->tmr_class) 10321da177e4SLinus Torvalds break; 10331da177e4SLinus Torvalds if (timer1->tmr_class < timer->tmr_class) 10341da177e4SLinus Torvalds continue; 10351da177e4SLinus Torvalds if (timer1->card && timer->card) { 10361da177e4SLinus Torvalds if (timer1->card->number > timer->card->number) 10371da177e4SLinus Torvalds break; 10381da177e4SLinus Torvalds if (timer1->card->number < timer->card->number) 10391da177e4SLinus Torvalds continue; 10401da177e4SLinus Torvalds } 10411da177e4SLinus Torvalds if (timer1->tmr_device > timer->tmr_device) 10421da177e4SLinus Torvalds break; 10431da177e4SLinus Torvalds if (timer1->tmr_device < timer->tmr_device) 10441da177e4SLinus Torvalds continue; 10451da177e4SLinus Torvalds if (timer1->tmr_subdevice > timer->tmr_subdevice) 10461da177e4SLinus Torvalds break; 10471da177e4SLinus Torvalds if (timer1->tmr_subdevice < timer->tmr_subdevice) 10481da177e4SLinus Torvalds continue; 10491da177e4SLinus Torvalds /* conflicts.. */ 10501a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 10511da177e4SLinus Torvalds return -EBUSY; 10521da177e4SLinus Torvalds } 10539244b2c3SJohannes Berg list_add_tail(&timer->device_list, &timer1->device_list); 10541a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 10551da177e4SLinus Torvalds return 0; 10561da177e4SLinus Torvalds } 10571da177e4SLinus Torvalds 1058c461482cSTakashi Iwai static int snd_timer_dev_disconnect(struct snd_device *device) 10591da177e4SLinus Torvalds { 106053d2f744STakashi Iwai struct snd_timer *timer = device->device_data; 1061230323daSTakashi Iwai struct snd_timer_instance *ti; 1062230323daSTakashi Iwai 1063c461482cSTakashi Iwai mutex_lock(®ister_mutex); 1064c461482cSTakashi Iwai list_del_init(&timer->device_list); 1065230323daSTakashi Iwai /* wake up pending sleepers */ 1066230323daSTakashi Iwai list_for_each_entry(ti, &timer->open_list_head, open_list) { 106740ed9444STakashi Iwai if (ti->disconnect) 106840ed9444STakashi Iwai ti->disconnect(ti); 1069230323daSTakashi Iwai } 1070c461482cSTakashi Iwai mutex_unlock(®ister_mutex); 1071c461482cSTakashi Iwai return 0; 10721da177e4SLinus Torvalds } 10731da177e4SLinus Torvalds 1074fcae40c9SBaolin Wang void snd_timer_notify(struct snd_timer *timer, int event, struct timespec64 *tstamp) 10751da177e4SLinus Torvalds { 10761da177e4SLinus Torvalds unsigned long flags; 10771da177e4SLinus Torvalds unsigned long resolution = 0; 107853d2f744STakashi Iwai struct snd_timer_instance *ti, *ts; 10791da177e4SLinus Torvalds 1080230323daSTakashi Iwai if (timer->card && timer->card->shutdown) 1081230323daSTakashi Iwai return; 10827c22f1aaSTakashi Iwai if (! (timer->hw.flags & SNDRV_TIMER_HW_SLAVE)) 10837c22f1aaSTakashi Iwai return; 10847eaa943cSTakashi Iwai if (snd_BUG_ON(event < SNDRV_TIMER_EVENT_MSTART || 10857eaa943cSTakashi Iwai event > SNDRV_TIMER_EVENT_MRESUME)) 10867eaa943cSTakashi Iwai return; 10871da177e4SLinus Torvalds spin_lock_irqsave(&timer->lock, flags); 1088a501dfa3SJaroslav Kysela if (event == SNDRV_TIMER_EVENT_MSTART || 1089a501dfa3SJaroslav Kysela event == SNDRV_TIMER_EVENT_MCONTINUE || 1090fdcb5761STakashi Iwai event == SNDRV_TIMER_EVENT_MRESUME) 1091fdcb5761STakashi Iwai resolution = snd_timer_hw_resolution(timer); 10929244b2c3SJohannes Berg list_for_each_entry(ti, &timer->active_list_head, active_list) { 10931da177e4SLinus Torvalds if (ti->ccallback) 10941da177e4SLinus Torvalds ti->ccallback(ti, event, tstamp, resolution); 10959244b2c3SJohannes Berg list_for_each_entry(ts, &ti->slave_active_head, active_list) 10961da177e4SLinus Torvalds if (ts->ccallback) 10971da177e4SLinus Torvalds ts->ccallback(ts, event, tstamp, resolution); 10981da177e4SLinus Torvalds } 10991da177e4SLinus Torvalds spin_unlock_irqrestore(&timer->lock, flags); 11001da177e4SLinus Torvalds } 110198856392STakashi Iwai EXPORT_SYMBOL(snd_timer_notify); 11021da177e4SLinus Torvalds 11031da177e4SLinus Torvalds /* 11041da177e4SLinus Torvalds * exported functions for global timers 11051da177e4SLinus Torvalds */ 110653d2f744STakashi Iwai int snd_timer_global_new(char *id, int device, struct snd_timer **rtimer) 11071da177e4SLinus Torvalds { 110853d2f744STakashi Iwai struct snd_timer_id tid; 11091da177e4SLinus Torvalds 11101da177e4SLinus Torvalds tid.dev_class = SNDRV_TIMER_CLASS_GLOBAL; 11111da177e4SLinus Torvalds tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE; 11121da177e4SLinus Torvalds tid.card = -1; 11131da177e4SLinus Torvalds tid.device = device; 11141da177e4SLinus Torvalds tid.subdevice = 0; 11151da177e4SLinus Torvalds return snd_timer_new(NULL, id, &tid, rtimer); 11161da177e4SLinus Torvalds } 111798856392STakashi Iwai EXPORT_SYMBOL(snd_timer_global_new); 11181da177e4SLinus Torvalds 111953d2f744STakashi Iwai int snd_timer_global_free(struct snd_timer *timer) 11201da177e4SLinus Torvalds { 11211da177e4SLinus Torvalds return snd_timer_free(timer); 11221da177e4SLinus Torvalds } 112398856392STakashi Iwai EXPORT_SYMBOL(snd_timer_global_free); 11241da177e4SLinus Torvalds 112553d2f744STakashi Iwai int snd_timer_global_register(struct snd_timer *timer) 11261da177e4SLinus Torvalds { 112753d2f744STakashi Iwai struct snd_device dev; 11281da177e4SLinus Torvalds 11291da177e4SLinus Torvalds memset(&dev, 0, sizeof(dev)); 11301da177e4SLinus Torvalds dev.device_data = timer; 11311da177e4SLinus Torvalds return snd_timer_dev_register(&dev); 11321da177e4SLinus Torvalds } 113398856392STakashi Iwai EXPORT_SYMBOL(snd_timer_global_register); 11341da177e4SLinus Torvalds 11351da177e4SLinus Torvalds /* 11361da177e4SLinus Torvalds * System timer 11371da177e4SLinus Torvalds */ 11381da177e4SLinus Torvalds 11391da177e4SLinus Torvalds struct snd_timer_system_private { 11401da177e4SLinus Torvalds struct timer_list tlist; 114138e9a80fSKees Cook struct snd_timer *snd_timer; 11421da177e4SLinus Torvalds unsigned long last_expires; 11431da177e4SLinus Torvalds unsigned long last_jiffies; 11441da177e4SLinus Torvalds unsigned long correction; 11451da177e4SLinus Torvalds }; 11461da177e4SLinus Torvalds 114738e9a80fSKees Cook static void snd_timer_s_function(struct timer_list *t) 11481da177e4SLinus Torvalds { 114938e9a80fSKees Cook struct snd_timer_system_private *priv = from_timer(priv, t, 115038e9a80fSKees Cook tlist); 115138e9a80fSKees Cook struct snd_timer *timer = priv->snd_timer; 11521da177e4SLinus Torvalds unsigned long jiff = jiffies; 11531da177e4SLinus Torvalds if (time_after(jiff, priv->last_expires)) 11546ed5eff0SClemens Ladisch priv->correction += (long)jiff - (long)priv->last_expires; 11551da177e4SLinus Torvalds snd_timer_interrupt(timer, (long)jiff - (long)priv->last_jiffies); 11561da177e4SLinus Torvalds } 11571da177e4SLinus Torvalds 115853d2f744STakashi Iwai static int snd_timer_s_start(struct snd_timer * timer) 11591da177e4SLinus Torvalds { 11601da177e4SLinus Torvalds struct snd_timer_system_private *priv; 11611da177e4SLinus Torvalds unsigned long njiff; 11621da177e4SLinus Torvalds 11631da177e4SLinus Torvalds priv = (struct snd_timer_system_private *) timer->private_data; 11641da177e4SLinus Torvalds njiff = (priv->last_jiffies = jiffies); 11651da177e4SLinus Torvalds if (priv->correction > timer->sticks - 1) { 11661da177e4SLinus Torvalds priv->correction -= timer->sticks - 1; 11671da177e4SLinus Torvalds njiff++; 11681da177e4SLinus Torvalds } else { 11691da177e4SLinus Torvalds njiff += timer->sticks - priv->correction; 117017f48ec3SClemens Ladisch priv->correction = 0; 11711da177e4SLinus Torvalds } 11724a07083eSTakashi Iwai priv->last_expires = njiff; 11734a07083eSTakashi Iwai mod_timer(&priv->tlist, njiff); 11741da177e4SLinus Torvalds return 0; 11751da177e4SLinus Torvalds } 11761da177e4SLinus Torvalds 117753d2f744STakashi Iwai static int snd_timer_s_stop(struct snd_timer * timer) 11781da177e4SLinus Torvalds { 11791da177e4SLinus Torvalds struct snd_timer_system_private *priv; 11801da177e4SLinus Torvalds unsigned long jiff; 11811da177e4SLinus Torvalds 11821da177e4SLinus Torvalds priv = (struct snd_timer_system_private *) timer->private_data; 11831da177e4SLinus Torvalds del_timer(&priv->tlist); 11841da177e4SLinus Torvalds jiff = jiffies; 11851da177e4SLinus Torvalds if (time_before(jiff, priv->last_expires)) 11861da177e4SLinus Torvalds timer->sticks = priv->last_expires - jiff; 11871da177e4SLinus Torvalds else 11881da177e4SLinus Torvalds timer->sticks = 1; 1189de2696d8SClemens Ladisch priv->correction = 0; 11901da177e4SLinus Torvalds return 0; 11911da177e4SLinus Torvalds } 11921da177e4SLinus Torvalds 1193f146357fSTakashi Iwai static int snd_timer_s_close(struct snd_timer *timer) 1194f146357fSTakashi Iwai { 1195f146357fSTakashi Iwai struct snd_timer_system_private *priv; 1196f146357fSTakashi Iwai 1197f146357fSTakashi Iwai priv = (struct snd_timer_system_private *)timer->private_data; 1198f146357fSTakashi Iwai del_timer_sync(&priv->tlist); 1199f146357fSTakashi Iwai return 0; 1200f146357fSTakashi Iwai } 1201f146357fSTakashi Iwai 120253d2f744STakashi Iwai static struct snd_timer_hardware snd_timer_system = 12031da177e4SLinus Torvalds { 12041da177e4SLinus Torvalds .flags = SNDRV_TIMER_HW_FIRST | SNDRV_TIMER_HW_TASKLET, 12051da177e4SLinus Torvalds .resolution = 1000000000L / HZ, 12061da177e4SLinus Torvalds .ticks = 10000000L, 1207f146357fSTakashi Iwai .close = snd_timer_s_close, 12081da177e4SLinus Torvalds .start = snd_timer_s_start, 12091da177e4SLinus Torvalds .stop = snd_timer_s_stop 12101da177e4SLinus Torvalds }; 12111da177e4SLinus Torvalds 121253d2f744STakashi Iwai static void snd_timer_free_system(struct snd_timer *timer) 12131da177e4SLinus Torvalds { 12141da177e4SLinus Torvalds kfree(timer->private_data); 12151da177e4SLinus Torvalds } 12161da177e4SLinus Torvalds 12171da177e4SLinus Torvalds static int snd_timer_register_system(void) 12181da177e4SLinus Torvalds { 121953d2f744STakashi Iwai struct snd_timer *timer; 12201da177e4SLinus Torvalds struct snd_timer_system_private *priv; 12211da177e4SLinus Torvalds int err; 12221da177e4SLinus Torvalds 12236b172a85SClemens Ladisch err = snd_timer_global_new("system", SNDRV_TIMER_GLOBAL_SYSTEM, &timer); 12246b172a85SClemens Ladisch if (err < 0) 12251da177e4SLinus Torvalds return err; 12261da177e4SLinus Torvalds strcpy(timer->name, "system timer"); 12271da177e4SLinus Torvalds timer->hw = snd_timer_system; 1228ca2c0966STakashi Iwai priv = kzalloc(sizeof(*priv), GFP_KERNEL); 12291da177e4SLinus Torvalds if (priv == NULL) { 12301da177e4SLinus Torvalds snd_timer_free(timer); 12311da177e4SLinus Torvalds return -ENOMEM; 12321da177e4SLinus Torvalds } 123338e9a80fSKees Cook priv->snd_timer = timer; 123438e9a80fSKees Cook timer_setup(&priv->tlist, snd_timer_s_function, 0); 12351da177e4SLinus Torvalds timer->private_data = priv; 12361da177e4SLinus Torvalds timer->private_free = snd_timer_free_system; 12371da177e4SLinus Torvalds return snd_timer_global_register(timer); 12381da177e4SLinus Torvalds } 12391da177e4SLinus Torvalds 1240cd6a6503SJie Yang #ifdef CONFIG_SND_PROC_FS 12411da177e4SLinus Torvalds /* 12421da177e4SLinus Torvalds * Info interface 12431da177e4SLinus Torvalds */ 12441da177e4SLinus Torvalds 124553d2f744STakashi Iwai static void snd_timer_proc_read(struct snd_info_entry *entry, 124653d2f744STakashi Iwai struct snd_info_buffer *buffer) 12471da177e4SLinus Torvalds { 124853d2f744STakashi Iwai struct snd_timer *timer; 124953d2f744STakashi Iwai struct snd_timer_instance *ti; 12501da177e4SLinus Torvalds 12511a60d4c5SIngo Molnar mutex_lock(®ister_mutex); 12529244b2c3SJohannes Berg list_for_each_entry(timer, &snd_timer_list, device_list) { 1253230323daSTakashi Iwai if (timer->card && timer->card->shutdown) 1254230323daSTakashi Iwai continue; 12551da177e4SLinus Torvalds switch (timer->tmr_class) { 12561da177e4SLinus Torvalds case SNDRV_TIMER_CLASS_GLOBAL: 12571da177e4SLinus Torvalds snd_iprintf(buffer, "G%i: ", timer->tmr_device); 12581da177e4SLinus Torvalds break; 12591da177e4SLinus Torvalds case SNDRV_TIMER_CLASS_CARD: 12606b172a85SClemens Ladisch snd_iprintf(buffer, "C%i-%i: ", 12616b172a85SClemens Ladisch timer->card->number, timer->tmr_device); 12621da177e4SLinus Torvalds break; 12631da177e4SLinus Torvalds case SNDRV_TIMER_CLASS_PCM: 12646b172a85SClemens Ladisch snd_iprintf(buffer, "P%i-%i-%i: ", timer->card->number, 12656b172a85SClemens Ladisch timer->tmr_device, timer->tmr_subdevice); 12661da177e4SLinus Torvalds break; 12671da177e4SLinus Torvalds default: 12686b172a85SClemens Ladisch snd_iprintf(buffer, "?%i-%i-%i-%i: ", timer->tmr_class, 12696b172a85SClemens Ladisch timer->card ? timer->card->number : -1, 12706b172a85SClemens Ladisch timer->tmr_device, timer->tmr_subdevice); 12711da177e4SLinus Torvalds } 12721da177e4SLinus Torvalds snd_iprintf(buffer, "%s :", timer->name); 12731da177e4SLinus Torvalds if (timer->hw.resolution) 12746b172a85SClemens Ladisch snd_iprintf(buffer, " %lu.%03luus (%lu ticks)", 12756b172a85SClemens Ladisch timer->hw.resolution / 1000, 12766b172a85SClemens Ladisch timer->hw.resolution % 1000, 12776b172a85SClemens Ladisch timer->hw.ticks); 12781da177e4SLinus Torvalds if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE) 12791da177e4SLinus Torvalds snd_iprintf(buffer, " SLAVE"); 12801da177e4SLinus Torvalds snd_iprintf(buffer, "\n"); 12819244b2c3SJohannes Berg list_for_each_entry(ti, &timer->open_list_head, open_list) 12826b172a85SClemens Ladisch snd_iprintf(buffer, " Client %s : %s\n", 12831da177e4SLinus Torvalds ti->owner ? ti->owner : "unknown", 12846b172a85SClemens Ladisch ti->flags & (SNDRV_TIMER_IFLG_START | 12856b172a85SClemens Ladisch SNDRV_TIMER_IFLG_RUNNING) 12866b172a85SClemens Ladisch ? "running" : "stopped"); 12871da177e4SLinus Torvalds } 12881a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 12891da177e4SLinus Torvalds } 12901da177e4SLinus Torvalds 12916581f4e7STakashi Iwai static struct snd_info_entry *snd_timer_proc_entry; 1292e28563ccSTakashi Iwai 1293e28563ccSTakashi Iwai static void __init snd_timer_proc_init(void) 1294e28563ccSTakashi Iwai { 1295e28563ccSTakashi Iwai struct snd_info_entry *entry; 1296e28563ccSTakashi Iwai 1297e28563ccSTakashi Iwai entry = snd_info_create_module_entry(THIS_MODULE, "timers", NULL); 1298e28563ccSTakashi Iwai if (entry != NULL) { 1299e28563ccSTakashi Iwai entry->c.text.read = snd_timer_proc_read; 1300e28563ccSTakashi Iwai if (snd_info_register(entry) < 0) { 1301e28563ccSTakashi Iwai snd_info_free_entry(entry); 1302e28563ccSTakashi Iwai entry = NULL; 1303e28563ccSTakashi Iwai } 1304e28563ccSTakashi Iwai } 1305e28563ccSTakashi Iwai snd_timer_proc_entry = entry; 1306e28563ccSTakashi Iwai } 1307e28563ccSTakashi Iwai 1308e28563ccSTakashi Iwai static void __exit snd_timer_proc_done(void) 1309e28563ccSTakashi Iwai { 1310746d4a02STakashi Iwai snd_info_free_entry(snd_timer_proc_entry); 1311e28563ccSTakashi Iwai } 1312cd6a6503SJie Yang #else /* !CONFIG_SND_PROC_FS */ 1313e28563ccSTakashi Iwai #define snd_timer_proc_init() 1314e28563ccSTakashi Iwai #define snd_timer_proc_done() 1315e28563ccSTakashi Iwai #endif 1316e28563ccSTakashi Iwai 13171da177e4SLinus Torvalds /* 13181da177e4SLinus Torvalds * USER SPACE interface 13191da177e4SLinus Torvalds */ 13201da177e4SLinus Torvalds 132153d2f744STakashi Iwai static void snd_timer_user_interrupt(struct snd_timer_instance *timeri, 13221da177e4SLinus Torvalds unsigned long resolution, 13231da177e4SLinus Torvalds unsigned long ticks) 13241da177e4SLinus Torvalds { 132553d2f744STakashi Iwai struct snd_timer_user *tu = timeri->callback_data; 132653d2f744STakashi Iwai struct snd_timer_read *r; 13271da177e4SLinus Torvalds int prev; 13281da177e4SLinus Torvalds 13291da177e4SLinus Torvalds spin_lock(&tu->qlock); 13301da177e4SLinus Torvalds if (tu->qused > 0) { 13311da177e4SLinus Torvalds prev = tu->qtail == 0 ? tu->queue_size - 1 : tu->qtail - 1; 13321da177e4SLinus Torvalds r = &tu->queue[prev]; 13331da177e4SLinus Torvalds if (r->resolution == resolution) { 13341da177e4SLinus Torvalds r->ticks += ticks; 13351da177e4SLinus Torvalds goto __wake; 13361da177e4SLinus Torvalds } 13371da177e4SLinus Torvalds } 13381da177e4SLinus Torvalds if (tu->qused >= tu->queue_size) { 13391da177e4SLinus Torvalds tu->overrun++; 13401da177e4SLinus Torvalds } else { 13411da177e4SLinus Torvalds r = &tu->queue[tu->qtail++]; 13421da177e4SLinus Torvalds tu->qtail %= tu->queue_size; 13431da177e4SLinus Torvalds r->resolution = resolution; 13441da177e4SLinus Torvalds r->ticks = ticks; 13451da177e4SLinus Torvalds tu->qused++; 13461da177e4SLinus Torvalds } 13471da177e4SLinus Torvalds __wake: 13481da177e4SLinus Torvalds spin_unlock(&tu->qlock); 13491da177e4SLinus Torvalds kill_fasync(&tu->fasync, SIGIO, POLL_IN); 13501da177e4SLinus Torvalds wake_up(&tu->qchange_sleep); 13511da177e4SLinus Torvalds } 13521da177e4SLinus Torvalds 135353d2f744STakashi Iwai static void snd_timer_user_append_to_tqueue(struct snd_timer_user *tu, 135407094ae6SBaolin Wang struct snd_timer_tread64 *tread) 13551da177e4SLinus Torvalds { 13561da177e4SLinus Torvalds if (tu->qused >= tu->queue_size) { 13571da177e4SLinus Torvalds tu->overrun++; 13581da177e4SLinus Torvalds } else { 13591da177e4SLinus Torvalds memcpy(&tu->tqueue[tu->qtail++], tread, sizeof(*tread)); 13601da177e4SLinus Torvalds tu->qtail %= tu->queue_size; 13611da177e4SLinus Torvalds tu->qused++; 13621da177e4SLinus Torvalds } 13631da177e4SLinus Torvalds } 13641da177e4SLinus Torvalds 136553d2f744STakashi Iwai static void snd_timer_user_ccallback(struct snd_timer_instance *timeri, 136653d2f744STakashi Iwai int event, 1367fcae40c9SBaolin Wang struct timespec64 *tstamp, 13681da177e4SLinus Torvalds unsigned long resolution) 13691da177e4SLinus Torvalds { 137053d2f744STakashi Iwai struct snd_timer_user *tu = timeri->callback_data; 137107094ae6SBaolin Wang struct snd_timer_tread64 r1; 1372bfe70783SDan Carpenter unsigned long flags; 13731da177e4SLinus Torvalds 13746b172a85SClemens Ladisch if (event >= SNDRV_TIMER_EVENT_START && 13756b172a85SClemens Ladisch event <= SNDRV_TIMER_EVENT_PAUSE) 13761da177e4SLinus Torvalds tu->tstamp = *tstamp; 13771da177e4SLinus Torvalds if ((tu->filter & (1 << event)) == 0 || !tu->tread) 13781da177e4SLinus Torvalds return; 13799a47e9cfSKangjie Lu memset(&r1, 0, sizeof(r1)); 13801da177e4SLinus Torvalds r1.event = event; 138107094ae6SBaolin Wang r1.tstamp_sec = tstamp->tv_sec; 138207094ae6SBaolin Wang r1.tstamp_nsec = tstamp->tv_nsec; 13831da177e4SLinus Torvalds r1.val = resolution; 1384bfe70783SDan Carpenter spin_lock_irqsave(&tu->qlock, flags); 13851da177e4SLinus Torvalds snd_timer_user_append_to_tqueue(tu, &r1); 1386bfe70783SDan Carpenter spin_unlock_irqrestore(&tu->qlock, flags); 13871da177e4SLinus Torvalds kill_fasync(&tu->fasync, SIGIO, POLL_IN); 13881da177e4SLinus Torvalds wake_up(&tu->qchange_sleep); 13891da177e4SLinus Torvalds } 13901da177e4SLinus Torvalds 139140ed9444STakashi Iwai static void snd_timer_user_disconnect(struct snd_timer_instance *timeri) 139240ed9444STakashi Iwai { 139340ed9444STakashi Iwai struct snd_timer_user *tu = timeri->callback_data; 139440ed9444STakashi Iwai 139540ed9444STakashi Iwai tu->disconnected = true; 139640ed9444STakashi Iwai wake_up(&tu->qchange_sleep); 139740ed9444STakashi Iwai } 139840ed9444STakashi Iwai 139953d2f744STakashi Iwai static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri, 14001da177e4SLinus Torvalds unsigned long resolution, 14011da177e4SLinus Torvalds unsigned long ticks) 14021da177e4SLinus Torvalds { 140353d2f744STakashi Iwai struct snd_timer_user *tu = timeri->callback_data; 140407094ae6SBaolin Wang struct snd_timer_tread64 *r, r1; 1405fcae40c9SBaolin Wang struct timespec64 tstamp; 14061da177e4SLinus Torvalds int prev, append = 0; 14071da177e4SLinus Torvalds 1408a8c006aaSDan Carpenter memset(&r1, 0, sizeof(r1)); 140907799e75STakashi Iwai memset(&tstamp, 0, sizeof(tstamp)); 14101da177e4SLinus Torvalds spin_lock(&tu->qlock); 14116b172a85SClemens Ladisch if ((tu->filter & ((1 << SNDRV_TIMER_EVENT_RESOLUTION) | 14126b172a85SClemens Ladisch (1 << SNDRV_TIMER_EVENT_TICK))) == 0) { 14131da177e4SLinus Torvalds spin_unlock(&tu->qlock); 14141da177e4SLinus Torvalds return; 14151da177e4SLinus Torvalds } 1416b751eef1SJaroslav Kysela if (tu->last_resolution != resolution || ticks > 0) { 1417b751eef1SJaroslav Kysela if (timer_tstamp_monotonic) 1418fcae40c9SBaolin Wang ktime_get_ts64(&tstamp); 1419b751eef1SJaroslav Kysela else 1420fcae40c9SBaolin Wang ktime_get_real_ts64(&tstamp); 1421b751eef1SJaroslav Kysela } 14226b172a85SClemens Ladisch if ((tu->filter & (1 << SNDRV_TIMER_EVENT_RESOLUTION)) && 14236b172a85SClemens Ladisch tu->last_resolution != resolution) { 14241da177e4SLinus Torvalds r1.event = SNDRV_TIMER_EVENT_RESOLUTION; 142507094ae6SBaolin Wang r1.tstamp_sec = tstamp.tv_sec; 142607094ae6SBaolin Wang r1.tstamp_nsec = tstamp.tv_nsec; 14271da177e4SLinus Torvalds r1.val = resolution; 14281da177e4SLinus Torvalds snd_timer_user_append_to_tqueue(tu, &r1); 14291da177e4SLinus Torvalds tu->last_resolution = resolution; 14301da177e4SLinus Torvalds append++; 14311da177e4SLinus Torvalds } 14321da177e4SLinus Torvalds if ((tu->filter & (1 << SNDRV_TIMER_EVENT_TICK)) == 0) 14331da177e4SLinus Torvalds goto __wake; 14341da177e4SLinus Torvalds if (ticks == 0) 14351da177e4SLinus Torvalds goto __wake; 14361da177e4SLinus Torvalds if (tu->qused > 0) { 14371da177e4SLinus Torvalds prev = tu->qtail == 0 ? tu->queue_size - 1 : tu->qtail - 1; 14381da177e4SLinus Torvalds r = &tu->tqueue[prev]; 14391da177e4SLinus Torvalds if (r->event == SNDRV_TIMER_EVENT_TICK) { 144007094ae6SBaolin Wang r->tstamp_sec = tstamp.tv_sec; 144107094ae6SBaolin Wang r->tstamp_nsec = tstamp.tv_nsec; 14421da177e4SLinus Torvalds r->val += ticks; 14431da177e4SLinus Torvalds append++; 14441da177e4SLinus Torvalds goto __wake; 14451da177e4SLinus Torvalds } 14461da177e4SLinus Torvalds } 14471da177e4SLinus Torvalds r1.event = SNDRV_TIMER_EVENT_TICK; 144807094ae6SBaolin Wang r1.tstamp_sec = tstamp.tv_sec; 144907094ae6SBaolin Wang r1.tstamp_nsec = tstamp.tv_nsec; 14501da177e4SLinus Torvalds r1.val = ticks; 14511da177e4SLinus Torvalds snd_timer_user_append_to_tqueue(tu, &r1); 14521da177e4SLinus Torvalds append++; 14531da177e4SLinus Torvalds __wake: 14541da177e4SLinus Torvalds spin_unlock(&tu->qlock); 14551da177e4SLinus Torvalds if (append == 0) 14561da177e4SLinus Torvalds return; 14571da177e4SLinus Torvalds kill_fasync(&tu->fasync, SIGIO, POLL_IN); 14581da177e4SLinus Torvalds wake_up(&tu->qchange_sleep); 14591da177e4SLinus Torvalds } 14601da177e4SLinus Torvalds 1461890e2cb5STakashi Iwai static int realloc_user_queue(struct snd_timer_user *tu, int size) 1462890e2cb5STakashi Iwai { 1463890e2cb5STakashi Iwai struct snd_timer_read *queue = NULL; 146407094ae6SBaolin Wang struct snd_timer_tread64 *tqueue = NULL; 1465890e2cb5STakashi Iwai 1466890e2cb5STakashi Iwai if (tu->tread) { 1467890e2cb5STakashi Iwai tqueue = kcalloc(size, sizeof(*tqueue), GFP_KERNEL); 1468890e2cb5STakashi Iwai if (!tqueue) 1469890e2cb5STakashi Iwai return -ENOMEM; 1470890e2cb5STakashi Iwai } else { 1471890e2cb5STakashi Iwai queue = kcalloc(size, sizeof(*queue), GFP_KERNEL); 1472890e2cb5STakashi Iwai if (!queue) 1473890e2cb5STakashi Iwai return -ENOMEM; 1474890e2cb5STakashi Iwai } 1475890e2cb5STakashi Iwai 1476890e2cb5STakashi Iwai spin_lock_irq(&tu->qlock); 1477890e2cb5STakashi Iwai kfree(tu->queue); 1478890e2cb5STakashi Iwai kfree(tu->tqueue); 1479890e2cb5STakashi Iwai tu->queue_size = size; 1480890e2cb5STakashi Iwai tu->queue = queue; 1481890e2cb5STakashi Iwai tu->tqueue = tqueue; 1482890e2cb5STakashi Iwai tu->qhead = tu->qtail = tu->qused = 0; 1483890e2cb5STakashi Iwai spin_unlock_irq(&tu->qlock); 1484890e2cb5STakashi Iwai 1485890e2cb5STakashi Iwai return 0; 1486890e2cb5STakashi Iwai } 1487890e2cb5STakashi Iwai 14881da177e4SLinus Torvalds static int snd_timer_user_open(struct inode *inode, struct file *file) 14891da177e4SLinus Torvalds { 149053d2f744STakashi Iwai struct snd_timer_user *tu; 149102f4865fSTakashi Iwai int err; 149202f4865fSTakashi Iwai 1493c5bf68feSKirill Smelkov err = stream_open(inode, file); 149402f4865fSTakashi Iwai if (err < 0) 149502f4865fSTakashi Iwai return err; 14961da177e4SLinus Torvalds 1497ca2c0966STakashi Iwai tu = kzalloc(sizeof(*tu), GFP_KERNEL); 14981da177e4SLinus Torvalds if (tu == NULL) 14991da177e4SLinus Torvalds return -ENOMEM; 15001da177e4SLinus Torvalds spin_lock_init(&tu->qlock); 15011da177e4SLinus Torvalds init_waitqueue_head(&tu->qchange_sleep); 1502af368027STakashi Iwai mutex_init(&tu->ioctl_lock); 15031da177e4SLinus Torvalds tu->ticks = 1; 1504890e2cb5STakashi Iwai if (realloc_user_queue(tu, 128) < 0) { 15051da177e4SLinus Torvalds kfree(tu); 15061da177e4SLinus Torvalds return -ENOMEM; 15071da177e4SLinus Torvalds } 15081da177e4SLinus Torvalds file->private_data = tu; 15091da177e4SLinus Torvalds return 0; 15101da177e4SLinus Torvalds } 15111da177e4SLinus Torvalds 15121da177e4SLinus Torvalds static int snd_timer_user_release(struct inode *inode, struct file *file) 15131da177e4SLinus Torvalds { 151453d2f744STakashi Iwai struct snd_timer_user *tu; 15151da177e4SLinus Torvalds 15161da177e4SLinus Torvalds if (file->private_data) { 15171da177e4SLinus Torvalds tu = file->private_data; 15181da177e4SLinus Torvalds file->private_data = NULL; 1519af368027STakashi Iwai mutex_lock(&tu->ioctl_lock); 15206a34367eSTakashi Iwai if (tu->timeri) { 15211da177e4SLinus Torvalds snd_timer_close(tu->timeri); 15226a34367eSTakashi Iwai snd_timer_instance_free(tu->timeri); 15236a34367eSTakashi Iwai } 1524af368027STakashi Iwai mutex_unlock(&tu->ioctl_lock); 15251da177e4SLinus Torvalds kfree(tu->queue); 15261da177e4SLinus Torvalds kfree(tu->tqueue); 15271da177e4SLinus Torvalds kfree(tu); 15281da177e4SLinus Torvalds } 15291da177e4SLinus Torvalds return 0; 15301da177e4SLinus Torvalds } 15311da177e4SLinus Torvalds 153253d2f744STakashi Iwai static void snd_timer_user_zero_id(struct snd_timer_id *id) 15331da177e4SLinus Torvalds { 15341da177e4SLinus Torvalds id->dev_class = SNDRV_TIMER_CLASS_NONE; 15351da177e4SLinus Torvalds id->dev_sclass = SNDRV_TIMER_SCLASS_NONE; 15361da177e4SLinus Torvalds id->card = -1; 15371da177e4SLinus Torvalds id->device = -1; 15381da177e4SLinus Torvalds id->subdevice = -1; 15391da177e4SLinus Torvalds } 15401da177e4SLinus Torvalds 154153d2f744STakashi Iwai static void snd_timer_user_copy_id(struct snd_timer_id *id, struct snd_timer *timer) 15421da177e4SLinus Torvalds { 15431da177e4SLinus Torvalds id->dev_class = timer->tmr_class; 15441da177e4SLinus Torvalds id->dev_sclass = SNDRV_TIMER_SCLASS_NONE; 15451da177e4SLinus Torvalds id->card = timer->card ? timer->card->number : -1; 15461da177e4SLinus Torvalds id->device = timer->tmr_device; 15471da177e4SLinus Torvalds id->subdevice = timer->tmr_subdevice; 15481da177e4SLinus Torvalds } 15491da177e4SLinus Torvalds 155053d2f744STakashi Iwai static int snd_timer_user_next_device(struct snd_timer_id __user *_tid) 15511da177e4SLinus Torvalds { 155253d2f744STakashi Iwai struct snd_timer_id id; 155353d2f744STakashi Iwai struct snd_timer *timer; 15541da177e4SLinus Torvalds struct list_head *p; 15551da177e4SLinus Torvalds 15561da177e4SLinus Torvalds if (copy_from_user(&id, _tid, sizeof(id))) 15571da177e4SLinus Torvalds return -EFAULT; 15581a60d4c5SIngo Molnar mutex_lock(®ister_mutex); 15591da177e4SLinus Torvalds if (id.dev_class < 0) { /* first item */ 15601da177e4SLinus Torvalds if (list_empty(&snd_timer_list)) 15611da177e4SLinus Torvalds snd_timer_user_zero_id(&id); 15621da177e4SLinus Torvalds else { 15639dfba380SClemens Ladisch timer = list_entry(snd_timer_list.next, 156453d2f744STakashi Iwai struct snd_timer, device_list); 15651da177e4SLinus Torvalds snd_timer_user_copy_id(&id, timer); 15661da177e4SLinus Torvalds } 15671da177e4SLinus Torvalds } else { 15681da177e4SLinus Torvalds switch (id.dev_class) { 15691da177e4SLinus Torvalds case SNDRV_TIMER_CLASS_GLOBAL: 15701da177e4SLinus Torvalds id.device = id.device < 0 ? 0 : id.device + 1; 15711da177e4SLinus Torvalds list_for_each(p, &snd_timer_list) { 157253d2f744STakashi Iwai timer = list_entry(p, struct snd_timer, device_list); 15731da177e4SLinus Torvalds if (timer->tmr_class > SNDRV_TIMER_CLASS_GLOBAL) { 15741da177e4SLinus Torvalds snd_timer_user_copy_id(&id, timer); 15751da177e4SLinus Torvalds break; 15761da177e4SLinus Torvalds } 15771da177e4SLinus Torvalds if (timer->tmr_device >= id.device) { 15781da177e4SLinus Torvalds snd_timer_user_copy_id(&id, timer); 15791da177e4SLinus Torvalds break; 15801da177e4SLinus Torvalds } 15811da177e4SLinus Torvalds } 15821da177e4SLinus Torvalds if (p == &snd_timer_list) 15831da177e4SLinus Torvalds snd_timer_user_zero_id(&id); 15841da177e4SLinus Torvalds break; 15851da177e4SLinus Torvalds case SNDRV_TIMER_CLASS_CARD: 15861da177e4SLinus Torvalds case SNDRV_TIMER_CLASS_PCM: 15871da177e4SLinus Torvalds if (id.card < 0) { 15881da177e4SLinus Torvalds id.card = 0; 15891da177e4SLinus Torvalds } else { 15901da177e4SLinus Torvalds if (id.device < 0) { 15911da177e4SLinus Torvalds id.device = 0; 15921da177e4SLinus Torvalds } else { 1593e8ed6820SDan Carpenter if (id.subdevice < 0) 15946b172a85SClemens Ladisch id.subdevice = 0; 1595b41f794fSTakashi Iwai else if (id.subdevice < INT_MAX) 15966b172a85SClemens Ladisch id.subdevice++; 15976b172a85SClemens Ladisch } 15981da177e4SLinus Torvalds } 15991da177e4SLinus Torvalds list_for_each(p, &snd_timer_list) { 160053d2f744STakashi Iwai timer = list_entry(p, struct snd_timer, device_list); 16011da177e4SLinus Torvalds if (timer->tmr_class > id.dev_class) { 16021da177e4SLinus Torvalds snd_timer_user_copy_id(&id, timer); 16031da177e4SLinus Torvalds break; 16041da177e4SLinus Torvalds } 16051da177e4SLinus Torvalds if (timer->tmr_class < id.dev_class) 16061da177e4SLinus Torvalds continue; 16071da177e4SLinus Torvalds if (timer->card->number > id.card) { 16081da177e4SLinus Torvalds snd_timer_user_copy_id(&id, timer); 16091da177e4SLinus Torvalds break; 16101da177e4SLinus Torvalds } 16111da177e4SLinus Torvalds if (timer->card->number < id.card) 16121da177e4SLinus Torvalds continue; 16131da177e4SLinus Torvalds if (timer->tmr_device > id.device) { 16141da177e4SLinus Torvalds snd_timer_user_copy_id(&id, timer); 16151da177e4SLinus Torvalds break; 16161da177e4SLinus Torvalds } 16171da177e4SLinus Torvalds if (timer->tmr_device < id.device) 16181da177e4SLinus Torvalds continue; 16191da177e4SLinus Torvalds if (timer->tmr_subdevice > id.subdevice) { 16201da177e4SLinus Torvalds snd_timer_user_copy_id(&id, timer); 16211da177e4SLinus Torvalds break; 16221da177e4SLinus Torvalds } 16231da177e4SLinus Torvalds if (timer->tmr_subdevice < id.subdevice) 16241da177e4SLinus Torvalds continue; 16251da177e4SLinus Torvalds snd_timer_user_copy_id(&id, timer); 16261da177e4SLinus Torvalds break; 16271da177e4SLinus Torvalds } 16281da177e4SLinus Torvalds if (p == &snd_timer_list) 16291da177e4SLinus Torvalds snd_timer_user_zero_id(&id); 16301da177e4SLinus Torvalds break; 16311da177e4SLinus Torvalds default: 16321da177e4SLinus Torvalds snd_timer_user_zero_id(&id); 16331da177e4SLinus Torvalds } 16341da177e4SLinus Torvalds } 16351a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 16361da177e4SLinus Torvalds if (copy_to_user(_tid, &id, sizeof(*_tid))) 16371da177e4SLinus Torvalds return -EFAULT; 16381da177e4SLinus Torvalds return 0; 16391da177e4SLinus Torvalds } 16401da177e4SLinus Torvalds 16416b172a85SClemens Ladisch static int snd_timer_user_ginfo(struct file *file, 164253d2f744STakashi Iwai struct snd_timer_ginfo __user *_ginfo) 16431da177e4SLinus Torvalds { 164453d2f744STakashi Iwai struct snd_timer_ginfo *ginfo; 164553d2f744STakashi Iwai struct snd_timer_id tid; 164653d2f744STakashi Iwai struct snd_timer *t; 16471da177e4SLinus Torvalds struct list_head *p; 16481da177e4SLinus Torvalds int err = 0; 16491da177e4SLinus Torvalds 1650ef44a1ecSLi Zefan ginfo = memdup_user(_ginfo, sizeof(*ginfo)); 1651ef44a1ecSLi Zefan if (IS_ERR(ginfo)) 1652ef44a1ecSLi Zefan return PTR_ERR(ginfo); 1653ef44a1ecSLi Zefan 16541da177e4SLinus Torvalds tid = ginfo->tid; 16551da177e4SLinus Torvalds memset(ginfo, 0, sizeof(*ginfo)); 16561da177e4SLinus Torvalds ginfo->tid = tid; 16571a60d4c5SIngo Molnar mutex_lock(®ister_mutex); 16581da177e4SLinus Torvalds t = snd_timer_find(&tid); 16591da177e4SLinus Torvalds if (t != NULL) { 16601da177e4SLinus Torvalds ginfo->card = t->card ? t->card->number : -1; 16611da177e4SLinus Torvalds if (t->hw.flags & SNDRV_TIMER_HW_SLAVE) 16621da177e4SLinus Torvalds ginfo->flags |= SNDRV_TIMER_FLG_SLAVE; 16631da177e4SLinus Torvalds strlcpy(ginfo->id, t->id, sizeof(ginfo->id)); 16641da177e4SLinus Torvalds strlcpy(ginfo->name, t->name, sizeof(ginfo->name)); 16651da177e4SLinus Torvalds ginfo->resolution = t->hw.resolution; 16661da177e4SLinus Torvalds if (t->hw.resolution_min > 0) { 16671da177e4SLinus Torvalds ginfo->resolution_min = t->hw.resolution_min; 16681da177e4SLinus Torvalds ginfo->resolution_max = t->hw.resolution_max; 16691da177e4SLinus Torvalds } 16701da177e4SLinus Torvalds list_for_each(p, &t->open_list_head) { 16711da177e4SLinus Torvalds ginfo->clients++; 16721da177e4SLinus Torvalds } 16731da177e4SLinus Torvalds } else { 16741da177e4SLinus Torvalds err = -ENODEV; 16751da177e4SLinus Torvalds } 16761a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 16771da177e4SLinus Torvalds if (err >= 0 && copy_to_user(_ginfo, ginfo, sizeof(*ginfo))) 16781da177e4SLinus Torvalds err = -EFAULT; 16791da177e4SLinus Torvalds kfree(ginfo); 16801da177e4SLinus Torvalds return err; 16811da177e4SLinus Torvalds } 16821da177e4SLinus Torvalds 168391d2178eSTakashi Sakamoto static int timer_set_gparams(struct snd_timer_gparams *gparams) 16841da177e4SLinus Torvalds { 168553d2f744STakashi Iwai struct snd_timer *t; 16861da177e4SLinus Torvalds int err; 16871da177e4SLinus Torvalds 16881a60d4c5SIngo Molnar mutex_lock(®ister_mutex); 168991d2178eSTakashi Sakamoto t = snd_timer_find(&gparams->tid); 16906b172a85SClemens Ladisch if (!t) { 16911da177e4SLinus Torvalds err = -ENODEV; 16926b172a85SClemens Ladisch goto _error; 16931da177e4SLinus Torvalds } 16946b172a85SClemens Ladisch if (!list_empty(&t->open_list_head)) { 16956b172a85SClemens Ladisch err = -EBUSY; 16966b172a85SClemens Ladisch goto _error; 16976b172a85SClemens Ladisch } 16986b172a85SClemens Ladisch if (!t->hw.set_period) { 16996b172a85SClemens Ladisch err = -ENOSYS; 17006b172a85SClemens Ladisch goto _error; 17016b172a85SClemens Ladisch } 170291d2178eSTakashi Sakamoto err = t->hw.set_period(t, gparams->period_num, gparams->period_den); 17036b172a85SClemens Ladisch _error: 17041a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 17051da177e4SLinus Torvalds return err; 17061da177e4SLinus Torvalds } 17071da177e4SLinus Torvalds 170891d2178eSTakashi Sakamoto static int snd_timer_user_gparams(struct file *file, 170991d2178eSTakashi Sakamoto struct snd_timer_gparams __user *_gparams) 171091d2178eSTakashi Sakamoto { 171191d2178eSTakashi Sakamoto struct snd_timer_gparams gparams; 171291d2178eSTakashi Sakamoto 171391d2178eSTakashi Sakamoto if (copy_from_user(&gparams, _gparams, sizeof(gparams))) 171491d2178eSTakashi Sakamoto return -EFAULT; 171591d2178eSTakashi Sakamoto return timer_set_gparams(&gparams); 171691d2178eSTakashi Sakamoto } 171791d2178eSTakashi Sakamoto 17186b172a85SClemens Ladisch static int snd_timer_user_gstatus(struct file *file, 171953d2f744STakashi Iwai struct snd_timer_gstatus __user *_gstatus) 17201da177e4SLinus Torvalds { 172153d2f744STakashi Iwai struct snd_timer_gstatus gstatus; 172253d2f744STakashi Iwai struct snd_timer_id tid; 172353d2f744STakashi Iwai struct snd_timer *t; 17241da177e4SLinus Torvalds int err = 0; 17251da177e4SLinus Torvalds 17261da177e4SLinus Torvalds if (copy_from_user(&gstatus, _gstatus, sizeof(gstatus))) 17271da177e4SLinus Torvalds return -EFAULT; 17281da177e4SLinus Torvalds tid = gstatus.tid; 17291da177e4SLinus Torvalds memset(&gstatus, 0, sizeof(gstatus)); 17301da177e4SLinus Torvalds gstatus.tid = tid; 17311a60d4c5SIngo Molnar mutex_lock(®ister_mutex); 17321da177e4SLinus Torvalds t = snd_timer_find(&tid); 17331da177e4SLinus Torvalds if (t != NULL) { 17349d4d207dSTakashi Iwai spin_lock_irq(&t->lock); 1735fdcb5761STakashi Iwai gstatus.resolution = snd_timer_hw_resolution(t); 17361da177e4SLinus Torvalds if (t->hw.precise_resolution) { 17376b172a85SClemens Ladisch t->hw.precise_resolution(t, &gstatus.resolution_num, 17386b172a85SClemens Ladisch &gstatus.resolution_den); 17391da177e4SLinus Torvalds } else { 17401da177e4SLinus Torvalds gstatus.resolution_num = gstatus.resolution; 17411da177e4SLinus Torvalds gstatus.resolution_den = 1000000000uL; 17421da177e4SLinus Torvalds } 17439d4d207dSTakashi Iwai spin_unlock_irq(&t->lock); 17441da177e4SLinus Torvalds } else { 17451da177e4SLinus Torvalds err = -ENODEV; 17461da177e4SLinus Torvalds } 17471a60d4c5SIngo Molnar mutex_unlock(®ister_mutex); 17481da177e4SLinus Torvalds if (err >= 0 && copy_to_user(_gstatus, &gstatus, sizeof(gstatus))) 17491da177e4SLinus Torvalds err = -EFAULT; 17501da177e4SLinus Torvalds return err; 17511da177e4SLinus Torvalds } 17521da177e4SLinus Torvalds 17536b172a85SClemens Ladisch static int snd_timer_user_tselect(struct file *file, 175453d2f744STakashi Iwai struct snd_timer_select __user *_tselect) 17551da177e4SLinus Torvalds { 175653d2f744STakashi Iwai struct snd_timer_user *tu; 175753d2f744STakashi Iwai struct snd_timer_select tselect; 17581da177e4SLinus Torvalds char str[32]; 1759c1935b4dSJaroslav Kysela int err = 0; 17601da177e4SLinus Torvalds 17611da177e4SLinus Torvalds tu = file->private_data; 1762c1935b4dSJaroslav Kysela if (tu->timeri) { 17631da177e4SLinus Torvalds snd_timer_close(tu->timeri); 17646a34367eSTakashi Iwai snd_timer_instance_free(tu->timeri); 1765c1935b4dSJaroslav Kysela tu->timeri = NULL; 1766c1935b4dSJaroslav Kysela } 1767c1935b4dSJaroslav Kysela if (copy_from_user(&tselect, _tselect, sizeof(tselect))) { 1768c1935b4dSJaroslav Kysela err = -EFAULT; 1769c1935b4dSJaroslav Kysela goto __err; 1770c1935b4dSJaroslav Kysela } 17711da177e4SLinus Torvalds sprintf(str, "application %i", current->pid); 17721da177e4SLinus Torvalds if (tselect.id.dev_class != SNDRV_TIMER_CLASS_SLAVE) 17731da177e4SLinus Torvalds tselect.id.dev_sclass = SNDRV_TIMER_SCLASS_APPLICATION; 17746a34367eSTakashi Iwai tu->timeri = snd_timer_instance_new(str); 17756a34367eSTakashi Iwai if (!tu->timeri) { 17766a34367eSTakashi Iwai err = -ENOMEM; 1777c1935b4dSJaroslav Kysela goto __err; 17786a34367eSTakashi Iwai } 17791da177e4SLinus Torvalds 17801da177e4SLinus Torvalds tu->timeri->flags |= SNDRV_TIMER_IFLG_FAST; 17816b172a85SClemens Ladisch tu->timeri->callback = tu->tread 17826b172a85SClemens Ladisch ? snd_timer_user_tinterrupt : snd_timer_user_interrupt; 17831da177e4SLinus Torvalds tu->timeri->ccallback = snd_timer_user_ccallback; 17841da177e4SLinus Torvalds tu->timeri->callback_data = (void *)tu; 178540ed9444STakashi Iwai tu->timeri->disconnect = snd_timer_user_disconnect; 1786c1935b4dSJaroslav Kysela 17876a34367eSTakashi Iwai err = snd_timer_open(tu->timeri, &tselect.id, current->pid); 17886a34367eSTakashi Iwai if (err < 0) { 17896a34367eSTakashi Iwai snd_timer_instance_free(tu->timeri); 17906a34367eSTakashi Iwai tu->timeri = NULL; 17916a34367eSTakashi Iwai } 17926a34367eSTakashi Iwai 1793c1935b4dSJaroslav Kysela __err: 1794c1935b4dSJaroslav Kysela return err; 17951da177e4SLinus Torvalds } 17961da177e4SLinus Torvalds 17976b172a85SClemens Ladisch static int snd_timer_user_info(struct file *file, 179853d2f744STakashi Iwai struct snd_timer_info __user *_info) 17991da177e4SLinus Torvalds { 180053d2f744STakashi Iwai struct snd_timer_user *tu; 180153d2f744STakashi Iwai struct snd_timer_info *info; 180253d2f744STakashi Iwai struct snd_timer *t; 18031da177e4SLinus Torvalds int err = 0; 18041da177e4SLinus Torvalds 18051da177e4SLinus Torvalds tu = file->private_data; 18067c64ec34SClemens Ladisch if (!tu->timeri) 18077c64ec34SClemens Ladisch return -EBADFD; 18081da177e4SLinus Torvalds t = tu->timeri->timer; 18097c64ec34SClemens Ladisch if (!t) 18107c64ec34SClemens Ladisch return -EBADFD; 18111da177e4SLinus Torvalds 1812ca2c0966STakashi Iwai info = kzalloc(sizeof(*info), GFP_KERNEL); 18131da177e4SLinus Torvalds if (! info) 18141da177e4SLinus Torvalds return -ENOMEM; 18151da177e4SLinus Torvalds info->card = t->card ? t->card->number : -1; 18161da177e4SLinus Torvalds if (t->hw.flags & SNDRV_TIMER_HW_SLAVE) 18171da177e4SLinus Torvalds info->flags |= SNDRV_TIMER_FLG_SLAVE; 18181da177e4SLinus Torvalds strlcpy(info->id, t->id, sizeof(info->id)); 18191da177e4SLinus Torvalds strlcpy(info->name, t->name, sizeof(info->name)); 18201da177e4SLinus Torvalds info->resolution = t->hw.resolution; 18211da177e4SLinus Torvalds if (copy_to_user(_info, info, sizeof(*_info))) 18221da177e4SLinus Torvalds err = -EFAULT; 18231da177e4SLinus Torvalds kfree(info); 18241da177e4SLinus Torvalds return err; 18251da177e4SLinus Torvalds } 18261da177e4SLinus Torvalds 18276b172a85SClemens Ladisch static int snd_timer_user_params(struct file *file, 182853d2f744STakashi Iwai struct snd_timer_params __user *_params) 18291da177e4SLinus Torvalds { 183053d2f744STakashi Iwai struct snd_timer_user *tu; 183153d2f744STakashi Iwai struct snd_timer_params params; 183253d2f744STakashi Iwai struct snd_timer *t; 18331da177e4SLinus Torvalds int err; 18341da177e4SLinus Torvalds 18351da177e4SLinus Torvalds tu = file->private_data; 18367c64ec34SClemens Ladisch if (!tu->timeri) 18377c64ec34SClemens Ladisch return -EBADFD; 18381da177e4SLinus Torvalds t = tu->timeri->timer; 18397c64ec34SClemens Ladisch if (!t) 18407c64ec34SClemens Ladisch return -EBADFD; 18411da177e4SLinus Torvalds if (copy_from_user(¶ms, _params, sizeof(params))) 18421da177e4SLinus Torvalds return -EFAULT; 184371321eb3STakashi Iwai if (!(t->hw.flags & SNDRV_TIMER_HW_SLAVE)) { 184471321eb3STakashi Iwai u64 resolution; 184571321eb3STakashi Iwai 184671321eb3STakashi Iwai if (params.ticks < 1) { 18471da177e4SLinus Torvalds err = -EINVAL; 18481da177e4SLinus Torvalds goto _end; 18491da177e4SLinus Torvalds } 185071321eb3STakashi Iwai 185171321eb3STakashi Iwai /* Don't allow resolution less than 1ms */ 185271321eb3STakashi Iwai resolution = snd_timer_resolution(tu->timeri); 185371321eb3STakashi Iwai resolution *= params.ticks; 185471321eb3STakashi Iwai if (resolution < 1000000) { 185571321eb3STakashi Iwai err = -EINVAL; 185671321eb3STakashi Iwai goto _end; 185771321eb3STakashi Iwai } 185871321eb3STakashi Iwai } 18596b172a85SClemens Ladisch if (params.queue_size > 0 && 18606b172a85SClemens Ladisch (params.queue_size < 32 || params.queue_size > 1024)) { 18611da177e4SLinus Torvalds err = -EINVAL; 18621da177e4SLinus Torvalds goto _end; 18631da177e4SLinus Torvalds } 18641da177e4SLinus Torvalds if (params.filter & ~((1<<SNDRV_TIMER_EVENT_RESOLUTION)| 18651da177e4SLinus Torvalds (1<<SNDRV_TIMER_EVENT_TICK)| 18661da177e4SLinus Torvalds (1<<SNDRV_TIMER_EVENT_START)| 18671da177e4SLinus Torvalds (1<<SNDRV_TIMER_EVENT_STOP)| 18681da177e4SLinus Torvalds (1<<SNDRV_TIMER_EVENT_CONTINUE)| 18691da177e4SLinus Torvalds (1<<SNDRV_TIMER_EVENT_PAUSE)| 1870a501dfa3SJaroslav Kysela (1<<SNDRV_TIMER_EVENT_SUSPEND)| 1871a501dfa3SJaroslav Kysela (1<<SNDRV_TIMER_EVENT_RESUME)| 18721da177e4SLinus Torvalds (1<<SNDRV_TIMER_EVENT_MSTART)| 18731da177e4SLinus Torvalds (1<<SNDRV_TIMER_EVENT_MSTOP)| 18741da177e4SLinus Torvalds (1<<SNDRV_TIMER_EVENT_MCONTINUE)| 187565d11d95SJaroslav Kysela (1<<SNDRV_TIMER_EVENT_MPAUSE)| 187665d11d95SJaroslav Kysela (1<<SNDRV_TIMER_EVENT_MSUSPEND)| 1877a501dfa3SJaroslav Kysela (1<<SNDRV_TIMER_EVENT_MRESUME))) { 18781da177e4SLinus Torvalds err = -EINVAL; 18791da177e4SLinus Torvalds goto _end; 18801da177e4SLinus Torvalds } 18811da177e4SLinus Torvalds snd_timer_stop(tu->timeri); 18821da177e4SLinus Torvalds spin_lock_irq(&t->lock); 18831da177e4SLinus Torvalds tu->timeri->flags &= ~(SNDRV_TIMER_IFLG_AUTO| 18841da177e4SLinus Torvalds SNDRV_TIMER_IFLG_EXCLUSIVE| 18851da177e4SLinus Torvalds SNDRV_TIMER_IFLG_EARLY_EVENT); 18861da177e4SLinus Torvalds if (params.flags & SNDRV_TIMER_PSFLG_AUTO) 18871da177e4SLinus Torvalds tu->timeri->flags |= SNDRV_TIMER_IFLG_AUTO; 18881da177e4SLinus Torvalds if (params.flags & SNDRV_TIMER_PSFLG_EXCLUSIVE) 18891da177e4SLinus Torvalds tu->timeri->flags |= SNDRV_TIMER_IFLG_EXCLUSIVE; 18901da177e4SLinus Torvalds if (params.flags & SNDRV_TIMER_PSFLG_EARLY_EVENT) 18911da177e4SLinus Torvalds tu->timeri->flags |= SNDRV_TIMER_IFLG_EARLY_EVENT; 18921da177e4SLinus Torvalds spin_unlock_irq(&t->lock); 18936b172a85SClemens Ladisch if (params.queue_size > 0 && 18946b172a85SClemens Ladisch (unsigned int)tu->queue_size != params.queue_size) { 1895890e2cb5STakashi Iwai err = realloc_user_queue(tu, params.queue_size); 1896890e2cb5STakashi Iwai if (err < 0) 1897890e2cb5STakashi Iwai goto _end; 18981da177e4SLinus Torvalds } 1899d7f910bfSTakashi Iwai spin_lock_irq(&tu->qlock); 19001da177e4SLinus Torvalds tu->qhead = tu->qtail = tu->qused = 0; 19011da177e4SLinus Torvalds if (tu->timeri->flags & SNDRV_TIMER_IFLG_EARLY_EVENT) { 19021da177e4SLinus Torvalds if (tu->tread) { 190307094ae6SBaolin Wang struct snd_timer_tread64 tread; 1904cec8f96eSKangjie Lu memset(&tread, 0, sizeof(tread)); 19051da177e4SLinus Torvalds tread.event = SNDRV_TIMER_EVENT_EARLY; 190607094ae6SBaolin Wang tread.tstamp_sec = 0; 190707094ae6SBaolin Wang tread.tstamp_nsec = 0; 19081da177e4SLinus Torvalds tread.val = 0; 19091da177e4SLinus Torvalds snd_timer_user_append_to_tqueue(tu, &tread); 19101da177e4SLinus Torvalds } else { 191153d2f744STakashi Iwai struct snd_timer_read *r = &tu->queue[0]; 19121da177e4SLinus Torvalds r->resolution = 0; 19131da177e4SLinus Torvalds r->ticks = 0; 19141da177e4SLinus Torvalds tu->qused++; 19151da177e4SLinus Torvalds tu->qtail++; 19161da177e4SLinus Torvalds } 19171da177e4SLinus Torvalds } 19181da177e4SLinus Torvalds tu->filter = params.filter; 19191da177e4SLinus Torvalds tu->ticks = params.ticks; 1920d7f910bfSTakashi Iwai spin_unlock_irq(&tu->qlock); 19211da177e4SLinus Torvalds err = 0; 19221da177e4SLinus Torvalds _end: 19231da177e4SLinus Torvalds if (copy_to_user(_params, ¶ms, sizeof(params))) 19241da177e4SLinus Torvalds return -EFAULT; 19251da177e4SLinus Torvalds return err; 19261da177e4SLinus Torvalds } 19271da177e4SLinus Torvalds 1928a07804ccSBaolin Wang static int snd_timer_user_status32(struct file *file, 1929a07804ccSBaolin Wang struct snd_timer_status32 __user *_status) 19301da177e4SLinus Torvalds { 193153d2f744STakashi Iwai struct snd_timer_user *tu; 1932a07804ccSBaolin Wang struct snd_timer_status32 status; 19331da177e4SLinus Torvalds 19341da177e4SLinus Torvalds tu = file->private_data; 19357c64ec34SClemens Ladisch if (!tu->timeri) 19367c64ec34SClemens Ladisch return -EBADFD; 19371da177e4SLinus Torvalds memset(&status, 0, sizeof(status)); 1938a07804ccSBaolin Wang status.tstamp_sec = tu->tstamp.tv_sec; 1939a07804ccSBaolin Wang status.tstamp_nsec = tu->tstamp.tv_nsec; 1940a07804ccSBaolin Wang status.resolution = snd_timer_resolution(tu->timeri); 1941a07804ccSBaolin Wang status.lost = tu->timeri->lost; 1942a07804ccSBaolin Wang status.overrun = tu->overrun; 1943a07804ccSBaolin Wang spin_lock_irq(&tu->qlock); 1944a07804ccSBaolin Wang status.queue = tu->qused; 1945a07804ccSBaolin Wang spin_unlock_irq(&tu->qlock); 1946a07804ccSBaolin Wang if (copy_to_user(_status, &status, sizeof(status))) 1947a07804ccSBaolin Wang return -EFAULT; 1948a07804ccSBaolin Wang return 0; 1949a07804ccSBaolin Wang } 1950a07804ccSBaolin Wang 1951a07804ccSBaolin Wang static int snd_timer_user_status64(struct file *file, 1952a07804ccSBaolin Wang struct snd_timer_status64 __user *_status) 1953a07804ccSBaolin Wang { 1954a07804ccSBaolin Wang struct snd_timer_user *tu; 1955a07804ccSBaolin Wang struct snd_timer_status64 status; 1956a07804ccSBaolin Wang 1957a07804ccSBaolin Wang tu = file->private_data; 1958a07804ccSBaolin Wang if (!tu->timeri) 1959a07804ccSBaolin Wang return -EBADFD; 1960a07804ccSBaolin Wang memset(&status, 0, sizeof(status)); 1961a07804ccSBaolin Wang status.tstamp_sec = tu->tstamp.tv_sec; 1962a07804ccSBaolin Wang status.tstamp_nsec = tu->tstamp.tv_nsec; 19631da177e4SLinus Torvalds status.resolution = snd_timer_resolution(tu->timeri); 19641da177e4SLinus Torvalds status.lost = tu->timeri->lost; 19651da177e4SLinus Torvalds status.overrun = tu->overrun; 19661da177e4SLinus Torvalds spin_lock_irq(&tu->qlock); 19671da177e4SLinus Torvalds status.queue = tu->qused; 19681da177e4SLinus Torvalds spin_unlock_irq(&tu->qlock); 19691da177e4SLinus Torvalds if (copy_to_user(_status, &status, sizeof(status))) 19701da177e4SLinus Torvalds return -EFAULT; 19711da177e4SLinus Torvalds return 0; 19721da177e4SLinus Torvalds } 19731da177e4SLinus Torvalds 19741da177e4SLinus Torvalds static int snd_timer_user_start(struct file *file) 19751da177e4SLinus Torvalds { 19761da177e4SLinus Torvalds int err; 197753d2f744STakashi Iwai struct snd_timer_user *tu; 19781da177e4SLinus Torvalds 19791da177e4SLinus Torvalds tu = file->private_data; 19807c64ec34SClemens Ladisch if (!tu->timeri) 19817c64ec34SClemens Ladisch return -EBADFD; 19821da177e4SLinus Torvalds snd_timer_stop(tu->timeri); 19831da177e4SLinus Torvalds tu->timeri->lost = 0; 19841da177e4SLinus Torvalds tu->last_resolution = 0; 19855d704b0dSTakashi Iwai err = snd_timer_start(tu->timeri, tu->ticks); 19865d704b0dSTakashi Iwai if (err < 0) 19875d704b0dSTakashi Iwai return err; 19885d704b0dSTakashi Iwai return 0; 19891da177e4SLinus Torvalds } 19901da177e4SLinus Torvalds 19911da177e4SLinus Torvalds static int snd_timer_user_stop(struct file *file) 19921da177e4SLinus Torvalds { 19931da177e4SLinus Torvalds int err; 199453d2f744STakashi Iwai struct snd_timer_user *tu; 19951da177e4SLinus Torvalds 19961da177e4SLinus Torvalds tu = file->private_data; 19977c64ec34SClemens Ladisch if (!tu->timeri) 19987c64ec34SClemens Ladisch return -EBADFD; 19995d704b0dSTakashi Iwai err = snd_timer_stop(tu->timeri); 20005d704b0dSTakashi Iwai if (err < 0) 20015d704b0dSTakashi Iwai return err; 20025d704b0dSTakashi Iwai return 0; 20031da177e4SLinus Torvalds } 20041da177e4SLinus Torvalds 20051da177e4SLinus Torvalds static int snd_timer_user_continue(struct file *file) 20061da177e4SLinus Torvalds { 20071da177e4SLinus Torvalds int err; 200853d2f744STakashi Iwai struct snd_timer_user *tu; 20091da177e4SLinus Torvalds 20101da177e4SLinus Torvalds tu = file->private_data; 20117c64ec34SClemens Ladisch if (!tu->timeri) 20127c64ec34SClemens Ladisch return -EBADFD; 20139f8a7658STakashi Iwai /* start timer instead of continue if it's not used before */ 20149f8a7658STakashi Iwai if (!(tu->timeri->flags & SNDRV_TIMER_IFLG_PAUSED)) 20159f8a7658STakashi Iwai return snd_timer_user_start(file); 20161da177e4SLinus Torvalds tu->timeri->lost = 0; 20175d704b0dSTakashi Iwai err = snd_timer_continue(tu->timeri); 20185d704b0dSTakashi Iwai if (err < 0) 20195d704b0dSTakashi Iwai return err; 20205d704b0dSTakashi Iwai return 0; 20211da177e4SLinus Torvalds } 20221da177e4SLinus Torvalds 202315790a6bSTakashi Iwai static int snd_timer_user_pause(struct file *file) 202415790a6bSTakashi Iwai { 202515790a6bSTakashi Iwai int err; 202653d2f744STakashi Iwai struct snd_timer_user *tu; 202715790a6bSTakashi Iwai 202815790a6bSTakashi Iwai tu = file->private_data; 20297c64ec34SClemens Ladisch if (!tu->timeri) 20307c64ec34SClemens Ladisch return -EBADFD; 20315d704b0dSTakashi Iwai err = snd_timer_pause(tu->timeri); 20325d704b0dSTakashi Iwai if (err < 0) 20335d704b0dSTakashi Iwai return err; 20345d704b0dSTakashi Iwai return 0; 203515790a6bSTakashi Iwai } 203615790a6bSTakashi Iwai 203707094ae6SBaolin Wang static int snd_timer_user_tread(void __user *argp, struct snd_timer_user *tu, 203807094ae6SBaolin Wang unsigned int cmd, bool compat) 203907094ae6SBaolin Wang { 204007094ae6SBaolin Wang int __user *p = argp; 204107094ae6SBaolin Wang int xarg, old_tread; 204207094ae6SBaolin Wang 204307094ae6SBaolin Wang if (tu->timeri) /* too late */ 204407094ae6SBaolin Wang return -EBUSY; 204507094ae6SBaolin Wang if (get_user(xarg, p)) 204607094ae6SBaolin Wang return -EFAULT; 204707094ae6SBaolin Wang 204807094ae6SBaolin Wang old_tread = tu->tread; 204907094ae6SBaolin Wang 205007094ae6SBaolin Wang if (!xarg) 205107094ae6SBaolin Wang tu->tread = TREAD_FORMAT_NONE; 205207094ae6SBaolin Wang else if (cmd == SNDRV_TIMER_IOCTL_TREAD64 || 205307094ae6SBaolin Wang (IS_ENABLED(CONFIG_64BIT) && !compat)) 205407094ae6SBaolin Wang tu->tread = TREAD_FORMAT_TIME64; 205507094ae6SBaolin Wang else 205607094ae6SBaolin Wang tu->tread = TREAD_FORMAT_TIME32; 205707094ae6SBaolin Wang 205807094ae6SBaolin Wang if (tu->tread != old_tread && 205907094ae6SBaolin Wang realloc_user_queue(tu, tu->queue_size) < 0) { 206007094ae6SBaolin Wang tu->tread = old_tread; 206107094ae6SBaolin Wang return -ENOMEM; 206207094ae6SBaolin Wang } 206307094ae6SBaolin Wang 206407094ae6SBaolin Wang return 0; 206507094ae6SBaolin Wang } 206607094ae6SBaolin Wang 20678c50b37cSTakashi Iwai enum { 20688c50b37cSTakashi Iwai SNDRV_TIMER_IOCTL_START_OLD = _IO('T', 0x20), 20698c50b37cSTakashi Iwai SNDRV_TIMER_IOCTL_STOP_OLD = _IO('T', 0x21), 20708c50b37cSTakashi Iwai SNDRV_TIMER_IOCTL_CONTINUE_OLD = _IO('T', 0x22), 20718c50b37cSTakashi Iwai SNDRV_TIMER_IOCTL_PAUSE_OLD = _IO('T', 0x23), 20728c50b37cSTakashi Iwai }; 20738c50b37cSTakashi Iwai 2074af368027STakashi Iwai static long __snd_timer_user_ioctl(struct file *file, unsigned int cmd, 207507094ae6SBaolin Wang unsigned long arg, bool compat) 20761da177e4SLinus Torvalds { 207753d2f744STakashi Iwai struct snd_timer_user *tu; 20781da177e4SLinus Torvalds void __user *argp = (void __user *)arg; 20791da177e4SLinus Torvalds int __user *p = argp; 20801da177e4SLinus Torvalds 20811da177e4SLinus Torvalds tu = file->private_data; 20821da177e4SLinus Torvalds switch (cmd) { 20831da177e4SLinus Torvalds case SNDRV_TIMER_IOCTL_PVERSION: 20841da177e4SLinus Torvalds return put_user(SNDRV_TIMER_VERSION, p) ? -EFAULT : 0; 20851da177e4SLinus Torvalds case SNDRV_TIMER_IOCTL_NEXT_DEVICE: 20861da177e4SLinus Torvalds return snd_timer_user_next_device(argp); 208707094ae6SBaolin Wang case SNDRV_TIMER_IOCTL_TREAD_OLD: 208807094ae6SBaolin Wang case SNDRV_TIMER_IOCTL_TREAD64: 208907094ae6SBaolin Wang return snd_timer_user_tread(argp, tu, cmd, compat); 20901da177e4SLinus Torvalds case SNDRV_TIMER_IOCTL_GINFO: 20911da177e4SLinus Torvalds return snd_timer_user_ginfo(file, argp); 20921da177e4SLinus Torvalds case SNDRV_TIMER_IOCTL_GPARAMS: 20931da177e4SLinus Torvalds return snd_timer_user_gparams(file, argp); 20941da177e4SLinus Torvalds case SNDRV_TIMER_IOCTL_GSTATUS: 20951da177e4SLinus Torvalds return snd_timer_user_gstatus(file, argp); 20961da177e4SLinus Torvalds case SNDRV_TIMER_IOCTL_SELECT: 20971da177e4SLinus Torvalds return snd_timer_user_tselect(file, argp); 20981da177e4SLinus Torvalds case SNDRV_TIMER_IOCTL_INFO: 20991da177e4SLinus Torvalds return snd_timer_user_info(file, argp); 21001da177e4SLinus Torvalds case SNDRV_TIMER_IOCTL_PARAMS: 21011da177e4SLinus Torvalds return snd_timer_user_params(file, argp); 2102a07804ccSBaolin Wang case SNDRV_TIMER_IOCTL_STATUS32: 2103a07804ccSBaolin Wang return snd_timer_user_status32(file, argp); 2104a07804ccSBaolin Wang case SNDRV_TIMER_IOCTL_STATUS64: 2105a07804ccSBaolin Wang return snd_timer_user_status64(file, argp); 21061da177e4SLinus Torvalds case SNDRV_TIMER_IOCTL_START: 21078c50b37cSTakashi Iwai case SNDRV_TIMER_IOCTL_START_OLD: 21081da177e4SLinus Torvalds return snd_timer_user_start(file); 21091da177e4SLinus Torvalds case SNDRV_TIMER_IOCTL_STOP: 21108c50b37cSTakashi Iwai case SNDRV_TIMER_IOCTL_STOP_OLD: 21111da177e4SLinus Torvalds return snd_timer_user_stop(file); 21121da177e4SLinus Torvalds case SNDRV_TIMER_IOCTL_CONTINUE: 21138c50b37cSTakashi Iwai case SNDRV_TIMER_IOCTL_CONTINUE_OLD: 21141da177e4SLinus Torvalds return snd_timer_user_continue(file); 211515790a6bSTakashi Iwai case SNDRV_TIMER_IOCTL_PAUSE: 21168c50b37cSTakashi Iwai case SNDRV_TIMER_IOCTL_PAUSE_OLD: 211715790a6bSTakashi Iwai return snd_timer_user_pause(file); 21181da177e4SLinus Torvalds } 21191da177e4SLinus Torvalds return -ENOTTY; 21201da177e4SLinus Torvalds } 21211da177e4SLinus Torvalds 2122af368027STakashi Iwai static long snd_timer_user_ioctl(struct file *file, unsigned int cmd, 2123af368027STakashi Iwai unsigned long arg) 2124af368027STakashi Iwai { 2125af368027STakashi Iwai struct snd_timer_user *tu = file->private_data; 2126af368027STakashi Iwai long ret; 2127af368027STakashi Iwai 2128af368027STakashi Iwai mutex_lock(&tu->ioctl_lock); 212907094ae6SBaolin Wang ret = __snd_timer_user_ioctl(file, cmd, arg, false); 2130af368027STakashi Iwai mutex_unlock(&tu->ioctl_lock); 2131af368027STakashi Iwai return ret; 2132af368027STakashi Iwai } 2133af368027STakashi Iwai 21341da177e4SLinus Torvalds static int snd_timer_user_fasync(int fd, struct file * file, int on) 21351da177e4SLinus Torvalds { 213653d2f744STakashi Iwai struct snd_timer_user *tu; 21371da177e4SLinus Torvalds 21381da177e4SLinus Torvalds tu = file->private_data; 213960aa4924SJonathan Corbet return fasync_helper(fd, file, on, &tu->fasync); 21401da177e4SLinus Torvalds } 21411da177e4SLinus Torvalds 21426b172a85SClemens Ladisch static ssize_t snd_timer_user_read(struct file *file, char __user *buffer, 21436b172a85SClemens Ladisch size_t count, loff_t *offset) 21441da177e4SLinus Torvalds { 214507094ae6SBaolin Wang struct snd_timer_tread64 *tread; 214607094ae6SBaolin Wang struct snd_timer_tread32 tread32; 214753d2f744STakashi Iwai struct snd_timer_user *tu; 21481da177e4SLinus Torvalds long result = 0, unit; 21494dff5c7bSTakashi Iwai int qhead; 21501da177e4SLinus Torvalds int err = 0; 21511da177e4SLinus Torvalds 21521da177e4SLinus Torvalds tu = file->private_data; 215307094ae6SBaolin Wang switch (tu->tread) { 215407094ae6SBaolin Wang case TREAD_FORMAT_TIME64: 215507094ae6SBaolin Wang unit = sizeof(struct snd_timer_tread64); 215607094ae6SBaolin Wang break; 215707094ae6SBaolin Wang case TREAD_FORMAT_TIME32: 215807094ae6SBaolin Wang unit = sizeof(struct snd_timer_tread32); 215907094ae6SBaolin Wang break; 216007094ae6SBaolin Wang case TREAD_FORMAT_NONE: 216107094ae6SBaolin Wang unit = sizeof(struct snd_timer_read); 216207094ae6SBaolin Wang break; 216307094ae6SBaolin Wang default: 216407094ae6SBaolin Wang WARN_ONCE(1, "Corrupt snd_timer_user\n"); 216507094ae6SBaolin Wang return -ENOTSUPP; 216607094ae6SBaolin Wang } 216707094ae6SBaolin Wang 2168d11662f4STakashi Iwai mutex_lock(&tu->ioctl_lock); 21691da177e4SLinus Torvalds spin_lock_irq(&tu->qlock); 21701da177e4SLinus Torvalds while ((long)count - result >= unit) { 21711da177e4SLinus Torvalds while (!tu->qused) { 2172ac6424b9SIngo Molnar wait_queue_entry_t wait; 21731da177e4SLinus Torvalds 21741da177e4SLinus Torvalds if ((file->f_flags & O_NONBLOCK) != 0 || result > 0) { 21751da177e4SLinus Torvalds err = -EAGAIN; 21764dff5c7bSTakashi Iwai goto _error; 21771da177e4SLinus Torvalds } 21781da177e4SLinus Torvalds 21791da177e4SLinus Torvalds set_current_state(TASK_INTERRUPTIBLE); 21801da177e4SLinus Torvalds init_waitqueue_entry(&wait, current); 21811da177e4SLinus Torvalds add_wait_queue(&tu->qchange_sleep, &wait); 21821da177e4SLinus Torvalds 21831da177e4SLinus Torvalds spin_unlock_irq(&tu->qlock); 2184d11662f4STakashi Iwai mutex_unlock(&tu->ioctl_lock); 21851da177e4SLinus Torvalds schedule(); 2186d11662f4STakashi Iwai mutex_lock(&tu->ioctl_lock); 21871da177e4SLinus Torvalds spin_lock_irq(&tu->qlock); 21881da177e4SLinus Torvalds 21891da177e4SLinus Torvalds remove_wait_queue(&tu->qchange_sleep, &wait); 21901da177e4SLinus Torvalds 2191230323daSTakashi Iwai if (tu->disconnected) { 2192230323daSTakashi Iwai err = -ENODEV; 21934dff5c7bSTakashi Iwai goto _error; 2194230323daSTakashi Iwai } 21951da177e4SLinus Torvalds if (signal_pending(current)) { 21961da177e4SLinus Torvalds err = -ERESTARTSYS; 21974dff5c7bSTakashi Iwai goto _error; 21981da177e4SLinus Torvalds } 21991da177e4SLinus Torvalds } 22001da177e4SLinus Torvalds 22014dff5c7bSTakashi Iwai qhead = tu->qhead++; 22024dff5c7bSTakashi Iwai tu->qhead %= tu->queue_size; 22033fa6993fSTakashi Iwai tu->qused--; 22041da177e4SLinus Torvalds spin_unlock_irq(&tu->qlock); 22051da177e4SLinus Torvalds 220607094ae6SBaolin Wang tread = &tu->tqueue[qhead]; 220707094ae6SBaolin Wang 220807094ae6SBaolin Wang switch (tu->tread) { 220907094ae6SBaolin Wang case TREAD_FORMAT_TIME64: 221007094ae6SBaolin Wang if (copy_to_user(buffer, tread, 221107094ae6SBaolin Wang sizeof(struct snd_timer_tread64))) 22121da177e4SLinus Torvalds err = -EFAULT; 221307094ae6SBaolin Wang break; 221407094ae6SBaolin Wang case TREAD_FORMAT_TIME32: 221507094ae6SBaolin Wang memset(&tread32, 0, sizeof(tread32)); 221607094ae6SBaolin Wang tread32 = (struct snd_timer_tread32) { 221707094ae6SBaolin Wang .event = tread->event, 221807094ae6SBaolin Wang .tstamp_sec = tread->tstamp_sec, 221907094ae6SBaolin Wang .tstamp_sec = tread->tstamp_nsec, 222007094ae6SBaolin Wang .val = tread->val, 222107094ae6SBaolin Wang }; 222207094ae6SBaolin Wang 222307094ae6SBaolin Wang if (copy_to_user(buffer, &tread32, sizeof(tread32))) 222407094ae6SBaolin Wang err = -EFAULT; 222507094ae6SBaolin Wang break; 222607094ae6SBaolin Wang case TREAD_FORMAT_NONE: 22274dff5c7bSTakashi Iwai if (copy_to_user(buffer, &tu->queue[qhead], 22284dff5c7bSTakashi Iwai sizeof(struct snd_timer_read))) 22291da177e4SLinus Torvalds err = -EFAULT; 223007094ae6SBaolin Wang break; 223107094ae6SBaolin Wang default: 223207094ae6SBaolin Wang err = -ENOTSUPP; 223307094ae6SBaolin Wang break; 22341da177e4SLinus Torvalds } 22351da177e4SLinus Torvalds 22361da177e4SLinus Torvalds spin_lock_irq(&tu->qlock); 22374dff5c7bSTakashi Iwai if (err < 0) 22384dff5c7bSTakashi Iwai goto _error; 22394dff5c7bSTakashi Iwai result += unit; 22404dff5c7bSTakashi Iwai buffer += unit; 22411da177e4SLinus Torvalds } 22421da177e4SLinus Torvalds _error: 22434dff5c7bSTakashi Iwai spin_unlock_irq(&tu->qlock); 2244d11662f4STakashi Iwai mutex_unlock(&tu->ioctl_lock); 22451da177e4SLinus Torvalds return result > 0 ? result : err; 22461da177e4SLinus Torvalds } 22471da177e4SLinus Torvalds 2248680ef72aSAl Viro static __poll_t snd_timer_user_poll(struct file *file, poll_table * wait) 22491da177e4SLinus Torvalds { 2250680ef72aSAl Viro __poll_t mask; 225153d2f744STakashi Iwai struct snd_timer_user *tu; 22521da177e4SLinus Torvalds 22531da177e4SLinus Torvalds tu = file->private_data; 22541da177e4SLinus Torvalds 22551da177e4SLinus Torvalds poll_wait(file, &tu->qchange_sleep, wait); 22561da177e4SLinus Torvalds 22571da177e4SLinus Torvalds mask = 0; 2258d7f910bfSTakashi Iwai spin_lock_irq(&tu->qlock); 22591da177e4SLinus Torvalds if (tu->qused) 2260a9a08845SLinus Torvalds mask |= EPOLLIN | EPOLLRDNORM; 2261230323daSTakashi Iwai if (tu->disconnected) 2262a9a08845SLinus Torvalds mask |= EPOLLERR; 2263d7f910bfSTakashi Iwai spin_unlock_irq(&tu->qlock); 22641da177e4SLinus Torvalds 22651da177e4SLinus Torvalds return mask; 22661da177e4SLinus Torvalds } 22671da177e4SLinus Torvalds 22681da177e4SLinus Torvalds #ifdef CONFIG_COMPAT 22691da177e4SLinus Torvalds #include "timer_compat.c" 22701da177e4SLinus Torvalds #else 22711da177e4SLinus Torvalds #define snd_timer_user_ioctl_compat NULL 22721da177e4SLinus Torvalds #endif 22731da177e4SLinus Torvalds 22749c2e08c5SArjan van de Ven static const struct file_operations snd_timer_f_ops = 22751da177e4SLinus Torvalds { 22761da177e4SLinus Torvalds .owner = THIS_MODULE, 22771da177e4SLinus Torvalds .read = snd_timer_user_read, 22781da177e4SLinus Torvalds .open = snd_timer_user_open, 22791da177e4SLinus Torvalds .release = snd_timer_user_release, 228002f4865fSTakashi Iwai .llseek = no_llseek, 22811da177e4SLinus Torvalds .poll = snd_timer_user_poll, 22821da177e4SLinus Torvalds .unlocked_ioctl = snd_timer_user_ioctl, 22831da177e4SLinus Torvalds .compat_ioctl = snd_timer_user_ioctl_compat, 22841da177e4SLinus Torvalds .fasync = snd_timer_user_fasync, 22851da177e4SLinus Torvalds }; 22861da177e4SLinus Torvalds 22877c35860dSTakashi Iwai /* unregister the system timer */ 22887c35860dSTakashi Iwai static void snd_timer_free_all(void) 22897c35860dSTakashi Iwai { 22907c35860dSTakashi Iwai struct snd_timer *timer, *n; 22917c35860dSTakashi Iwai 22927c35860dSTakashi Iwai list_for_each_entry_safe(timer, n, &snd_timer_list, device_list) 22937c35860dSTakashi Iwai snd_timer_free(timer); 22947c35860dSTakashi Iwai } 22957c35860dSTakashi Iwai 229689da061fSTakashi Iwai static struct device timer_dev; 229789da061fSTakashi Iwai 22981da177e4SLinus Torvalds /* 22991da177e4SLinus Torvalds * ENTRY functions 23001da177e4SLinus Torvalds */ 23011da177e4SLinus Torvalds 23021da177e4SLinus Torvalds static int __init alsa_timer_init(void) 23031da177e4SLinus Torvalds { 23041da177e4SLinus Torvalds int err; 23051da177e4SLinus Torvalds 230689da061fSTakashi Iwai snd_device_initialize(&timer_dev, NULL); 230789da061fSTakashi Iwai dev_set_name(&timer_dev, "timer"); 230889da061fSTakashi Iwai 23091da177e4SLinus Torvalds #ifdef SNDRV_OSS_INFO_DEV_TIMERS 23106b172a85SClemens Ladisch snd_oss_info_register(SNDRV_OSS_INFO_DEV_TIMERS, SNDRV_CARDS - 1, 23116b172a85SClemens Ladisch "system timer"); 23121da177e4SLinus Torvalds #endif 2313e28563ccSTakashi Iwai 23147c35860dSTakashi Iwai err = snd_timer_register_system(); 23157c35860dSTakashi Iwai if (err < 0) { 2316cf74dcf3STakashi Iwai pr_err("ALSA: unable to register system timer (%i)\n", err); 23171ae0e4ceSMarkus Elfring goto put_timer; 23187c35860dSTakashi Iwai } 23197c35860dSTakashi Iwai 232040a4b263STakashi Iwai err = snd_register_device(SNDRV_DEVICE_TYPE_TIMER, NULL, 0, 232140a4b263STakashi Iwai &snd_timer_f_ops, NULL, &timer_dev); 23227c35860dSTakashi Iwai if (err < 0) { 2323cf74dcf3STakashi Iwai pr_err("ALSA: unable to register timer device (%i)\n", err); 23247c35860dSTakashi Iwai snd_timer_free_all(); 23251ae0e4ceSMarkus Elfring goto put_timer; 23267c35860dSTakashi Iwai } 23277c35860dSTakashi Iwai 2328e28563ccSTakashi Iwai snd_timer_proc_init(); 23291da177e4SLinus Torvalds return 0; 23301ae0e4ceSMarkus Elfring 23311ae0e4ceSMarkus Elfring put_timer: 23321ae0e4ceSMarkus Elfring put_device(&timer_dev); 23331ae0e4ceSMarkus Elfring return err; 23341da177e4SLinus Torvalds } 23351da177e4SLinus Torvalds 23361da177e4SLinus Torvalds static void __exit alsa_timer_exit(void) 23371da177e4SLinus Torvalds { 233840a4b263STakashi Iwai snd_unregister_device(&timer_dev); 23397c35860dSTakashi Iwai snd_timer_free_all(); 234089da061fSTakashi Iwai put_device(&timer_dev); 2341e28563ccSTakashi Iwai snd_timer_proc_done(); 23421da177e4SLinus Torvalds #ifdef SNDRV_OSS_INFO_DEV_TIMERS 23431da177e4SLinus Torvalds snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_TIMERS, SNDRV_CARDS - 1); 23441da177e4SLinus Torvalds #endif 23451da177e4SLinus Torvalds } 23461da177e4SLinus Torvalds 23471da177e4SLinus Torvalds module_init(alsa_timer_init) 23481da177e4SLinus Torvalds module_exit(alsa_timer_exit) 2349