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