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