xref: /openbmc/linux/sound/core/timer.c (revision 2c95241a)
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;
8695cc637cSTakashi Iwai 	struct snd_fasync *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  */
snd_timer_instance_new(const char * owner)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 
snd_timer_instance_free(struct snd_timer_instance * timeri)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  */
snd_timer_find(struct snd_timer_id * tid)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 
snd_timer_request(struct snd_timer_id * tid)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 */
check_matching_master_slave(struct snd_timer_instance * master,struct snd_timer_instance * slave)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  */
snd_timer_check_slave(struct snd_timer_instance * slave)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  */
snd_timer_check_master(struct snd_timer_instance * master)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  */
snd_timer_open(struct snd_timer_instance * timeri,struct snd_timer_id * tid,unsigned int slave_id)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  */
snd_timer_close_locked(struct snd_timer_instance * timeri,struct device ** card_devp_to_put)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  */
snd_timer_close(struct snd_timer_instance * timeri)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 
snd_timer_hw_resolution(struct snd_timer * timer)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 
snd_timer_resolution(struct snd_timer_instance * timeri)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 
snd_timer_notify1(struct snd_timer_instance * ti,int event)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;
5239c1fe96bSTakashi Iwai 	event += 10; /* convert to SNDRV_TIMER_EVENT_MXXX */
5249244b2c3SJohannes Berg 	list_for_each_entry(ts, &ti->slave_active_head, active_list)
5251da177e4SLinus Torvalds 		if (ts->ccallback)
5269c1fe96bSTakashi Iwai 			ts->ccallback(ts, event, &tstamp, resolution);
5271da177e4SLinus Torvalds }
5281da177e4SLinus Torvalds 
529f65e0d29STakashi Iwai /* start/continue a master timer */
snd_timer_start1(struct snd_timer_instance * timeri,bool start,unsigned long ticks)530f65e0d29STakashi Iwai static int snd_timer_start1(struct snd_timer_instance *timeri,
531f65e0d29STakashi Iwai 			    bool start, unsigned long ticks)
5321da177e4SLinus Torvalds {
533f65e0d29STakashi Iwai 	struct snd_timer *timer;
534f65e0d29STakashi Iwai 	int result;
535f65e0d29STakashi Iwai 	unsigned long flags;
536f65e0d29STakashi Iwai 
537f65e0d29STakashi Iwai 	timer = timeri->timer;
538f65e0d29STakashi Iwai 	if (!timer)
539f65e0d29STakashi Iwai 		return -EINVAL;
540f65e0d29STakashi Iwai 
541f65e0d29STakashi Iwai 	spin_lock_irqsave(&timer->lock, flags);
542fe1b26c9STakashi Iwai 	if (timeri->flags & SNDRV_TIMER_IFLG_DEAD) {
543fe1b26c9STakashi Iwai 		result = -EINVAL;
544fe1b26c9STakashi Iwai 		goto unlock;
545fe1b26c9STakashi Iwai 	}
546f65e0d29STakashi Iwai 	if (timer->card && timer->card->shutdown) {
547f65e0d29STakashi Iwai 		result = -ENODEV;
548f65e0d29STakashi Iwai 		goto unlock;
549f65e0d29STakashi Iwai 	}
550f65e0d29STakashi Iwai 	if (timeri->flags & (SNDRV_TIMER_IFLG_RUNNING |
551f65e0d29STakashi Iwai 			     SNDRV_TIMER_IFLG_START)) {
552f65e0d29STakashi Iwai 		result = -EBUSY;
553f65e0d29STakashi Iwai 		goto unlock;
554f65e0d29STakashi Iwai 	}
555f65e0d29STakashi Iwai 
5562c95241aSTakashi Iwai 	/* check the actual time for the start tick;
5572c95241aSTakashi Iwai 	 * bail out as error if it's way too low (< 100us)
5582c95241aSTakashi Iwai 	 */
5592c95241aSTakashi Iwai 	if (start) {
5602c95241aSTakashi Iwai 		if ((u64)snd_timer_hw_resolution(timer) * ticks < 100000) {
5612c95241aSTakashi Iwai 			result = -EINVAL;
5622c95241aSTakashi Iwai 			goto unlock;
5632c95241aSTakashi Iwai 		}
5642c95241aSTakashi Iwai 	}
5652c95241aSTakashi Iwai 
566f65e0d29STakashi Iwai 	if (start)
567f65e0d29STakashi Iwai 		timeri->ticks = timeri->cticks = ticks;
568f65e0d29STakashi Iwai 	else if (!timeri->cticks)
569f65e0d29STakashi Iwai 		timeri->cticks = 1;
570f65e0d29STakashi Iwai 	timeri->pticks = 0;
571f65e0d29STakashi Iwai 
5725b7c757dSNicolas Kaiser 	list_move_tail(&timeri->active_list, &timer->active_list_head);
5731da177e4SLinus Torvalds 	if (timer->running) {
5741da177e4SLinus Torvalds 		if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE)
5751da177e4SLinus Torvalds 			goto __start_now;
5761da177e4SLinus Torvalds 		timer->flags |= SNDRV_TIMER_FLG_RESCHED;
5771da177e4SLinus Torvalds 		timeri->flags |= SNDRV_TIMER_IFLG_START;
578f65e0d29STakashi Iwai 		result = 1; /* delayed start */
5791da177e4SLinus Torvalds 	} else {
580f65e0d29STakashi Iwai 		if (start)
581f65e0d29STakashi Iwai 			timer->sticks = ticks;
5821da177e4SLinus Torvalds 		timer->hw.start(timer);
5831da177e4SLinus Torvalds 	      __start_now:
5841da177e4SLinus Torvalds 		timer->running++;
5851da177e4SLinus Torvalds 		timeri->flags |= SNDRV_TIMER_IFLG_RUNNING;
586f65e0d29STakashi Iwai 		result = 0;
5871da177e4SLinus Torvalds 	}
588f65e0d29STakashi Iwai 	snd_timer_notify1(timeri, start ? SNDRV_TIMER_EVENT_START :
589f65e0d29STakashi Iwai 			  SNDRV_TIMER_EVENT_CONTINUE);
590f65e0d29STakashi Iwai  unlock:
591f65e0d29STakashi Iwai 	spin_unlock_irqrestore(&timer->lock, flags);
592f65e0d29STakashi Iwai 	return result;
5931da177e4SLinus Torvalds }
5941da177e4SLinus Torvalds 
595f65e0d29STakashi Iwai /* start/continue a slave timer */
snd_timer_start_slave(struct snd_timer_instance * timeri,bool start)596f65e0d29STakashi Iwai static int snd_timer_start_slave(struct snd_timer_instance *timeri,
597f65e0d29STakashi Iwai 				 bool start)
5981da177e4SLinus Torvalds {
5991da177e4SLinus Torvalds 	unsigned long flags;
600fe1b26c9STakashi Iwai 	int err;
6011da177e4SLinus Torvalds 
6021da177e4SLinus Torvalds 	spin_lock_irqsave(&slave_active_lock, flags);
603fe1b26c9STakashi Iwai 	if (timeri->flags & SNDRV_TIMER_IFLG_DEAD) {
604fe1b26c9STakashi Iwai 		err = -EINVAL;
605fe1b26c9STakashi Iwai 		goto unlock;
606fe1b26c9STakashi Iwai 	}
607f784beb7STakashi Iwai 	if (timeri->flags & SNDRV_TIMER_IFLG_RUNNING) {
608fe1b26c9STakashi Iwai 		err = -EBUSY;
609fe1b26c9STakashi Iwai 		goto unlock;
610f784beb7STakashi Iwai 	}
6111da177e4SLinus Torvalds 	timeri->flags |= SNDRV_TIMER_IFLG_RUNNING;
612b5a663aaSTakashi Iwai 	if (timeri->master && timeri->timer) {
613b5a663aaSTakashi Iwai 		spin_lock(&timeri->timer->lock);
6146b172a85SClemens Ladisch 		list_add_tail(&timeri->active_list,
6156b172a85SClemens Ladisch 			      &timeri->master->slave_active_head);
616f65e0d29STakashi Iwai 		snd_timer_notify1(timeri, start ? SNDRV_TIMER_EVENT_START :
617f65e0d29STakashi Iwai 				  SNDRV_TIMER_EVENT_CONTINUE);
618b5a663aaSTakashi Iwai 		spin_unlock(&timeri->timer->lock);
619b5a663aaSTakashi Iwai 	}
620fe1b26c9STakashi Iwai 	err = 1; /* delayed start */
621fe1b26c9STakashi Iwai  unlock:
6221da177e4SLinus Torvalds 	spin_unlock_irqrestore(&slave_active_lock, flags);
623fe1b26c9STakashi Iwai 	return err;
6241da177e4SLinus Torvalds }
6251da177e4SLinus Torvalds 
626f65e0d29STakashi Iwai /* stop/pause a master timer */
snd_timer_stop1(struct snd_timer_instance * timeri,bool stop)627f65e0d29STakashi Iwai static int snd_timer_stop1(struct snd_timer_instance *timeri, bool stop)
6281da177e4SLinus Torvalds {
62953d2f744STakashi Iwai 	struct snd_timer *timer;
630f65e0d29STakashi Iwai 	int result = 0;
6311da177e4SLinus Torvalds 	unsigned long flags;
6321da177e4SLinus Torvalds 
6331da177e4SLinus Torvalds 	timer = timeri->timer;
6341da177e4SLinus Torvalds 	if (!timer)
6351da177e4SLinus Torvalds 		return -EINVAL;
6361da177e4SLinus Torvalds 	spin_lock_irqsave(&timer->lock, flags);
637c0317c0eSWang Wensheng 	list_del_init(&timeri->ack_list);
638c0317c0eSWang Wensheng 	list_del_init(&timeri->active_list);
639f784beb7STakashi Iwai 	if (!(timeri->flags & (SNDRV_TIMER_IFLG_RUNNING |
640f784beb7STakashi Iwai 			       SNDRV_TIMER_IFLG_START))) {
641f65e0d29STakashi Iwai 		result = -EBUSY;
642f65e0d29STakashi Iwai 		goto unlock;
643f784beb7STakashi Iwai 	}
644f65e0d29STakashi Iwai 	if (timer->card && timer->card->shutdown)
645f65e0d29STakashi Iwai 		goto unlock;
646f65e0d29STakashi Iwai 	if (stop) {
647f65e0d29STakashi Iwai 		timeri->cticks = timeri->ticks;
648f65e0d29STakashi Iwai 		timeri->pticks = 0;
649230323daSTakashi Iwai 	}
6501da177e4SLinus Torvalds 	if ((timeri->flags & SNDRV_TIMER_IFLG_RUNNING) &&
6511da177e4SLinus Torvalds 	    !(--timer->running)) {
6521da177e4SLinus Torvalds 		timer->hw.stop(timer);
6531da177e4SLinus Torvalds 		if (timer->flags & SNDRV_TIMER_FLG_RESCHED) {
6541da177e4SLinus Torvalds 			timer->flags &= ~SNDRV_TIMER_FLG_RESCHED;
6551da177e4SLinus Torvalds 			snd_timer_reschedule(timer, 0);
6561da177e4SLinus Torvalds 			if (timer->flags & SNDRV_TIMER_FLG_CHANGE) {
6571da177e4SLinus Torvalds 				timer->flags &= ~SNDRV_TIMER_FLG_CHANGE;
6581da177e4SLinus Torvalds 				timer->hw.start(timer);
6591da177e4SLinus Torvalds 			}
6601da177e4SLinus Torvalds 		}
6611da177e4SLinus Torvalds 	}
662c3b16813STakashi Iwai 	timeri->flags &= ~(SNDRV_TIMER_IFLG_RUNNING | SNDRV_TIMER_IFLG_START);
6639f8a7658STakashi Iwai 	if (stop)
6649f8a7658STakashi Iwai 		timeri->flags &= ~SNDRV_TIMER_IFLG_PAUSED;
6659f8a7658STakashi Iwai 	else
6669f8a7658STakashi Iwai 		timeri->flags |= SNDRV_TIMER_IFLG_PAUSED;
667f65e0d29STakashi Iwai 	snd_timer_notify1(timeri, stop ? SNDRV_TIMER_EVENT_STOP :
6683ae18097SBen Hutchings 			  SNDRV_TIMER_EVENT_PAUSE);
669f65e0d29STakashi Iwai  unlock:
6701da177e4SLinus Torvalds 	spin_unlock_irqrestore(&timer->lock, flags);
671f65e0d29STakashi Iwai 	return result;
672f65e0d29STakashi Iwai }
673f65e0d29STakashi Iwai 
674f65e0d29STakashi Iwai /* stop/pause a slave timer */
snd_timer_stop_slave(struct snd_timer_instance * timeri,bool stop)675f65e0d29STakashi Iwai static int snd_timer_stop_slave(struct snd_timer_instance *timeri, bool stop)
676f65e0d29STakashi Iwai {
677f65e0d29STakashi Iwai 	unsigned long flags;
678ffdd9827STakashi Iwai 	bool running;
679f65e0d29STakashi Iwai 
680f65e0d29STakashi Iwai 	spin_lock_irqsave(&slave_active_lock, flags);
681ffdd9827STakashi Iwai 	running = timeri->flags & SNDRV_TIMER_IFLG_RUNNING;
682f65e0d29STakashi Iwai 	timeri->flags &= ~SNDRV_TIMER_IFLG_RUNNING;
683f65e0d29STakashi Iwai 	if (timeri->timer) {
684f65e0d29STakashi Iwai 		spin_lock(&timeri->timer->lock);
685f65e0d29STakashi Iwai 		list_del_init(&timeri->ack_list);
686f65e0d29STakashi Iwai 		list_del_init(&timeri->active_list);
687ffdd9827STakashi Iwai 		if (running)
688f65e0d29STakashi Iwai 			snd_timer_notify1(timeri, stop ? SNDRV_TIMER_EVENT_STOP :
6893ae18097SBen Hutchings 					  SNDRV_TIMER_EVENT_PAUSE);
690f65e0d29STakashi Iwai 		spin_unlock(&timeri->timer->lock);
691f65e0d29STakashi Iwai 	}
692f65e0d29STakashi Iwai 	spin_unlock_irqrestore(&slave_active_lock, flags);
693ffdd9827STakashi Iwai 	return running ? 0 : -EBUSY;
6941da177e4SLinus Torvalds }
6951da177e4SLinus Torvalds 
6961da177e4SLinus Torvalds /*
697f65e0d29STakashi Iwai  *  start the timer instance
698f65e0d29STakashi Iwai  */
snd_timer_start(struct snd_timer_instance * timeri,unsigned int ticks)699f65e0d29STakashi Iwai int snd_timer_start(struct snd_timer_instance *timeri, unsigned int ticks)
700f65e0d29STakashi Iwai {
701f65e0d29STakashi Iwai 	if (timeri == NULL || ticks < 1)
702f65e0d29STakashi Iwai 		return -EINVAL;
703f65e0d29STakashi Iwai 	if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
704f65e0d29STakashi Iwai 		return snd_timer_start_slave(timeri, true);
705f65e0d29STakashi Iwai 	else
706f65e0d29STakashi Iwai 		return snd_timer_start1(timeri, true, ticks);
707f65e0d29STakashi Iwai }
70898856392STakashi Iwai EXPORT_SYMBOL(snd_timer_start);
709f65e0d29STakashi Iwai 
710f65e0d29STakashi Iwai /*
7111da177e4SLinus Torvalds  * stop the timer instance.
7121da177e4SLinus Torvalds  *
7131da177e4SLinus Torvalds  * do not call this from the timer callback!
7141da177e4SLinus Torvalds  */
snd_timer_stop(struct snd_timer_instance * timeri)71553d2f744STakashi Iwai int snd_timer_stop(struct snd_timer_instance *timeri)
7161da177e4SLinus Torvalds {
717f65e0d29STakashi Iwai 	if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
718f65e0d29STakashi Iwai 		return snd_timer_stop_slave(timeri, true);
719f65e0d29STakashi Iwai 	else
720f65e0d29STakashi Iwai 		return snd_timer_stop1(timeri, true);
7211da177e4SLinus Torvalds }
72298856392STakashi Iwai EXPORT_SYMBOL(snd_timer_stop);
7231da177e4SLinus Torvalds 
7241da177e4SLinus Torvalds /*
7251da177e4SLinus Torvalds  * start again..  the tick is kept.
7261da177e4SLinus Torvalds  */
snd_timer_continue(struct snd_timer_instance * timeri)72753d2f744STakashi Iwai int snd_timer_continue(struct snd_timer_instance *timeri)
7281da177e4SLinus Torvalds {
7299f8a7658STakashi Iwai 	/* timer can continue only after pause */
7309f8a7658STakashi Iwai 	if (!(timeri->flags & SNDRV_TIMER_IFLG_PAUSED))
7319f8a7658STakashi Iwai 		return -EINVAL;
7329f8a7658STakashi Iwai 
7331da177e4SLinus Torvalds 	if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
734f65e0d29STakashi Iwai 		return snd_timer_start_slave(timeri, false);
735f65e0d29STakashi Iwai 	else
736f65e0d29STakashi Iwai 		return snd_timer_start1(timeri, false, 0);
7371da177e4SLinus Torvalds }
73898856392STakashi Iwai EXPORT_SYMBOL(snd_timer_continue);
7391da177e4SLinus Torvalds 
7401da177e4SLinus Torvalds /*
7411da177e4SLinus Torvalds  * pause.. remember the ticks left
7421da177e4SLinus Torvalds  */
snd_timer_pause(struct snd_timer_instance * timeri)74353d2f744STakashi Iwai int snd_timer_pause(struct snd_timer_instance * timeri)
7441da177e4SLinus Torvalds {
745f65e0d29STakashi Iwai 	if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
746f65e0d29STakashi Iwai 		return snd_timer_stop_slave(timeri, false);
747f65e0d29STakashi Iwai 	else
748f65e0d29STakashi Iwai 		return snd_timer_stop1(timeri, false);
7491da177e4SLinus Torvalds }
75098856392STakashi Iwai EXPORT_SYMBOL(snd_timer_pause);
7511da177e4SLinus Torvalds 
7521da177e4SLinus Torvalds /*
7531da177e4SLinus Torvalds  * reschedule the timer
7541da177e4SLinus Torvalds  *
7551da177e4SLinus Torvalds  * start pending instances and check the scheduling ticks.
7561da177e4SLinus Torvalds  * when the scheduling ticks is changed set CHANGE flag to reprogram the timer.
7571da177e4SLinus Torvalds  */
snd_timer_reschedule(struct snd_timer * timer,unsigned long ticks_left)75853d2f744STakashi Iwai static void snd_timer_reschedule(struct snd_timer * timer, unsigned long ticks_left)
7591da177e4SLinus Torvalds {
76053d2f744STakashi Iwai 	struct snd_timer_instance *ti;
7611da177e4SLinus Torvalds 	unsigned long ticks = ~0UL;
7621da177e4SLinus Torvalds 
7639244b2c3SJohannes Berg 	list_for_each_entry(ti, &timer->active_list_head, active_list) {
7641da177e4SLinus Torvalds 		if (ti->flags & SNDRV_TIMER_IFLG_START) {
7651da177e4SLinus Torvalds 			ti->flags &= ~SNDRV_TIMER_IFLG_START;
7661da177e4SLinus Torvalds 			ti->flags |= SNDRV_TIMER_IFLG_RUNNING;
7671da177e4SLinus Torvalds 			timer->running++;
7681da177e4SLinus Torvalds 		}
7691da177e4SLinus Torvalds 		if (ti->flags & SNDRV_TIMER_IFLG_RUNNING) {
7701da177e4SLinus Torvalds 			if (ticks > ti->cticks)
7711da177e4SLinus Torvalds 				ticks = ti->cticks;
7721da177e4SLinus Torvalds 		}
7731da177e4SLinus Torvalds 	}
7741da177e4SLinus Torvalds 	if (ticks == ~0UL) {
7751da177e4SLinus Torvalds 		timer->flags &= ~SNDRV_TIMER_FLG_RESCHED;
7761da177e4SLinus Torvalds 		return;
7771da177e4SLinus Torvalds 	}
7781da177e4SLinus Torvalds 	if (ticks > timer->hw.ticks)
7791da177e4SLinus Torvalds 		ticks = timer->hw.ticks;
7801da177e4SLinus Torvalds 	if (ticks_left != ticks)
7811da177e4SLinus Torvalds 		timer->flags |= SNDRV_TIMER_FLG_CHANGE;
7821da177e4SLinus Torvalds 	timer->sticks = ticks;
7831da177e4SLinus Torvalds }
7841da177e4SLinus Torvalds 
7858748b850STakashi Iwai /* call callbacks in timer ack list */
snd_timer_process_callbacks(struct snd_timer * timer,struct list_head * head)7868748b850STakashi Iwai static void snd_timer_process_callbacks(struct snd_timer *timer,
7878748b850STakashi Iwai 					struct list_head *head)
7881da177e4SLinus Torvalds {
78953d2f744STakashi Iwai 	struct snd_timer_instance *ti;
7901da177e4SLinus Torvalds 	unsigned long resolution, ticks;
7911da177e4SLinus Torvalds 
7928748b850STakashi Iwai 	while (!list_empty(head)) {
7938748b850STakashi Iwai 		ti = list_first_entry(head, struct snd_timer_instance,
7948748b850STakashi Iwai 				      ack_list);
7951da177e4SLinus Torvalds 
7961da177e4SLinus Torvalds 		/* remove from ack_list and make empty */
797df55531bSTakashi Iwai 		list_del_init(&ti->ack_list);
7981da177e4SLinus Torvalds 
799fe1b26c9STakashi Iwai 		if (!(ti->flags & SNDRV_TIMER_IFLG_DEAD)) {
8001da177e4SLinus Torvalds 			ticks = ti->pticks;
8011da177e4SLinus Torvalds 			ti->pticks = 0;
8021da177e4SLinus Torvalds 			resolution = ti->resolution;
8031da177e4SLinus Torvalds 			ti->flags |= SNDRV_TIMER_IFLG_CALLBACK;
8041da177e4SLinus Torvalds 			spin_unlock(&timer->lock);
8051da177e4SLinus Torvalds 			if (ti->callback)
8061da177e4SLinus Torvalds 				ti->callback(ti, resolution, ticks);
8071da177e4SLinus Torvalds 			spin_lock(&timer->lock);
8081da177e4SLinus Torvalds 			ti->flags &= ~SNDRV_TIMER_IFLG_CALLBACK;
8091da177e4SLinus Torvalds 		}
8101da177e4SLinus Torvalds 	}
8118748b850STakashi Iwai }
8128748b850STakashi Iwai 
8137bb4a8a2STakashi Iwai /* clear pending instances from ack list */
snd_timer_clear_callbacks(struct snd_timer * timer,struct list_head * head)8147bb4a8a2STakashi Iwai static void snd_timer_clear_callbacks(struct snd_timer *timer,
8157bb4a8a2STakashi Iwai 				      struct list_head *head)
8167bb4a8a2STakashi Iwai {
8177bb4a8a2STakashi Iwai 	unsigned long flags;
8187bb4a8a2STakashi Iwai 
8197bb4a8a2STakashi Iwai 	spin_lock_irqsave(&timer->lock, flags);
8207bb4a8a2STakashi Iwai 	while (!list_empty(head))
8217bb4a8a2STakashi Iwai 		list_del_init(head->next);
8227bb4a8a2STakashi Iwai 	spin_unlock_irqrestore(&timer->lock, flags);
8237bb4a8a2STakashi Iwai }
8247bb4a8a2STakashi Iwai 
8258748b850STakashi Iwai /*
826bf083595STakashi Iwai  * timer work
8278748b850STakashi Iwai  *
8288748b850STakashi Iwai  */
snd_timer_work(struct work_struct * work)829bf083595STakashi Iwai static void snd_timer_work(struct work_struct *work)
8308748b850STakashi Iwai {
831bf083595STakashi Iwai 	struct snd_timer *timer = container_of(work, struct snd_timer, task_work);
8328748b850STakashi Iwai 	unsigned long flags;
8338748b850STakashi Iwai 
8347bb4a8a2STakashi Iwai 	if (timer->card && timer->card->shutdown) {
8357bb4a8a2STakashi Iwai 		snd_timer_clear_callbacks(timer, &timer->sack_list_head);
8368748b850STakashi Iwai 		return;
8377bb4a8a2STakashi Iwai 	}
8388748b850STakashi Iwai 
8398748b850STakashi Iwai 	spin_lock_irqsave(&timer->lock, flags);
8408748b850STakashi Iwai 	snd_timer_process_callbacks(timer, &timer->sack_list_head);
8412999ff5bSTakashi Iwai 	spin_unlock_irqrestore(&timer->lock, flags);
8421da177e4SLinus Torvalds }
8431da177e4SLinus Torvalds 
8441da177e4SLinus Torvalds /*
8451da177e4SLinus Torvalds  * timer interrupt
8461da177e4SLinus Torvalds  *
8471da177e4SLinus Torvalds  * ticks_left is usually equal to timer->sticks.
8481da177e4SLinus Torvalds  *
8491da177e4SLinus Torvalds  */
snd_timer_interrupt(struct snd_timer * timer,unsigned long ticks_left)85053d2f744STakashi Iwai void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
8511da177e4SLinus Torvalds {
8529244b2c3SJohannes Berg 	struct snd_timer_instance *ti, *ts, *tmp;
8538748b850STakashi Iwai 	unsigned long resolution;
8548748b850STakashi Iwai 	struct list_head *ack_list_head;
855b32425acSTakashi Iwai 	unsigned long flags;
856bf083595STakashi Iwai 	bool use_work = false;
8571da177e4SLinus Torvalds 
8581da177e4SLinus Torvalds 	if (timer == NULL)
8591da177e4SLinus Torvalds 		return;
8601da177e4SLinus Torvalds 
8617bb4a8a2STakashi Iwai 	if (timer->card && timer->card->shutdown) {
8627bb4a8a2STakashi Iwai 		snd_timer_clear_callbacks(timer, &timer->ack_list_head);
863230323daSTakashi Iwai 		return;
8647bb4a8a2STakashi Iwai 	}
865230323daSTakashi Iwai 
866b32425acSTakashi Iwai 	spin_lock_irqsave(&timer->lock, flags);
8671da177e4SLinus Torvalds 
8681da177e4SLinus Torvalds 	/* remember the current resolution */
869fdcb5761STakashi Iwai 	resolution = snd_timer_hw_resolution(timer);
8701da177e4SLinus Torvalds 
8711da177e4SLinus Torvalds 	/* loop for all active instances
8729244b2c3SJohannes Berg 	 * Here we cannot use list_for_each_entry because the active_list of a
8736b172a85SClemens Ladisch 	 * processed instance is relinked to done_list_head before the callback
8746b172a85SClemens Ladisch 	 * is called.
8751da177e4SLinus Torvalds 	 */
8769244b2c3SJohannes Berg 	list_for_each_entry_safe(ti, tmp, &timer->active_list_head,
8779244b2c3SJohannes Berg 				 active_list) {
878fe1b26c9STakashi Iwai 		if (ti->flags & SNDRV_TIMER_IFLG_DEAD)
879fe1b26c9STakashi Iwai 			continue;
8801da177e4SLinus Torvalds 		if (!(ti->flags & SNDRV_TIMER_IFLG_RUNNING))
8811da177e4SLinus Torvalds 			continue;
8821da177e4SLinus Torvalds 		ti->pticks += ticks_left;
8831da177e4SLinus Torvalds 		ti->resolution = resolution;
8841da177e4SLinus Torvalds 		if (ti->cticks < ticks_left)
8851da177e4SLinus Torvalds 			ti->cticks = 0;
8861da177e4SLinus Torvalds 		else
8871da177e4SLinus Torvalds 			ti->cticks -= ticks_left;
8881da177e4SLinus Torvalds 		if (ti->cticks) /* not expired */
8891da177e4SLinus Torvalds 			continue;
8901da177e4SLinus Torvalds 		if (ti->flags & SNDRV_TIMER_IFLG_AUTO) {
8911da177e4SLinus Torvalds 			ti->cticks = ti->ticks;
8921da177e4SLinus Torvalds 		} else {
8931da177e4SLinus Torvalds 			ti->flags &= ~SNDRV_TIMER_IFLG_RUNNING;
894094fd3beSTakashi Iwai 			--timer->running;
895ee8413b0STakashi Iwai 			list_del_init(&ti->active_list);
8961da177e4SLinus Torvalds 		}
897bf083595STakashi Iwai 		if ((timer->hw.flags & SNDRV_TIMER_HW_WORK) ||
8986b172a85SClemens Ladisch 		    (ti->flags & SNDRV_TIMER_IFLG_FAST))
8996b172a85SClemens Ladisch 			ack_list_head = &timer->ack_list_head;
9006b172a85SClemens Ladisch 		else
9016b172a85SClemens Ladisch 			ack_list_head = &timer->sack_list_head;
9026b172a85SClemens Ladisch 		if (list_empty(&ti->ack_list))
9036b172a85SClemens Ladisch 			list_add_tail(&ti->ack_list, ack_list_head);
9049244b2c3SJohannes Berg 		list_for_each_entry(ts, &ti->slave_active_head, active_list) {
9051da177e4SLinus Torvalds 			ts->pticks = ti->pticks;
9061da177e4SLinus Torvalds 			ts->resolution = resolution;
9076b172a85SClemens Ladisch 			if (list_empty(&ts->ack_list))
9086b172a85SClemens Ladisch 				list_add_tail(&ts->ack_list, ack_list_head);
9091da177e4SLinus Torvalds 		}
9101da177e4SLinus Torvalds 	}
9111da177e4SLinus Torvalds 	if (timer->flags & SNDRV_TIMER_FLG_RESCHED)
912cd93fe47SClemens Ladisch 		snd_timer_reschedule(timer, timer->sticks);
9131da177e4SLinus Torvalds 	if (timer->running) {
9141da177e4SLinus Torvalds 		if (timer->hw.flags & SNDRV_TIMER_HW_STOP) {
9151da177e4SLinus Torvalds 			timer->hw.stop(timer);
9161da177e4SLinus Torvalds 			timer->flags |= SNDRV_TIMER_FLG_CHANGE;
9171da177e4SLinus Torvalds 		}
9181da177e4SLinus Torvalds 		if (!(timer->hw.flags & SNDRV_TIMER_HW_AUTO) ||
9191da177e4SLinus Torvalds 		    (timer->flags & SNDRV_TIMER_FLG_CHANGE)) {
9201da177e4SLinus Torvalds 			/* restart timer */
9211da177e4SLinus Torvalds 			timer->flags &= ~SNDRV_TIMER_FLG_CHANGE;
9221da177e4SLinus Torvalds 			timer->hw.start(timer);
9231da177e4SLinus Torvalds 		}
9241da177e4SLinus Torvalds 	} else {
9251da177e4SLinus Torvalds 		timer->hw.stop(timer);
9261da177e4SLinus Torvalds 	}
9271da177e4SLinus Torvalds 
9281da177e4SLinus Torvalds 	/* now process all fast callbacks */
9298748b850STakashi Iwai 	snd_timer_process_callbacks(timer, &timer->ack_list_head);
9301da177e4SLinus Torvalds 
9311da177e4SLinus Torvalds 	/* do we have any slow callbacks? */
932bf083595STakashi Iwai 	use_work = !list_empty(&timer->sack_list_head);
933b32425acSTakashi Iwai 	spin_unlock_irqrestore(&timer->lock, flags);
9341da177e4SLinus Torvalds 
935bf083595STakashi Iwai 	if (use_work)
936bf083595STakashi Iwai 		queue_work(system_highpri_wq, &timer->task_work);
9371da177e4SLinus Torvalds }
93898856392STakashi Iwai EXPORT_SYMBOL(snd_timer_interrupt);
9391da177e4SLinus Torvalds 
9401da177e4SLinus Torvalds /*
9411da177e4SLinus Torvalds 
9421da177e4SLinus Torvalds  */
9431da177e4SLinus Torvalds 
snd_timer_new(struct snd_card * card,char * id,struct snd_timer_id * tid,struct snd_timer ** rtimer)94453d2f744STakashi Iwai int snd_timer_new(struct snd_card *card, char *id, struct snd_timer_id *tid,
94553d2f744STakashi Iwai 		  struct snd_timer **rtimer)
9461da177e4SLinus Torvalds {
94753d2f744STakashi Iwai 	struct snd_timer *timer;
9481da177e4SLinus Torvalds 	int err;
949f15ee210STakashi Iwai 	static const struct snd_device_ops ops = {
9501da177e4SLinus Torvalds 		.dev_free = snd_timer_dev_free,
9511da177e4SLinus Torvalds 		.dev_register = snd_timer_dev_register,
952c461482cSTakashi Iwai 		.dev_disconnect = snd_timer_dev_disconnect,
9531da177e4SLinus Torvalds 	};
9541da177e4SLinus Torvalds 
9557eaa943cSTakashi Iwai 	if (snd_BUG_ON(!tid))
9567eaa943cSTakashi Iwai 		return -EINVAL;
957d10ee9c5SSrikanth K H 	if (tid->dev_class == SNDRV_TIMER_CLASS_CARD ||
958d10ee9c5SSrikanth K H 	    tid->dev_class == SNDRV_TIMER_CLASS_PCM) {
959d10ee9c5SSrikanth K H 		if (WARN_ON(!card))
960d10ee9c5SSrikanth K H 			return -EINVAL;
961d10ee9c5SSrikanth K H 	}
9627eaa943cSTakashi Iwai 	if (rtimer)
9631da177e4SLinus Torvalds 		*rtimer = NULL;
964ca2c0966STakashi Iwai 	timer = kzalloc(sizeof(*timer), GFP_KERNEL);
965ec0e9937STakashi Iwai 	if (!timer)
9661da177e4SLinus Torvalds 		return -ENOMEM;
9671da177e4SLinus Torvalds 	timer->tmr_class = tid->dev_class;
9681da177e4SLinus Torvalds 	timer->card = card;
9691da177e4SLinus Torvalds 	timer->tmr_device = tid->device;
9701da177e4SLinus Torvalds 	timer->tmr_subdevice = tid->subdevice;
9711da177e4SLinus Torvalds 	if (id)
97275b1a8f9SJoe Perches 		strscpy(timer->id, id, sizeof(timer->id));
9736b760bb2SVegard Nossum 	timer->sticks = 1;
9741da177e4SLinus Torvalds 	INIT_LIST_HEAD(&timer->device_list);
9751da177e4SLinus Torvalds 	INIT_LIST_HEAD(&timer->open_list_head);
9761da177e4SLinus Torvalds 	INIT_LIST_HEAD(&timer->active_list_head);
9771da177e4SLinus Torvalds 	INIT_LIST_HEAD(&timer->ack_list_head);
9781da177e4SLinus Torvalds 	INIT_LIST_HEAD(&timer->sack_list_head);
9791da177e4SLinus Torvalds 	spin_lock_init(&timer->lock);
980bf083595STakashi Iwai 	INIT_WORK(&timer->task_work, snd_timer_work);
9819b7d869eSTakashi Iwai 	timer->max_instances = 1000; /* default limit per timer */
9821da177e4SLinus Torvalds 	if (card != NULL) {
983de24214dSClemens Ladisch 		timer->module = card->module;
9846b172a85SClemens Ladisch 		err = snd_device_new(card, SNDRV_DEV_TIMER, timer, &ops);
9856b172a85SClemens Ladisch 		if (err < 0) {
9861da177e4SLinus Torvalds 			snd_timer_free(timer);
9871da177e4SLinus Torvalds 			return err;
9881da177e4SLinus Torvalds 		}
9891da177e4SLinus Torvalds 	}
9907eaa943cSTakashi Iwai 	if (rtimer)
9911da177e4SLinus Torvalds 		*rtimer = timer;
9921da177e4SLinus Torvalds 	return 0;
9931da177e4SLinus Torvalds }
99498856392STakashi Iwai EXPORT_SYMBOL(snd_timer_new);
9951da177e4SLinus Torvalds 
snd_timer_free(struct snd_timer * timer)99653d2f744STakashi Iwai static int snd_timer_free(struct snd_timer *timer)
9971da177e4SLinus Torvalds {
9987eaa943cSTakashi Iwai 	if (!timer)
9997eaa943cSTakashi Iwai 		return 0;
1000c461482cSTakashi Iwai 
1001c461482cSTakashi Iwai 	mutex_lock(&register_mutex);
1002c461482cSTakashi Iwai 	if (! list_empty(&timer->open_list_head)) {
1003c461482cSTakashi Iwai 		struct list_head *p, *n;
1004c461482cSTakashi Iwai 		struct snd_timer_instance *ti;
1005cf74dcf3STakashi Iwai 		pr_warn("ALSA: timer %p is busy?\n", timer);
1006c461482cSTakashi Iwai 		list_for_each_safe(p, n, &timer->open_list_head) {
1007c461482cSTakashi Iwai 			list_del_init(p);
1008c461482cSTakashi Iwai 			ti = list_entry(p, struct snd_timer_instance, open_list);
1009c461482cSTakashi Iwai 			ti->timer = NULL;
1010c461482cSTakashi Iwai 		}
1011c461482cSTakashi Iwai 	}
1012c461482cSTakashi Iwai 	list_del(&timer->device_list);
1013c461482cSTakashi Iwai 	mutex_unlock(&register_mutex);
1014c461482cSTakashi Iwai 
10151da177e4SLinus Torvalds 	if (timer->private_free)
10161da177e4SLinus Torvalds 		timer->private_free(timer);
10171da177e4SLinus Torvalds 	kfree(timer);
10181da177e4SLinus Torvalds 	return 0;
10191da177e4SLinus Torvalds }
10201da177e4SLinus Torvalds 
snd_timer_dev_free(struct snd_device * device)102153d2f744STakashi Iwai static int snd_timer_dev_free(struct snd_device *device)
10221da177e4SLinus Torvalds {
102353d2f744STakashi Iwai 	struct snd_timer *timer = device->device_data;
10241da177e4SLinus Torvalds 	return snd_timer_free(timer);
10251da177e4SLinus Torvalds }
10261da177e4SLinus Torvalds 
snd_timer_dev_register(struct snd_device * dev)102753d2f744STakashi Iwai static int snd_timer_dev_register(struct snd_device *dev)
10281da177e4SLinus Torvalds {
102953d2f744STakashi Iwai 	struct snd_timer *timer = dev->device_data;
103053d2f744STakashi Iwai 	struct snd_timer *timer1;
10311da177e4SLinus Torvalds 
10327eaa943cSTakashi Iwai 	if (snd_BUG_ON(!timer || !timer->hw.start || !timer->hw.stop))
10337eaa943cSTakashi Iwai 		return -ENXIO;
10341da177e4SLinus Torvalds 	if (!(timer->hw.flags & SNDRV_TIMER_HW_SLAVE) &&
10351da177e4SLinus Torvalds 	    !timer->hw.resolution && timer->hw.c_resolution == NULL)
10361da177e4SLinus Torvalds 	    	return -EINVAL;
10371da177e4SLinus Torvalds 
10381a60d4c5SIngo Molnar 	mutex_lock(&register_mutex);
10399244b2c3SJohannes Berg 	list_for_each_entry(timer1, &snd_timer_list, device_list) {
10401da177e4SLinus Torvalds 		if (timer1->tmr_class > timer->tmr_class)
10411da177e4SLinus Torvalds 			break;
10421da177e4SLinus Torvalds 		if (timer1->tmr_class < timer->tmr_class)
10431da177e4SLinus Torvalds 			continue;
10441da177e4SLinus Torvalds 		if (timer1->card && timer->card) {
10451da177e4SLinus Torvalds 			if (timer1->card->number > timer->card->number)
10461da177e4SLinus Torvalds 				break;
10471da177e4SLinus Torvalds 			if (timer1->card->number < timer->card->number)
10481da177e4SLinus Torvalds 				continue;
10491da177e4SLinus Torvalds 		}
10501da177e4SLinus Torvalds 		if (timer1->tmr_device > timer->tmr_device)
10511da177e4SLinus Torvalds 			break;
10521da177e4SLinus Torvalds 		if (timer1->tmr_device < timer->tmr_device)
10531da177e4SLinus Torvalds 			continue;
10541da177e4SLinus Torvalds 		if (timer1->tmr_subdevice > timer->tmr_subdevice)
10551da177e4SLinus Torvalds 			break;
10561da177e4SLinus Torvalds 		if (timer1->tmr_subdevice < timer->tmr_subdevice)
10571da177e4SLinus Torvalds 			continue;
10581da177e4SLinus Torvalds 		/* conflicts.. */
10591a60d4c5SIngo Molnar 		mutex_unlock(&register_mutex);
10601da177e4SLinus Torvalds 		return -EBUSY;
10611da177e4SLinus Torvalds 	}
10629244b2c3SJohannes Berg 	list_add_tail(&timer->device_list, &timer1->device_list);
10631a60d4c5SIngo Molnar 	mutex_unlock(&register_mutex);
10641da177e4SLinus Torvalds 	return 0;
10651da177e4SLinus Torvalds }
10661da177e4SLinus Torvalds 
snd_timer_dev_disconnect(struct snd_device * device)1067c461482cSTakashi Iwai static int snd_timer_dev_disconnect(struct snd_device *device)
10681da177e4SLinus Torvalds {
106953d2f744STakashi Iwai 	struct snd_timer *timer = device->device_data;
1070230323daSTakashi Iwai 	struct snd_timer_instance *ti;
1071230323daSTakashi Iwai 
1072c461482cSTakashi Iwai 	mutex_lock(&register_mutex);
1073c461482cSTakashi Iwai 	list_del_init(&timer->device_list);
1074230323daSTakashi Iwai 	/* wake up pending sleepers */
1075230323daSTakashi Iwai 	list_for_each_entry(ti, &timer->open_list_head, open_list) {
107640ed9444STakashi Iwai 		if (ti->disconnect)
107740ed9444STakashi Iwai 			ti->disconnect(ti);
1078230323daSTakashi Iwai 	}
1079c461482cSTakashi Iwai 	mutex_unlock(&register_mutex);
1080c461482cSTakashi Iwai 	return 0;
10811da177e4SLinus Torvalds }
10821da177e4SLinus Torvalds 
snd_timer_notify(struct snd_timer * timer,int event,struct timespec64 * tstamp)1083fcae40c9SBaolin Wang void snd_timer_notify(struct snd_timer *timer, int event, struct timespec64 *tstamp)
10841da177e4SLinus Torvalds {
10851da177e4SLinus Torvalds 	unsigned long flags;
10861da177e4SLinus Torvalds 	unsigned long resolution = 0;
108753d2f744STakashi Iwai 	struct snd_timer_instance *ti, *ts;
10881da177e4SLinus Torvalds 
1089230323daSTakashi Iwai 	if (timer->card && timer->card->shutdown)
1090230323daSTakashi Iwai 		return;
10917c22f1aaSTakashi Iwai 	if (! (timer->hw.flags & SNDRV_TIMER_HW_SLAVE))
10927c22f1aaSTakashi Iwai 		return;
10937eaa943cSTakashi Iwai 	if (snd_BUG_ON(event < SNDRV_TIMER_EVENT_MSTART ||
10947eaa943cSTakashi Iwai 		       event > SNDRV_TIMER_EVENT_MRESUME))
10957eaa943cSTakashi Iwai 		return;
10961da177e4SLinus Torvalds 	spin_lock_irqsave(&timer->lock, flags);
1097a501dfa3SJaroslav Kysela 	if (event == SNDRV_TIMER_EVENT_MSTART ||
1098a501dfa3SJaroslav Kysela 	    event == SNDRV_TIMER_EVENT_MCONTINUE ||
1099fdcb5761STakashi Iwai 	    event == SNDRV_TIMER_EVENT_MRESUME)
1100fdcb5761STakashi Iwai 		resolution = snd_timer_hw_resolution(timer);
11019244b2c3SJohannes Berg 	list_for_each_entry(ti, &timer->active_list_head, active_list) {
11021da177e4SLinus Torvalds 		if (ti->ccallback)
11031da177e4SLinus Torvalds 			ti->ccallback(ti, event, tstamp, resolution);
11049244b2c3SJohannes Berg 		list_for_each_entry(ts, &ti->slave_active_head, active_list)
11051da177e4SLinus Torvalds 			if (ts->ccallback)
11061da177e4SLinus Torvalds 				ts->ccallback(ts, event, tstamp, resolution);
11071da177e4SLinus Torvalds 	}
11081da177e4SLinus Torvalds 	spin_unlock_irqrestore(&timer->lock, flags);
11091da177e4SLinus Torvalds }
111098856392STakashi Iwai EXPORT_SYMBOL(snd_timer_notify);
11111da177e4SLinus Torvalds 
11121da177e4SLinus Torvalds /*
11131da177e4SLinus Torvalds  * exported functions for global timers
11141da177e4SLinus Torvalds  */
snd_timer_global_new(char * id,int device,struct snd_timer ** rtimer)111553d2f744STakashi Iwai int snd_timer_global_new(char *id, int device, struct snd_timer **rtimer)
11161da177e4SLinus Torvalds {
111753d2f744STakashi Iwai 	struct snd_timer_id tid;
11181da177e4SLinus Torvalds 
11191da177e4SLinus Torvalds 	tid.dev_class = SNDRV_TIMER_CLASS_GLOBAL;
11201da177e4SLinus Torvalds 	tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE;
11211da177e4SLinus Torvalds 	tid.card = -1;
11221da177e4SLinus Torvalds 	tid.device = device;
11231da177e4SLinus Torvalds 	tid.subdevice = 0;
11241da177e4SLinus Torvalds 	return snd_timer_new(NULL, id, &tid, rtimer);
11251da177e4SLinus Torvalds }
112698856392STakashi Iwai EXPORT_SYMBOL(snd_timer_global_new);
11271da177e4SLinus Torvalds 
snd_timer_global_free(struct snd_timer * timer)112853d2f744STakashi Iwai int snd_timer_global_free(struct snd_timer *timer)
11291da177e4SLinus Torvalds {
11301da177e4SLinus Torvalds 	return snd_timer_free(timer);
11311da177e4SLinus Torvalds }
113298856392STakashi Iwai EXPORT_SYMBOL(snd_timer_global_free);
11331da177e4SLinus Torvalds 
snd_timer_global_register(struct snd_timer * timer)113453d2f744STakashi Iwai int snd_timer_global_register(struct snd_timer *timer)
11351da177e4SLinus Torvalds {
113653d2f744STakashi Iwai 	struct snd_device dev;
11371da177e4SLinus Torvalds 
11381da177e4SLinus Torvalds 	memset(&dev, 0, sizeof(dev));
11391da177e4SLinus Torvalds 	dev.device_data = timer;
11401da177e4SLinus Torvalds 	return snd_timer_dev_register(&dev);
11411da177e4SLinus Torvalds }
114298856392STakashi Iwai EXPORT_SYMBOL(snd_timer_global_register);
11431da177e4SLinus Torvalds 
11441da177e4SLinus Torvalds /*
11451da177e4SLinus Torvalds  *  System timer
11461da177e4SLinus Torvalds  */
11471da177e4SLinus Torvalds 
11481da177e4SLinus Torvalds struct snd_timer_system_private {
11491da177e4SLinus Torvalds 	struct timer_list tlist;
115038e9a80fSKees Cook 	struct snd_timer *snd_timer;
11511da177e4SLinus Torvalds 	unsigned long last_expires;
11521da177e4SLinus Torvalds 	unsigned long last_jiffies;
11531da177e4SLinus Torvalds 	unsigned long correction;
11541da177e4SLinus Torvalds };
11551da177e4SLinus Torvalds 
snd_timer_s_function(struct timer_list * t)115638e9a80fSKees Cook static void snd_timer_s_function(struct timer_list *t)
11571da177e4SLinus Torvalds {
115838e9a80fSKees Cook 	struct snd_timer_system_private *priv = from_timer(priv, t,
115938e9a80fSKees Cook 								tlist);
116038e9a80fSKees Cook 	struct snd_timer *timer = priv->snd_timer;
11611da177e4SLinus Torvalds 	unsigned long jiff = jiffies;
11621da177e4SLinus Torvalds 	if (time_after(jiff, priv->last_expires))
11636ed5eff0SClemens Ladisch 		priv->correction += (long)jiff - (long)priv->last_expires;
11641da177e4SLinus Torvalds 	snd_timer_interrupt(timer, (long)jiff - (long)priv->last_jiffies);
11651da177e4SLinus Torvalds }
11661da177e4SLinus Torvalds 
snd_timer_s_start(struct snd_timer * timer)116753d2f744STakashi Iwai static int snd_timer_s_start(struct snd_timer * timer)
11681da177e4SLinus Torvalds {
11691da177e4SLinus Torvalds 	struct snd_timer_system_private *priv;
11701da177e4SLinus Torvalds 	unsigned long njiff;
11711da177e4SLinus Torvalds 
11721da177e4SLinus Torvalds 	priv = (struct snd_timer_system_private *) timer->private_data;
11731da177e4SLinus Torvalds 	njiff = (priv->last_jiffies = jiffies);
11741da177e4SLinus Torvalds 	if (priv->correction > timer->sticks - 1) {
11751da177e4SLinus Torvalds 		priv->correction -= timer->sticks - 1;
11761da177e4SLinus Torvalds 		njiff++;
11771da177e4SLinus Torvalds 	} else {
11781da177e4SLinus Torvalds 		njiff += timer->sticks - priv->correction;
117917f48ec3SClemens Ladisch 		priv->correction = 0;
11801da177e4SLinus Torvalds 	}
11814a07083eSTakashi Iwai 	priv->last_expires = njiff;
11824a07083eSTakashi Iwai 	mod_timer(&priv->tlist, njiff);
11831da177e4SLinus Torvalds 	return 0;
11841da177e4SLinus Torvalds }
11851da177e4SLinus Torvalds 
snd_timer_s_stop(struct snd_timer * timer)118653d2f744STakashi Iwai static int snd_timer_s_stop(struct snd_timer * timer)
11871da177e4SLinus Torvalds {
11881da177e4SLinus Torvalds 	struct snd_timer_system_private *priv;
11891da177e4SLinus Torvalds 	unsigned long jiff;
11901da177e4SLinus Torvalds 
11911da177e4SLinus Torvalds 	priv = (struct snd_timer_system_private *) timer->private_data;
11921da177e4SLinus Torvalds 	del_timer(&priv->tlist);
11931da177e4SLinus Torvalds 	jiff = jiffies;
11941da177e4SLinus Torvalds 	if (time_before(jiff, priv->last_expires))
11951da177e4SLinus Torvalds 		timer->sticks = priv->last_expires - jiff;
11961da177e4SLinus Torvalds 	else
11971da177e4SLinus Torvalds 		timer->sticks = 1;
1198de2696d8SClemens Ladisch 	priv->correction = 0;
11991da177e4SLinus Torvalds 	return 0;
12001da177e4SLinus Torvalds }
12011da177e4SLinus Torvalds 
snd_timer_s_close(struct snd_timer * timer)1202f146357fSTakashi Iwai static int snd_timer_s_close(struct snd_timer *timer)
1203f146357fSTakashi Iwai {
1204f146357fSTakashi Iwai 	struct snd_timer_system_private *priv;
1205f146357fSTakashi Iwai 
1206f146357fSTakashi Iwai 	priv = (struct snd_timer_system_private *)timer->private_data;
1207f146357fSTakashi Iwai 	del_timer_sync(&priv->tlist);
1208f146357fSTakashi Iwai 	return 0;
1209f146357fSTakashi Iwai }
1210f146357fSTakashi Iwai 
1211df76996aSTakashi Iwai static const struct snd_timer_hardware snd_timer_system =
12121da177e4SLinus Torvalds {
1213bf083595STakashi Iwai 	.flags =	SNDRV_TIMER_HW_FIRST | SNDRV_TIMER_HW_WORK,
12141da177e4SLinus Torvalds 	.resolution =	1000000000L / HZ,
12151da177e4SLinus Torvalds 	.ticks =	10000000L,
1216f146357fSTakashi Iwai 	.close =	snd_timer_s_close,
12171da177e4SLinus Torvalds 	.start =	snd_timer_s_start,
12181da177e4SLinus Torvalds 	.stop =		snd_timer_s_stop
12191da177e4SLinus Torvalds };
12201da177e4SLinus Torvalds 
snd_timer_free_system(struct snd_timer * timer)122153d2f744STakashi Iwai static void snd_timer_free_system(struct snd_timer *timer)
12221da177e4SLinus Torvalds {
12231da177e4SLinus Torvalds 	kfree(timer->private_data);
12241da177e4SLinus Torvalds }
12251da177e4SLinus Torvalds 
snd_timer_register_system(void)12261da177e4SLinus Torvalds static int snd_timer_register_system(void)
12271da177e4SLinus Torvalds {
122853d2f744STakashi Iwai 	struct snd_timer *timer;
12291da177e4SLinus Torvalds 	struct snd_timer_system_private *priv;
12301da177e4SLinus Torvalds 	int err;
12311da177e4SLinus Torvalds 
12326b172a85SClemens Ladisch 	err = snd_timer_global_new("system", SNDRV_TIMER_GLOBAL_SYSTEM, &timer);
12336b172a85SClemens Ladisch 	if (err < 0)
12341da177e4SLinus Torvalds 		return err;
12351da177e4SLinus Torvalds 	strcpy(timer->name, "system timer");
12361da177e4SLinus Torvalds 	timer->hw = snd_timer_system;
1237ca2c0966STakashi Iwai 	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
12381da177e4SLinus Torvalds 	if (priv == NULL) {
12391da177e4SLinus Torvalds 		snd_timer_free(timer);
12401da177e4SLinus Torvalds 		return -ENOMEM;
12411da177e4SLinus Torvalds 	}
124238e9a80fSKees Cook 	priv->snd_timer = timer;
124338e9a80fSKees Cook 	timer_setup(&priv->tlist, snd_timer_s_function, 0);
12441da177e4SLinus Torvalds 	timer->private_data = priv;
12451da177e4SLinus Torvalds 	timer->private_free = snd_timer_free_system;
12461da177e4SLinus Torvalds 	return snd_timer_global_register(timer);
12471da177e4SLinus Torvalds }
12481da177e4SLinus Torvalds 
1249cd6a6503SJie Yang #ifdef CONFIG_SND_PROC_FS
12501da177e4SLinus Torvalds /*
12511da177e4SLinus Torvalds  *  Info interface
12521da177e4SLinus Torvalds  */
12531da177e4SLinus Torvalds 
snd_timer_proc_read(struct snd_info_entry * entry,struct snd_info_buffer * buffer)125453d2f744STakashi Iwai static void snd_timer_proc_read(struct snd_info_entry *entry,
125553d2f744STakashi Iwai 				struct snd_info_buffer *buffer)
12561da177e4SLinus Torvalds {
125753d2f744STakashi Iwai 	struct snd_timer *timer;
125853d2f744STakashi Iwai 	struct snd_timer_instance *ti;
12596cc84450SOswald Buddenhagen 	unsigned long resolution;
12601da177e4SLinus Torvalds 
12611a60d4c5SIngo Molnar 	mutex_lock(&register_mutex);
12629244b2c3SJohannes Berg 	list_for_each_entry(timer, &snd_timer_list, device_list) {
1263230323daSTakashi Iwai 		if (timer->card && timer->card->shutdown)
1264230323daSTakashi Iwai 			continue;
12651da177e4SLinus Torvalds 		switch (timer->tmr_class) {
12661da177e4SLinus Torvalds 		case SNDRV_TIMER_CLASS_GLOBAL:
12671da177e4SLinus Torvalds 			snd_iprintf(buffer, "G%i: ", timer->tmr_device);
12681da177e4SLinus Torvalds 			break;
12691da177e4SLinus Torvalds 		case SNDRV_TIMER_CLASS_CARD:
12706b172a85SClemens Ladisch 			snd_iprintf(buffer, "C%i-%i: ",
12716b172a85SClemens Ladisch 				    timer->card->number, timer->tmr_device);
12721da177e4SLinus Torvalds 			break;
12731da177e4SLinus Torvalds 		case SNDRV_TIMER_CLASS_PCM:
12746b172a85SClemens Ladisch 			snd_iprintf(buffer, "P%i-%i-%i: ", timer->card->number,
12756b172a85SClemens Ladisch 				    timer->tmr_device, timer->tmr_subdevice);
12761da177e4SLinus Torvalds 			break;
12771da177e4SLinus Torvalds 		default:
12786b172a85SClemens Ladisch 			snd_iprintf(buffer, "?%i-%i-%i-%i: ", timer->tmr_class,
12796b172a85SClemens Ladisch 				    timer->card ? timer->card->number : -1,
12806b172a85SClemens Ladisch 				    timer->tmr_device, timer->tmr_subdevice);
12811da177e4SLinus Torvalds 		}
12821da177e4SLinus Torvalds 		snd_iprintf(buffer, "%s :", timer->name);
12836cc84450SOswald Buddenhagen 		spin_lock_irq(&timer->lock);
12846cc84450SOswald Buddenhagen 		resolution = snd_timer_hw_resolution(timer);
12856cc84450SOswald Buddenhagen 		spin_unlock_irq(&timer->lock);
12866cc84450SOswald Buddenhagen 		if (resolution)
12876b172a85SClemens Ladisch 			snd_iprintf(buffer, " %lu.%03luus (%lu ticks)",
12886cc84450SOswald Buddenhagen 				    resolution / 1000,
12896cc84450SOswald Buddenhagen 				    resolution % 1000,
12906b172a85SClemens Ladisch 				    timer->hw.ticks);
12911da177e4SLinus Torvalds 		if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE)
12921da177e4SLinus Torvalds 			snd_iprintf(buffer, " SLAVE");
12931da177e4SLinus Torvalds 		snd_iprintf(buffer, "\n");
12949244b2c3SJohannes Berg 		list_for_each_entry(ti, &timer->open_list_head, open_list)
12956b172a85SClemens Ladisch 			snd_iprintf(buffer, "  Client %s : %s\n",
12961da177e4SLinus Torvalds 				    ti->owner ? ti->owner : "unknown",
12973bcf8eebSPierre-Louis Bossart 				    (ti->flags & (SNDRV_TIMER_IFLG_START |
12983bcf8eebSPierre-Louis Bossart 						  SNDRV_TIMER_IFLG_RUNNING))
12996b172a85SClemens Ladisch 				    ? "running" : "stopped");
13001da177e4SLinus Torvalds 	}
13011a60d4c5SIngo Molnar 	mutex_unlock(&register_mutex);
13021da177e4SLinus Torvalds }
13031da177e4SLinus Torvalds 
13046581f4e7STakashi Iwai static struct snd_info_entry *snd_timer_proc_entry;
1305e28563ccSTakashi Iwai 
snd_timer_proc_init(void)1306e28563ccSTakashi Iwai static void __init snd_timer_proc_init(void)
1307e28563ccSTakashi Iwai {
1308e28563ccSTakashi Iwai 	struct snd_info_entry *entry;
1309e28563ccSTakashi Iwai 
1310e28563ccSTakashi Iwai 	entry = snd_info_create_module_entry(THIS_MODULE, "timers", NULL);
1311e28563ccSTakashi Iwai 	if (entry != NULL) {
1312e28563ccSTakashi Iwai 		entry->c.text.read = snd_timer_proc_read;
1313e28563ccSTakashi Iwai 		if (snd_info_register(entry) < 0) {
1314e28563ccSTakashi Iwai 			snd_info_free_entry(entry);
1315e28563ccSTakashi Iwai 			entry = NULL;
1316e28563ccSTakashi Iwai 		}
1317e28563ccSTakashi Iwai 	}
1318e28563ccSTakashi Iwai 	snd_timer_proc_entry = entry;
1319e28563ccSTakashi Iwai }
1320e28563ccSTakashi Iwai 
snd_timer_proc_done(void)1321e28563ccSTakashi Iwai static void __exit snd_timer_proc_done(void)
1322e28563ccSTakashi Iwai {
1323746d4a02STakashi Iwai 	snd_info_free_entry(snd_timer_proc_entry);
1324e28563ccSTakashi Iwai }
1325cd6a6503SJie Yang #else /* !CONFIG_SND_PROC_FS */
1326e28563ccSTakashi Iwai #define snd_timer_proc_init()
1327e28563ccSTakashi Iwai #define snd_timer_proc_done()
1328e28563ccSTakashi Iwai #endif
1329e28563ccSTakashi Iwai 
13301da177e4SLinus Torvalds /*
13311da177e4SLinus Torvalds  *  USER SPACE interface
13321da177e4SLinus Torvalds  */
13331da177e4SLinus Torvalds 
snd_timer_user_interrupt(struct snd_timer_instance * timeri,unsigned long resolution,unsigned long ticks)133453d2f744STakashi Iwai static void snd_timer_user_interrupt(struct snd_timer_instance *timeri,
13351da177e4SLinus Torvalds 				     unsigned long resolution,
13361da177e4SLinus Torvalds 				     unsigned long ticks)
13371da177e4SLinus Torvalds {
133853d2f744STakashi Iwai 	struct snd_timer_user *tu = timeri->callback_data;
133953d2f744STakashi Iwai 	struct snd_timer_read *r;
13401da177e4SLinus Torvalds 	int prev;
13411da177e4SLinus Torvalds 
13421da177e4SLinus Torvalds 	spin_lock(&tu->qlock);
13431da177e4SLinus Torvalds 	if (tu->qused > 0) {
13441da177e4SLinus Torvalds 		prev = tu->qtail == 0 ? tu->queue_size - 1 : tu->qtail - 1;
13451da177e4SLinus Torvalds 		r = &tu->queue[prev];
13461da177e4SLinus Torvalds 		if (r->resolution == resolution) {
13471da177e4SLinus Torvalds 			r->ticks += ticks;
13481da177e4SLinus Torvalds 			goto __wake;
13491da177e4SLinus Torvalds 		}
13501da177e4SLinus Torvalds 	}
13511da177e4SLinus Torvalds 	if (tu->qused >= tu->queue_size) {
13521da177e4SLinus Torvalds 		tu->overrun++;
13531da177e4SLinus Torvalds 	} else {
13541da177e4SLinus Torvalds 		r = &tu->queue[tu->qtail++];
13551da177e4SLinus Torvalds 		tu->qtail %= tu->queue_size;
13561da177e4SLinus Torvalds 		r->resolution = resolution;
13571da177e4SLinus Torvalds 		r->ticks = ticks;
13581da177e4SLinus Torvalds 		tu->qused++;
13591da177e4SLinus Torvalds 	}
13601da177e4SLinus Torvalds       __wake:
13611da177e4SLinus Torvalds 	spin_unlock(&tu->qlock);
136295cc637cSTakashi Iwai 	snd_kill_fasync(tu->fasync, SIGIO, POLL_IN);
13631da177e4SLinus Torvalds 	wake_up(&tu->qchange_sleep);
13641da177e4SLinus Torvalds }
13651da177e4SLinus Torvalds 
snd_timer_user_append_to_tqueue(struct snd_timer_user * tu,struct snd_timer_tread64 * tread)136653d2f744STakashi Iwai static void snd_timer_user_append_to_tqueue(struct snd_timer_user *tu,
136707094ae6SBaolin Wang 					    struct snd_timer_tread64 *tread)
13681da177e4SLinus Torvalds {
13691da177e4SLinus Torvalds 	if (tu->qused >= tu->queue_size) {
13701da177e4SLinus Torvalds 		tu->overrun++;
13711da177e4SLinus Torvalds 	} else {
13721da177e4SLinus Torvalds 		memcpy(&tu->tqueue[tu->qtail++], tread, sizeof(*tread));
13731da177e4SLinus Torvalds 		tu->qtail %= tu->queue_size;
13741da177e4SLinus Torvalds 		tu->qused++;
13751da177e4SLinus Torvalds 	}
13761da177e4SLinus Torvalds }
13771da177e4SLinus Torvalds 
snd_timer_user_ccallback(struct snd_timer_instance * timeri,int event,struct timespec64 * tstamp,unsigned long resolution)137853d2f744STakashi Iwai static void snd_timer_user_ccallback(struct snd_timer_instance *timeri,
137953d2f744STakashi Iwai 				     int event,
1380fcae40c9SBaolin Wang 				     struct timespec64 *tstamp,
13811da177e4SLinus Torvalds 				     unsigned long resolution)
13821da177e4SLinus Torvalds {
138353d2f744STakashi Iwai 	struct snd_timer_user *tu = timeri->callback_data;
138407094ae6SBaolin Wang 	struct snd_timer_tread64 r1;
1385bfe70783SDan Carpenter 	unsigned long flags;
13861da177e4SLinus Torvalds 
13876b172a85SClemens Ladisch 	if (event >= SNDRV_TIMER_EVENT_START &&
13886b172a85SClemens Ladisch 	    event <= SNDRV_TIMER_EVENT_PAUSE)
13891da177e4SLinus Torvalds 		tu->tstamp = *tstamp;
13901da177e4SLinus Torvalds 	if ((tu->filter & (1 << event)) == 0 || !tu->tread)
13911da177e4SLinus Torvalds 		return;
13929a47e9cfSKangjie Lu 	memset(&r1, 0, sizeof(r1));
13931da177e4SLinus Torvalds 	r1.event = event;
139407094ae6SBaolin Wang 	r1.tstamp_sec = tstamp->tv_sec;
139507094ae6SBaolin Wang 	r1.tstamp_nsec = tstamp->tv_nsec;
13961da177e4SLinus Torvalds 	r1.val = resolution;
1397bfe70783SDan Carpenter 	spin_lock_irqsave(&tu->qlock, flags);
13981da177e4SLinus Torvalds 	snd_timer_user_append_to_tqueue(tu, &r1);
1399bfe70783SDan Carpenter 	spin_unlock_irqrestore(&tu->qlock, flags);
140095cc637cSTakashi Iwai 	snd_kill_fasync(tu->fasync, SIGIO, POLL_IN);
14011da177e4SLinus Torvalds 	wake_up(&tu->qchange_sleep);
14021da177e4SLinus Torvalds }
14031da177e4SLinus Torvalds 
snd_timer_user_disconnect(struct snd_timer_instance * timeri)140440ed9444STakashi Iwai static void snd_timer_user_disconnect(struct snd_timer_instance *timeri)
140540ed9444STakashi Iwai {
140640ed9444STakashi Iwai 	struct snd_timer_user *tu = timeri->callback_data;
140740ed9444STakashi Iwai 
140840ed9444STakashi Iwai 	tu->disconnected = true;
140940ed9444STakashi Iwai 	wake_up(&tu->qchange_sleep);
141040ed9444STakashi Iwai }
141140ed9444STakashi Iwai 
snd_timer_user_tinterrupt(struct snd_timer_instance * timeri,unsigned long resolution,unsigned long ticks)141253d2f744STakashi Iwai static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri,
14131da177e4SLinus Torvalds 				      unsigned long resolution,
14141da177e4SLinus Torvalds 				      unsigned long ticks)
14151da177e4SLinus Torvalds {
141653d2f744STakashi Iwai 	struct snd_timer_user *tu = timeri->callback_data;
141707094ae6SBaolin Wang 	struct snd_timer_tread64 *r, r1;
1418fcae40c9SBaolin Wang 	struct timespec64 tstamp;
14191da177e4SLinus Torvalds 	int prev, append = 0;
14201da177e4SLinus Torvalds 
1421a8c006aaSDan Carpenter 	memset(&r1, 0, sizeof(r1));
142207799e75STakashi Iwai 	memset(&tstamp, 0, sizeof(tstamp));
14231da177e4SLinus Torvalds 	spin_lock(&tu->qlock);
14246b172a85SClemens Ladisch 	if ((tu->filter & ((1 << SNDRV_TIMER_EVENT_RESOLUTION) |
14256b172a85SClemens Ladisch 			   (1 << SNDRV_TIMER_EVENT_TICK))) == 0) {
14261da177e4SLinus Torvalds 		spin_unlock(&tu->qlock);
14271da177e4SLinus Torvalds 		return;
14281da177e4SLinus Torvalds 	}
1429b751eef1SJaroslav Kysela 	if (tu->last_resolution != resolution || ticks > 0) {
1430b751eef1SJaroslav Kysela 		if (timer_tstamp_monotonic)
1431fcae40c9SBaolin Wang 			ktime_get_ts64(&tstamp);
1432b751eef1SJaroslav Kysela 		else
1433fcae40c9SBaolin Wang 			ktime_get_real_ts64(&tstamp);
1434b751eef1SJaroslav Kysela 	}
14356b172a85SClemens Ladisch 	if ((tu->filter & (1 << SNDRV_TIMER_EVENT_RESOLUTION)) &&
14366b172a85SClemens Ladisch 	    tu->last_resolution != resolution) {
14371da177e4SLinus Torvalds 		r1.event = SNDRV_TIMER_EVENT_RESOLUTION;
143807094ae6SBaolin Wang 		r1.tstamp_sec = tstamp.tv_sec;
143907094ae6SBaolin Wang 		r1.tstamp_nsec = tstamp.tv_nsec;
14401da177e4SLinus Torvalds 		r1.val = resolution;
14411da177e4SLinus Torvalds 		snd_timer_user_append_to_tqueue(tu, &r1);
14421da177e4SLinus Torvalds 		tu->last_resolution = resolution;
14431da177e4SLinus Torvalds 		append++;
14441da177e4SLinus Torvalds 	}
14451da177e4SLinus Torvalds 	if ((tu->filter & (1 << SNDRV_TIMER_EVENT_TICK)) == 0)
14461da177e4SLinus Torvalds 		goto __wake;
14471da177e4SLinus Torvalds 	if (ticks == 0)
14481da177e4SLinus Torvalds 		goto __wake;
14491da177e4SLinus Torvalds 	if (tu->qused > 0) {
14501da177e4SLinus Torvalds 		prev = tu->qtail == 0 ? tu->queue_size - 1 : tu->qtail - 1;
14511da177e4SLinus Torvalds 		r = &tu->tqueue[prev];
14521da177e4SLinus Torvalds 		if (r->event == SNDRV_TIMER_EVENT_TICK) {
145307094ae6SBaolin Wang 			r->tstamp_sec = tstamp.tv_sec;
145407094ae6SBaolin Wang 			r->tstamp_nsec = tstamp.tv_nsec;
14551da177e4SLinus Torvalds 			r->val += ticks;
14561da177e4SLinus Torvalds 			append++;
14571da177e4SLinus Torvalds 			goto __wake;
14581da177e4SLinus Torvalds 		}
14591da177e4SLinus Torvalds 	}
14601da177e4SLinus Torvalds 	r1.event = SNDRV_TIMER_EVENT_TICK;
146107094ae6SBaolin Wang 	r1.tstamp_sec = tstamp.tv_sec;
146207094ae6SBaolin Wang 	r1.tstamp_nsec = tstamp.tv_nsec;
14631da177e4SLinus Torvalds 	r1.val = ticks;
14641da177e4SLinus Torvalds 	snd_timer_user_append_to_tqueue(tu, &r1);
14651da177e4SLinus Torvalds 	append++;
14661da177e4SLinus Torvalds       __wake:
14671da177e4SLinus Torvalds 	spin_unlock(&tu->qlock);
14681da177e4SLinus Torvalds 	if (append == 0)
14691da177e4SLinus Torvalds 		return;
147095cc637cSTakashi Iwai 	snd_kill_fasync(tu->fasync, SIGIO, POLL_IN);
14711da177e4SLinus Torvalds 	wake_up(&tu->qchange_sleep);
14721da177e4SLinus Torvalds }
14731da177e4SLinus Torvalds 
realloc_user_queue(struct snd_timer_user * tu,int size)1474890e2cb5STakashi Iwai static int realloc_user_queue(struct snd_timer_user *tu, int size)
1475890e2cb5STakashi Iwai {
1476890e2cb5STakashi Iwai 	struct snd_timer_read *queue = NULL;
147707094ae6SBaolin Wang 	struct snd_timer_tread64 *tqueue = NULL;
1478890e2cb5STakashi Iwai 
1479890e2cb5STakashi Iwai 	if (tu->tread) {
1480890e2cb5STakashi Iwai 		tqueue = kcalloc(size, sizeof(*tqueue), GFP_KERNEL);
1481890e2cb5STakashi Iwai 		if (!tqueue)
1482890e2cb5STakashi Iwai 			return -ENOMEM;
1483890e2cb5STakashi Iwai 	} else {
1484890e2cb5STakashi Iwai 		queue = kcalloc(size, sizeof(*queue), GFP_KERNEL);
1485890e2cb5STakashi Iwai 		if (!queue)
1486890e2cb5STakashi Iwai 			return -ENOMEM;
1487890e2cb5STakashi Iwai 	}
1488890e2cb5STakashi Iwai 
1489890e2cb5STakashi Iwai 	spin_lock_irq(&tu->qlock);
1490890e2cb5STakashi Iwai 	kfree(tu->queue);
1491890e2cb5STakashi Iwai 	kfree(tu->tqueue);
1492890e2cb5STakashi Iwai 	tu->queue_size = size;
1493890e2cb5STakashi Iwai 	tu->queue = queue;
1494890e2cb5STakashi Iwai 	tu->tqueue = tqueue;
1495890e2cb5STakashi Iwai 	tu->qhead = tu->qtail = tu->qused = 0;
1496890e2cb5STakashi Iwai 	spin_unlock_irq(&tu->qlock);
1497890e2cb5STakashi Iwai 
1498890e2cb5STakashi Iwai 	return 0;
1499890e2cb5STakashi Iwai }
1500890e2cb5STakashi Iwai 
snd_timer_user_open(struct inode * inode,struct file * file)15011da177e4SLinus Torvalds static int snd_timer_user_open(struct inode *inode, struct file *file)
15021da177e4SLinus Torvalds {
150353d2f744STakashi Iwai 	struct snd_timer_user *tu;
150402f4865fSTakashi Iwai 	int err;
150502f4865fSTakashi Iwai 
1506c5bf68feSKirill Smelkov 	err = stream_open(inode, file);
150702f4865fSTakashi Iwai 	if (err < 0)
150802f4865fSTakashi Iwai 		return err;
15091da177e4SLinus Torvalds 
1510ca2c0966STakashi Iwai 	tu = kzalloc(sizeof(*tu), GFP_KERNEL);
15111da177e4SLinus Torvalds 	if (tu == NULL)
15121da177e4SLinus Torvalds 		return -ENOMEM;
15131da177e4SLinus Torvalds 	spin_lock_init(&tu->qlock);
15141da177e4SLinus Torvalds 	init_waitqueue_head(&tu->qchange_sleep);
1515af368027STakashi Iwai 	mutex_init(&tu->ioctl_lock);
15161da177e4SLinus Torvalds 	tu->ticks = 1;
1517890e2cb5STakashi Iwai 	if (realloc_user_queue(tu, 128) < 0) {
15181da177e4SLinus Torvalds 		kfree(tu);
15191da177e4SLinus Torvalds 		return -ENOMEM;
15201da177e4SLinus Torvalds 	}
15211da177e4SLinus Torvalds 	file->private_data = tu;
15221da177e4SLinus Torvalds 	return 0;
15231da177e4SLinus Torvalds }
15241da177e4SLinus Torvalds 
snd_timer_user_release(struct inode * inode,struct file * file)15251da177e4SLinus Torvalds static int snd_timer_user_release(struct inode *inode, struct file *file)
15261da177e4SLinus Torvalds {
152753d2f744STakashi Iwai 	struct snd_timer_user *tu;
15281da177e4SLinus Torvalds 
15291da177e4SLinus Torvalds 	if (file->private_data) {
15301da177e4SLinus Torvalds 		tu = file->private_data;
15311da177e4SLinus Torvalds 		file->private_data = NULL;
1532af368027STakashi Iwai 		mutex_lock(&tu->ioctl_lock);
15336a34367eSTakashi Iwai 		if (tu->timeri) {
15341da177e4SLinus Torvalds 			snd_timer_close(tu->timeri);
15356a34367eSTakashi Iwai 			snd_timer_instance_free(tu->timeri);
15366a34367eSTakashi Iwai 		}
1537af368027STakashi Iwai 		mutex_unlock(&tu->ioctl_lock);
153895cc637cSTakashi Iwai 		snd_fasync_free(tu->fasync);
15391da177e4SLinus Torvalds 		kfree(tu->queue);
15401da177e4SLinus Torvalds 		kfree(tu->tqueue);
15411da177e4SLinus Torvalds 		kfree(tu);
15421da177e4SLinus Torvalds 	}
15431da177e4SLinus Torvalds 	return 0;
15441da177e4SLinus Torvalds }
15451da177e4SLinus Torvalds 
snd_timer_user_zero_id(struct snd_timer_id * id)154653d2f744STakashi Iwai static void snd_timer_user_zero_id(struct snd_timer_id *id)
15471da177e4SLinus Torvalds {
15481da177e4SLinus Torvalds 	id->dev_class = SNDRV_TIMER_CLASS_NONE;
15491da177e4SLinus Torvalds 	id->dev_sclass = SNDRV_TIMER_SCLASS_NONE;
15501da177e4SLinus Torvalds 	id->card = -1;
15511da177e4SLinus Torvalds 	id->device = -1;
15521da177e4SLinus Torvalds 	id->subdevice = -1;
15531da177e4SLinus Torvalds }
15541da177e4SLinus Torvalds 
snd_timer_user_copy_id(struct snd_timer_id * id,struct snd_timer * timer)155553d2f744STakashi Iwai static void snd_timer_user_copy_id(struct snd_timer_id *id, struct snd_timer *timer)
15561da177e4SLinus Torvalds {
15571da177e4SLinus Torvalds 	id->dev_class = timer->tmr_class;
15581da177e4SLinus Torvalds 	id->dev_sclass = SNDRV_TIMER_SCLASS_NONE;
15591da177e4SLinus Torvalds 	id->card = timer->card ? timer->card->number : -1;
15601da177e4SLinus Torvalds 	id->device = timer->tmr_device;
15611da177e4SLinus Torvalds 	id->subdevice = timer->tmr_subdevice;
15621da177e4SLinus Torvalds }
15631da177e4SLinus Torvalds 
snd_timer_user_next_device(struct snd_timer_id __user * _tid)156453d2f744STakashi Iwai static int snd_timer_user_next_device(struct snd_timer_id __user *_tid)
15651da177e4SLinus Torvalds {
156653d2f744STakashi Iwai 	struct snd_timer_id id;
156753d2f744STakashi Iwai 	struct snd_timer *timer;
15681da177e4SLinus Torvalds 	struct list_head *p;
15691da177e4SLinus Torvalds 
15701da177e4SLinus Torvalds 	if (copy_from_user(&id, _tid, sizeof(id)))
15711da177e4SLinus Torvalds 		return -EFAULT;
15721a60d4c5SIngo Molnar 	mutex_lock(&register_mutex);
15731da177e4SLinus Torvalds 	if (id.dev_class < 0) {		/* first item */
15741da177e4SLinus Torvalds 		if (list_empty(&snd_timer_list))
15751da177e4SLinus Torvalds 			snd_timer_user_zero_id(&id);
15761da177e4SLinus Torvalds 		else {
15779dfba380SClemens Ladisch 			timer = list_entry(snd_timer_list.next,
157853d2f744STakashi Iwai 					   struct snd_timer, device_list);
15791da177e4SLinus Torvalds 			snd_timer_user_copy_id(&id, timer);
15801da177e4SLinus Torvalds 		}
15811da177e4SLinus Torvalds 	} else {
15821da177e4SLinus Torvalds 		switch (id.dev_class) {
15831da177e4SLinus Torvalds 		case SNDRV_TIMER_CLASS_GLOBAL:
15841da177e4SLinus Torvalds 			id.device = id.device < 0 ? 0 : id.device + 1;
15851da177e4SLinus Torvalds 			list_for_each(p, &snd_timer_list) {
158653d2f744STakashi Iwai 				timer = list_entry(p, struct snd_timer, device_list);
15871da177e4SLinus Torvalds 				if (timer->tmr_class > SNDRV_TIMER_CLASS_GLOBAL) {
15881da177e4SLinus Torvalds 					snd_timer_user_copy_id(&id, timer);
15891da177e4SLinus Torvalds 					break;
15901da177e4SLinus Torvalds 				}
15911da177e4SLinus Torvalds 				if (timer->tmr_device >= id.device) {
15921da177e4SLinus Torvalds 					snd_timer_user_copy_id(&id, timer);
15931da177e4SLinus Torvalds 					break;
15941da177e4SLinus Torvalds 				}
15951da177e4SLinus Torvalds 			}
15961da177e4SLinus Torvalds 			if (p == &snd_timer_list)
15971da177e4SLinus Torvalds 				snd_timer_user_zero_id(&id);
15981da177e4SLinus Torvalds 			break;
15991da177e4SLinus Torvalds 		case SNDRV_TIMER_CLASS_CARD:
16001da177e4SLinus Torvalds 		case SNDRV_TIMER_CLASS_PCM:
16011da177e4SLinus Torvalds 			if (id.card < 0) {
16021da177e4SLinus Torvalds 				id.card = 0;
16031da177e4SLinus Torvalds 			} else {
16041da177e4SLinus Torvalds 				if (id.device < 0) {
16051da177e4SLinus Torvalds 					id.device = 0;
16061da177e4SLinus Torvalds 				} else {
1607e8ed6820SDan Carpenter 					if (id.subdevice < 0)
16086b172a85SClemens Ladisch 						id.subdevice = 0;
1609b41f794fSTakashi Iwai 					else if (id.subdevice < INT_MAX)
16106b172a85SClemens Ladisch 						id.subdevice++;
16116b172a85SClemens Ladisch 				}
16121da177e4SLinus Torvalds 			}
16131da177e4SLinus Torvalds 			list_for_each(p, &snd_timer_list) {
161453d2f744STakashi Iwai 				timer = list_entry(p, struct snd_timer, device_list);
16151da177e4SLinus Torvalds 				if (timer->tmr_class > id.dev_class) {
16161da177e4SLinus Torvalds 					snd_timer_user_copy_id(&id, timer);
16171da177e4SLinus Torvalds 					break;
16181da177e4SLinus Torvalds 				}
16191da177e4SLinus Torvalds 				if (timer->tmr_class < id.dev_class)
16201da177e4SLinus Torvalds 					continue;
16211da177e4SLinus Torvalds 				if (timer->card->number > id.card) {
16221da177e4SLinus Torvalds 					snd_timer_user_copy_id(&id, timer);
16231da177e4SLinus Torvalds 					break;
16241da177e4SLinus Torvalds 				}
16251da177e4SLinus Torvalds 				if (timer->card->number < id.card)
16261da177e4SLinus Torvalds 					continue;
16271da177e4SLinus Torvalds 				if (timer->tmr_device > id.device) {
16281da177e4SLinus Torvalds 					snd_timer_user_copy_id(&id, timer);
16291da177e4SLinus Torvalds 					break;
16301da177e4SLinus Torvalds 				}
16311da177e4SLinus Torvalds 				if (timer->tmr_device < id.device)
16321da177e4SLinus Torvalds 					continue;
16331da177e4SLinus Torvalds 				if (timer->tmr_subdevice > id.subdevice) {
16341da177e4SLinus Torvalds 					snd_timer_user_copy_id(&id, timer);
16351da177e4SLinus Torvalds 					break;
16361da177e4SLinus Torvalds 				}
16371da177e4SLinus Torvalds 				if (timer->tmr_subdevice < id.subdevice)
16381da177e4SLinus Torvalds 					continue;
16391da177e4SLinus Torvalds 				snd_timer_user_copy_id(&id, timer);
16401da177e4SLinus Torvalds 				break;
16411da177e4SLinus Torvalds 			}
16421da177e4SLinus Torvalds 			if (p == &snd_timer_list)
16431da177e4SLinus Torvalds 				snd_timer_user_zero_id(&id);
16441da177e4SLinus Torvalds 			break;
16451da177e4SLinus Torvalds 		default:
16461da177e4SLinus Torvalds 			snd_timer_user_zero_id(&id);
16471da177e4SLinus Torvalds 		}
16481da177e4SLinus Torvalds 	}
16491a60d4c5SIngo Molnar 	mutex_unlock(&register_mutex);
16501da177e4SLinus Torvalds 	if (copy_to_user(_tid, &id, sizeof(*_tid)))
16511da177e4SLinus Torvalds 		return -EFAULT;
16521da177e4SLinus Torvalds 	return 0;
16531da177e4SLinus Torvalds }
16541da177e4SLinus Torvalds 
snd_timer_user_ginfo(struct file * file,struct snd_timer_ginfo __user * _ginfo)16556b172a85SClemens Ladisch static int snd_timer_user_ginfo(struct file *file,
165653d2f744STakashi Iwai 				struct snd_timer_ginfo __user *_ginfo)
16571da177e4SLinus Torvalds {
165853d2f744STakashi Iwai 	struct snd_timer_ginfo *ginfo;
165953d2f744STakashi Iwai 	struct snd_timer_id tid;
166053d2f744STakashi Iwai 	struct snd_timer *t;
16611da177e4SLinus Torvalds 	struct list_head *p;
16621da177e4SLinus Torvalds 	int err = 0;
16631da177e4SLinus Torvalds 
1664ef44a1ecSLi Zefan 	ginfo = memdup_user(_ginfo, sizeof(*ginfo));
1665ef44a1ecSLi Zefan 	if (IS_ERR(ginfo))
1666ef44a1ecSLi Zefan 		return PTR_ERR(ginfo);
1667ef44a1ecSLi Zefan 
16681da177e4SLinus Torvalds 	tid = ginfo->tid;
16691da177e4SLinus Torvalds 	memset(ginfo, 0, sizeof(*ginfo));
16701da177e4SLinus Torvalds 	ginfo->tid = tid;
16711a60d4c5SIngo Molnar 	mutex_lock(&register_mutex);
16721da177e4SLinus Torvalds 	t = snd_timer_find(&tid);
16731da177e4SLinus Torvalds 	if (t != NULL) {
16741da177e4SLinus Torvalds 		ginfo->card = t->card ? t->card->number : -1;
16751da177e4SLinus Torvalds 		if (t->hw.flags & SNDRV_TIMER_HW_SLAVE)
16761da177e4SLinus Torvalds 			ginfo->flags |= SNDRV_TIMER_FLG_SLAVE;
167775b1a8f9SJoe Perches 		strscpy(ginfo->id, t->id, sizeof(ginfo->id));
167875b1a8f9SJoe Perches 		strscpy(ginfo->name, t->name, sizeof(ginfo->name));
16796cc84450SOswald Buddenhagen 		spin_lock_irq(&t->lock);
16806cc84450SOswald Buddenhagen 		ginfo->resolution = snd_timer_hw_resolution(t);
16816cc84450SOswald Buddenhagen 		spin_unlock_irq(&t->lock);
16821da177e4SLinus Torvalds 		if (t->hw.resolution_min > 0) {
16831da177e4SLinus Torvalds 			ginfo->resolution_min = t->hw.resolution_min;
16841da177e4SLinus Torvalds 			ginfo->resolution_max = t->hw.resolution_max;
16851da177e4SLinus Torvalds 		}
16861da177e4SLinus Torvalds 		list_for_each(p, &t->open_list_head) {
16871da177e4SLinus Torvalds 			ginfo->clients++;
16881da177e4SLinus Torvalds 		}
16891da177e4SLinus Torvalds 	} else {
16901da177e4SLinus Torvalds 		err = -ENODEV;
16911da177e4SLinus Torvalds 	}
16921a60d4c5SIngo Molnar 	mutex_unlock(&register_mutex);
16931da177e4SLinus Torvalds 	if (err >= 0 && copy_to_user(_ginfo, ginfo, sizeof(*ginfo)))
16941da177e4SLinus Torvalds 		err = -EFAULT;
16951da177e4SLinus Torvalds 	kfree(ginfo);
16961da177e4SLinus Torvalds 	return err;
16971da177e4SLinus Torvalds }
16981da177e4SLinus Torvalds 
timer_set_gparams(struct snd_timer_gparams * gparams)169991d2178eSTakashi Sakamoto static int timer_set_gparams(struct snd_timer_gparams *gparams)
17001da177e4SLinus Torvalds {
170153d2f744STakashi Iwai 	struct snd_timer *t;
17021da177e4SLinus Torvalds 	int err;
17031da177e4SLinus Torvalds 
17041a60d4c5SIngo Molnar 	mutex_lock(&register_mutex);
170591d2178eSTakashi Sakamoto 	t = snd_timer_find(&gparams->tid);
17066b172a85SClemens Ladisch 	if (!t) {
17071da177e4SLinus Torvalds 		err = -ENODEV;
17086b172a85SClemens Ladisch 		goto _error;
17091da177e4SLinus Torvalds 	}
17106b172a85SClemens Ladisch 	if (!list_empty(&t->open_list_head)) {
17116b172a85SClemens Ladisch 		err = -EBUSY;
17126b172a85SClemens Ladisch 		goto _error;
17136b172a85SClemens Ladisch 	}
17146b172a85SClemens Ladisch 	if (!t->hw.set_period) {
17156b172a85SClemens Ladisch 		err = -ENOSYS;
17166b172a85SClemens Ladisch 		goto _error;
17176b172a85SClemens Ladisch 	}
171891d2178eSTakashi Sakamoto 	err = t->hw.set_period(t, gparams->period_num, gparams->period_den);
17196b172a85SClemens Ladisch _error:
17201a60d4c5SIngo Molnar 	mutex_unlock(&register_mutex);
17211da177e4SLinus Torvalds 	return err;
17221da177e4SLinus Torvalds }
17231da177e4SLinus Torvalds 
snd_timer_user_gparams(struct file * file,struct snd_timer_gparams __user * _gparams)172491d2178eSTakashi Sakamoto static int snd_timer_user_gparams(struct file *file,
172591d2178eSTakashi Sakamoto 				  struct snd_timer_gparams __user *_gparams)
172691d2178eSTakashi Sakamoto {
172791d2178eSTakashi Sakamoto 	struct snd_timer_gparams gparams;
172891d2178eSTakashi Sakamoto 
172991d2178eSTakashi Sakamoto 	if (copy_from_user(&gparams, _gparams, sizeof(gparams)))
173091d2178eSTakashi Sakamoto 		return -EFAULT;
173191d2178eSTakashi Sakamoto 	return timer_set_gparams(&gparams);
173291d2178eSTakashi Sakamoto }
173391d2178eSTakashi Sakamoto 
snd_timer_user_gstatus(struct file * file,struct snd_timer_gstatus __user * _gstatus)17346b172a85SClemens Ladisch static int snd_timer_user_gstatus(struct file *file,
173553d2f744STakashi Iwai 				  struct snd_timer_gstatus __user *_gstatus)
17361da177e4SLinus Torvalds {
173753d2f744STakashi Iwai 	struct snd_timer_gstatus gstatus;
173853d2f744STakashi Iwai 	struct snd_timer_id tid;
173953d2f744STakashi Iwai 	struct snd_timer *t;
17401da177e4SLinus Torvalds 	int err = 0;
17411da177e4SLinus Torvalds 
17421da177e4SLinus Torvalds 	if (copy_from_user(&gstatus, _gstatus, sizeof(gstatus)))
17431da177e4SLinus Torvalds 		return -EFAULT;
17441da177e4SLinus Torvalds 	tid = gstatus.tid;
17451da177e4SLinus Torvalds 	memset(&gstatus, 0, sizeof(gstatus));
17461da177e4SLinus Torvalds 	gstatus.tid = tid;
17471a60d4c5SIngo Molnar 	mutex_lock(&register_mutex);
17481da177e4SLinus Torvalds 	t = snd_timer_find(&tid);
17491da177e4SLinus Torvalds 	if (t != NULL) {
17509d4d207dSTakashi Iwai 		spin_lock_irq(&t->lock);
1751fdcb5761STakashi Iwai 		gstatus.resolution = snd_timer_hw_resolution(t);
17521da177e4SLinus Torvalds 		if (t->hw.precise_resolution) {
17536b172a85SClemens Ladisch 			t->hw.precise_resolution(t, &gstatus.resolution_num,
17546b172a85SClemens Ladisch 						 &gstatus.resolution_den);
17551da177e4SLinus Torvalds 		} else {
17561da177e4SLinus Torvalds 			gstatus.resolution_num = gstatus.resolution;
17571da177e4SLinus Torvalds 			gstatus.resolution_den = 1000000000uL;
17581da177e4SLinus Torvalds 		}
17599d4d207dSTakashi Iwai 		spin_unlock_irq(&t->lock);
17601da177e4SLinus Torvalds 	} else {
17611da177e4SLinus Torvalds 		err = -ENODEV;
17621da177e4SLinus Torvalds 	}
17631a60d4c5SIngo Molnar 	mutex_unlock(&register_mutex);
17641da177e4SLinus Torvalds 	if (err >= 0 && copy_to_user(_gstatus, &gstatus, sizeof(gstatus)))
17651da177e4SLinus Torvalds 		err = -EFAULT;
17661da177e4SLinus Torvalds 	return err;
17671da177e4SLinus Torvalds }
17681da177e4SLinus Torvalds 
snd_timer_user_tselect(struct file * file,struct snd_timer_select __user * _tselect)17696b172a85SClemens Ladisch static int snd_timer_user_tselect(struct file *file,
177053d2f744STakashi Iwai 				  struct snd_timer_select __user *_tselect)
17711da177e4SLinus Torvalds {
177253d2f744STakashi Iwai 	struct snd_timer_user *tu;
177353d2f744STakashi Iwai 	struct snd_timer_select tselect;
17741da177e4SLinus Torvalds 	char str[32];
1775c1935b4dSJaroslav Kysela 	int err = 0;
17761da177e4SLinus Torvalds 
17771da177e4SLinus Torvalds 	tu = file->private_data;
1778c1935b4dSJaroslav Kysela 	if (tu->timeri) {
17791da177e4SLinus Torvalds 		snd_timer_close(tu->timeri);
17806a34367eSTakashi Iwai 		snd_timer_instance_free(tu->timeri);
1781c1935b4dSJaroslav Kysela 		tu->timeri = NULL;
1782c1935b4dSJaroslav Kysela 	}
1783c1935b4dSJaroslav Kysela 	if (copy_from_user(&tselect, _tselect, sizeof(tselect))) {
1784c1935b4dSJaroslav Kysela 		err = -EFAULT;
1785c1935b4dSJaroslav Kysela 		goto __err;
1786c1935b4dSJaroslav Kysela 	}
17871da177e4SLinus Torvalds 	sprintf(str, "application %i", current->pid);
17881da177e4SLinus Torvalds 	if (tselect.id.dev_class != SNDRV_TIMER_CLASS_SLAVE)
17891da177e4SLinus Torvalds 		tselect.id.dev_sclass = SNDRV_TIMER_SCLASS_APPLICATION;
17906a34367eSTakashi Iwai 	tu->timeri = snd_timer_instance_new(str);
17916a34367eSTakashi Iwai 	if (!tu->timeri) {
17926a34367eSTakashi Iwai 		err = -ENOMEM;
1793c1935b4dSJaroslav Kysela 		goto __err;
17946a34367eSTakashi Iwai 	}
17951da177e4SLinus Torvalds 
17961da177e4SLinus Torvalds 	tu->timeri->flags |= SNDRV_TIMER_IFLG_FAST;
17976b172a85SClemens Ladisch 	tu->timeri->callback = tu->tread
17986b172a85SClemens Ladisch 			? snd_timer_user_tinterrupt : snd_timer_user_interrupt;
17991da177e4SLinus Torvalds 	tu->timeri->ccallback = snd_timer_user_ccallback;
18001da177e4SLinus Torvalds 	tu->timeri->callback_data = (void *)tu;
180140ed9444STakashi Iwai 	tu->timeri->disconnect = snd_timer_user_disconnect;
1802c1935b4dSJaroslav Kysela 
18036a34367eSTakashi Iwai 	err = snd_timer_open(tu->timeri, &tselect.id, current->pid);
18046a34367eSTakashi Iwai 	if (err < 0) {
18056a34367eSTakashi Iwai 		snd_timer_instance_free(tu->timeri);
18066a34367eSTakashi Iwai 		tu->timeri = NULL;
18076a34367eSTakashi Iwai 	}
18086a34367eSTakashi Iwai 
1809c1935b4dSJaroslav Kysela       __err:
1810c1935b4dSJaroslav Kysela 	return err;
18111da177e4SLinus Torvalds }
18121da177e4SLinus Torvalds 
snd_timer_user_info(struct file * file,struct snd_timer_info __user * _info)18136b172a85SClemens Ladisch static int snd_timer_user_info(struct file *file,
181453d2f744STakashi Iwai 			       struct snd_timer_info __user *_info)
18151da177e4SLinus Torvalds {
181653d2f744STakashi Iwai 	struct snd_timer_user *tu;
181753d2f744STakashi Iwai 	struct snd_timer_info *info;
181853d2f744STakashi Iwai 	struct snd_timer *t;
18191da177e4SLinus Torvalds 	int err = 0;
18201da177e4SLinus Torvalds 
18211da177e4SLinus Torvalds 	tu = file->private_data;
18227c64ec34SClemens Ladisch 	if (!tu->timeri)
18237c64ec34SClemens Ladisch 		return -EBADFD;
18241da177e4SLinus Torvalds 	t = tu->timeri->timer;
18257c64ec34SClemens Ladisch 	if (!t)
18267c64ec34SClemens Ladisch 		return -EBADFD;
18271da177e4SLinus Torvalds 
1828ca2c0966STakashi Iwai 	info = kzalloc(sizeof(*info), GFP_KERNEL);
18291da177e4SLinus Torvalds 	if (! info)
18301da177e4SLinus Torvalds 		return -ENOMEM;
18311da177e4SLinus Torvalds 	info->card = t->card ? t->card->number : -1;
18321da177e4SLinus Torvalds 	if (t->hw.flags & SNDRV_TIMER_HW_SLAVE)
18331da177e4SLinus Torvalds 		info->flags |= SNDRV_TIMER_FLG_SLAVE;
183475b1a8f9SJoe Perches 	strscpy(info->id, t->id, sizeof(info->id));
183575b1a8f9SJoe Perches 	strscpy(info->name, t->name, sizeof(info->name));
18366cc84450SOswald Buddenhagen 	spin_lock_irq(&t->lock);
18376cc84450SOswald Buddenhagen 	info->resolution = snd_timer_hw_resolution(t);
18386cc84450SOswald Buddenhagen 	spin_unlock_irq(&t->lock);
18391da177e4SLinus Torvalds 	if (copy_to_user(_info, info, sizeof(*_info)))
18401da177e4SLinus Torvalds 		err = -EFAULT;
18411da177e4SLinus Torvalds 	kfree(info);
18421da177e4SLinus Torvalds 	return err;
18431da177e4SLinus Torvalds }
18441da177e4SLinus Torvalds 
snd_timer_user_params(struct file * file,struct snd_timer_params __user * _params)18456b172a85SClemens Ladisch static int snd_timer_user_params(struct file *file,
184653d2f744STakashi Iwai 				 struct snd_timer_params __user *_params)
18471da177e4SLinus Torvalds {
184853d2f744STakashi Iwai 	struct snd_timer_user *tu;
184953d2f744STakashi Iwai 	struct snd_timer_params params;
185053d2f744STakashi Iwai 	struct snd_timer *t;
18511da177e4SLinus Torvalds 	int err;
18521da177e4SLinus Torvalds 
18531da177e4SLinus Torvalds 	tu = file->private_data;
18547c64ec34SClemens Ladisch 	if (!tu->timeri)
18557c64ec34SClemens Ladisch 		return -EBADFD;
18561da177e4SLinus Torvalds 	t = tu->timeri->timer;
18577c64ec34SClemens Ladisch 	if (!t)
18587c64ec34SClemens Ladisch 		return -EBADFD;
18591da177e4SLinus Torvalds 	if (copy_from_user(&params, _params, sizeof(params)))
18601da177e4SLinus Torvalds 		return -EFAULT;
186171321eb3STakashi Iwai 	if (!(t->hw.flags & SNDRV_TIMER_HW_SLAVE)) {
186271321eb3STakashi Iwai 		u64 resolution;
186371321eb3STakashi Iwai 
186471321eb3STakashi Iwai 		if (params.ticks < 1) {
18651da177e4SLinus Torvalds 			err = -EINVAL;
18661da177e4SLinus Torvalds 			goto _end;
18671da177e4SLinus Torvalds 		}
186871321eb3STakashi Iwai 
186971321eb3STakashi Iwai 		/* Don't allow resolution less than 1ms */
187071321eb3STakashi Iwai 		resolution = snd_timer_resolution(tu->timeri);
187171321eb3STakashi Iwai 		resolution *= params.ticks;
187271321eb3STakashi Iwai 		if (resolution < 1000000) {
187371321eb3STakashi Iwai 			err = -EINVAL;
187471321eb3STakashi Iwai 			goto _end;
187571321eb3STakashi Iwai 		}
187671321eb3STakashi Iwai 	}
18776b172a85SClemens Ladisch 	if (params.queue_size > 0 &&
18786b172a85SClemens Ladisch 	    (params.queue_size < 32 || params.queue_size > 1024)) {
18791da177e4SLinus Torvalds 		err = -EINVAL;
18801da177e4SLinus Torvalds 		goto _end;
18811da177e4SLinus Torvalds 	}
18821da177e4SLinus Torvalds 	if (params.filter & ~((1<<SNDRV_TIMER_EVENT_RESOLUTION)|
18831da177e4SLinus Torvalds 			      (1<<SNDRV_TIMER_EVENT_TICK)|
18841da177e4SLinus Torvalds 			      (1<<SNDRV_TIMER_EVENT_START)|
18851da177e4SLinus Torvalds 			      (1<<SNDRV_TIMER_EVENT_STOP)|
18861da177e4SLinus Torvalds 			      (1<<SNDRV_TIMER_EVENT_CONTINUE)|
18871da177e4SLinus Torvalds 			      (1<<SNDRV_TIMER_EVENT_PAUSE)|
1888a501dfa3SJaroslav Kysela 			      (1<<SNDRV_TIMER_EVENT_SUSPEND)|
1889a501dfa3SJaroslav Kysela 			      (1<<SNDRV_TIMER_EVENT_RESUME)|
18901da177e4SLinus Torvalds 			      (1<<SNDRV_TIMER_EVENT_MSTART)|
18911da177e4SLinus Torvalds 			      (1<<SNDRV_TIMER_EVENT_MSTOP)|
18921da177e4SLinus Torvalds 			      (1<<SNDRV_TIMER_EVENT_MCONTINUE)|
189365d11d95SJaroslav Kysela 			      (1<<SNDRV_TIMER_EVENT_MPAUSE)|
189465d11d95SJaroslav Kysela 			      (1<<SNDRV_TIMER_EVENT_MSUSPEND)|
1895a501dfa3SJaroslav Kysela 			      (1<<SNDRV_TIMER_EVENT_MRESUME))) {
18961da177e4SLinus Torvalds 		err = -EINVAL;
18971da177e4SLinus Torvalds 		goto _end;
18981da177e4SLinus Torvalds 	}
18991da177e4SLinus Torvalds 	snd_timer_stop(tu->timeri);
19001da177e4SLinus Torvalds 	spin_lock_irq(&t->lock);
19011da177e4SLinus Torvalds 	tu->timeri->flags &= ~(SNDRV_TIMER_IFLG_AUTO|
19021da177e4SLinus Torvalds 			       SNDRV_TIMER_IFLG_EXCLUSIVE|
19031da177e4SLinus Torvalds 			       SNDRV_TIMER_IFLG_EARLY_EVENT);
19041da177e4SLinus Torvalds 	if (params.flags & SNDRV_TIMER_PSFLG_AUTO)
19051da177e4SLinus Torvalds 		tu->timeri->flags |= SNDRV_TIMER_IFLG_AUTO;
19061da177e4SLinus Torvalds 	if (params.flags & SNDRV_TIMER_PSFLG_EXCLUSIVE)
19071da177e4SLinus Torvalds 		tu->timeri->flags |= SNDRV_TIMER_IFLG_EXCLUSIVE;
19081da177e4SLinus Torvalds 	if (params.flags & SNDRV_TIMER_PSFLG_EARLY_EVENT)
19091da177e4SLinus Torvalds 		tu->timeri->flags |= SNDRV_TIMER_IFLG_EARLY_EVENT;
19101da177e4SLinus Torvalds 	spin_unlock_irq(&t->lock);
19116b172a85SClemens Ladisch 	if (params.queue_size > 0 &&
19126b172a85SClemens Ladisch 	    (unsigned int)tu->queue_size != params.queue_size) {
1913890e2cb5STakashi Iwai 		err = realloc_user_queue(tu, params.queue_size);
1914890e2cb5STakashi Iwai 		if (err < 0)
1915890e2cb5STakashi Iwai 			goto _end;
19161da177e4SLinus Torvalds 	}
1917d7f910bfSTakashi Iwai 	spin_lock_irq(&tu->qlock);
19181da177e4SLinus Torvalds 	tu->qhead = tu->qtail = tu->qused = 0;
19191da177e4SLinus Torvalds 	if (tu->timeri->flags & SNDRV_TIMER_IFLG_EARLY_EVENT) {
19201da177e4SLinus Torvalds 		if (tu->tread) {
192107094ae6SBaolin Wang 			struct snd_timer_tread64 tread;
1922cec8f96eSKangjie Lu 			memset(&tread, 0, sizeof(tread));
19231da177e4SLinus Torvalds 			tread.event = SNDRV_TIMER_EVENT_EARLY;
192407094ae6SBaolin Wang 			tread.tstamp_sec = 0;
192507094ae6SBaolin Wang 			tread.tstamp_nsec = 0;
19261da177e4SLinus Torvalds 			tread.val = 0;
19271da177e4SLinus Torvalds 			snd_timer_user_append_to_tqueue(tu, &tread);
19281da177e4SLinus Torvalds 		} else {
192953d2f744STakashi Iwai 			struct snd_timer_read *r = &tu->queue[0];
19301da177e4SLinus Torvalds 			r->resolution = 0;
19311da177e4SLinus Torvalds 			r->ticks = 0;
19321da177e4SLinus Torvalds 			tu->qused++;
19331da177e4SLinus Torvalds 			tu->qtail++;
19341da177e4SLinus Torvalds 		}
19351da177e4SLinus Torvalds 	}
19361da177e4SLinus Torvalds 	tu->filter = params.filter;
19371da177e4SLinus Torvalds 	tu->ticks = params.ticks;
1938d7f910bfSTakashi Iwai 	spin_unlock_irq(&tu->qlock);
19391da177e4SLinus Torvalds 	err = 0;
19401da177e4SLinus Torvalds  _end:
19411da177e4SLinus Torvalds 	if (copy_to_user(_params, &params, sizeof(params)))
19421da177e4SLinus Torvalds 		return -EFAULT;
19431da177e4SLinus Torvalds 	return err;
19441da177e4SLinus Torvalds }
19451da177e4SLinus Torvalds 
snd_timer_user_status32(struct file * file,struct snd_timer_status32 __user * _status)1946a07804ccSBaolin Wang static int snd_timer_user_status32(struct file *file,
1947a07804ccSBaolin Wang 				   struct snd_timer_status32 __user *_status)
19481da177e4SLinus Torvalds  {
194953d2f744STakashi Iwai 	struct snd_timer_user *tu;
1950a07804ccSBaolin Wang 	struct snd_timer_status32 status;
19511da177e4SLinus Torvalds 
19521da177e4SLinus Torvalds 	tu = file->private_data;
19537c64ec34SClemens Ladisch 	if (!tu->timeri)
19547c64ec34SClemens Ladisch 		return -EBADFD;
19551da177e4SLinus Torvalds 	memset(&status, 0, sizeof(status));
1956a07804ccSBaolin Wang 	status.tstamp_sec = tu->tstamp.tv_sec;
1957a07804ccSBaolin Wang 	status.tstamp_nsec = tu->tstamp.tv_nsec;
1958a07804ccSBaolin Wang 	status.resolution = snd_timer_resolution(tu->timeri);
1959a07804ccSBaolin Wang 	status.lost = tu->timeri->lost;
1960a07804ccSBaolin Wang 	status.overrun = tu->overrun;
1961a07804ccSBaolin Wang 	spin_lock_irq(&tu->qlock);
1962a07804ccSBaolin Wang 	status.queue = tu->qused;
1963a07804ccSBaolin Wang 	spin_unlock_irq(&tu->qlock);
1964a07804ccSBaolin Wang 	if (copy_to_user(_status, &status, sizeof(status)))
1965a07804ccSBaolin Wang 		return -EFAULT;
1966a07804ccSBaolin Wang 	return 0;
1967a07804ccSBaolin Wang }
1968a07804ccSBaolin Wang 
snd_timer_user_status64(struct file * file,struct snd_timer_status64 __user * _status)1969a07804ccSBaolin Wang static int snd_timer_user_status64(struct file *file,
1970a07804ccSBaolin Wang 				   struct snd_timer_status64 __user *_status)
1971a07804ccSBaolin Wang {
1972a07804ccSBaolin Wang 	struct snd_timer_user *tu;
1973a07804ccSBaolin Wang 	struct snd_timer_status64 status;
1974a07804ccSBaolin Wang 
1975a07804ccSBaolin Wang 	tu = file->private_data;
1976a07804ccSBaolin Wang 	if (!tu->timeri)
1977a07804ccSBaolin Wang 		return -EBADFD;
1978a07804ccSBaolin Wang 	memset(&status, 0, sizeof(status));
1979a07804ccSBaolin Wang 	status.tstamp_sec = tu->tstamp.tv_sec;
1980a07804ccSBaolin Wang 	status.tstamp_nsec = tu->tstamp.tv_nsec;
19811da177e4SLinus Torvalds 	status.resolution = snd_timer_resolution(tu->timeri);
19821da177e4SLinus Torvalds 	status.lost = tu->timeri->lost;
19831da177e4SLinus Torvalds 	status.overrun = tu->overrun;
19841da177e4SLinus Torvalds 	spin_lock_irq(&tu->qlock);
19851da177e4SLinus Torvalds 	status.queue = tu->qused;
19861da177e4SLinus Torvalds 	spin_unlock_irq(&tu->qlock);
19871da177e4SLinus Torvalds 	if (copy_to_user(_status, &status, sizeof(status)))
19881da177e4SLinus Torvalds 		return -EFAULT;
19891da177e4SLinus Torvalds 	return 0;
19901da177e4SLinus Torvalds }
19911da177e4SLinus Torvalds 
snd_timer_user_start(struct file * file)19921da177e4SLinus Torvalds static int snd_timer_user_start(struct file *file)
19931da177e4SLinus Torvalds {
19941da177e4SLinus Torvalds 	int err;
199553d2f744STakashi Iwai 	struct snd_timer_user *tu;
19961da177e4SLinus Torvalds 
19971da177e4SLinus Torvalds 	tu = file->private_data;
19987c64ec34SClemens Ladisch 	if (!tu->timeri)
19997c64ec34SClemens Ladisch 		return -EBADFD;
20001da177e4SLinus Torvalds 	snd_timer_stop(tu->timeri);
20011da177e4SLinus Torvalds 	tu->timeri->lost = 0;
20021da177e4SLinus Torvalds 	tu->last_resolution = 0;
20035d704b0dSTakashi Iwai 	err = snd_timer_start(tu->timeri, tu->ticks);
20045d704b0dSTakashi Iwai 	if (err < 0)
20055d704b0dSTakashi Iwai 		return err;
20065d704b0dSTakashi Iwai 	return 0;
20071da177e4SLinus Torvalds }
20081da177e4SLinus Torvalds 
snd_timer_user_stop(struct file * file)20091da177e4SLinus Torvalds static int snd_timer_user_stop(struct file *file)
20101da177e4SLinus Torvalds {
20111da177e4SLinus Torvalds 	int err;
201253d2f744STakashi Iwai 	struct snd_timer_user *tu;
20131da177e4SLinus Torvalds 
20141da177e4SLinus Torvalds 	tu = file->private_data;
20157c64ec34SClemens Ladisch 	if (!tu->timeri)
20167c64ec34SClemens Ladisch 		return -EBADFD;
20175d704b0dSTakashi Iwai 	err = snd_timer_stop(tu->timeri);
20185d704b0dSTakashi Iwai 	if (err < 0)
20195d704b0dSTakashi Iwai 		return err;
20205d704b0dSTakashi Iwai 	return 0;
20211da177e4SLinus Torvalds }
20221da177e4SLinus Torvalds 
snd_timer_user_continue(struct file * file)20231da177e4SLinus Torvalds static int snd_timer_user_continue(struct file *file)
20241da177e4SLinus Torvalds {
20251da177e4SLinus Torvalds 	int err;
202653d2f744STakashi Iwai 	struct snd_timer_user *tu;
20271da177e4SLinus Torvalds 
20281da177e4SLinus Torvalds 	tu = file->private_data;
20297c64ec34SClemens Ladisch 	if (!tu->timeri)
20307c64ec34SClemens Ladisch 		return -EBADFD;
20319f8a7658STakashi Iwai 	/* start timer instead of continue if it's not used before */
20329f8a7658STakashi Iwai 	if (!(tu->timeri->flags & SNDRV_TIMER_IFLG_PAUSED))
20339f8a7658STakashi Iwai 		return snd_timer_user_start(file);
20341da177e4SLinus Torvalds 	tu->timeri->lost = 0;
20355d704b0dSTakashi Iwai 	err = snd_timer_continue(tu->timeri);
20365d704b0dSTakashi Iwai 	if (err < 0)
20375d704b0dSTakashi Iwai 		return err;
20385d704b0dSTakashi Iwai 	return 0;
20391da177e4SLinus Torvalds }
20401da177e4SLinus Torvalds 
snd_timer_user_pause(struct file * file)204115790a6bSTakashi Iwai static int snd_timer_user_pause(struct file *file)
204215790a6bSTakashi Iwai {
204315790a6bSTakashi Iwai 	int err;
204453d2f744STakashi Iwai 	struct snd_timer_user *tu;
204515790a6bSTakashi Iwai 
204615790a6bSTakashi Iwai 	tu = file->private_data;
20477c64ec34SClemens Ladisch 	if (!tu->timeri)
20487c64ec34SClemens Ladisch 		return -EBADFD;
20495d704b0dSTakashi Iwai 	err = snd_timer_pause(tu->timeri);
20505d704b0dSTakashi Iwai 	if (err < 0)
20515d704b0dSTakashi Iwai 		return err;
20525d704b0dSTakashi Iwai 	return 0;
205315790a6bSTakashi Iwai }
205415790a6bSTakashi Iwai 
snd_timer_user_tread(void __user * argp,struct snd_timer_user * tu,unsigned int cmd,bool compat)205507094ae6SBaolin Wang static int snd_timer_user_tread(void __user *argp, struct snd_timer_user *tu,
205607094ae6SBaolin Wang 				unsigned int cmd, bool compat)
205707094ae6SBaolin Wang {
205807094ae6SBaolin Wang 	int __user *p = argp;
205907094ae6SBaolin Wang 	int xarg, old_tread;
206007094ae6SBaolin Wang 
206107094ae6SBaolin Wang 	if (tu->timeri)	/* too late */
206207094ae6SBaolin Wang 		return -EBUSY;
206307094ae6SBaolin Wang 	if (get_user(xarg, p))
206407094ae6SBaolin Wang 		return -EFAULT;
206507094ae6SBaolin Wang 
206607094ae6SBaolin Wang 	old_tread = tu->tread;
206707094ae6SBaolin Wang 
206807094ae6SBaolin Wang 	if (!xarg)
206907094ae6SBaolin Wang 		tu->tread = TREAD_FORMAT_NONE;
207007094ae6SBaolin Wang 	else if (cmd == SNDRV_TIMER_IOCTL_TREAD64 ||
207107094ae6SBaolin Wang 		 (IS_ENABLED(CONFIG_64BIT) && !compat))
207207094ae6SBaolin Wang 		tu->tread = TREAD_FORMAT_TIME64;
207307094ae6SBaolin Wang 	else
207407094ae6SBaolin Wang 		tu->tread = TREAD_FORMAT_TIME32;
207507094ae6SBaolin Wang 
207607094ae6SBaolin Wang 	if (tu->tread != old_tread &&
207707094ae6SBaolin Wang 	    realloc_user_queue(tu, tu->queue_size) < 0) {
207807094ae6SBaolin Wang 		tu->tread = old_tread;
207907094ae6SBaolin Wang 		return -ENOMEM;
208007094ae6SBaolin Wang 	}
208107094ae6SBaolin Wang 
208207094ae6SBaolin Wang 	return 0;
208307094ae6SBaolin Wang }
208407094ae6SBaolin Wang 
20858c50b37cSTakashi Iwai enum {
20868c50b37cSTakashi Iwai 	SNDRV_TIMER_IOCTL_START_OLD = _IO('T', 0x20),
20878c50b37cSTakashi Iwai 	SNDRV_TIMER_IOCTL_STOP_OLD = _IO('T', 0x21),
20888c50b37cSTakashi Iwai 	SNDRV_TIMER_IOCTL_CONTINUE_OLD = _IO('T', 0x22),
20898c50b37cSTakashi Iwai 	SNDRV_TIMER_IOCTL_PAUSE_OLD = _IO('T', 0x23),
20908c50b37cSTakashi Iwai };
20918c50b37cSTakashi Iwai 
__snd_timer_user_ioctl(struct file * file,unsigned int cmd,unsigned long arg,bool compat)2092af368027STakashi Iwai static long __snd_timer_user_ioctl(struct file *file, unsigned int cmd,
209307094ae6SBaolin Wang 				 unsigned long arg, bool compat)
20941da177e4SLinus Torvalds {
209553d2f744STakashi Iwai 	struct snd_timer_user *tu;
20961da177e4SLinus Torvalds 	void __user *argp = (void __user *)arg;
20971da177e4SLinus Torvalds 	int __user *p = argp;
20981da177e4SLinus Torvalds 
20991da177e4SLinus Torvalds 	tu = file->private_data;
21001da177e4SLinus Torvalds 	switch (cmd) {
21011da177e4SLinus Torvalds 	case SNDRV_TIMER_IOCTL_PVERSION:
21021da177e4SLinus Torvalds 		return put_user(SNDRV_TIMER_VERSION, p) ? -EFAULT : 0;
21031da177e4SLinus Torvalds 	case SNDRV_TIMER_IOCTL_NEXT_DEVICE:
21041da177e4SLinus Torvalds 		return snd_timer_user_next_device(argp);
210507094ae6SBaolin Wang 	case SNDRV_TIMER_IOCTL_TREAD_OLD:
210607094ae6SBaolin Wang 	case SNDRV_TIMER_IOCTL_TREAD64:
210707094ae6SBaolin Wang 		return snd_timer_user_tread(argp, tu, cmd, compat);
21081da177e4SLinus Torvalds 	case SNDRV_TIMER_IOCTL_GINFO:
21091da177e4SLinus Torvalds 		return snd_timer_user_ginfo(file, argp);
21101da177e4SLinus Torvalds 	case SNDRV_TIMER_IOCTL_GPARAMS:
21111da177e4SLinus Torvalds 		return snd_timer_user_gparams(file, argp);
21121da177e4SLinus Torvalds 	case SNDRV_TIMER_IOCTL_GSTATUS:
21131da177e4SLinus Torvalds 		return snd_timer_user_gstatus(file, argp);
21141da177e4SLinus Torvalds 	case SNDRV_TIMER_IOCTL_SELECT:
21151da177e4SLinus Torvalds 		return snd_timer_user_tselect(file, argp);
21161da177e4SLinus Torvalds 	case SNDRV_TIMER_IOCTL_INFO:
21171da177e4SLinus Torvalds 		return snd_timer_user_info(file, argp);
21181da177e4SLinus Torvalds 	case SNDRV_TIMER_IOCTL_PARAMS:
21191da177e4SLinus Torvalds 		return snd_timer_user_params(file, argp);
2120a07804ccSBaolin Wang 	case SNDRV_TIMER_IOCTL_STATUS32:
2121a07804ccSBaolin Wang 		return snd_timer_user_status32(file, argp);
2122a07804ccSBaolin Wang 	case SNDRV_TIMER_IOCTL_STATUS64:
2123a07804ccSBaolin Wang 		return snd_timer_user_status64(file, argp);
21241da177e4SLinus Torvalds 	case SNDRV_TIMER_IOCTL_START:
21258c50b37cSTakashi Iwai 	case SNDRV_TIMER_IOCTL_START_OLD:
21261da177e4SLinus Torvalds 		return snd_timer_user_start(file);
21271da177e4SLinus Torvalds 	case SNDRV_TIMER_IOCTL_STOP:
21288c50b37cSTakashi Iwai 	case SNDRV_TIMER_IOCTL_STOP_OLD:
21291da177e4SLinus Torvalds 		return snd_timer_user_stop(file);
21301da177e4SLinus Torvalds 	case SNDRV_TIMER_IOCTL_CONTINUE:
21318c50b37cSTakashi Iwai 	case SNDRV_TIMER_IOCTL_CONTINUE_OLD:
21321da177e4SLinus Torvalds 		return snd_timer_user_continue(file);
213315790a6bSTakashi Iwai 	case SNDRV_TIMER_IOCTL_PAUSE:
21348c50b37cSTakashi Iwai 	case SNDRV_TIMER_IOCTL_PAUSE_OLD:
213515790a6bSTakashi Iwai 		return snd_timer_user_pause(file);
21361da177e4SLinus Torvalds 	}
21371da177e4SLinus Torvalds 	return -ENOTTY;
21381da177e4SLinus Torvalds }
21391da177e4SLinus Torvalds 
snd_timer_user_ioctl(struct file * file,unsigned int cmd,unsigned long arg)2140af368027STakashi Iwai static long snd_timer_user_ioctl(struct file *file, unsigned int cmd,
2141af368027STakashi Iwai 				 unsigned long arg)
2142af368027STakashi Iwai {
2143af368027STakashi Iwai 	struct snd_timer_user *tu = file->private_data;
2144af368027STakashi Iwai 	long ret;
2145af368027STakashi Iwai 
2146af368027STakashi Iwai 	mutex_lock(&tu->ioctl_lock);
214707094ae6SBaolin Wang 	ret = __snd_timer_user_ioctl(file, cmd, arg, false);
2148af368027STakashi Iwai 	mutex_unlock(&tu->ioctl_lock);
2149af368027STakashi Iwai 	return ret;
2150af368027STakashi Iwai }
2151af368027STakashi Iwai 
snd_timer_user_fasync(int fd,struct file * file,int on)21521da177e4SLinus Torvalds static int snd_timer_user_fasync(int fd, struct file * file, int on)
21531da177e4SLinus Torvalds {
215453d2f744STakashi Iwai 	struct snd_timer_user *tu;
21551da177e4SLinus Torvalds 
21561da177e4SLinus Torvalds 	tu = file->private_data;
215795cc637cSTakashi Iwai 	return snd_fasync_helper(fd, file, on, &tu->fasync);
21581da177e4SLinus Torvalds }
21591da177e4SLinus Torvalds 
snd_timer_user_read(struct file * file,char __user * buffer,size_t count,loff_t * offset)21606b172a85SClemens Ladisch static ssize_t snd_timer_user_read(struct file *file, char __user *buffer,
21616b172a85SClemens Ladisch 				   size_t count, loff_t *offset)
21621da177e4SLinus Torvalds {
216307094ae6SBaolin Wang 	struct snd_timer_tread64 *tread;
216407094ae6SBaolin Wang 	struct snd_timer_tread32 tread32;
216553d2f744STakashi Iwai 	struct snd_timer_user *tu;
21661da177e4SLinus Torvalds 	long result = 0, unit;
21674dff5c7bSTakashi Iwai 	int qhead;
21681da177e4SLinus Torvalds 	int err = 0;
21691da177e4SLinus Torvalds 
21701da177e4SLinus Torvalds 	tu = file->private_data;
217107094ae6SBaolin Wang 	switch (tu->tread) {
217207094ae6SBaolin Wang 	case TREAD_FORMAT_TIME64:
217307094ae6SBaolin Wang 		unit = sizeof(struct snd_timer_tread64);
217407094ae6SBaolin Wang 		break;
217507094ae6SBaolin Wang 	case TREAD_FORMAT_TIME32:
217607094ae6SBaolin Wang 		unit = sizeof(struct snd_timer_tread32);
217707094ae6SBaolin Wang 		break;
217807094ae6SBaolin Wang 	case TREAD_FORMAT_NONE:
217907094ae6SBaolin Wang 		unit = sizeof(struct snd_timer_read);
218007094ae6SBaolin Wang 		break;
218107094ae6SBaolin Wang 	default:
218207094ae6SBaolin Wang 		WARN_ONCE(1, "Corrupt snd_timer_user\n");
218307094ae6SBaolin Wang 		return -ENOTSUPP;
218407094ae6SBaolin Wang 	}
218507094ae6SBaolin Wang 
2186d11662f4STakashi Iwai 	mutex_lock(&tu->ioctl_lock);
21871da177e4SLinus Torvalds 	spin_lock_irq(&tu->qlock);
21881da177e4SLinus Torvalds 	while ((long)count - result >= unit) {
21891da177e4SLinus Torvalds 		while (!tu->qused) {
2190ac6424b9SIngo Molnar 			wait_queue_entry_t wait;
21911da177e4SLinus Torvalds 
21921da177e4SLinus Torvalds 			if ((file->f_flags & O_NONBLOCK) != 0 || result > 0) {
21931da177e4SLinus Torvalds 				err = -EAGAIN;
21944dff5c7bSTakashi Iwai 				goto _error;
21951da177e4SLinus Torvalds 			}
21961da177e4SLinus Torvalds 
21971da177e4SLinus Torvalds 			set_current_state(TASK_INTERRUPTIBLE);
21981da177e4SLinus Torvalds 			init_waitqueue_entry(&wait, current);
21991da177e4SLinus Torvalds 			add_wait_queue(&tu->qchange_sleep, &wait);
22001da177e4SLinus Torvalds 
22011da177e4SLinus Torvalds 			spin_unlock_irq(&tu->qlock);
2202d11662f4STakashi Iwai 			mutex_unlock(&tu->ioctl_lock);
22031da177e4SLinus Torvalds 			schedule();
2204d11662f4STakashi Iwai 			mutex_lock(&tu->ioctl_lock);
22051da177e4SLinus Torvalds 			spin_lock_irq(&tu->qlock);
22061da177e4SLinus Torvalds 
22071da177e4SLinus Torvalds 			remove_wait_queue(&tu->qchange_sleep, &wait);
22081da177e4SLinus Torvalds 
2209230323daSTakashi Iwai 			if (tu->disconnected) {
2210230323daSTakashi Iwai 				err = -ENODEV;
22114dff5c7bSTakashi Iwai 				goto _error;
2212230323daSTakashi Iwai 			}
22131da177e4SLinus Torvalds 			if (signal_pending(current)) {
22141da177e4SLinus Torvalds 				err = -ERESTARTSYS;
22154dff5c7bSTakashi Iwai 				goto _error;
22161da177e4SLinus Torvalds 			}
22171da177e4SLinus Torvalds 		}
22181da177e4SLinus Torvalds 
22194dff5c7bSTakashi Iwai 		qhead = tu->qhead++;
22204dff5c7bSTakashi Iwai 		tu->qhead %= tu->queue_size;
22213fa6993fSTakashi Iwai 		tu->qused--;
22221da177e4SLinus Torvalds 		spin_unlock_irq(&tu->qlock);
22231da177e4SLinus Torvalds 
222407094ae6SBaolin Wang 		tread = &tu->tqueue[qhead];
222507094ae6SBaolin Wang 
222607094ae6SBaolin Wang 		switch (tu->tread) {
222707094ae6SBaolin Wang 		case TREAD_FORMAT_TIME64:
222807094ae6SBaolin Wang 			if (copy_to_user(buffer, tread,
222907094ae6SBaolin Wang 					 sizeof(struct snd_timer_tread64)))
22301da177e4SLinus Torvalds 				err = -EFAULT;
223107094ae6SBaolin Wang 			break;
223207094ae6SBaolin Wang 		case TREAD_FORMAT_TIME32:
223307094ae6SBaolin Wang 			memset(&tread32, 0, sizeof(tread32));
223407094ae6SBaolin Wang 			tread32 = (struct snd_timer_tread32) {
223507094ae6SBaolin Wang 				.event = tread->event,
223607094ae6SBaolin Wang 				.tstamp_sec = tread->tstamp_sec,
2237f9993480SPierre-Louis Bossart 				.tstamp_nsec = tread->tstamp_nsec,
223807094ae6SBaolin Wang 				.val = tread->val,
223907094ae6SBaolin Wang 			};
224007094ae6SBaolin Wang 
224107094ae6SBaolin Wang 			if (copy_to_user(buffer, &tread32, sizeof(tread32)))
224207094ae6SBaolin Wang 				err = -EFAULT;
224307094ae6SBaolin Wang 			break;
224407094ae6SBaolin Wang 		case TREAD_FORMAT_NONE:
22454dff5c7bSTakashi Iwai 			if (copy_to_user(buffer, &tu->queue[qhead],
22464dff5c7bSTakashi Iwai 					 sizeof(struct snd_timer_read)))
22471da177e4SLinus Torvalds 				err = -EFAULT;
224807094ae6SBaolin Wang 			break;
224907094ae6SBaolin Wang 		default:
225007094ae6SBaolin Wang 			err = -ENOTSUPP;
225107094ae6SBaolin Wang 			break;
22521da177e4SLinus Torvalds 		}
22531da177e4SLinus Torvalds 
22541da177e4SLinus Torvalds 		spin_lock_irq(&tu->qlock);
22554dff5c7bSTakashi Iwai 		if (err < 0)
22564dff5c7bSTakashi Iwai 			goto _error;
22574dff5c7bSTakashi Iwai 		result += unit;
22584dff5c7bSTakashi Iwai 		buffer += unit;
22591da177e4SLinus Torvalds 	}
22601da177e4SLinus Torvalds  _error:
22614dff5c7bSTakashi Iwai 	spin_unlock_irq(&tu->qlock);
2262d11662f4STakashi Iwai 	mutex_unlock(&tu->ioctl_lock);
22631da177e4SLinus Torvalds 	return result > 0 ? result : err;
22641da177e4SLinus Torvalds }
22651da177e4SLinus Torvalds 
snd_timer_user_poll(struct file * file,poll_table * wait)2266680ef72aSAl Viro static __poll_t snd_timer_user_poll(struct file *file, poll_table * wait)
22671da177e4SLinus Torvalds {
2268680ef72aSAl Viro         __poll_t mask;
226953d2f744STakashi Iwai         struct snd_timer_user *tu;
22701da177e4SLinus Torvalds 
22711da177e4SLinus Torvalds         tu = file->private_data;
22721da177e4SLinus Torvalds 
22731da177e4SLinus Torvalds         poll_wait(file, &tu->qchange_sleep, wait);
22741da177e4SLinus Torvalds 
22751da177e4SLinus Torvalds 	mask = 0;
2276d7f910bfSTakashi Iwai 	spin_lock_irq(&tu->qlock);
22771da177e4SLinus Torvalds 	if (tu->qused)
2278a9a08845SLinus Torvalds 		mask |= EPOLLIN | EPOLLRDNORM;
2279230323daSTakashi Iwai 	if (tu->disconnected)
2280a9a08845SLinus Torvalds 		mask |= EPOLLERR;
2281d7f910bfSTakashi Iwai 	spin_unlock_irq(&tu->qlock);
22821da177e4SLinus Torvalds 
22831da177e4SLinus Torvalds 	return mask;
22841da177e4SLinus Torvalds }
22851da177e4SLinus Torvalds 
22861da177e4SLinus Torvalds #ifdef CONFIG_COMPAT
22871da177e4SLinus Torvalds #include "timer_compat.c"
22881da177e4SLinus Torvalds #else
22891da177e4SLinus Torvalds #define snd_timer_user_ioctl_compat	NULL
22901da177e4SLinus Torvalds #endif
22911da177e4SLinus Torvalds 
22929c2e08c5SArjan van de Ven static const struct file_operations snd_timer_f_ops =
22931da177e4SLinus Torvalds {
22941da177e4SLinus Torvalds 	.owner =	THIS_MODULE,
22951da177e4SLinus Torvalds 	.read =		snd_timer_user_read,
22961da177e4SLinus Torvalds 	.open =		snd_timer_user_open,
22971da177e4SLinus Torvalds 	.release =	snd_timer_user_release,
229802f4865fSTakashi Iwai 	.llseek =	no_llseek,
22991da177e4SLinus Torvalds 	.poll =		snd_timer_user_poll,
23001da177e4SLinus Torvalds 	.unlocked_ioctl =	snd_timer_user_ioctl,
23011da177e4SLinus Torvalds 	.compat_ioctl =	snd_timer_user_ioctl_compat,
23021da177e4SLinus Torvalds 	.fasync = 	snd_timer_user_fasync,
23031da177e4SLinus Torvalds };
23041da177e4SLinus Torvalds 
23057c35860dSTakashi Iwai /* unregister the system timer */
snd_timer_free_all(void)23067c35860dSTakashi Iwai static void snd_timer_free_all(void)
23077c35860dSTakashi Iwai {
23087c35860dSTakashi Iwai 	struct snd_timer *timer, *n;
23097c35860dSTakashi Iwai 
23107c35860dSTakashi Iwai 	list_for_each_entry_safe(timer, n, &snd_timer_list, device_list)
23117c35860dSTakashi Iwai 		snd_timer_free(timer);
23127c35860dSTakashi Iwai }
23137c35860dSTakashi Iwai 
2314911fcb76STakashi Iwai static struct device *timer_dev;
231589da061fSTakashi Iwai 
23161da177e4SLinus Torvalds /*
23171da177e4SLinus Torvalds  *  ENTRY functions
23181da177e4SLinus Torvalds  */
23191da177e4SLinus Torvalds 
alsa_timer_init(void)23201da177e4SLinus Torvalds static int __init alsa_timer_init(void)
23211da177e4SLinus Torvalds {
23221da177e4SLinus Torvalds 	int err;
23231da177e4SLinus Torvalds 
2324911fcb76STakashi Iwai 	err = snd_device_alloc(&timer_dev, NULL);
2325911fcb76STakashi Iwai 	if (err < 0)
2326911fcb76STakashi Iwai 		return err;
2327911fcb76STakashi Iwai 	dev_set_name(timer_dev, "timer");
232889da061fSTakashi Iwai 
23291da177e4SLinus Torvalds #ifdef SNDRV_OSS_INFO_DEV_TIMERS
23306b172a85SClemens Ladisch 	snd_oss_info_register(SNDRV_OSS_INFO_DEV_TIMERS, SNDRV_CARDS - 1,
23316b172a85SClemens Ladisch 			      "system timer");
23321da177e4SLinus Torvalds #endif
2333e28563ccSTakashi Iwai 
23347c35860dSTakashi Iwai 	err = snd_timer_register_system();
23357c35860dSTakashi Iwai 	if (err < 0) {
2336cf74dcf3STakashi Iwai 		pr_err("ALSA: unable to register system timer (%i)\n", err);
23371ae0e4ceSMarkus Elfring 		goto put_timer;
23387c35860dSTakashi Iwai 	}
23397c35860dSTakashi Iwai 
234040a4b263STakashi Iwai 	err = snd_register_device(SNDRV_DEVICE_TYPE_TIMER, NULL, 0,
2341911fcb76STakashi Iwai 				  &snd_timer_f_ops, NULL, timer_dev);
23427c35860dSTakashi Iwai 	if (err < 0) {
2343cf74dcf3STakashi Iwai 		pr_err("ALSA: unable to register timer device (%i)\n", err);
23447c35860dSTakashi Iwai 		snd_timer_free_all();
23451ae0e4ceSMarkus Elfring 		goto put_timer;
23467c35860dSTakashi Iwai 	}
23477c35860dSTakashi Iwai 
2348e28563ccSTakashi Iwai 	snd_timer_proc_init();
23491da177e4SLinus Torvalds 	return 0;
23501ae0e4ceSMarkus Elfring 
23511ae0e4ceSMarkus Elfring put_timer:
2352911fcb76STakashi Iwai 	put_device(timer_dev);
23531ae0e4ceSMarkus Elfring 	return err;
23541da177e4SLinus Torvalds }
23551da177e4SLinus Torvalds 
alsa_timer_exit(void)23561da177e4SLinus Torvalds static void __exit alsa_timer_exit(void)
23571da177e4SLinus Torvalds {
2358911fcb76STakashi Iwai 	snd_unregister_device(timer_dev);
23597c35860dSTakashi Iwai 	snd_timer_free_all();
2360911fcb76STakashi Iwai 	put_device(timer_dev);
2361e28563ccSTakashi Iwai 	snd_timer_proc_done();
23621da177e4SLinus Torvalds #ifdef SNDRV_OSS_INFO_DEV_TIMERS
23631da177e4SLinus Torvalds 	snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_TIMERS, SNDRV_CARDS - 1);
23641da177e4SLinus Torvalds #endif
23651da177e4SLinus Torvalds }
23661da177e4SLinus Torvalds 
23671da177e4SLinus Torvalds module_init(alsa_timer_init)
23681da177e4SLinus Torvalds module_exit(alsa_timer_exit)
2369