xref: /openbmc/linux/sound/core/timer.c (revision 71321eb3)
11da177e4SLinus Torvalds /*
21da177e4SLinus Torvalds  *  Timers abstract layer
3c1017a4cSJaroslav Kysela  *  Copyright (c) by Jaroslav Kysela <perex@perex.cz>
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  *
61da177e4SLinus Torvalds  *   This program is free software; you can redistribute it and/or modify
71da177e4SLinus Torvalds  *   it under the terms of the GNU General Public License as published by
81da177e4SLinus Torvalds  *   the Free Software Foundation; either version 2 of the License, or
91da177e4SLinus Torvalds  *   (at your option) any later version.
101da177e4SLinus Torvalds  *
111da177e4SLinus Torvalds  *   This program is distributed in the hope that it will be useful,
121da177e4SLinus Torvalds  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
131da177e4SLinus Torvalds  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
141da177e4SLinus Torvalds  *   GNU General Public License for more details.
151da177e4SLinus Torvalds  *
161da177e4SLinus Torvalds  *   You should have received a copy of the GNU General Public License
171da177e4SLinus Torvalds  *   along with this program; if not, write to the Free Software
181da177e4SLinus Torvalds  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
191da177e4SLinus Torvalds  *
201da177e4SLinus Torvalds  */
211da177e4SLinus Torvalds 
221da177e4SLinus Torvalds #include <linux/delay.h>
231da177e4SLinus Torvalds #include <linux/init.h>
241da177e4SLinus Torvalds #include <linux/slab.h>
251da177e4SLinus Torvalds #include <linux/time.h>
261a60d4c5SIngo Molnar #include <linux/mutex.h>
2751990e82SPaul Gortmaker #include <linux/device.h>
2865a77217SPaul Gortmaker #include <linux/module.h>
29543537bdSPaulo Marques #include <linux/string.h>
301da177e4SLinus Torvalds #include <sound/core.h>
311da177e4SLinus Torvalds #include <sound/timer.h>
321da177e4SLinus Torvalds #include <sound/control.h>
331da177e4SLinus Torvalds #include <sound/info.h>
341da177e4SLinus Torvalds #include <sound/minors.h>
351da177e4SLinus Torvalds #include <sound/initval.h>
361da177e4SLinus Torvalds #include <linux/kmod.h>
371da177e4SLinus Torvalds 
389f8a7658STakashi Iwai /* internal flags */
399f8a7658STakashi Iwai #define SNDRV_TIMER_IFLG_PAUSED		0x00010000
409f8a7658STakashi Iwai 
418eeaa2f9STakashi Iwai #if IS_ENABLED(CONFIG_SND_HRTIMER)
42109fef9eSClemens Ladisch #define DEFAULT_TIMER_LIMIT 4
431da177e4SLinus Torvalds #else
441da177e4SLinus Torvalds #define DEFAULT_TIMER_LIMIT 1
451da177e4SLinus Torvalds #endif
461da177e4SLinus Torvalds 
471da177e4SLinus Torvalds static int timer_limit = DEFAULT_TIMER_LIMIT;
48b751eef1SJaroslav Kysela static int timer_tstamp_monotonic = 1;
49c1017a4cSJaroslav Kysela MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>, Takashi Iwai <tiwai@suse.de>");
501da177e4SLinus Torvalds MODULE_DESCRIPTION("ALSA timer interface");
511da177e4SLinus Torvalds MODULE_LICENSE("GPL");
521da177e4SLinus Torvalds module_param(timer_limit, int, 0444);
531da177e4SLinus Torvalds MODULE_PARM_DESC(timer_limit, "Maximum global timers in system.");
54b751eef1SJaroslav Kysela module_param(timer_tstamp_monotonic, int, 0444);
55b751eef1SJaroslav Kysela MODULE_PARM_DESC(timer_tstamp_monotonic, "Use posix monotonic clock source for timestamps (default).");
561da177e4SLinus Torvalds 
5703cfe6f5SKay Sievers MODULE_ALIAS_CHARDEV(CONFIG_SND_MAJOR, SNDRV_MINOR_TIMER);
5803cfe6f5SKay Sievers MODULE_ALIAS("devname:snd/timer");
5903cfe6f5SKay Sievers 
6053d2f744STakashi Iwai struct snd_timer_user {
6153d2f744STakashi Iwai 	struct snd_timer_instance *timeri;
621da177e4SLinus Torvalds 	int tread;		/* enhanced read with timestamps and events */
631da177e4SLinus Torvalds 	unsigned long ticks;
641da177e4SLinus Torvalds 	unsigned long overrun;
651da177e4SLinus Torvalds 	int qhead;
661da177e4SLinus Torvalds 	int qtail;
671da177e4SLinus Torvalds 	int qused;
681da177e4SLinus Torvalds 	int queue_size;
69230323daSTakashi Iwai 	bool disconnected;
7053d2f744STakashi Iwai 	struct snd_timer_read *queue;
7153d2f744STakashi Iwai 	struct snd_timer_tread *tqueue;
721da177e4SLinus Torvalds 	spinlock_t qlock;
731da177e4SLinus Torvalds 	unsigned long last_resolution;
741da177e4SLinus Torvalds 	unsigned int filter;
751da177e4SLinus Torvalds 	struct timespec tstamp;		/* trigger tstamp */
761da177e4SLinus Torvalds 	wait_queue_head_t qchange_sleep;
771da177e4SLinus Torvalds 	struct fasync_struct *fasync;
78af368027STakashi Iwai 	struct mutex ioctl_lock;
7953d2f744STakashi Iwai };
801da177e4SLinus Torvalds 
811da177e4SLinus Torvalds /* list of timers */
821da177e4SLinus Torvalds static LIST_HEAD(snd_timer_list);
831da177e4SLinus Torvalds 
841da177e4SLinus Torvalds /* list of slave instances */
851da177e4SLinus Torvalds static LIST_HEAD(snd_timer_slave_list);
861da177e4SLinus Torvalds 
871da177e4SLinus Torvalds /* lock for slave active lists */
881da177e4SLinus Torvalds static DEFINE_SPINLOCK(slave_active_lock);
891da177e4SLinus Torvalds 
901a60d4c5SIngo Molnar static DEFINE_MUTEX(register_mutex);
911da177e4SLinus Torvalds 
9253d2f744STakashi Iwai static int snd_timer_free(struct snd_timer *timer);
9353d2f744STakashi Iwai static int snd_timer_dev_free(struct snd_device *device);
9453d2f744STakashi Iwai static int snd_timer_dev_register(struct snd_device *device);
95c461482cSTakashi Iwai static int snd_timer_dev_disconnect(struct snd_device *device);
961da177e4SLinus Torvalds 
9753d2f744STakashi Iwai static void snd_timer_reschedule(struct snd_timer * timer, unsigned long ticks_left);
981da177e4SLinus Torvalds 
991da177e4SLinus Torvalds /*
1001da177e4SLinus Torvalds  * create a timer instance with the given owner string.
1011da177e4SLinus Torvalds  * when timer is not NULL, increments the module counter
1021da177e4SLinus Torvalds  */
10353d2f744STakashi Iwai static struct snd_timer_instance *snd_timer_instance_new(char *owner,
10453d2f744STakashi Iwai 							 struct snd_timer *timer)
1051da177e4SLinus Torvalds {
10653d2f744STakashi Iwai 	struct snd_timer_instance *timeri;
107ca2c0966STakashi Iwai 	timeri = kzalloc(sizeof(*timeri), GFP_KERNEL);
1081da177e4SLinus Torvalds 	if (timeri == NULL)
1091da177e4SLinus Torvalds 		return NULL;
110543537bdSPaulo Marques 	timeri->owner = kstrdup(owner, GFP_KERNEL);
1111da177e4SLinus Torvalds 	if (! timeri->owner) {
1121da177e4SLinus Torvalds 		kfree(timeri);
1131da177e4SLinus Torvalds 		return NULL;
1141da177e4SLinus Torvalds 	}
1151da177e4SLinus Torvalds 	INIT_LIST_HEAD(&timeri->open_list);
1161da177e4SLinus Torvalds 	INIT_LIST_HEAD(&timeri->active_list);
1171da177e4SLinus Torvalds 	INIT_LIST_HEAD(&timeri->ack_list);
1181da177e4SLinus Torvalds 	INIT_LIST_HEAD(&timeri->slave_list_head);
1191da177e4SLinus Torvalds 	INIT_LIST_HEAD(&timeri->slave_active_head);
1201da177e4SLinus Torvalds 
1211da177e4SLinus Torvalds 	timeri->timer = timer;
122de24214dSClemens Ladisch 	if (timer && !try_module_get(timer->module)) {
1231da177e4SLinus Torvalds 		kfree(timeri->owner);
1241da177e4SLinus Torvalds 		kfree(timeri);
1251da177e4SLinus Torvalds 		return NULL;
1261da177e4SLinus Torvalds 	}
1271da177e4SLinus Torvalds 
1281da177e4SLinus Torvalds 	return timeri;
1291da177e4SLinus Torvalds }
1301da177e4SLinus Torvalds 
1311da177e4SLinus Torvalds /*
1321da177e4SLinus Torvalds  * find a timer instance from the given timer id
1331da177e4SLinus Torvalds  */
13453d2f744STakashi Iwai static struct snd_timer *snd_timer_find(struct snd_timer_id *tid)
1351da177e4SLinus Torvalds {
13653d2f744STakashi Iwai 	struct snd_timer *timer = NULL;
1371da177e4SLinus Torvalds 
1389244b2c3SJohannes Berg 	list_for_each_entry(timer, &snd_timer_list, device_list) {
1391da177e4SLinus Torvalds 		if (timer->tmr_class != tid->dev_class)
1401da177e4SLinus Torvalds 			continue;
1411da177e4SLinus Torvalds 		if ((timer->tmr_class == SNDRV_TIMER_CLASS_CARD ||
1421da177e4SLinus Torvalds 		     timer->tmr_class == SNDRV_TIMER_CLASS_PCM) &&
1431da177e4SLinus Torvalds 		    (timer->card == NULL ||
1441da177e4SLinus Torvalds 		     timer->card->number != tid->card))
1451da177e4SLinus Torvalds 			continue;
1461da177e4SLinus Torvalds 		if (timer->tmr_device != tid->device)
1471da177e4SLinus Torvalds 			continue;
1481da177e4SLinus Torvalds 		if (timer->tmr_subdevice != tid->subdevice)
1491da177e4SLinus Torvalds 			continue;
1501da177e4SLinus Torvalds 		return timer;
1511da177e4SLinus Torvalds 	}
1521da177e4SLinus Torvalds 	return NULL;
1531da177e4SLinus Torvalds }
1541da177e4SLinus Torvalds 
155ee2da997SJohannes Berg #ifdef CONFIG_MODULES
1561da177e4SLinus Torvalds 
15753d2f744STakashi Iwai static void snd_timer_request(struct snd_timer_id *tid)
1581da177e4SLinus Torvalds {
1591da177e4SLinus Torvalds 	switch (tid->dev_class) {
1601da177e4SLinus Torvalds 	case SNDRV_TIMER_CLASS_GLOBAL:
1611da177e4SLinus Torvalds 		if (tid->device < timer_limit)
1621da177e4SLinus Torvalds 			request_module("snd-timer-%i", tid->device);
1631da177e4SLinus Torvalds 		break;
1641da177e4SLinus Torvalds 	case SNDRV_TIMER_CLASS_CARD:
1651da177e4SLinus Torvalds 	case SNDRV_TIMER_CLASS_PCM:
1661da177e4SLinus Torvalds 		if (tid->card < snd_ecards_limit)
1671da177e4SLinus Torvalds 			request_module("snd-card-%i", tid->card);
1681da177e4SLinus Torvalds 		break;
1691da177e4SLinus Torvalds 	default:
1701da177e4SLinus Torvalds 		break;
1711da177e4SLinus Torvalds 	}
1721da177e4SLinus Torvalds }
1731da177e4SLinus Torvalds 
1741da177e4SLinus Torvalds #endif
1751da177e4SLinus Torvalds 
1761da177e4SLinus Torvalds /*
1771da177e4SLinus Torvalds  * look for a master instance matching with the slave id of the given slave.
1781da177e4SLinus Torvalds  * when found, relink the open_link of the slave.
1791da177e4SLinus Torvalds  *
1801da177e4SLinus Torvalds  * call this with register_mutex down.
1811da177e4SLinus Torvalds  */
18253d2f744STakashi Iwai static void snd_timer_check_slave(struct snd_timer_instance *slave)
1831da177e4SLinus Torvalds {
18453d2f744STakashi Iwai 	struct snd_timer *timer;
18553d2f744STakashi Iwai 	struct snd_timer_instance *master;
1861da177e4SLinus Torvalds 
1871da177e4SLinus Torvalds 	/* FIXME: it's really dumb to look up all entries.. */
1889244b2c3SJohannes Berg 	list_for_each_entry(timer, &snd_timer_list, device_list) {
1899244b2c3SJohannes Berg 		list_for_each_entry(master, &timer->open_list_head, open_list) {
1901da177e4SLinus Torvalds 			if (slave->slave_class == master->slave_class &&
1911da177e4SLinus Torvalds 			    slave->slave_id == master->slave_id) {
1925b7c757dSNicolas Kaiser 				list_move_tail(&slave->open_list,
1936b172a85SClemens Ladisch 					       &master->slave_list_head);
1941da177e4SLinus Torvalds 				spin_lock_irq(&slave_active_lock);
1951da177e4SLinus Torvalds 				slave->master = master;
1961da177e4SLinus Torvalds 				slave->timer = master->timer;
1971da177e4SLinus Torvalds 				spin_unlock_irq(&slave_active_lock);
1981da177e4SLinus Torvalds 				return;
1991da177e4SLinus Torvalds 			}
2001da177e4SLinus Torvalds 		}
2011da177e4SLinus Torvalds 	}
2021da177e4SLinus Torvalds }
2031da177e4SLinus Torvalds 
2041da177e4SLinus Torvalds /*
2051da177e4SLinus Torvalds  * look for slave instances matching with the slave id of the given master.
2061da177e4SLinus Torvalds  * when found, relink the open_link of slaves.
2071da177e4SLinus Torvalds  *
2081da177e4SLinus Torvalds  * call this with register_mutex down.
2091da177e4SLinus Torvalds  */
21053d2f744STakashi Iwai static void snd_timer_check_master(struct snd_timer_instance *master)
2111da177e4SLinus Torvalds {
2129244b2c3SJohannes Berg 	struct snd_timer_instance *slave, *tmp;
2131da177e4SLinus Torvalds 
2141da177e4SLinus Torvalds 	/* check all pending slaves */
2159244b2c3SJohannes Berg 	list_for_each_entry_safe(slave, tmp, &snd_timer_slave_list, open_list) {
2161da177e4SLinus Torvalds 		if (slave->slave_class == master->slave_class &&
2171da177e4SLinus Torvalds 		    slave->slave_id == master->slave_id) {
2189244b2c3SJohannes Berg 			list_move_tail(&slave->open_list, &master->slave_list_head);
2191da177e4SLinus Torvalds 			spin_lock_irq(&slave_active_lock);
220b5a663aaSTakashi Iwai 			spin_lock(&master->timer->lock);
2211da177e4SLinus Torvalds 			slave->master = master;
2221da177e4SLinus Torvalds 			slave->timer = master->timer;
2231da177e4SLinus Torvalds 			if (slave->flags & SNDRV_TIMER_IFLG_RUNNING)
2246b172a85SClemens Ladisch 				list_add_tail(&slave->active_list,
2256b172a85SClemens Ladisch 					      &master->slave_active_head);
226b5a663aaSTakashi Iwai 			spin_unlock(&master->timer->lock);
2271da177e4SLinus Torvalds 			spin_unlock_irq(&slave_active_lock);
2281da177e4SLinus Torvalds 		}
2291da177e4SLinus Torvalds 	}
2301da177e4SLinus Torvalds }
2311da177e4SLinus Torvalds 
2321da177e4SLinus Torvalds /*
2331da177e4SLinus Torvalds  * open a timer instance
2341da177e4SLinus Torvalds  * when opening a master, the slave id must be here given.
2351da177e4SLinus Torvalds  */
23653d2f744STakashi Iwai int snd_timer_open(struct snd_timer_instance **ti,
23753d2f744STakashi Iwai 		   char *owner, struct snd_timer_id *tid,
2381da177e4SLinus Torvalds 		   unsigned int slave_id)
2391da177e4SLinus Torvalds {
24053d2f744STakashi Iwai 	struct snd_timer *timer;
24153d2f744STakashi Iwai 	struct snd_timer_instance *timeri = NULL;
2421da177e4SLinus Torvalds 
2431da177e4SLinus Torvalds 	if (tid->dev_class == SNDRV_TIMER_CLASS_SLAVE) {
2441da177e4SLinus Torvalds 		/* open a slave instance */
2451da177e4SLinus Torvalds 		if (tid->dev_sclass <= SNDRV_TIMER_SCLASS_NONE ||
2461da177e4SLinus Torvalds 		    tid->dev_sclass > SNDRV_TIMER_SCLASS_OSS_SEQUENCER) {
247cf74dcf3STakashi Iwai 			pr_debug("ALSA: timer: invalid slave class %i\n",
248cf74dcf3STakashi Iwai 				 tid->dev_sclass);
2491da177e4SLinus Torvalds 			return -EINVAL;
2501da177e4SLinus Torvalds 		}
2511a60d4c5SIngo Molnar 		mutex_lock(&register_mutex);
2521da177e4SLinus Torvalds 		timeri = snd_timer_instance_new(owner, NULL);
2532fd43d11SClemens Ladisch 		if (!timeri) {
2541a60d4c5SIngo Molnar 			mutex_unlock(&register_mutex);
2552fd43d11SClemens Ladisch 			return -ENOMEM;
2562fd43d11SClemens Ladisch 		}
2571da177e4SLinus Torvalds 		timeri->slave_class = tid->dev_sclass;
2581da177e4SLinus Torvalds 		timeri->slave_id = tid->device;
2591da177e4SLinus Torvalds 		timeri->flags |= SNDRV_TIMER_IFLG_SLAVE;
2601da177e4SLinus Torvalds 		list_add_tail(&timeri->open_list, &snd_timer_slave_list);
2611da177e4SLinus Torvalds 		snd_timer_check_slave(timeri);
2621a60d4c5SIngo Molnar 		mutex_unlock(&register_mutex);
2631da177e4SLinus Torvalds 		*ti = timeri;
2641da177e4SLinus Torvalds 		return 0;
2651da177e4SLinus Torvalds 	}
2661da177e4SLinus Torvalds 
2671da177e4SLinus Torvalds 	/* open a master instance */
2681a60d4c5SIngo Molnar 	mutex_lock(&register_mutex);
2691da177e4SLinus Torvalds 	timer = snd_timer_find(tid);
270ee2da997SJohannes Berg #ifdef CONFIG_MODULES
271ee2da997SJohannes Berg 	if (!timer) {
2721a60d4c5SIngo Molnar 		mutex_unlock(&register_mutex);
2731da177e4SLinus Torvalds 		snd_timer_request(tid);
2741a60d4c5SIngo Molnar 		mutex_lock(&register_mutex);
2751da177e4SLinus Torvalds 		timer = snd_timer_find(tid);
2761da177e4SLinus Torvalds 	}
2771da177e4SLinus Torvalds #endif
2782fd43d11SClemens Ladisch 	if (!timer) {
2791a60d4c5SIngo Molnar 		mutex_unlock(&register_mutex);
2802fd43d11SClemens Ladisch 		return -ENODEV;
2812fd43d11SClemens Ladisch 	}
2821da177e4SLinus Torvalds 	if (!list_empty(&timer->open_list_head)) {
2832fd43d11SClemens Ladisch 		timeri = list_entry(timer->open_list_head.next,
28453d2f744STakashi Iwai 				    struct snd_timer_instance, open_list);
2851da177e4SLinus Torvalds 		if (timeri->flags & SNDRV_TIMER_IFLG_EXCLUSIVE) {
2861a60d4c5SIngo Molnar 			mutex_unlock(&register_mutex);
2871da177e4SLinus Torvalds 			return -EBUSY;
2881da177e4SLinus Torvalds 		}
2891da177e4SLinus Torvalds 	}
2901da177e4SLinus Torvalds 	timeri = snd_timer_instance_new(owner, timer);
2912fd43d11SClemens Ladisch 	if (!timeri) {
2921a60d4c5SIngo Molnar 		mutex_unlock(&register_mutex);
2932fd43d11SClemens Ladisch 		return -ENOMEM;
2942fd43d11SClemens Ladisch 	}
295230323daSTakashi Iwai 	/* take a card refcount for safe disconnection */
296230323daSTakashi Iwai 	if (timer->card)
297230323daSTakashi Iwai 		get_device(&timer->card->card_dev);
2981da177e4SLinus Torvalds 	timeri->slave_class = tid->dev_sclass;
2991da177e4SLinus Torvalds 	timeri->slave_id = slave_id;
3008ddc0563SVegard Nossum 
3018ddc0563SVegard Nossum 	if (list_empty(&timer->open_list_head) && timer->hw.open) {
3028ddc0563SVegard Nossum 		int err = timer->hw.open(timer);
3038ddc0563SVegard Nossum 		if (err) {
3048ddc0563SVegard Nossum 			kfree(timeri->owner);
3058ddc0563SVegard Nossum 			kfree(timeri);
3068ddc0563SVegard Nossum 
3078ddc0563SVegard Nossum 			if (timer->card)
3088ddc0563SVegard Nossum 				put_device(&timer->card->card_dev);
3098ddc0563SVegard Nossum 			module_put(timer->module);
3108ddc0563SVegard Nossum 			mutex_unlock(&register_mutex);
3118ddc0563SVegard Nossum 			return err;
3128ddc0563SVegard Nossum 		}
3138ddc0563SVegard Nossum 	}
3148ddc0563SVegard Nossum 
3151da177e4SLinus Torvalds 	list_add_tail(&timeri->open_list, &timer->open_list_head);
3161da177e4SLinus Torvalds 	snd_timer_check_master(timeri);
3171a60d4c5SIngo Molnar 	mutex_unlock(&register_mutex);
3181da177e4SLinus Torvalds 	*ti = timeri;
3191da177e4SLinus Torvalds 	return 0;
3201da177e4SLinus Torvalds }
3211da177e4SLinus Torvalds 
3221da177e4SLinus Torvalds /*
3231da177e4SLinus Torvalds  * close a timer instance
3241da177e4SLinus Torvalds  */
32553d2f744STakashi Iwai int snd_timer_close(struct snd_timer_instance *timeri)
3261da177e4SLinus Torvalds {
32753d2f744STakashi Iwai 	struct snd_timer *timer = NULL;
3289244b2c3SJohannes Berg 	struct snd_timer_instance *slave, *tmp;
3291da177e4SLinus Torvalds 
3300072889aSTakashi Iwai 	if (snd_BUG_ON(!timeri))
3317eaa943cSTakashi Iwai 		return -ENXIO;
3321da177e4SLinus Torvalds 
3339984d1b5STakashi Iwai 	mutex_lock(&register_mutex);
3349984d1b5STakashi Iwai 	list_del(&timeri->open_list);
3359984d1b5STakashi Iwai 
3361da177e4SLinus Torvalds 	/* force to stop the timer */
3371da177e4SLinus Torvalds 	snd_timer_stop(timeri);
3381da177e4SLinus Torvalds 
3391da177e4SLinus Torvalds 	timer = timeri->timer;
3409984d1b5STakashi Iwai 	if (timer) {
3411da177e4SLinus Torvalds 		/* wait, until the active callback is finished */
3421da177e4SLinus Torvalds 		spin_lock_irq(&timer->lock);
3431da177e4SLinus Torvalds 		while (timeri->flags & SNDRV_TIMER_IFLG_CALLBACK) {
3441da177e4SLinus Torvalds 			spin_unlock_irq(&timer->lock);
3451da177e4SLinus Torvalds 			udelay(10);
3461da177e4SLinus Torvalds 			spin_lock_irq(&timer->lock);
3471da177e4SLinus Torvalds 		}
3481da177e4SLinus Torvalds 		spin_unlock_irq(&timer->lock);
3499984d1b5STakashi Iwai 
3501da177e4SLinus Torvalds 		/* remove slave links */
351b5a663aaSTakashi Iwai 		spin_lock_irq(&slave_active_lock);
352b5a663aaSTakashi Iwai 		spin_lock(&timer->lock);
3539244b2c3SJohannes Berg 		list_for_each_entry_safe(slave, tmp, &timeri->slave_list_head,
3549244b2c3SJohannes Berg 					 open_list) {
3559244b2c3SJohannes Berg 			list_move_tail(&slave->open_list, &snd_timer_slave_list);
3561da177e4SLinus Torvalds 			slave->master = NULL;
3571da177e4SLinus Torvalds 			slave->timer = NULL;
358b5a663aaSTakashi Iwai 			list_del_init(&slave->ack_list);
359b5a663aaSTakashi Iwai 			list_del_init(&slave->active_list);
3601da177e4SLinus Torvalds 		}
361b5a663aaSTakashi Iwai 		spin_unlock(&timer->lock);
362b5a663aaSTakashi Iwai 		spin_unlock_irq(&slave_active_lock);
3639984d1b5STakashi Iwai 
3649984d1b5STakashi Iwai 		/* slave doesn't need to release timer resources below */
3659984d1b5STakashi Iwai 		if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
3669984d1b5STakashi Iwai 			timer = NULL;
3671da177e4SLinus Torvalds 	}
3689984d1b5STakashi Iwai 
3691da177e4SLinus Torvalds 	if (timeri->private_free)
3701da177e4SLinus Torvalds 		timeri->private_free(timeri);
3711da177e4SLinus Torvalds 	kfree(timeri->owner);
3721da177e4SLinus Torvalds 	kfree(timeri);
3739984d1b5STakashi Iwai 
3749984d1b5STakashi Iwai 	if (timer) {
3759984d1b5STakashi Iwai 		if (list_empty(&timer->open_list_head) && timer->hw.close)
3769984d1b5STakashi Iwai 			timer->hw.close(timer);
3779984d1b5STakashi Iwai 		/* release a card refcount for safe disconnection */
3789984d1b5STakashi Iwai 		if (timer->card)
3799984d1b5STakashi Iwai 			put_device(&timer->card->card_dev);
380de24214dSClemens Ladisch 		module_put(timer->module);
3819984d1b5STakashi Iwai 	}
3829984d1b5STakashi Iwai 
3839984d1b5STakashi Iwai 	mutex_unlock(&register_mutex);
3841da177e4SLinus Torvalds 	return 0;
3851da177e4SLinus Torvalds }
3861da177e4SLinus Torvalds 
38753d2f744STakashi Iwai unsigned long snd_timer_resolution(struct snd_timer_instance *timeri)
3881da177e4SLinus Torvalds {
38953d2f744STakashi Iwai 	struct snd_timer * timer;
3901da177e4SLinus Torvalds 
3911da177e4SLinus Torvalds 	if (timeri == NULL)
3921da177e4SLinus Torvalds 		return 0;
3931da177e4SLinus Torvalds 	if ((timer = timeri->timer) != NULL) {
3941da177e4SLinus Torvalds 		if (timer->hw.c_resolution)
3951da177e4SLinus Torvalds 			return timer->hw.c_resolution(timer);
3961da177e4SLinus Torvalds 		return timer->hw.resolution;
3971da177e4SLinus Torvalds 	}
3981da177e4SLinus Torvalds 	return 0;
3991da177e4SLinus Torvalds }
4001da177e4SLinus Torvalds 
40153d2f744STakashi Iwai static void snd_timer_notify1(struct snd_timer_instance *ti, int event)
4021da177e4SLinus Torvalds {
40353d2f744STakashi Iwai 	struct snd_timer *timer;
4041da177e4SLinus Torvalds 	unsigned long resolution = 0;
40553d2f744STakashi Iwai 	struct snd_timer_instance *ts;
4061da177e4SLinus Torvalds 	struct timespec tstamp;
4071da177e4SLinus Torvalds 
408b751eef1SJaroslav Kysela 	if (timer_tstamp_monotonic)
40926204e04SThomas Gleixner 		ktime_get_ts(&tstamp);
410b751eef1SJaroslav Kysela 	else
41107799e75STakashi Iwai 		getnstimeofday(&tstamp);
4127eaa943cSTakashi Iwai 	if (snd_BUG_ON(event < SNDRV_TIMER_EVENT_START ||
4137eaa943cSTakashi Iwai 		       event > SNDRV_TIMER_EVENT_PAUSE))
4147eaa943cSTakashi Iwai 		return;
4156b172a85SClemens Ladisch 	if (event == SNDRV_TIMER_EVENT_START ||
4166b172a85SClemens Ladisch 	    event == SNDRV_TIMER_EVENT_CONTINUE)
4171da177e4SLinus Torvalds 		resolution = snd_timer_resolution(ti);
4181da177e4SLinus Torvalds 	if (ti->ccallback)
419b30477d5SJaroslav Kysela 		ti->ccallback(ti, event, &tstamp, resolution);
4201da177e4SLinus Torvalds 	if (ti->flags & SNDRV_TIMER_IFLG_SLAVE)
4211da177e4SLinus Torvalds 		return;
4221da177e4SLinus Torvalds 	timer = ti->timer;
4231da177e4SLinus Torvalds 	if (timer == NULL)
4241da177e4SLinus Torvalds 		return;
4251da177e4SLinus Torvalds 	if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE)
4261da177e4SLinus Torvalds 		return;
4279244b2c3SJohannes Berg 	list_for_each_entry(ts, &ti->slave_active_head, active_list)
4281da177e4SLinus Torvalds 		if (ts->ccallback)
429117159f0STakashi Iwai 			ts->ccallback(ts, event + 100, &tstamp, resolution);
4301da177e4SLinus Torvalds }
4311da177e4SLinus Torvalds 
432f65e0d29STakashi Iwai /* start/continue a master timer */
433f65e0d29STakashi Iwai static int snd_timer_start1(struct snd_timer_instance *timeri,
434f65e0d29STakashi Iwai 			    bool start, unsigned long ticks)
4351da177e4SLinus Torvalds {
436f65e0d29STakashi Iwai 	struct snd_timer *timer;
437f65e0d29STakashi Iwai 	int result;
438f65e0d29STakashi Iwai 	unsigned long flags;
439f65e0d29STakashi Iwai 
440f65e0d29STakashi Iwai 	timer = timeri->timer;
441f65e0d29STakashi Iwai 	if (!timer)
442f65e0d29STakashi Iwai 		return -EINVAL;
443f65e0d29STakashi Iwai 
444f65e0d29STakashi Iwai 	spin_lock_irqsave(&timer->lock, flags);
445f65e0d29STakashi Iwai 	if (timer->card && timer->card->shutdown) {
446f65e0d29STakashi Iwai 		result = -ENODEV;
447f65e0d29STakashi Iwai 		goto unlock;
448f65e0d29STakashi Iwai 	}
449f65e0d29STakashi Iwai 	if (timeri->flags & (SNDRV_TIMER_IFLG_RUNNING |
450f65e0d29STakashi Iwai 			     SNDRV_TIMER_IFLG_START)) {
451f65e0d29STakashi Iwai 		result = -EBUSY;
452f65e0d29STakashi Iwai 		goto unlock;
453f65e0d29STakashi Iwai 	}
454f65e0d29STakashi Iwai 
455f65e0d29STakashi Iwai 	if (start)
456f65e0d29STakashi Iwai 		timeri->ticks = timeri->cticks = ticks;
457f65e0d29STakashi Iwai 	else if (!timeri->cticks)
458f65e0d29STakashi Iwai 		timeri->cticks = 1;
459f65e0d29STakashi Iwai 	timeri->pticks = 0;
460f65e0d29STakashi Iwai 
4615b7c757dSNicolas Kaiser 	list_move_tail(&timeri->active_list, &timer->active_list_head);
4621da177e4SLinus Torvalds 	if (timer->running) {
4631da177e4SLinus Torvalds 		if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE)
4641da177e4SLinus Torvalds 			goto __start_now;
4651da177e4SLinus Torvalds 		timer->flags |= SNDRV_TIMER_FLG_RESCHED;
4661da177e4SLinus Torvalds 		timeri->flags |= SNDRV_TIMER_IFLG_START;
467f65e0d29STakashi Iwai 		result = 1; /* delayed start */
4681da177e4SLinus Torvalds 	} else {
469f65e0d29STakashi Iwai 		if (start)
470f65e0d29STakashi Iwai 			timer->sticks = ticks;
4711da177e4SLinus Torvalds 		timer->hw.start(timer);
4721da177e4SLinus Torvalds 	      __start_now:
4731da177e4SLinus Torvalds 		timer->running++;
4741da177e4SLinus Torvalds 		timeri->flags |= SNDRV_TIMER_IFLG_RUNNING;
475f65e0d29STakashi Iwai 		result = 0;
4761da177e4SLinus Torvalds 	}
477f65e0d29STakashi Iwai 	snd_timer_notify1(timeri, start ? SNDRV_TIMER_EVENT_START :
478f65e0d29STakashi Iwai 			  SNDRV_TIMER_EVENT_CONTINUE);
479f65e0d29STakashi Iwai  unlock:
480f65e0d29STakashi Iwai 	spin_unlock_irqrestore(&timer->lock, flags);
481f65e0d29STakashi Iwai 	return result;
4821da177e4SLinus Torvalds }
4831da177e4SLinus Torvalds 
484f65e0d29STakashi Iwai /* start/continue a slave timer */
485f65e0d29STakashi Iwai static int snd_timer_start_slave(struct snd_timer_instance *timeri,
486f65e0d29STakashi Iwai 				 bool start)
4871da177e4SLinus Torvalds {
4881da177e4SLinus Torvalds 	unsigned long flags;
4891da177e4SLinus Torvalds 
4901da177e4SLinus Torvalds 	spin_lock_irqsave(&slave_active_lock, flags);
491f784beb7STakashi Iwai 	if (timeri->flags & SNDRV_TIMER_IFLG_RUNNING) {
492f784beb7STakashi Iwai 		spin_unlock_irqrestore(&slave_active_lock, flags);
493f784beb7STakashi Iwai 		return -EBUSY;
494f784beb7STakashi Iwai 	}
4951da177e4SLinus Torvalds 	timeri->flags |= SNDRV_TIMER_IFLG_RUNNING;
496b5a663aaSTakashi Iwai 	if (timeri->master && timeri->timer) {
497b5a663aaSTakashi Iwai 		spin_lock(&timeri->timer->lock);
4986b172a85SClemens Ladisch 		list_add_tail(&timeri->active_list,
4996b172a85SClemens Ladisch 			      &timeri->master->slave_active_head);
500f65e0d29STakashi Iwai 		snd_timer_notify1(timeri, start ? SNDRV_TIMER_EVENT_START :
501f65e0d29STakashi Iwai 				  SNDRV_TIMER_EVENT_CONTINUE);
502b5a663aaSTakashi Iwai 		spin_unlock(&timeri->timer->lock);
503b5a663aaSTakashi Iwai 	}
5041da177e4SLinus Torvalds 	spin_unlock_irqrestore(&slave_active_lock, flags);
5051da177e4SLinus Torvalds 	return 1; /* delayed start */
5061da177e4SLinus Torvalds }
5071da177e4SLinus Torvalds 
508f65e0d29STakashi Iwai /* stop/pause a master timer */
509f65e0d29STakashi Iwai static int snd_timer_stop1(struct snd_timer_instance *timeri, bool stop)
5101da177e4SLinus Torvalds {
51153d2f744STakashi Iwai 	struct snd_timer *timer;
512f65e0d29STakashi Iwai 	int result = 0;
5131da177e4SLinus Torvalds 	unsigned long flags;
5141da177e4SLinus Torvalds 
5151da177e4SLinus Torvalds 	timer = timeri->timer;
5161da177e4SLinus Torvalds 	if (!timer)
5171da177e4SLinus Torvalds 		return -EINVAL;
5181da177e4SLinus Torvalds 	spin_lock_irqsave(&timer->lock, flags);
519f784beb7STakashi Iwai 	if (!(timeri->flags & (SNDRV_TIMER_IFLG_RUNNING |
520f784beb7STakashi Iwai 			       SNDRV_TIMER_IFLG_START))) {
521f65e0d29STakashi Iwai 		result = -EBUSY;
522f65e0d29STakashi Iwai 		goto unlock;
523f784beb7STakashi Iwai 	}
5241da177e4SLinus Torvalds 	list_del_init(&timeri->ack_list);
5251da177e4SLinus Torvalds 	list_del_init(&timeri->active_list);
526f65e0d29STakashi Iwai 	if (timer->card && timer->card->shutdown)
527f65e0d29STakashi Iwai 		goto unlock;
528f65e0d29STakashi Iwai 	if (stop) {
529f65e0d29STakashi Iwai 		timeri->cticks = timeri->ticks;
530f65e0d29STakashi Iwai 		timeri->pticks = 0;
531230323daSTakashi Iwai 	}
5321da177e4SLinus Torvalds 	if ((timeri->flags & SNDRV_TIMER_IFLG_RUNNING) &&
5331da177e4SLinus Torvalds 	    !(--timer->running)) {
5341da177e4SLinus Torvalds 		timer->hw.stop(timer);
5351da177e4SLinus Torvalds 		if (timer->flags & SNDRV_TIMER_FLG_RESCHED) {
5361da177e4SLinus Torvalds 			timer->flags &= ~SNDRV_TIMER_FLG_RESCHED;
5371da177e4SLinus Torvalds 			snd_timer_reschedule(timer, 0);
5381da177e4SLinus Torvalds 			if (timer->flags & SNDRV_TIMER_FLG_CHANGE) {
5391da177e4SLinus Torvalds 				timer->flags &= ~SNDRV_TIMER_FLG_CHANGE;
5401da177e4SLinus Torvalds 				timer->hw.start(timer);
5411da177e4SLinus Torvalds 			}
5421da177e4SLinus Torvalds 		}
5431da177e4SLinus Torvalds 	}
544c3b16813STakashi Iwai 	timeri->flags &= ~(SNDRV_TIMER_IFLG_RUNNING | SNDRV_TIMER_IFLG_START);
5459f8a7658STakashi Iwai 	if (stop)
5469f8a7658STakashi Iwai 		timeri->flags &= ~SNDRV_TIMER_IFLG_PAUSED;
5479f8a7658STakashi Iwai 	else
5489f8a7658STakashi Iwai 		timeri->flags |= SNDRV_TIMER_IFLG_PAUSED;
549f65e0d29STakashi Iwai 	snd_timer_notify1(timeri, stop ? SNDRV_TIMER_EVENT_STOP :
550f65e0d29STakashi Iwai 			  SNDRV_TIMER_EVENT_CONTINUE);
551f65e0d29STakashi Iwai  unlock:
5521da177e4SLinus Torvalds 	spin_unlock_irqrestore(&timer->lock, flags);
553f65e0d29STakashi Iwai 	return result;
554f65e0d29STakashi Iwai }
555f65e0d29STakashi Iwai 
556f65e0d29STakashi Iwai /* stop/pause a slave timer */
557f65e0d29STakashi Iwai static int snd_timer_stop_slave(struct snd_timer_instance *timeri, bool stop)
558f65e0d29STakashi Iwai {
559f65e0d29STakashi Iwai 	unsigned long flags;
560f65e0d29STakashi Iwai 
561f65e0d29STakashi Iwai 	spin_lock_irqsave(&slave_active_lock, flags);
562f65e0d29STakashi Iwai 	if (!(timeri->flags & SNDRV_TIMER_IFLG_RUNNING)) {
563f65e0d29STakashi Iwai 		spin_unlock_irqrestore(&slave_active_lock, flags);
564f65e0d29STakashi Iwai 		return -EBUSY;
565f65e0d29STakashi Iwai 	}
566f65e0d29STakashi Iwai 	timeri->flags &= ~SNDRV_TIMER_IFLG_RUNNING;
567f65e0d29STakashi Iwai 	if (timeri->timer) {
568f65e0d29STakashi Iwai 		spin_lock(&timeri->timer->lock);
569f65e0d29STakashi Iwai 		list_del_init(&timeri->ack_list);
570f65e0d29STakashi Iwai 		list_del_init(&timeri->active_list);
571f65e0d29STakashi Iwai 		snd_timer_notify1(timeri, stop ? SNDRV_TIMER_EVENT_STOP :
572f65e0d29STakashi Iwai 				  SNDRV_TIMER_EVENT_CONTINUE);
573f65e0d29STakashi Iwai 		spin_unlock(&timeri->timer->lock);
574f65e0d29STakashi Iwai 	}
575f65e0d29STakashi Iwai 	spin_unlock_irqrestore(&slave_active_lock, flags);
5761da177e4SLinus Torvalds 	return 0;
5771da177e4SLinus Torvalds }
5781da177e4SLinus Torvalds 
5791da177e4SLinus Torvalds /*
580f65e0d29STakashi Iwai  *  start the timer instance
581f65e0d29STakashi Iwai  */
582f65e0d29STakashi Iwai int snd_timer_start(struct snd_timer_instance *timeri, unsigned int ticks)
583f65e0d29STakashi Iwai {
584f65e0d29STakashi Iwai 	if (timeri == NULL || ticks < 1)
585f65e0d29STakashi Iwai 		return -EINVAL;
586f65e0d29STakashi Iwai 	if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
587f65e0d29STakashi Iwai 		return snd_timer_start_slave(timeri, true);
588f65e0d29STakashi Iwai 	else
589f65e0d29STakashi Iwai 		return snd_timer_start1(timeri, true, ticks);
590f65e0d29STakashi Iwai }
591f65e0d29STakashi Iwai 
592f65e0d29STakashi Iwai /*
5931da177e4SLinus Torvalds  * stop the timer instance.
5941da177e4SLinus Torvalds  *
5951da177e4SLinus Torvalds  * do not call this from the timer callback!
5961da177e4SLinus Torvalds  */
59753d2f744STakashi Iwai int snd_timer_stop(struct snd_timer_instance *timeri)
5981da177e4SLinus Torvalds {
599f65e0d29STakashi Iwai 	if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
600f65e0d29STakashi Iwai 		return snd_timer_stop_slave(timeri, true);
601f65e0d29STakashi Iwai 	else
602f65e0d29STakashi Iwai 		return snd_timer_stop1(timeri, true);
6031da177e4SLinus Torvalds }
6041da177e4SLinus Torvalds 
6051da177e4SLinus Torvalds /*
6061da177e4SLinus Torvalds  * start again..  the tick is kept.
6071da177e4SLinus Torvalds  */
60853d2f744STakashi Iwai int snd_timer_continue(struct snd_timer_instance *timeri)
6091da177e4SLinus Torvalds {
6109f8a7658STakashi Iwai 	/* timer can continue only after pause */
6119f8a7658STakashi Iwai 	if (!(timeri->flags & SNDRV_TIMER_IFLG_PAUSED))
6129f8a7658STakashi Iwai 		return -EINVAL;
6139f8a7658STakashi Iwai 
6141da177e4SLinus Torvalds 	if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
615f65e0d29STakashi Iwai 		return snd_timer_start_slave(timeri, false);
616f65e0d29STakashi Iwai 	else
617f65e0d29STakashi Iwai 		return snd_timer_start1(timeri, false, 0);
6181da177e4SLinus Torvalds }
6191da177e4SLinus Torvalds 
6201da177e4SLinus Torvalds /*
6211da177e4SLinus Torvalds  * pause.. remember the ticks left
6221da177e4SLinus Torvalds  */
62353d2f744STakashi Iwai int snd_timer_pause(struct snd_timer_instance * timeri)
6241da177e4SLinus Torvalds {
625f65e0d29STakashi Iwai 	if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
626f65e0d29STakashi Iwai 		return snd_timer_stop_slave(timeri, false);
627f65e0d29STakashi Iwai 	else
628f65e0d29STakashi Iwai 		return snd_timer_stop1(timeri, false);
6291da177e4SLinus Torvalds }
6301da177e4SLinus Torvalds 
6311da177e4SLinus Torvalds /*
6321da177e4SLinus Torvalds  * reschedule the timer
6331da177e4SLinus Torvalds  *
6341da177e4SLinus Torvalds  * start pending instances and check the scheduling ticks.
6351da177e4SLinus Torvalds  * when the scheduling ticks is changed set CHANGE flag to reprogram the timer.
6361da177e4SLinus Torvalds  */
63753d2f744STakashi Iwai static void snd_timer_reschedule(struct snd_timer * timer, unsigned long ticks_left)
6381da177e4SLinus Torvalds {
63953d2f744STakashi Iwai 	struct snd_timer_instance *ti;
6401da177e4SLinus Torvalds 	unsigned long ticks = ~0UL;
6411da177e4SLinus Torvalds 
6429244b2c3SJohannes Berg 	list_for_each_entry(ti, &timer->active_list_head, active_list) {
6431da177e4SLinus Torvalds 		if (ti->flags & SNDRV_TIMER_IFLG_START) {
6441da177e4SLinus Torvalds 			ti->flags &= ~SNDRV_TIMER_IFLG_START;
6451da177e4SLinus Torvalds 			ti->flags |= SNDRV_TIMER_IFLG_RUNNING;
6461da177e4SLinus Torvalds 			timer->running++;
6471da177e4SLinus Torvalds 		}
6481da177e4SLinus Torvalds 		if (ti->flags & SNDRV_TIMER_IFLG_RUNNING) {
6491da177e4SLinus Torvalds 			if (ticks > ti->cticks)
6501da177e4SLinus Torvalds 				ticks = ti->cticks;
6511da177e4SLinus Torvalds 		}
6521da177e4SLinus Torvalds 	}
6531da177e4SLinus Torvalds 	if (ticks == ~0UL) {
6541da177e4SLinus Torvalds 		timer->flags &= ~SNDRV_TIMER_FLG_RESCHED;
6551da177e4SLinus Torvalds 		return;
6561da177e4SLinus Torvalds 	}
6571da177e4SLinus Torvalds 	if (ticks > timer->hw.ticks)
6581da177e4SLinus Torvalds 		ticks = timer->hw.ticks;
6591da177e4SLinus Torvalds 	if (ticks_left != ticks)
6601da177e4SLinus Torvalds 		timer->flags |= SNDRV_TIMER_FLG_CHANGE;
6611da177e4SLinus Torvalds 	timer->sticks = ticks;
6621da177e4SLinus Torvalds }
6631da177e4SLinus Torvalds 
6641da177e4SLinus Torvalds /*
6651da177e4SLinus Torvalds  * timer tasklet
6661da177e4SLinus Torvalds  *
6671da177e4SLinus Torvalds  */
6681da177e4SLinus Torvalds static void snd_timer_tasklet(unsigned long arg)
6691da177e4SLinus Torvalds {
67053d2f744STakashi Iwai 	struct snd_timer *timer = (struct snd_timer *) arg;
67153d2f744STakashi Iwai 	struct snd_timer_instance *ti;
6721da177e4SLinus Torvalds 	struct list_head *p;
6731da177e4SLinus Torvalds 	unsigned long resolution, ticks;
6742999ff5bSTakashi Iwai 	unsigned long flags;
6751da177e4SLinus Torvalds 
676230323daSTakashi Iwai 	if (timer->card && timer->card->shutdown)
677230323daSTakashi Iwai 		return;
678230323daSTakashi Iwai 
6792999ff5bSTakashi Iwai 	spin_lock_irqsave(&timer->lock, flags);
6801da177e4SLinus Torvalds 	/* now process all callbacks */
6811da177e4SLinus Torvalds 	while (!list_empty(&timer->sack_list_head)) {
6821da177e4SLinus Torvalds 		p = timer->sack_list_head.next;		/* get first item */
68353d2f744STakashi Iwai 		ti = list_entry(p, struct snd_timer_instance, ack_list);
6841da177e4SLinus Torvalds 
6851da177e4SLinus Torvalds 		/* remove from ack_list and make empty */
6861da177e4SLinus Torvalds 		list_del_init(p);
6871da177e4SLinus Torvalds 
6881da177e4SLinus Torvalds 		ticks = ti->pticks;
6891da177e4SLinus Torvalds 		ti->pticks = 0;
6901da177e4SLinus Torvalds 		resolution = ti->resolution;
6911da177e4SLinus Torvalds 
6921da177e4SLinus Torvalds 		ti->flags |= SNDRV_TIMER_IFLG_CALLBACK;
6931da177e4SLinus Torvalds 		spin_unlock(&timer->lock);
6941da177e4SLinus Torvalds 		if (ti->callback)
6951da177e4SLinus Torvalds 			ti->callback(ti, resolution, ticks);
6961da177e4SLinus Torvalds 		spin_lock(&timer->lock);
6971da177e4SLinus Torvalds 		ti->flags &= ~SNDRV_TIMER_IFLG_CALLBACK;
6981da177e4SLinus Torvalds 	}
6992999ff5bSTakashi Iwai 	spin_unlock_irqrestore(&timer->lock, flags);
7001da177e4SLinus Torvalds }
7011da177e4SLinus Torvalds 
7021da177e4SLinus Torvalds /*
7031da177e4SLinus Torvalds  * timer interrupt
7041da177e4SLinus Torvalds  *
7051da177e4SLinus Torvalds  * ticks_left is usually equal to timer->sticks.
7061da177e4SLinus Torvalds  *
7071da177e4SLinus Torvalds  */
70853d2f744STakashi Iwai void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
7091da177e4SLinus Torvalds {
7109244b2c3SJohannes Berg 	struct snd_timer_instance *ti, *ts, *tmp;
7111da177e4SLinus Torvalds 	unsigned long resolution, ticks;
7129244b2c3SJohannes Berg 	struct list_head *p, *ack_list_head;
713b32425acSTakashi Iwai 	unsigned long flags;
7141da177e4SLinus Torvalds 	int use_tasklet = 0;
7151da177e4SLinus Torvalds 
7161da177e4SLinus Torvalds 	if (timer == NULL)
7171da177e4SLinus Torvalds 		return;
7181da177e4SLinus Torvalds 
719230323daSTakashi Iwai 	if (timer->card && timer->card->shutdown)
720230323daSTakashi Iwai 		return;
721230323daSTakashi Iwai 
722b32425acSTakashi Iwai 	spin_lock_irqsave(&timer->lock, flags);
7231da177e4SLinus Torvalds 
7241da177e4SLinus Torvalds 	/* remember the current resolution */
7251da177e4SLinus Torvalds 	if (timer->hw.c_resolution)
7261da177e4SLinus Torvalds 		resolution = timer->hw.c_resolution(timer);
7271da177e4SLinus Torvalds 	else
7281da177e4SLinus Torvalds 		resolution = timer->hw.resolution;
7291da177e4SLinus Torvalds 
7301da177e4SLinus Torvalds 	/* loop for all active instances
7319244b2c3SJohannes Berg 	 * Here we cannot use list_for_each_entry because the active_list of a
7326b172a85SClemens Ladisch 	 * processed instance is relinked to done_list_head before the callback
7336b172a85SClemens Ladisch 	 * is called.
7341da177e4SLinus Torvalds 	 */
7359244b2c3SJohannes Berg 	list_for_each_entry_safe(ti, tmp, &timer->active_list_head,
7369244b2c3SJohannes Berg 				 active_list) {
7371da177e4SLinus Torvalds 		if (!(ti->flags & SNDRV_TIMER_IFLG_RUNNING))
7381da177e4SLinus Torvalds 			continue;
7391da177e4SLinus Torvalds 		ti->pticks += ticks_left;
7401da177e4SLinus Torvalds 		ti->resolution = resolution;
7411da177e4SLinus Torvalds 		if (ti->cticks < ticks_left)
7421da177e4SLinus Torvalds 			ti->cticks = 0;
7431da177e4SLinus Torvalds 		else
7441da177e4SLinus Torvalds 			ti->cticks -= ticks_left;
7451da177e4SLinus Torvalds 		if (ti->cticks) /* not expired */
7461da177e4SLinus Torvalds 			continue;
7471da177e4SLinus Torvalds 		if (ti->flags & SNDRV_TIMER_IFLG_AUTO) {
7481da177e4SLinus Torvalds 			ti->cticks = ti->ticks;
7491da177e4SLinus Torvalds 		} else {
7501da177e4SLinus Torvalds 			ti->flags &= ~SNDRV_TIMER_IFLG_RUNNING;
751094fd3beSTakashi Iwai 			--timer->running;
752ee8413b0STakashi Iwai 			list_del_init(&ti->active_list);
7531da177e4SLinus Torvalds 		}
7541da177e4SLinus Torvalds 		if ((timer->hw.flags & SNDRV_TIMER_HW_TASKLET) ||
7556b172a85SClemens Ladisch 		    (ti->flags & SNDRV_TIMER_IFLG_FAST))
7566b172a85SClemens Ladisch 			ack_list_head = &timer->ack_list_head;
7576b172a85SClemens Ladisch 		else
7586b172a85SClemens Ladisch 			ack_list_head = &timer->sack_list_head;
7596b172a85SClemens Ladisch 		if (list_empty(&ti->ack_list))
7606b172a85SClemens Ladisch 			list_add_tail(&ti->ack_list, ack_list_head);
7619244b2c3SJohannes Berg 		list_for_each_entry(ts, &ti->slave_active_head, active_list) {
7621da177e4SLinus Torvalds 			ts->pticks = ti->pticks;
7631da177e4SLinus Torvalds 			ts->resolution = resolution;
7646b172a85SClemens Ladisch 			if (list_empty(&ts->ack_list))
7656b172a85SClemens Ladisch 				list_add_tail(&ts->ack_list, ack_list_head);
7661da177e4SLinus Torvalds 		}
7671da177e4SLinus Torvalds 	}
7681da177e4SLinus Torvalds 	if (timer->flags & SNDRV_TIMER_FLG_RESCHED)
769cd93fe47SClemens Ladisch 		snd_timer_reschedule(timer, timer->sticks);
7701da177e4SLinus Torvalds 	if (timer->running) {
7711da177e4SLinus Torvalds 		if (timer->hw.flags & SNDRV_TIMER_HW_STOP) {
7721da177e4SLinus Torvalds 			timer->hw.stop(timer);
7731da177e4SLinus Torvalds 			timer->flags |= SNDRV_TIMER_FLG_CHANGE;
7741da177e4SLinus Torvalds 		}
7751da177e4SLinus Torvalds 		if (!(timer->hw.flags & SNDRV_TIMER_HW_AUTO) ||
7761da177e4SLinus Torvalds 		    (timer->flags & SNDRV_TIMER_FLG_CHANGE)) {
7771da177e4SLinus Torvalds 			/* restart timer */
7781da177e4SLinus Torvalds 			timer->flags &= ~SNDRV_TIMER_FLG_CHANGE;
7791da177e4SLinus Torvalds 			timer->hw.start(timer);
7801da177e4SLinus Torvalds 		}
7811da177e4SLinus Torvalds 	} else {
7821da177e4SLinus Torvalds 		timer->hw.stop(timer);
7831da177e4SLinus Torvalds 	}
7841da177e4SLinus Torvalds 
7851da177e4SLinus Torvalds 	/* now process all fast callbacks */
7861da177e4SLinus Torvalds 	while (!list_empty(&timer->ack_list_head)) {
7871da177e4SLinus Torvalds 		p = timer->ack_list_head.next;		/* get first item */
78853d2f744STakashi Iwai 		ti = list_entry(p, struct snd_timer_instance, ack_list);
7891da177e4SLinus Torvalds 
7901da177e4SLinus Torvalds 		/* remove from ack_list and make empty */
7911da177e4SLinus Torvalds 		list_del_init(p);
7921da177e4SLinus Torvalds 
7931da177e4SLinus Torvalds 		ticks = ti->pticks;
7941da177e4SLinus Torvalds 		ti->pticks = 0;
7951da177e4SLinus Torvalds 
7961da177e4SLinus Torvalds 		ti->flags |= SNDRV_TIMER_IFLG_CALLBACK;
7971da177e4SLinus Torvalds 		spin_unlock(&timer->lock);
7981da177e4SLinus Torvalds 		if (ti->callback)
7991da177e4SLinus Torvalds 			ti->callback(ti, resolution, ticks);
8001da177e4SLinus Torvalds 		spin_lock(&timer->lock);
8011da177e4SLinus Torvalds 		ti->flags &= ~SNDRV_TIMER_IFLG_CALLBACK;
8021da177e4SLinus Torvalds 	}
8031da177e4SLinus Torvalds 
8041da177e4SLinus Torvalds 	/* do we have any slow callbacks? */
8051da177e4SLinus Torvalds 	use_tasklet = !list_empty(&timer->sack_list_head);
806b32425acSTakashi Iwai 	spin_unlock_irqrestore(&timer->lock, flags);
8071da177e4SLinus Torvalds 
8081da177e4SLinus Torvalds 	if (use_tasklet)
8091f04128aSTakashi Iwai 		tasklet_schedule(&timer->task_queue);
8101da177e4SLinus Torvalds }
8111da177e4SLinus Torvalds 
8121da177e4SLinus Torvalds /*
8131da177e4SLinus Torvalds 
8141da177e4SLinus Torvalds  */
8151da177e4SLinus Torvalds 
81653d2f744STakashi Iwai int snd_timer_new(struct snd_card *card, char *id, struct snd_timer_id *tid,
81753d2f744STakashi Iwai 		  struct snd_timer **rtimer)
8181da177e4SLinus Torvalds {
81953d2f744STakashi Iwai 	struct snd_timer *timer;
8201da177e4SLinus Torvalds 	int err;
82153d2f744STakashi Iwai 	static struct snd_device_ops ops = {
8221da177e4SLinus Torvalds 		.dev_free = snd_timer_dev_free,
8231da177e4SLinus Torvalds 		.dev_register = snd_timer_dev_register,
824c461482cSTakashi Iwai 		.dev_disconnect = snd_timer_dev_disconnect,
8251da177e4SLinus Torvalds 	};
8261da177e4SLinus Torvalds 
8277eaa943cSTakashi Iwai 	if (snd_BUG_ON(!tid))
8287eaa943cSTakashi Iwai 		return -EINVAL;
8297eaa943cSTakashi Iwai 	if (rtimer)
8301da177e4SLinus Torvalds 		*rtimer = NULL;
831ca2c0966STakashi Iwai 	timer = kzalloc(sizeof(*timer), GFP_KERNEL);
832ec0e9937STakashi Iwai 	if (!timer)
8331da177e4SLinus Torvalds 		return -ENOMEM;
8341da177e4SLinus Torvalds 	timer->tmr_class = tid->dev_class;
8351da177e4SLinus Torvalds 	timer->card = card;
8361da177e4SLinus Torvalds 	timer->tmr_device = tid->device;
8371da177e4SLinus Torvalds 	timer->tmr_subdevice = tid->subdevice;
8381da177e4SLinus Torvalds 	if (id)
8391da177e4SLinus Torvalds 		strlcpy(timer->id, id, sizeof(timer->id));
8406b760bb2SVegard Nossum 	timer->sticks = 1;
8411da177e4SLinus Torvalds 	INIT_LIST_HEAD(&timer->device_list);
8421da177e4SLinus Torvalds 	INIT_LIST_HEAD(&timer->open_list_head);
8431da177e4SLinus Torvalds 	INIT_LIST_HEAD(&timer->active_list_head);
8441da177e4SLinus Torvalds 	INIT_LIST_HEAD(&timer->ack_list_head);
8451da177e4SLinus Torvalds 	INIT_LIST_HEAD(&timer->sack_list_head);
8461da177e4SLinus Torvalds 	spin_lock_init(&timer->lock);
8476b172a85SClemens Ladisch 	tasklet_init(&timer->task_queue, snd_timer_tasklet,
8486b172a85SClemens Ladisch 		     (unsigned long)timer);
8491da177e4SLinus Torvalds 	if (card != NULL) {
850de24214dSClemens Ladisch 		timer->module = card->module;
8516b172a85SClemens Ladisch 		err = snd_device_new(card, SNDRV_DEV_TIMER, timer, &ops);
8526b172a85SClemens Ladisch 		if (err < 0) {
8531da177e4SLinus Torvalds 			snd_timer_free(timer);
8541da177e4SLinus Torvalds 			return err;
8551da177e4SLinus Torvalds 		}
8561da177e4SLinus Torvalds 	}
8577eaa943cSTakashi Iwai 	if (rtimer)
8581da177e4SLinus Torvalds 		*rtimer = timer;
8591da177e4SLinus Torvalds 	return 0;
8601da177e4SLinus Torvalds }
8611da177e4SLinus Torvalds 
86253d2f744STakashi Iwai static int snd_timer_free(struct snd_timer *timer)
8631da177e4SLinus Torvalds {
8647eaa943cSTakashi Iwai 	if (!timer)
8657eaa943cSTakashi Iwai 		return 0;
866c461482cSTakashi Iwai 
867c461482cSTakashi Iwai 	mutex_lock(&register_mutex);
868c461482cSTakashi Iwai 	if (! list_empty(&timer->open_list_head)) {
869c461482cSTakashi Iwai 		struct list_head *p, *n;
870c461482cSTakashi Iwai 		struct snd_timer_instance *ti;
871cf74dcf3STakashi Iwai 		pr_warn("ALSA: timer %p is busy?\n", timer);
872c461482cSTakashi Iwai 		list_for_each_safe(p, n, &timer->open_list_head) {
873c461482cSTakashi Iwai 			list_del_init(p);
874c461482cSTakashi Iwai 			ti = list_entry(p, struct snd_timer_instance, open_list);
875c461482cSTakashi Iwai 			ti->timer = NULL;
876c461482cSTakashi Iwai 		}
877c461482cSTakashi Iwai 	}
878c461482cSTakashi Iwai 	list_del(&timer->device_list);
879c461482cSTakashi Iwai 	mutex_unlock(&register_mutex);
880c461482cSTakashi Iwai 
8811da177e4SLinus Torvalds 	if (timer->private_free)
8821da177e4SLinus Torvalds 		timer->private_free(timer);
8831da177e4SLinus Torvalds 	kfree(timer);
8841da177e4SLinus Torvalds 	return 0;
8851da177e4SLinus Torvalds }
8861da177e4SLinus Torvalds 
88753d2f744STakashi Iwai static int snd_timer_dev_free(struct snd_device *device)
8881da177e4SLinus Torvalds {
88953d2f744STakashi Iwai 	struct snd_timer *timer = device->device_data;
8901da177e4SLinus Torvalds 	return snd_timer_free(timer);
8911da177e4SLinus Torvalds }
8921da177e4SLinus Torvalds 
89353d2f744STakashi Iwai static int snd_timer_dev_register(struct snd_device *dev)
8941da177e4SLinus Torvalds {
89553d2f744STakashi Iwai 	struct snd_timer *timer = dev->device_data;
89653d2f744STakashi Iwai 	struct snd_timer *timer1;
8971da177e4SLinus Torvalds 
8987eaa943cSTakashi Iwai 	if (snd_BUG_ON(!timer || !timer->hw.start || !timer->hw.stop))
8997eaa943cSTakashi Iwai 		return -ENXIO;
9001da177e4SLinus Torvalds 	if (!(timer->hw.flags & SNDRV_TIMER_HW_SLAVE) &&
9011da177e4SLinus Torvalds 	    !timer->hw.resolution && timer->hw.c_resolution == NULL)
9021da177e4SLinus Torvalds 	    	return -EINVAL;
9031da177e4SLinus Torvalds 
9041a60d4c5SIngo Molnar 	mutex_lock(&register_mutex);
9059244b2c3SJohannes Berg 	list_for_each_entry(timer1, &snd_timer_list, device_list) {
9061da177e4SLinus Torvalds 		if (timer1->tmr_class > timer->tmr_class)
9071da177e4SLinus Torvalds 			break;
9081da177e4SLinus Torvalds 		if (timer1->tmr_class < timer->tmr_class)
9091da177e4SLinus Torvalds 			continue;
9101da177e4SLinus Torvalds 		if (timer1->card && timer->card) {
9111da177e4SLinus Torvalds 			if (timer1->card->number > timer->card->number)
9121da177e4SLinus Torvalds 				break;
9131da177e4SLinus Torvalds 			if (timer1->card->number < timer->card->number)
9141da177e4SLinus Torvalds 				continue;
9151da177e4SLinus Torvalds 		}
9161da177e4SLinus Torvalds 		if (timer1->tmr_device > timer->tmr_device)
9171da177e4SLinus Torvalds 			break;
9181da177e4SLinus Torvalds 		if (timer1->tmr_device < timer->tmr_device)
9191da177e4SLinus Torvalds 			continue;
9201da177e4SLinus Torvalds 		if (timer1->tmr_subdevice > timer->tmr_subdevice)
9211da177e4SLinus Torvalds 			break;
9221da177e4SLinus Torvalds 		if (timer1->tmr_subdevice < timer->tmr_subdevice)
9231da177e4SLinus Torvalds 			continue;
9241da177e4SLinus Torvalds 		/* conflicts.. */
9251a60d4c5SIngo Molnar 		mutex_unlock(&register_mutex);
9261da177e4SLinus Torvalds 		return -EBUSY;
9271da177e4SLinus Torvalds 	}
9289244b2c3SJohannes Berg 	list_add_tail(&timer->device_list, &timer1->device_list);
9291a60d4c5SIngo Molnar 	mutex_unlock(&register_mutex);
9301da177e4SLinus Torvalds 	return 0;
9311da177e4SLinus Torvalds }
9321da177e4SLinus Torvalds 
933c461482cSTakashi Iwai static int snd_timer_dev_disconnect(struct snd_device *device)
9341da177e4SLinus Torvalds {
93553d2f744STakashi Iwai 	struct snd_timer *timer = device->device_data;
936230323daSTakashi Iwai 	struct snd_timer_instance *ti;
937230323daSTakashi Iwai 
938c461482cSTakashi Iwai 	mutex_lock(&register_mutex);
939c461482cSTakashi Iwai 	list_del_init(&timer->device_list);
940230323daSTakashi Iwai 	/* wake up pending sleepers */
941230323daSTakashi Iwai 	list_for_each_entry(ti, &timer->open_list_head, open_list) {
94240ed9444STakashi Iwai 		if (ti->disconnect)
94340ed9444STakashi Iwai 			ti->disconnect(ti);
944230323daSTakashi Iwai 	}
945c461482cSTakashi Iwai 	mutex_unlock(&register_mutex);
946c461482cSTakashi Iwai 	return 0;
9471da177e4SLinus Torvalds }
9481da177e4SLinus Torvalds 
94953d2f744STakashi Iwai void snd_timer_notify(struct snd_timer *timer, int event, struct timespec *tstamp)
9501da177e4SLinus Torvalds {
9511da177e4SLinus Torvalds 	unsigned long flags;
9521da177e4SLinus Torvalds 	unsigned long resolution = 0;
95353d2f744STakashi Iwai 	struct snd_timer_instance *ti, *ts;
9541da177e4SLinus Torvalds 
955230323daSTakashi Iwai 	if (timer->card && timer->card->shutdown)
956230323daSTakashi Iwai 		return;
9577c22f1aaSTakashi Iwai 	if (! (timer->hw.flags & SNDRV_TIMER_HW_SLAVE))
9587c22f1aaSTakashi Iwai 		return;
9597eaa943cSTakashi Iwai 	if (snd_BUG_ON(event < SNDRV_TIMER_EVENT_MSTART ||
9607eaa943cSTakashi Iwai 		       event > SNDRV_TIMER_EVENT_MRESUME))
9617eaa943cSTakashi Iwai 		return;
9621da177e4SLinus Torvalds 	spin_lock_irqsave(&timer->lock, flags);
963a501dfa3SJaroslav Kysela 	if (event == SNDRV_TIMER_EVENT_MSTART ||
964a501dfa3SJaroslav Kysela 	    event == SNDRV_TIMER_EVENT_MCONTINUE ||
965a501dfa3SJaroslav Kysela 	    event == SNDRV_TIMER_EVENT_MRESUME) {
9661da177e4SLinus Torvalds 		if (timer->hw.c_resolution)
9671da177e4SLinus Torvalds 			resolution = timer->hw.c_resolution(timer);
9681da177e4SLinus Torvalds 		else
9691da177e4SLinus Torvalds 			resolution = timer->hw.resolution;
9701da177e4SLinus Torvalds 	}
9719244b2c3SJohannes Berg 	list_for_each_entry(ti, &timer->active_list_head, active_list) {
9721da177e4SLinus Torvalds 		if (ti->ccallback)
9731da177e4SLinus Torvalds 			ti->ccallback(ti, event, tstamp, resolution);
9749244b2c3SJohannes Berg 		list_for_each_entry(ts, &ti->slave_active_head, active_list)
9751da177e4SLinus Torvalds 			if (ts->ccallback)
9761da177e4SLinus Torvalds 				ts->ccallback(ts, event, tstamp, resolution);
9771da177e4SLinus Torvalds 	}
9781da177e4SLinus Torvalds 	spin_unlock_irqrestore(&timer->lock, flags);
9791da177e4SLinus Torvalds }
9801da177e4SLinus Torvalds 
9811da177e4SLinus Torvalds /*
9821da177e4SLinus Torvalds  * exported functions for global timers
9831da177e4SLinus Torvalds  */
98453d2f744STakashi Iwai int snd_timer_global_new(char *id, int device, struct snd_timer **rtimer)
9851da177e4SLinus Torvalds {
98653d2f744STakashi Iwai 	struct snd_timer_id tid;
9871da177e4SLinus Torvalds 
9881da177e4SLinus Torvalds 	tid.dev_class = SNDRV_TIMER_CLASS_GLOBAL;
9891da177e4SLinus Torvalds 	tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE;
9901da177e4SLinus Torvalds 	tid.card = -1;
9911da177e4SLinus Torvalds 	tid.device = device;
9921da177e4SLinus Torvalds 	tid.subdevice = 0;
9931da177e4SLinus Torvalds 	return snd_timer_new(NULL, id, &tid, rtimer);
9941da177e4SLinus Torvalds }
9951da177e4SLinus Torvalds 
99653d2f744STakashi Iwai int snd_timer_global_free(struct snd_timer *timer)
9971da177e4SLinus Torvalds {
9981da177e4SLinus Torvalds 	return snd_timer_free(timer);
9991da177e4SLinus Torvalds }
10001da177e4SLinus Torvalds 
100153d2f744STakashi Iwai int snd_timer_global_register(struct snd_timer *timer)
10021da177e4SLinus Torvalds {
100353d2f744STakashi Iwai 	struct snd_device dev;
10041da177e4SLinus Torvalds 
10051da177e4SLinus Torvalds 	memset(&dev, 0, sizeof(dev));
10061da177e4SLinus Torvalds 	dev.device_data = timer;
10071da177e4SLinus Torvalds 	return snd_timer_dev_register(&dev);
10081da177e4SLinus Torvalds }
10091da177e4SLinus Torvalds 
10101da177e4SLinus Torvalds /*
10111da177e4SLinus Torvalds  *  System timer
10121da177e4SLinus Torvalds  */
10131da177e4SLinus Torvalds 
10141da177e4SLinus Torvalds struct snd_timer_system_private {
10151da177e4SLinus Torvalds 	struct timer_list tlist;
10161da177e4SLinus Torvalds 	unsigned long last_expires;
10171da177e4SLinus Torvalds 	unsigned long last_jiffies;
10181da177e4SLinus Torvalds 	unsigned long correction;
10191da177e4SLinus Torvalds };
10201da177e4SLinus Torvalds 
10211da177e4SLinus Torvalds static void snd_timer_s_function(unsigned long data)
10221da177e4SLinus Torvalds {
102353d2f744STakashi Iwai 	struct snd_timer *timer = (struct snd_timer *)data;
10241da177e4SLinus Torvalds 	struct snd_timer_system_private *priv = timer->private_data;
10251da177e4SLinus Torvalds 	unsigned long jiff = jiffies;
10261da177e4SLinus Torvalds 	if (time_after(jiff, priv->last_expires))
10276ed5eff0SClemens Ladisch 		priv->correction += (long)jiff - (long)priv->last_expires;
10281da177e4SLinus Torvalds 	snd_timer_interrupt(timer, (long)jiff - (long)priv->last_jiffies);
10291da177e4SLinus Torvalds }
10301da177e4SLinus Torvalds 
103153d2f744STakashi Iwai static int snd_timer_s_start(struct snd_timer * timer)
10321da177e4SLinus Torvalds {
10331da177e4SLinus Torvalds 	struct snd_timer_system_private *priv;
10341da177e4SLinus Torvalds 	unsigned long njiff;
10351da177e4SLinus Torvalds 
10361da177e4SLinus Torvalds 	priv = (struct snd_timer_system_private *) timer->private_data;
10371da177e4SLinus Torvalds 	njiff = (priv->last_jiffies = jiffies);
10381da177e4SLinus Torvalds 	if (priv->correction > timer->sticks - 1) {
10391da177e4SLinus Torvalds 		priv->correction -= timer->sticks - 1;
10401da177e4SLinus Torvalds 		njiff++;
10411da177e4SLinus Torvalds 	} else {
10421da177e4SLinus Torvalds 		njiff += timer->sticks - priv->correction;
104317f48ec3SClemens Ladisch 		priv->correction = 0;
10441da177e4SLinus Torvalds 	}
10454a07083eSTakashi Iwai 	priv->last_expires = njiff;
10464a07083eSTakashi Iwai 	mod_timer(&priv->tlist, njiff);
10471da177e4SLinus Torvalds 	return 0;
10481da177e4SLinus Torvalds }
10491da177e4SLinus Torvalds 
105053d2f744STakashi Iwai static int snd_timer_s_stop(struct snd_timer * timer)
10511da177e4SLinus Torvalds {
10521da177e4SLinus Torvalds 	struct snd_timer_system_private *priv;
10531da177e4SLinus Torvalds 	unsigned long jiff;
10541da177e4SLinus Torvalds 
10551da177e4SLinus Torvalds 	priv = (struct snd_timer_system_private *) timer->private_data;
10561da177e4SLinus Torvalds 	del_timer(&priv->tlist);
10571da177e4SLinus Torvalds 	jiff = jiffies;
10581da177e4SLinus Torvalds 	if (time_before(jiff, priv->last_expires))
10591da177e4SLinus Torvalds 		timer->sticks = priv->last_expires - jiff;
10601da177e4SLinus Torvalds 	else
10611da177e4SLinus Torvalds 		timer->sticks = 1;
1062de2696d8SClemens Ladisch 	priv->correction = 0;
10631da177e4SLinus Torvalds 	return 0;
10641da177e4SLinus Torvalds }
10651da177e4SLinus Torvalds 
1066f146357fSTakashi Iwai static int snd_timer_s_close(struct snd_timer *timer)
1067f146357fSTakashi Iwai {
1068f146357fSTakashi Iwai 	struct snd_timer_system_private *priv;
1069f146357fSTakashi Iwai 
1070f146357fSTakashi Iwai 	priv = (struct snd_timer_system_private *)timer->private_data;
1071f146357fSTakashi Iwai 	del_timer_sync(&priv->tlist);
1072f146357fSTakashi Iwai 	return 0;
1073f146357fSTakashi Iwai }
1074f146357fSTakashi Iwai 
107553d2f744STakashi Iwai static struct snd_timer_hardware snd_timer_system =
10761da177e4SLinus Torvalds {
10771da177e4SLinus Torvalds 	.flags =	SNDRV_TIMER_HW_FIRST | SNDRV_TIMER_HW_TASKLET,
10781da177e4SLinus Torvalds 	.resolution =	1000000000L / HZ,
10791da177e4SLinus Torvalds 	.ticks =	10000000L,
1080f146357fSTakashi Iwai 	.close =	snd_timer_s_close,
10811da177e4SLinus Torvalds 	.start =	snd_timer_s_start,
10821da177e4SLinus Torvalds 	.stop =		snd_timer_s_stop
10831da177e4SLinus Torvalds };
10841da177e4SLinus Torvalds 
108553d2f744STakashi Iwai static void snd_timer_free_system(struct snd_timer *timer)
10861da177e4SLinus Torvalds {
10871da177e4SLinus Torvalds 	kfree(timer->private_data);
10881da177e4SLinus Torvalds }
10891da177e4SLinus Torvalds 
10901da177e4SLinus Torvalds static int snd_timer_register_system(void)
10911da177e4SLinus Torvalds {
109253d2f744STakashi Iwai 	struct snd_timer *timer;
10931da177e4SLinus Torvalds 	struct snd_timer_system_private *priv;
10941da177e4SLinus Torvalds 	int err;
10951da177e4SLinus Torvalds 
10966b172a85SClemens Ladisch 	err = snd_timer_global_new("system", SNDRV_TIMER_GLOBAL_SYSTEM, &timer);
10976b172a85SClemens Ladisch 	if (err < 0)
10981da177e4SLinus Torvalds 		return err;
10991da177e4SLinus Torvalds 	strcpy(timer->name, "system timer");
11001da177e4SLinus Torvalds 	timer->hw = snd_timer_system;
1101ca2c0966STakashi Iwai 	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
11021da177e4SLinus Torvalds 	if (priv == NULL) {
11031da177e4SLinus Torvalds 		snd_timer_free(timer);
11041da177e4SLinus Torvalds 		return -ENOMEM;
11051da177e4SLinus Torvalds 	}
1106f169c105STakashi Iwai 	setup_timer(&priv->tlist, snd_timer_s_function, (unsigned long) timer);
11071da177e4SLinus Torvalds 	timer->private_data = priv;
11081da177e4SLinus Torvalds 	timer->private_free = snd_timer_free_system;
11091da177e4SLinus Torvalds 	return snd_timer_global_register(timer);
11101da177e4SLinus Torvalds }
11111da177e4SLinus Torvalds 
1112cd6a6503SJie Yang #ifdef CONFIG_SND_PROC_FS
11131da177e4SLinus Torvalds /*
11141da177e4SLinus Torvalds  *  Info interface
11151da177e4SLinus Torvalds  */
11161da177e4SLinus Torvalds 
111753d2f744STakashi Iwai static void snd_timer_proc_read(struct snd_info_entry *entry,
111853d2f744STakashi Iwai 				struct snd_info_buffer *buffer)
11191da177e4SLinus Torvalds {
112053d2f744STakashi Iwai 	struct snd_timer *timer;
112153d2f744STakashi Iwai 	struct snd_timer_instance *ti;
11221da177e4SLinus Torvalds 
11231a60d4c5SIngo Molnar 	mutex_lock(&register_mutex);
11249244b2c3SJohannes Berg 	list_for_each_entry(timer, &snd_timer_list, device_list) {
1125230323daSTakashi Iwai 		if (timer->card && timer->card->shutdown)
1126230323daSTakashi Iwai 			continue;
11271da177e4SLinus Torvalds 		switch (timer->tmr_class) {
11281da177e4SLinus Torvalds 		case SNDRV_TIMER_CLASS_GLOBAL:
11291da177e4SLinus Torvalds 			snd_iprintf(buffer, "G%i: ", timer->tmr_device);
11301da177e4SLinus Torvalds 			break;
11311da177e4SLinus Torvalds 		case SNDRV_TIMER_CLASS_CARD:
11326b172a85SClemens Ladisch 			snd_iprintf(buffer, "C%i-%i: ",
11336b172a85SClemens Ladisch 				    timer->card->number, timer->tmr_device);
11341da177e4SLinus Torvalds 			break;
11351da177e4SLinus Torvalds 		case SNDRV_TIMER_CLASS_PCM:
11366b172a85SClemens Ladisch 			snd_iprintf(buffer, "P%i-%i-%i: ", timer->card->number,
11376b172a85SClemens Ladisch 				    timer->tmr_device, timer->tmr_subdevice);
11381da177e4SLinus Torvalds 			break;
11391da177e4SLinus Torvalds 		default:
11406b172a85SClemens Ladisch 			snd_iprintf(buffer, "?%i-%i-%i-%i: ", timer->tmr_class,
11416b172a85SClemens Ladisch 				    timer->card ? timer->card->number : -1,
11426b172a85SClemens Ladisch 				    timer->tmr_device, timer->tmr_subdevice);
11431da177e4SLinus Torvalds 		}
11441da177e4SLinus Torvalds 		snd_iprintf(buffer, "%s :", timer->name);
11451da177e4SLinus Torvalds 		if (timer->hw.resolution)
11466b172a85SClemens Ladisch 			snd_iprintf(buffer, " %lu.%03luus (%lu ticks)",
11476b172a85SClemens Ladisch 				    timer->hw.resolution / 1000,
11486b172a85SClemens Ladisch 				    timer->hw.resolution % 1000,
11496b172a85SClemens Ladisch 				    timer->hw.ticks);
11501da177e4SLinus Torvalds 		if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE)
11511da177e4SLinus Torvalds 			snd_iprintf(buffer, " SLAVE");
11521da177e4SLinus Torvalds 		snd_iprintf(buffer, "\n");
11539244b2c3SJohannes Berg 		list_for_each_entry(ti, &timer->open_list_head, open_list)
11546b172a85SClemens Ladisch 			snd_iprintf(buffer, "  Client %s : %s\n",
11551da177e4SLinus Torvalds 				    ti->owner ? ti->owner : "unknown",
11566b172a85SClemens Ladisch 				    ti->flags & (SNDRV_TIMER_IFLG_START |
11576b172a85SClemens Ladisch 						 SNDRV_TIMER_IFLG_RUNNING)
11586b172a85SClemens Ladisch 				    ? "running" : "stopped");
11591da177e4SLinus Torvalds 	}
11601a60d4c5SIngo Molnar 	mutex_unlock(&register_mutex);
11611da177e4SLinus Torvalds }
11621da177e4SLinus Torvalds 
11636581f4e7STakashi Iwai static struct snd_info_entry *snd_timer_proc_entry;
1164e28563ccSTakashi Iwai 
1165e28563ccSTakashi Iwai static void __init snd_timer_proc_init(void)
1166e28563ccSTakashi Iwai {
1167e28563ccSTakashi Iwai 	struct snd_info_entry *entry;
1168e28563ccSTakashi Iwai 
1169e28563ccSTakashi Iwai 	entry = snd_info_create_module_entry(THIS_MODULE, "timers", NULL);
1170e28563ccSTakashi Iwai 	if (entry != NULL) {
1171e28563ccSTakashi Iwai 		entry->c.text.read = snd_timer_proc_read;
1172e28563ccSTakashi Iwai 		if (snd_info_register(entry) < 0) {
1173e28563ccSTakashi Iwai 			snd_info_free_entry(entry);
1174e28563ccSTakashi Iwai 			entry = NULL;
1175e28563ccSTakashi Iwai 		}
1176e28563ccSTakashi Iwai 	}
1177e28563ccSTakashi Iwai 	snd_timer_proc_entry = entry;
1178e28563ccSTakashi Iwai }
1179e28563ccSTakashi Iwai 
1180e28563ccSTakashi Iwai static void __exit snd_timer_proc_done(void)
1181e28563ccSTakashi Iwai {
1182746d4a02STakashi Iwai 	snd_info_free_entry(snd_timer_proc_entry);
1183e28563ccSTakashi Iwai }
1184cd6a6503SJie Yang #else /* !CONFIG_SND_PROC_FS */
1185e28563ccSTakashi Iwai #define snd_timer_proc_init()
1186e28563ccSTakashi Iwai #define snd_timer_proc_done()
1187e28563ccSTakashi Iwai #endif
1188e28563ccSTakashi Iwai 
11891da177e4SLinus Torvalds /*
11901da177e4SLinus Torvalds  *  USER SPACE interface
11911da177e4SLinus Torvalds  */
11921da177e4SLinus Torvalds 
119353d2f744STakashi Iwai static void snd_timer_user_interrupt(struct snd_timer_instance *timeri,
11941da177e4SLinus Torvalds 				     unsigned long resolution,
11951da177e4SLinus Torvalds 				     unsigned long ticks)
11961da177e4SLinus Torvalds {
119753d2f744STakashi Iwai 	struct snd_timer_user *tu = timeri->callback_data;
119853d2f744STakashi Iwai 	struct snd_timer_read *r;
11991da177e4SLinus Torvalds 	int prev;
12001da177e4SLinus Torvalds 
12011da177e4SLinus Torvalds 	spin_lock(&tu->qlock);
12021da177e4SLinus Torvalds 	if (tu->qused > 0) {
12031da177e4SLinus Torvalds 		prev = tu->qtail == 0 ? tu->queue_size - 1 : tu->qtail - 1;
12041da177e4SLinus Torvalds 		r = &tu->queue[prev];
12051da177e4SLinus Torvalds 		if (r->resolution == resolution) {
12061da177e4SLinus Torvalds 			r->ticks += ticks;
12071da177e4SLinus Torvalds 			goto __wake;
12081da177e4SLinus Torvalds 		}
12091da177e4SLinus Torvalds 	}
12101da177e4SLinus Torvalds 	if (tu->qused >= tu->queue_size) {
12111da177e4SLinus Torvalds 		tu->overrun++;
12121da177e4SLinus Torvalds 	} else {
12131da177e4SLinus Torvalds 		r = &tu->queue[tu->qtail++];
12141da177e4SLinus Torvalds 		tu->qtail %= tu->queue_size;
12151da177e4SLinus Torvalds 		r->resolution = resolution;
12161da177e4SLinus Torvalds 		r->ticks = ticks;
12171da177e4SLinus Torvalds 		tu->qused++;
12181da177e4SLinus Torvalds 	}
12191da177e4SLinus Torvalds       __wake:
12201da177e4SLinus Torvalds 	spin_unlock(&tu->qlock);
12211da177e4SLinus Torvalds 	kill_fasync(&tu->fasync, SIGIO, POLL_IN);
12221da177e4SLinus Torvalds 	wake_up(&tu->qchange_sleep);
12231da177e4SLinus Torvalds }
12241da177e4SLinus Torvalds 
122553d2f744STakashi Iwai static void snd_timer_user_append_to_tqueue(struct snd_timer_user *tu,
122653d2f744STakashi Iwai 					    struct snd_timer_tread *tread)
12271da177e4SLinus Torvalds {
12281da177e4SLinus Torvalds 	if (tu->qused >= tu->queue_size) {
12291da177e4SLinus Torvalds 		tu->overrun++;
12301da177e4SLinus Torvalds 	} else {
12311da177e4SLinus Torvalds 		memcpy(&tu->tqueue[tu->qtail++], tread, sizeof(*tread));
12321da177e4SLinus Torvalds 		tu->qtail %= tu->queue_size;
12331da177e4SLinus Torvalds 		tu->qused++;
12341da177e4SLinus Torvalds 	}
12351da177e4SLinus Torvalds }
12361da177e4SLinus Torvalds 
123753d2f744STakashi Iwai static void snd_timer_user_ccallback(struct snd_timer_instance *timeri,
123853d2f744STakashi Iwai 				     int event,
12391da177e4SLinus Torvalds 				     struct timespec *tstamp,
12401da177e4SLinus Torvalds 				     unsigned long resolution)
12411da177e4SLinus Torvalds {
124253d2f744STakashi Iwai 	struct snd_timer_user *tu = timeri->callback_data;
124353d2f744STakashi Iwai 	struct snd_timer_tread r1;
1244bfe70783SDan Carpenter 	unsigned long flags;
12451da177e4SLinus Torvalds 
12466b172a85SClemens Ladisch 	if (event >= SNDRV_TIMER_EVENT_START &&
12476b172a85SClemens Ladisch 	    event <= SNDRV_TIMER_EVENT_PAUSE)
12481da177e4SLinus Torvalds 		tu->tstamp = *tstamp;
12491da177e4SLinus Torvalds 	if ((tu->filter & (1 << event)) == 0 || !tu->tread)
12501da177e4SLinus Torvalds 		return;
12519a47e9cfSKangjie Lu 	memset(&r1, 0, sizeof(r1));
12521da177e4SLinus Torvalds 	r1.event = event;
12531da177e4SLinus Torvalds 	r1.tstamp = *tstamp;
12541da177e4SLinus Torvalds 	r1.val = resolution;
1255bfe70783SDan Carpenter 	spin_lock_irqsave(&tu->qlock, flags);
12561da177e4SLinus Torvalds 	snd_timer_user_append_to_tqueue(tu, &r1);
1257bfe70783SDan Carpenter 	spin_unlock_irqrestore(&tu->qlock, flags);
12581da177e4SLinus Torvalds 	kill_fasync(&tu->fasync, SIGIO, POLL_IN);
12591da177e4SLinus Torvalds 	wake_up(&tu->qchange_sleep);
12601da177e4SLinus Torvalds }
12611da177e4SLinus Torvalds 
126240ed9444STakashi Iwai static void snd_timer_user_disconnect(struct snd_timer_instance *timeri)
126340ed9444STakashi Iwai {
126440ed9444STakashi Iwai 	struct snd_timer_user *tu = timeri->callback_data;
126540ed9444STakashi Iwai 
126640ed9444STakashi Iwai 	tu->disconnected = true;
126740ed9444STakashi Iwai 	wake_up(&tu->qchange_sleep);
126840ed9444STakashi Iwai }
126940ed9444STakashi Iwai 
127053d2f744STakashi Iwai static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri,
12711da177e4SLinus Torvalds 				      unsigned long resolution,
12721da177e4SLinus Torvalds 				      unsigned long ticks)
12731da177e4SLinus Torvalds {
127453d2f744STakashi Iwai 	struct snd_timer_user *tu = timeri->callback_data;
127553d2f744STakashi Iwai 	struct snd_timer_tread *r, r1;
12761da177e4SLinus Torvalds 	struct timespec tstamp;
12771da177e4SLinus Torvalds 	int prev, append = 0;
12781da177e4SLinus Torvalds 
127907799e75STakashi Iwai 	memset(&tstamp, 0, sizeof(tstamp));
12801da177e4SLinus Torvalds 	spin_lock(&tu->qlock);
12816b172a85SClemens Ladisch 	if ((tu->filter & ((1 << SNDRV_TIMER_EVENT_RESOLUTION) |
12826b172a85SClemens Ladisch 			   (1 << SNDRV_TIMER_EVENT_TICK))) == 0) {
12831da177e4SLinus Torvalds 		spin_unlock(&tu->qlock);
12841da177e4SLinus Torvalds 		return;
12851da177e4SLinus Torvalds 	}
1286b751eef1SJaroslav Kysela 	if (tu->last_resolution != resolution || ticks > 0) {
1287b751eef1SJaroslav Kysela 		if (timer_tstamp_monotonic)
128826204e04SThomas Gleixner 			ktime_get_ts(&tstamp);
1289b751eef1SJaroslav Kysela 		else
129007799e75STakashi Iwai 			getnstimeofday(&tstamp);
1291b751eef1SJaroslav Kysela 	}
12926b172a85SClemens Ladisch 	if ((tu->filter & (1 << SNDRV_TIMER_EVENT_RESOLUTION)) &&
12936b172a85SClemens Ladisch 	    tu->last_resolution != resolution) {
1294e4ec8cc8SKangjie Lu 		memset(&r1, 0, sizeof(r1));
12951da177e4SLinus Torvalds 		r1.event = SNDRV_TIMER_EVENT_RESOLUTION;
12961da177e4SLinus Torvalds 		r1.tstamp = tstamp;
12971da177e4SLinus Torvalds 		r1.val = resolution;
12981da177e4SLinus Torvalds 		snd_timer_user_append_to_tqueue(tu, &r1);
12991da177e4SLinus Torvalds 		tu->last_resolution = resolution;
13001da177e4SLinus Torvalds 		append++;
13011da177e4SLinus Torvalds 	}
13021da177e4SLinus Torvalds 	if ((tu->filter & (1 << SNDRV_TIMER_EVENT_TICK)) == 0)
13031da177e4SLinus Torvalds 		goto __wake;
13041da177e4SLinus Torvalds 	if (ticks == 0)
13051da177e4SLinus Torvalds 		goto __wake;
13061da177e4SLinus Torvalds 	if (tu->qused > 0) {
13071da177e4SLinus Torvalds 		prev = tu->qtail == 0 ? tu->queue_size - 1 : tu->qtail - 1;
13081da177e4SLinus Torvalds 		r = &tu->tqueue[prev];
13091da177e4SLinus Torvalds 		if (r->event == SNDRV_TIMER_EVENT_TICK) {
13101da177e4SLinus Torvalds 			r->tstamp = tstamp;
13111da177e4SLinus Torvalds 			r->val += ticks;
13121da177e4SLinus Torvalds 			append++;
13131da177e4SLinus Torvalds 			goto __wake;
13141da177e4SLinus Torvalds 		}
13151da177e4SLinus Torvalds 	}
13161da177e4SLinus Torvalds 	r1.event = SNDRV_TIMER_EVENT_TICK;
13171da177e4SLinus Torvalds 	r1.tstamp = tstamp;
13181da177e4SLinus Torvalds 	r1.val = ticks;
13191da177e4SLinus Torvalds 	snd_timer_user_append_to_tqueue(tu, &r1);
13201da177e4SLinus Torvalds 	append++;
13211da177e4SLinus Torvalds       __wake:
13221da177e4SLinus Torvalds 	spin_unlock(&tu->qlock);
13231da177e4SLinus Torvalds 	if (append == 0)
13241da177e4SLinus Torvalds 		return;
13251da177e4SLinus Torvalds 	kill_fasync(&tu->fasync, SIGIO, POLL_IN);
13261da177e4SLinus Torvalds 	wake_up(&tu->qchange_sleep);
13271da177e4SLinus Torvalds }
13281da177e4SLinus Torvalds 
13291da177e4SLinus Torvalds static int snd_timer_user_open(struct inode *inode, struct file *file)
13301da177e4SLinus Torvalds {
133153d2f744STakashi Iwai 	struct snd_timer_user *tu;
133202f4865fSTakashi Iwai 	int err;
133302f4865fSTakashi Iwai 
133402f4865fSTakashi Iwai 	err = nonseekable_open(inode, file);
133502f4865fSTakashi Iwai 	if (err < 0)
133602f4865fSTakashi Iwai 		return err;
13371da177e4SLinus Torvalds 
1338ca2c0966STakashi Iwai 	tu = kzalloc(sizeof(*tu), GFP_KERNEL);
13391da177e4SLinus Torvalds 	if (tu == NULL)
13401da177e4SLinus Torvalds 		return -ENOMEM;
13411da177e4SLinus Torvalds 	spin_lock_init(&tu->qlock);
13421da177e4SLinus Torvalds 	init_waitqueue_head(&tu->qchange_sleep);
1343af368027STakashi Iwai 	mutex_init(&tu->ioctl_lock);
13441da177e4SLinus Torvalds 	tu->ticks = 1;
13451da177e4SLinus Torvalds 	tu->queue_size = 128;
134653d2f744STakashi Iwai 	tu->queue = kmalloc(tu->queue_size * sizeof(struct snd_timer_read),
13476b172a85SClemens Ladisch 			    GFP_KERNEL);
13481da177e4SLinus Torvalds 	if (tu->queue == NULL) {
13491da177e4SLinus Torvalds 		kfree(tu);
13501da177e4SLinus Torvalds 		return -ENOMEM;
13511da177e4SLinus Torvalds 	}
13521da177e4SLinus Torvalds 	file->private_data = tu;
13531da177e4SLinus Torvalds 	return 0;
13541da177e4SLinus Torvalds }
13551da177e4SLinus Torvalds 
13561da177e4SLinus Torvalds static int snd_timer_user_release(struct inode *inode, struct file *file)
13571da177e4SLinus Torvalds {
135853d2f744STakashi Iwai 	struct snd_timer_user *tu;
13591da177e4SLinus Torvalds 
13601da177e4SLinus Torvalds 	if (file->private_data) {
13611da177e4SLinus Torvalds 		tu = file->private_data;
13621da177e4SLinus Torvalds 		file->private_data = NULL;
1363af368027STakashi Iwai 		mutex_lock(&tu->ioctl_lock);
13641da177e4SLinus Torvalds 		if (tu->timeri)
13651da177e4SLinus Torvalds 			snd_timer_close(tu->timeri);
1366af368027STakashi Iwai 		mutex_unlock(&tu->ioctl_lock);
13671da177e4SLinus Torvalds 		kfree(tu->queue);
13681da177e4SLinus Torvalds 		kfree(tu->tqueue);
13691da177e4SLinus Torvalds 		kfree(tu);
13701da177e4SLinus Torvalds 	}
13711da177e4SLinus Torvalds 	return 0;
13721da177e4SLinus Torvalds }
13731da177e4SLinus Torvalds 
137453d2f744STakashi Iwai static void snd_timer_user_zero_id(struct snd_timer_id *id)
13751da177e4SLinus Torvalds {
13761da177e4SLinus Torvalds 	id->dev_class = SNDRV_TIMER_CLASS_NONE;
13771da177e4SLinus Torvalds 	id->dev_sclass = SNDRV_TIMER_SCLASS_NONE;
13781da177e4SLinus Torvalds 	id->card = -1;
13791da177e4SLinus Torvalds 	id->device = -1;
13801da177e4SLinus Torvalds 	id->subdevice = -1;
13811da177e4SLinus Torvalds }
13821da177e4SLinus Torvalds 
138353d2f744STakashi Iwai static void snd_timer_user_copy_id(struct snd_timer_id *id, struct snd_timer *timer)
13841da177e4SLinus Torvalds {
13851da177e4SLinus Torvalds 	id->dev_class = timer->tmr_class;
13861da177e4SLinus Torvalds 	id->dev_sclass = SNDRV_TIMER_SCLASS_NONE;
13871da177e4SLinus Torvalds 	id->card = timer->card ? timer->card->number : -1;
13881da177e4SLinus Torvalds 	id->device = timer->tmr_device;
13891da177e4SLinus Torvalds 	id->subdevice = timer->tmr_subdevice;
13901da177e4SLinus Torvalds }
13911da177e4SLinus Torvalds 
139253d2f744STakashi Iwai static int snd_timer_user_next_device(struct snd_timer_id __user *_tid)
13931da177e4SLinus Torvalds {
139453d2f744STakashi Iwai 	struct snd_timer_id id;
139553d2f744STakashi Iwai 	struct snd_timer *timer;
13961da177e4SLinus Torvalds 	struct list_head *p;
13971da177e4SLinus Torvalds 
13981da177e4SLinus Torvalds 	if (copy_from_user(&id, _tid, sizeof(id)))
13991da177e4SLinus Torvalds 		return -EFAULT;
14001a60d4c5SIngo Molnar 	mutex_lock(&register_mutex);
14011da177e4SLinus Torvalds 	if (id.dev_class < 0) {		/* first item */
14021da177e4SLinus Torvalds 		if (list_empty(&snd_timer_list))
14031da177e4SLinus Torvalds 			snd_timer_user_zero_id(&id);
14041da177e4SLinus Torvalds 		else {
14059dfba380SClemens Ladisch 			timer = list_entry(snd_timer_list.next,
140653d2f744STakashi Iwai 					   struct snd_timer, device_list);
14071da177e4SLinus Torvalds 			snd_timer_user_copy_id(&id, timer);
14081da177e4SLinus Torvalds 		}
14091da177e4SLinus Torvalds 	} else {
14101da177e4SLinus Torvalds 		switch (id.dev_class) {
14111da177e4SLinus Torvalds 		case SNDRV_TIMER_CLASS_GLOBAL:
14121da177e4SLinus Torvalds 			id.device = id.device < 0 ? 0 : id.device + 1;
14131da177e4SLinus Torvalds 			list_for_each(p, &snd_timer_list) {
141453d2f744STakashi Iwai 				timer = list_entry(p, struct snd_timer, device_list);
14151da177e4SLinus Torvalds 				if (timer->tmr_class > SNDRV_TIMER_CLASS_GLOBAL) {
14161da177e4SLinus Torvalds 					snd_timer_user_copy_id(&id, timer);
14171da177e4SLinus Torvalds 					break;
14181da177e4SLinus Torvalds 				}
14191da177e4SLinus Torvalds 				if (timer->tmr_device >= id.device) {
14201da177e4SLinus Torvalds 					snd_timer_user_copy_id(&id, timer);
14211da177e4SLinus Torvalds 					break;
14221da177e4SLinus Torvalds 				}
14231da177e4SLinus Torvalds 			}
14241da177e4SLinus Torvalds 			if (p == &snd_timer_list)
14251da177e4SLinus Torvalds 				snd_timer_user_zero_id(&id);
14261da177e4SLinus Torvalds 			break;
14271da177e4SLinus Torvalds 		case SNDRV_TIMER_CLASS_CARD:
14281da177e4SLinus Torvalds 		case SNDRV_TIMER_CLASS_PCM:
14291da177e4SLinus Torvalds 			if (id.card < 0) {
14301da177e4SLinus Torvalds 				id.card = 0;
14311da177e4SLinus Torvalds 			} else {
14321da177e4SLinus Torvalds 				if (id.card < 0) {
14331da177e4SLinus Torvalds 					id.card = 0;
14341da177e4SLinus Torvalds 				} else {
14351da177e4SLinus Torvalds 					if (id.device < 0) {
14361da177e4SLinus Torvalds 						id.device = 0;
14371da177e4SLinus Torvalds 					} else {
14386b172a85SClemens Ladisch 						if (id.subdevice < 0) {
14396b172a85SClemens Ladisch 							id.subdevice = 0;
14406b172a85SClemens Ladisch 						} else {
14416b172a85SClemens Ladisch 							id.subdevice++;
14426b172a85SClemens Ladisch 						}
14431da177e4SLinus Torvalds 					}
14441da177e4SLinus Torvalds 				}
14451da177e4SLinus Torvalds 			}
14461da177e4SLinus Torvalds 			list_for_each(p, &snd_timer_list) {
144753d2f744STakashi Iwai 				timer = list_entry(p, struct snd_timer, device_list);
14481da177e4SLinus Torvalds 				if (timer->tmr_class > id.dev_class) {
14491da177e4SLinus Torvalds 					snd_timer_user_copy_id(&id, timer);
14501da177e4SLinus Torvalds 					break;
14511da177e4SLinus Torvalds 				}
14521da177e4SLinus Torvalds 				if (timer->tmr_class < id.dev_class)
14531da177e4SLinus Torvalds 					continue;
14541da177e4SLinus Torvalds 				if (timer->card->number > id.card) {
14551da177e4SLinus Torvalds 					snd_timer_user_copy_id(&id, timer);
14561da177e4SLinus Torvalds 					break;
14571da177e4SLinus Torvalds 				}
14581da177e4SLinus Torvalds 				if (timer->card->number < id.card)
14591da177e4SLinus Torvalds 					continue;
14601da177e4SLinus Torvalds 				if (timer->tmr_device > id.device) {
14611da177e4SLinus Torvalds 					snd_timer_user_copy_id(&id, timer);
14621da177e4SLinus Torvalds 					break;
14631da177e4SLinus Torvalds 				}
14641da177e4SLinus Torvalds 				if (timer->tmr_device < id.device)
14651da177e4SLinus Torvalds 					continue;
14661da177e4SLinus Torvalds 				if (timer->tmr_subdevice > id.subdevice) {
14671da177e4SLinus Torvalds 					snd_timer_user_copy_id(&id, timer);
14681da177e4SLinus Torvalds 					break;
14691da177e4SLinus Torvalds 				}
14701da177e4SLinus Torvalds 				if (timer->tmr_subdevice < id.subdevice)
14711da177e4SLinus Torvalds 					continue;
14721da177e4SLinus Torvalds 				snd_timer_user_copy_id(&id, timer);
14731da177e4SLinus Torvalds 				break;
14741da177e4SLinus Torvalds 			}
14751da177e4SLinus Torvalds 			if (p == &snd_timer_list)
14761da177e4SLinus Torvalds 				snd_timer_user_zero_id(&id);
14771da177e4SLinus Torvalds 			break;
14781da177e4SLinus Torvalds 		default:
14791da177e4SLinus Torvalds 			snd_timer_user_zero_id(&id);
14801da177e4SLinus Torvalds 		}
14811da177e4SLinus Torvalds 	}
14821a60d4c5SIngo Molnar 	mutex_unlock(&register_mutex);
14831da177e4SLinus Torvalds 	if (copy_to_user(_tid, &id, sizeof(*_tid)))
14841da177e4SLinus Torvalds 		return -EFAULT;
14851da177e4SLinus Torvalds 	return 0;
14861da177e4SLinus Torvalds }
14871da177e4SLinus Torvalds 
14886b172a85SClemens Ladisch static int snd_timer_user_ginfo(struct file *file,
148953d2f744STakashi Iwai 				struct snd_timer_ginfo __user *_ginfo)
14901da177e4SLinus Torvalds {
149153d2f744STakashi Iwai 	struct snd_timer_ginfo *ginfo;
149253d2f744STakashi Iwai 	struct snd_timer_id tid;
149353d2f744STakashi Iwai 	struct snd_timer *t;
14941da177e4SLinus Torvalds 	struct list_head *p;
14951da177e4SLinus Torvalds 	int err = 0;
14961da177e4SLinus Torvalds 
1497ef44a1ecSLi Zefan 	ginfo = memdup_user(_ginfo, sizeof(*ginfo));
1498ef44a1ecSLi Zefan 	if (IS_ERR(ginfo))
1499ef44a1ecSLi Zefan 		return PTR_ERR(ginfo);
1500ef44a1ecSLi Zefan 
15011da177e4SLinus Torvalds 	tid = ginfo->tid;
15021da177e4SLinus Torvalds 	memset(ginfo, 0, sizeof(*ginfo));
15031da177e4SLinus Torvalds 	ginfo->tid = tid;
15041a60d4c5SIngo Molnar 	mutex_lock(&register_mutex);
15051da177e4SLinus Torvalds 	t = snd_timer_find(&tid);
15061da177e4SLinus Torvalds 	if (t != NULL) {
15071da177e4SLinus Torvalds 		ginfo->card = t->card ? t->card->number : -1;
15081da177e4SLinus Torvalds 		if (t->hw.flags & SNDRV_TIMER_HW_SLAVE)
15091da177e4SLinus Torvalds 			ginfo->flags |= SNDRV_TIMER_FLG_SLAVE;
15101da177e4SLinus Torvalds 		strlcpy(ginfo->id, t->id, sizeof(ginfo->id));
15111da177e4SLinus Torvalds 		strlcpy(ginfo->name, t->name, sizeof(ginfo->name));
15121da177e4SLinus Torvalds 		ginfo->resolution = t->hw.resolution;
15131da177e4SLinus Torvalds 		if (t->hw.resolution_min > 0) {
15141da177e4SLinus Torvalds 			ginfo->resolution_min = t->hw.resolution_min;
15151da177e4SLinus Torvalds 			ginfo->resolution_max = t->hw.resolution_max;
15161da177e4SLinus Torvalds 		}
15171da177e4SLinus Torvalds 		list_for_each(p, &t->open_list_head) {
15181da177e4SLinus Torvalds 			ginfo->clients++;
15191da177e4SLinus Torvalds 		}
15201da177e4SLinus Torvalds 	} else {
15211da177e4SLinus Torvalds 		err = -ENODEV;
15221da177e4SLinus Torvalds 	}
15231a60d4c5SIngo Molnar 	mutex_unlock(&register_mutex);
15241da177e4SLinus Torvalds 	if (err >= 0 && copy_to_user(_ginfo, ginfo, sizeof(*ginfo)))
15251da177e4SLinus Torvalds 		err = -EFAULT;
15261da177e4SLinus Torvalds 	kfree(ginfo);
15271da177e4SLinus Torvalds 	return err;
15281da177e4SLinus Torvalds }
15291da177e4SLinus Torvalds 
153091d2178eSTakashi Sakamoto static int timer_set_gparams(struct snd_timer_gparams *gparams)
15311da177e4SLinus Torvalds {
153253d2f744STakashi Iwai 	struct snd_timer *t;
15331da177e4SLinus Torvalds 	int err;
15341da177e4SLinus Torvalds 
15351a60d4c5SIngo Molnar 	mutex_lock(&register_mutex);
153691d2178eSTakashi Sakamoto 	t = snd_timer_find(&gparams->tid);
15376b172a85SClemens Ladisch 	if (!t) {
15381da177e4SLinus Torvalds 		err = -ENODEV;
15396b172a85SClemens Ladisch 		goto _error;
15401da177e4SLinus Torvalds 	}
15416b172a85SClemens Ladisch 	if (!list_empty(&t->open_list_head)) {
15426b172a85SClemens Ladisch 		err = -EBUSY;
15436b172a85SClemens Ladisch 		goto _error;
15446b172a85SClemens Ladisch 	}
15456b172a85SClemens Ladisch 	if (!t->hw.set_period) {
15466b172a85SClemens Ladisch 		err = -ENOSYS;
15476b172a85SClemens Ladisch 		goto _error;
15486b172a85SClemens Ladisch 	}
154991d2178eSTakashi Sakamoto 	err = t->hw.set_period(t, gparams->period_num, gparams->period_den);
15506b172a85SClemens Ladisch _error:
15511a60d4c5SIngo Molnar 	mutex_unlock(&register_mutex);
15521da177e4SLinus Torvalds 	return err;
15531da177e4SLinus Torvalds }
15541da177e4SLinus Torvalds 
155591d2178eSTakashi Sakamoto static int snd_timer_user_gparams(struct file *file,
155691d2178eSTakashi Sakamoto 				  struct snd_timer_gparams __user *_gparams)
155791d2178eSTakashi Sakamoto {
155891d2178eSTakashi Sakamoto 	struct snd_timer_gparams gparams;
155991d2178eSTakashi Sakamoto 
156091d2178eSTakashi Sakamoto 	if (copy_from_user(&gparams, _gparams, sizeof(gparams)))
156191d2178eSTakashi Sakamoto 		return -EFAULT;
156291d2178eSTakashi Sakamoto 	return timer_set_gparams(&gparams);
156391d2178eSTakashi Sakamoto }
156491d2178eSTakashi Sakamoto 
15656b172a85SClemens Ladisch static int snd_timer_user_gstatus(struct file *file,
156653d2f744STakashi Iwai 				  struct snd_timer_gstatus __user *_gstatus)
15671da177e4SLinus Torvalds {
156853d2f744STakashi Iwai 	struct snd_timer_gstatus gstatus;
156953d2f744STakashi Iwai 	struct snd_timer_id tid;
157053d2f744STakashi Iwai 	struct snd_timer *t;
15711da177e4SLinus Torvalds 	int err = 0;
15721da177e4SLinus Torvalds 
15731da177e4SLinus Torvalds 	if (copy_from_user(&gstatus, _gstatus, sizeof(gstatus)))
15741da177e4SLinus Torvalds 		return -EFAULT;
15751da177e4SLinus Torvalds 	tid = gstatus.tid;
15761da177e4SLinus Torvalds 	memset(&gstatus, 0, sizeof(gstatus));
15771da177e4SLinus Torvalds 	gstatus.tid = tid;
15781a60d4c5SIngo Molnar 	mutex_lock(&register_mutex);
15791da177e4SLinus Torvalds 	t = snd_timer_find(&tid);
15801da177e4SLinus Torvalds 	if (t != NULL) {
15811da177e4SLinus Torvalds 		if (t->hw.c_resolution)
15821da177e4SLinus Torvalds 			gstatus.resolution = t->hw.c_resolution(t);
15831da177e4SLinus Torvalds 		else
15841da177e4SLinus Torvalds 			gstatus.resolution = t->hw.resolution;
15851da177e4SLinus Torvalds 		if (t->hw.precise_resolution) {
15866b172a85SClemens Ladisch 			t->hw.precise_resolution(t, &gstatus.resolution_num,
15876b172a85SClemens Ladisch 						 &gstatus.resolution_den);
15881da177e4SLinus Torvalds 		} else {
15891da177e4SLinus Torvalds 			gstatus.resolution_num = gstatus.resolution;
15901da177e4SLinus Torvalds 			gstatus.resolution_den = 1000000000uL;
15911da177e4SLinus Torvalds 		}
15921da177e4SLinus Torvalds 	} else {
15931da177e4SLinus Torvalds 		err = -ENODEV;
15941da177e4SLinus Torvalds 	}
15951a60d4c5SIngo Molnar 	mutex_unlock(&register_mutex);
15961da177e4SLinus Torvalds 	if (err >= 0 && copy_to_user(_gstatus, &gstatus, sizeof(gstatus)))
15971da177e4SLinus Torvalds 		err = -EFAULT;
15981da177e4SLinus Torvalds 	return err;
15991da177e4SLinus Torvalds }
16001da177e4SLinus Torvalds 
16016b172a85SClemens Ladisch static int snd_timer_user_tselect(struct file *file,
160253d2f744STakashi Iwai 				  struct snd_timer_select __user *_tselect)
16031da177e4SLinus Torvalds {
160453d2f744STakashi Iwai 	struct snd_timer_user *tu;
160553d2f744STakashi Iwai 	struct snd_timer_select tselect;
16061da177e4SLinus Torvalds 	char str[32];
1607c1935b4dSJaroslav Kysela 	int err = 0;
16081da177e4SLinus Torvalds 
16091da177e4SLinus Torvalds 	tu = file->private_data;
1610c1935b4dSJaroslav Kysela 	if (tu->timeri) {
16111da177e4SLinus Torvalds 		snd_timer_close(tu->timeri);
1612c1935b4dSJaroslav Kysela 		tu->timeri = NULL;
1613c1935b4dSJaroslav Kysela 	}
1614c1935b4dSJaroslav Kysela 	if (copy_from_user(&tselect, _tselect, sizeof(tselect))) {
1615c1935b4dSJaroslav Kysela 		err = -EFAULT;
1616c1935b4dSJaroslav Kysela 		goto __err;
1617c1935b4dSJaroslav Kysela 	}
16181da177e4SLinus Torvalds 	sprintf(str, "application %i", current->pid);
16191da177e4SLinus Torvalds 	if (tselect.id.dev_class != SNDRV_TIMER_CLASS_SLAVE)
16201da177e4SLinus Torvalds 		tselect.id.dev_sclass = SNDRV_TIMER_SCLASS_APPLICATION;
16216b172a85SClemens Ladisch 	err = snd_timer_open(&tu->timeri, str, &tselect.id, current->pid);
16226b172a85SClemens Ladisch 	if (err < 0)
1623c1935b4dSJaroslav Kysela 		goto __err;
16241da177e4SLinus Torvalds 
16251da177e4SLinus Torvalds 	kfree(tu->queue);
16261da177e4SLinus Torvalds 	tu->queue = NULL;
16271da177e4SLinus Torvalds 	kfree(tu->tqueue);
16281da177e4SLinus Torvalds 	tu->tqueue = NULL;
16291da177e4SLinus Torvalds 	if (tu->tread) {
163053d2f744STakashi Iwai 		tu->tqueue = kmalloc(tu->queue_size * sizeof(struct snd_timer_tread),
16316b172a85SClemens Ladisch 				     GFP_KERNEL);
1632c1935b4dSJaroslav Kysela 		if (tu->tqueue == NULL)
1633c1935b4dSJaroslav Kysela 			err = -ENOMEM;
16341da177e4SLinus Torvalds 	} else {
163553d2f744STakashi Iwai 		tu->queue = kmalloc(tu->queue_size * sizeof(struct snd_timer_read),
16366b172a85SClemens Ladisch 				    GFP_KERNEL);
1637c1935b4dSJaroslav Kysela 		if (tu->queue == NULL)
1638c1935b4dSJaroslav Kysela 			err = -ENOMEM;
16391da177e4SLinus Torvalds 	}
16401da177e4SLinus Torvalds 
1641c1935b4dSJaroslav Kysela       	if (err < 0) {
1642c1935b4dSJaroslav Kysela 		snd_timer_close(tu->timeri);
1643c1935b4dSJaroslav Kysela       		tu->timeri = NULL;
1644c1935b4dSJaroslav Kysela       	} else {
16451da177e4SLinus Torvalds 		tu->timeri->flags |= SNDRV_TIMER_IFLG_FAST;
16466b172a85SClemens Ladisch 		tu->timeri->callback = tu->tread
16476b172a85SClemens Ladisch 			? snd_timer_user_tinterrupt : snd_timer_user_interrupt;
16481da177e4SLinus Torvalds 		tu->timeri->ccallback = snd_timer_user_ccallback;
16491da177e4SLinus Torvalds 		tu->timeri->callback_data = (void *)tu;
165040ed9444STakashi Iwai 		tu->timeri->disconnect = snd_timer_user_disconnect;
1651c1935b4dSJaroslav Kysela 	}
1652c1935b4dSJaroslav Kysela 
1653c1935b4dSJaroslav Kysela       __err:
1654c1935b4dSJaroslav Kysela 	return err;
16551da177e4SLinus Torvalds }
16561da177e4SLinus Torvalds 
16576b172a85SClemens Ladisch static int snd_timer_user_info(struct file *file,
165853d2f744STakashi Iwai 			       struct snd_timer_info __user *_info)
16591da177e4SLinus Torvalds {
166053d2f744STakashi Iwai 	struct snd_timer_user *tu;
166153d2f744STakashi Iwai 	struct snd_timer_info *info;
166253d2f744STakashi Iwai 	struct snd_timer *t;
16631da177e4SLinus Torvalds 	int err = 0;
16641da177e4SLinus Torvalds 
16651da177e4SLinus Torvalds 	tu = file->private_data;
16667c64ec34SClemens Ladisch 	if (!tu->timeri)
16677c64ec34SClemens Ladisch 		return -EBADFD;
16681da177e4SLinus Torvalds 	t = tu->timeri->timer;
16697c64ec34SClemens Ladisch 	if (!t)
16707c64ec34SClemens Ladisch 		return -EBADFD;
16711da177e4SLinus Torvalds 
1672ca2c0966STakashi Iwai 	info = kzalloc(sizeof(*info), GFP_KERNEL);
16731da177e4SLinus Torvalds 	if (! info)
16741da177e4SLinus Torvalds 		return -ENOMEM;
16751da177e4SLinus Torvalds 	info->card = t->card ? t->card->number : -1;
16761da177e4SLinus Torvalds 	if (t->hw.flags & SNDRV_TIMER_HW_SLAVE)
16771da177e4SLinus Torvalds 		info->flags |= SNDRV_TIMER_FLG_SLAVE;
16781da177e4SLinus Torvalds 	strlcpy(info->id, t->id, sizeof(info->id));
16791da177e4SLinus Torvalds 	strlcpy(info->name, t->name, sizeof(info->name));
16801da177e4SLinus Torvalds 	info->resolution = t->hw.resolution;
16811da177e4SLinus Torvalds 	if (copy_to_user(_info, info, sizeof(*_info)))
16821da177e4SLinus Torvalds 		err = -EFAULT;
16831da177e4SLinus Torvalds 	kfree(info);
16841da177e4SLinus Torvalds 	return err;
16851da177e4SLinus Torvalds }
16861da177e4SLinus Torvalds 
16876b172a85SClemens Ladisch static int snd_timer_user_params(struct file *file,
168853d2f744STakashi Iwai 				 struct snd_timer_params __user *_params)
16891da177e4SLinus Torvalds {
169053d2f744STakashi Iwai 	struct snd_timer_user *tu;
169153d2f744STakashi Iwai 	struct snd_timer_params params;
169253d2f744STakashi Iwai 	struct snd_timer *t;
169353d2f744STakashi Iwai 	struct snd_timer_read *tr;
169453d2f744STakashi Iwai 	struct snd_timer_tread *ttr;
16951da177e4SLinus Torvalds 	int err;
16961da177e4SLinus Torvalds 
16971da177e4SLinus Torvalds 	tu = file->private_data;
16987c64ec34SClemens Ladisch 	if (!tu->timeri)
16997c64ec34SClemens Ladisch 		return -EBADFD;
17001da177e4SLinus Torvalds 	t = tu->timeri->timer;
17017c64ec34SClemens Ladisch 	if (!t)
17027c64ec34SClemens Ladisch 		return -EBADFD;
17031da177e4SLinus Torvalds 	if (copy_from_user(&params, _params, sizeof(params)))
17041da177e4SLinus Torvalds 		return -EFAULT;
1705*71321eb3STakashi Iwai 	if (!(t->hw.flags & SNDRV_TIMER_HW_SLAVE)) {
1706*71321eb3STakashi Iwai 		u64 resolution;
1707*71321eb3STakashi Iwai 
1708*71321eb3STakashi Iwai 		if (params.ticks < 1) {
17091da177e4SLinus Torvalds 			err = -EINVAL;
17101da177e4SLinus Torvalds 			goto _end;
17111da177e4SLinus Torvalds 		}
1712*71321eb3STakashi Iwai 
1713*71321eb3STakashi Iwai 		/* Don't allow resolution less than 1ms */
1714*71321eb3STakashi Iwai 		resolution = snd_timer_resolution(tu->timeri);
1715*71321eb3STakashi Iwai 		resolution *= params.ticks;
1716*71321eb3STakashi Iwai 		if (resolution < 1000000) {
1717*71321eb3STakashi Iwai 			err = -EINVAL;
1718*71321eb3STakashi Iwai 			goto _end;
1719*71321eb3STakashi Iwai 		}
1720*71321eb3STakashi Iwai 	}
17216b172a85SClemens Ladisch 	if (params.queue_size > 0 &&
17226b172a85SClemens Ladisch 	    (params.queue_size < 32 || params.queue_size > 1024)) {
17231da177e4SLinus Torvalds 		err = -EINVAL;
17241da177e4SLinus Torvalds 		goto _end;
17251da177e4SLinus Torvalds 	}
17261da177e4SLinus Torvalds 	if (params.filter & ~((1<<SNDRV_TIMER_EVENT_RESOLUTION)|
17271da177e4SLinus Torvalds 			      (1<<SNDRV_TIMER_EVENT_TICK)|
17281da177e4SLinus Torvalds 			      (1<<SNDRV_TIMER_EVENT_START)|
17291da177e4SLinus Torvalds 			      (1<<SNDRV_TIMER_EVENT_STOP)|
17301da177e4SLinus Torvalds 			      (1<<SNDRV_TIMER_EVENT_CONTINUE)|
17311da177e4SLinus Torvalds 			      (1<<SNDRV_TIMER_EVENT_PAUSE)|
1732a501dfa3SJaroslav Kysela 			      (1<<SNDRV_TIMER_EVENT_SUSPEND)|
1733a501dfa3SJaroslav Kysela 			      (1<<SNDRV_TIMER_EVENT_RESUME)|
17341da177e4SLinus Torvalds 			      (1<<SNDRV_TIMER_EVENT_MSTART)|
17351da177e4SLinus Torvalds 			      (1<<SNDRV_TIMER_EVENT_MSTOP)|
17361da177e4SLinus Torvalds 			      (1<<SNDRV_TIMER_EVENT_MCONTINUE)|
173765d11d95SJaroslav Kysela 			      (1<<SNDRV_TIMER_EVENT_MPAUSE)|
173865d11d95SJaroslav Kysela 			      (1<<SNDRV_TIMER_EVENT_MSUSPEND)|
1739a501dfa3SJaroslav Kysela 			      (1<<SNDRV_TIMER_EVENT_MRESUME))) {
17401da177e4SLinus Torvalds 		err = -EINVAL;
17411da177e4SLinus Torvalds 		goto _end;
17421da177e4SLinus Torvalds 	}
17431da177e4SLinus Torvalds 	snd_timer_stop(tu->timeri);
17441da177e4SLinus Torvalds 	spin_lock_irq(&t->lock);
17451da177e4SLinus Torvalds 	tu->timeri->flags &= ~(SNDRV_TIMER_IFLG_AUTO|
17461da177e4SLinus Torvalds 			       SNDRV_TIMER_IFLG_EXCLUSIVE|
17471da177e4SLinus Torvalds 			       SNDRV_TIMER_IFLG_EARLY_EVENT);
17481da177e4SLinus Torvalds 	if (params.flags & SNDRV_TIMER_PSFLG_AUTO)
17491da177e4SLinus Torvalds 		tu->timeri->flags |= SNDRV_TIMER_IFLG_AUTO;
17501da177e4SLinus Torvalds 	if (params.flags & SNDRV_TIMER_PSFLG_EXCLUSIVE)
17511da177e4SLinus Torvalds 		tu->timeri->flags |= SNDRV_TIMER_IFLG_EXCLUSIVE;
17521da177e4SLinus Torvalds 	if (params.flags & SNDRV_TIMER_PSFLG_EARLY_EVENT)
17531da177e4SLinus Torvalds 		tu->timeri->flags |= SNDRV_TIMER_IFLG_EARLY_EVENT;
17541da177e4SLinus Torvalds 	spin_unlock_irq(&t->lock);
17556b172a85SClemens Ladisch 	if (params.queue_size > 0 &&
17566b172a85SClemens Ladisch 	    (unsigned int)tu->queue_size != params.queue_size) {
17571da177e4SLinus Torvalds 		if (tu->tread) {
17586b172a85SClemens Ladisch 			ttr = kmalloc(params.queue_size * sizeof(*ttr),
17596b172a85SClemens Ladisch 				      GFP_KERNEL);
17601da177e4SLinus Torvalds 			if (ttr) {
17611da177e4SLinus Torvalds 				kfree(tu->tqueue);
17621da177e4SLinus Torvalds 				tu->queue_size = params.queue_size;
17631da177e4SLinus Torvalds 				tu->tqueue = ttr;
17641da177e4SLinus Torvalds 			}
17651da177e4SLinus Torvalds 		} else {
17666b172a85SClemens Ladisch 			tr = kmalloc(params.queue_size * sizeof(*tr),
17676b172a85SClemens Ladisch 				     GFP_KERNEL);
17681da177e4SLinus Torvalds 			if (tr) {
17691da177e4SLinus Torvalds 				kfree(tu->queue);
17701da177e4SLinus Torvalds 				tu->queue_size = params.queue_size;
17711da177e4SLinus Torvalds 				tu->queue = tr;
17721da177e4SLinus Torvalds 			}
17731da177e4SLinus Torvalds 		}
17741da177e4SLinus Torvalds 	}
17751da177e4SLinus Torvalds 	tu->qhead = tu->qtail = tu->qused = 0;
17761da177e4SLinus Torvalds 	if (tu->timeri->flags & SNDRV_TIMER_IFLG_EARLY_EVENT) {
17771da177e4SLinus Torvalds 		if (tu->tread) {
177853d2f744STakashi Iwai 			struct snd_timer_tread tread;
1779cec8f96eSKangjie Lu 			memset(&tread, 0, sizeof(tread));
17801da177e4SLinus Torvalds 			tread.event = SNDRV_TIMER_EVENT_EARLY;
17811da177e4SLinus Torvalds 			tread.tstamp.tv_sec = 0;
17821da177e4SLinus Torvalds 			tread.tstamp.tv_nsec = 0;
17831da177e4SLinus Torvalds 			tread.val = 0;
17841da177e4SLinus Torvalds 			snd_timer_user_append_to_tqueue(tu, &tread);
17851da177e4SLinus Torvalds 		} else {
178653d2f744STakashi Iwai 			struct snd_timer_read *r = &tu->queue[0];
17871da177e4SLinus Torvalds 			r->resolution = 0;
17881da177e4SLinus Torvalds 			r->ticks = 0;
17891da177e4SLinus Torvalds 			tu->qused++;
17901da177e4SLinus Torvalds 			tu->qtail++;
17911da177e4SLinus Torvalds 		}
17921da177e4SLinus Torvalds 	}
17931da177e4SLinus Torvalds 	tu->filter = params.filter;
17941da177e4SLinus Torvalds 	tu->ticks = params.ticks;
17951da177e4SLinus Torvalds 	err = 0;
17961da177e4SLinus Torvalds  _end:
17971da177e4SLinus Torvalds 	if (copy_to_user(_params, &params, sizeof(params)))
17981da177e4SLinus Torvalds 		return -EFAULT;
17991da177e4SLinus Torvalds 	return err;
18001da177e4SLinus Torvalds }
18011da177e4SLinus Torvalds 
18026b172a85SClemens Ladisch static int snd_timer_user_status(struct file *file,
180353d2f744STakashi Iwai 				 struct snd_timer_status __user *_status)
18041da177e4SLinus Torvalds {
180553d2f744STakashi Iwai 	struct snd_timer_user *tu;
180653d2f744STakashi Iwai 	struct snd_timer_status status;
18071da177e4SLinus Torvalds 
18081da177e4SLinus Torvalds 	tu = file->private_data;
18097c64ec34SClemens Ladisch 	if (!tu->timeri)
18107c64ec34SClemens Ladisch 		return -EBADFD;
18111da177e4SLinus Torvalds 	memset(&status, 0, sizeof(status));
18121da177e4SLinus Torvalds 	status.tstamp = tu->tstamp;
18131da177e4SLinus Torvalds 	status.resolution = snd_timer_resolution(tu->timeri);
18141da177e4SLinus Torvalds 	status.lost = tu->timeri->lost;
18151da177e4SLinus Torvalds 	status.overrun = tu->overrun;
18161da177e4SLinus Torvalds 	spin_lock_irq(&tu->qlock);
18171da177e4SLinus Torvalds 	status.queue = tu->qused;
18181da177e4SLinus Torvalds 	spin_unlock_irq(&tu->qlock);
18191da177e4SLinus Torvalds 	if (copy_to_user(_status, &status, sizeof(status)))
18201da177e4SLinus Torvalds 		return -EFAULT;
18211da177e4SLinus Torvalds 	return 0;
18221da177e4SLinus Torvalds }
18231da177e4SLinus Torvalds 
18241da177e4SLinus Torvalds static int snd_timer_user_start(struct file *file)
18251da177e4SLinus Torvalds {
18261da177e4SLinus Torvalds 	int err;
182753d2f744STakashi Iwai 	struct snd_timer_user *tu;
18281da177e4SLinus Torvalds 
18291da177e4SLinus Torvalds 	tu = file->private_data;
18307c64ec34SClemens Ladisch 	if (!tu->timeri)
18317c64ec34SClemens Ladisch 		return -EBADFD;
18321da177e4SLinus Torvalds 	snd_timer_stop(tu->timeri);
18331da177e4SLinus Torvalds 	tu->timeri->lost = 0;
18341da177e4SLinus Torvalds 	tu->last_resolution = 0;
18351da177e4SLinus Torvalds 	return (err = snd_timer_start(tu->timeri, tu->ticks)) < 0 ? err : 0;
18361da177e4SLinus Torvalds }
18371da177e4SLinus Torvalds 
18381da177e4SLinus Torvalds static int snd_timer_user_stop(struct file *file)
18391da177e4SLinus Torvalds {
18401da177e4SLinus Torvalds 	int err;
184153d2f744STakashi Iwai 	struct snd_timer_user *tu;
18421da177e4SLinus Torvalds 
18431da177e4SLinus Torvalds 	tu = file->private_data;
18447c64ec34SClemens Ladisch 	if (!tu->timeri)
18457c64ec34SClemens Ladisch 		return -EBADFD;
18461da177e4SLinus Torvalds 	return (err = snd_timer_stop(tu->timeri)) < 0 ? err : 0;
18471da177e4SLinus Torvalds }
18481da177e4SLinus Torvalds 
18491da177e4SLinus Torvalds static int snd_timer_user_continue(struct file *file)
18501da177e4SLinus Torvalds {
18511da177e4SLinus Torvalds 	int err;
185253d2f744STakashi Iwai 	struct snd_timer_user *tu;
18531da177e4SLinus Torvalds 
18541da177e4SLinus Torvalds 	tu = file->private_data;
18557c64ec34SClemens Ladisch 	if (!tu->timeri)
18567c64ec34SClemens Ladisch 		return -EBADFD;
18579f8a7658STakashi Iwai 	/* start timer instead of continue if it's not used before */
18589f8a7658STakashi Iwai 	if (!(tu->timeri->flags & SNDRV_TIMER_IFLG_PAUSED))
18599f8a7658STakashi Iwai 		return snd_timer_user_start(file);
18601da177e4SLinus Torvalds 	tu->timeri->lost = 0;
18611da177e4SLinus Torvalds 	return (err = snd_timer_continue(tu->timeri)) < 0 ? err : 0;
18621da177e4SLinus Torvalds }
18631da177e4SLinus Torvalds 
186415790a6bSTakashi Iwai static int snd_timer_user_pause(struct file *file)
186515790a6bSTakashi Iwai {
186615790a6bSTakashi Iwai 	int err;
186753d2f744STakashi Iwai 	struct snd_timer_user *tu;
186815790a6bSTakashi Iwai 
186915790a6bSTakashi Iwai 	tu = file->private_data;
18707c64ec34SClemens Ladisch 	if (!tu->timeri)
18717c64ec34SClemens Ladisch 		return -EBADFD;
1872d138b445SJaroslav Kysela 	return (err = snd_timer_pause(tu->timeri)) < 0 ? err : 0;
187315790a6bSTakashi Iwai }
187415790a6bSTakashi Iwai 
18758c50b37cSTakashi Iwai enum {
18768c50b37cSTakashi Iwai 	SNDRV_TIMER_IOCTL_START_OLD = _IO('T', 0x20),
18778c50b37cSTakashi Iwai 	SNDRV_TIMER_IOCTL_STOP_OLD = _IO('T', 0x21),
18788c50b37cSTakashi Iwai 	SNDRV_TIMER_IOCTL_CONTINUE_OLD = _IO('T', 0x22),
18798c50b37cSTakashi Iwai 	SNDRV_TIMER_IOCTL_PAUSE_OLD = _IO('T', 0x23),
18808c50b37cSTakashi Iwai };
18818c50b37cSTakashi Iwai 
1882af368027STakashi Iwai static long __snd_timer_user_ioctl(struct file *file, unsigned int cmd,
18836b172a85SClemens Ladisch 				 unsigned long arg)
18841da177e4SLinus Torvalds {
188553d2f744STakashi Iwai 	struct snd_timer_user *tu;
18861da177e4SLinus Torvalds 	void __user *argp = (void __user *)arg;
18871da177e4SLinus Torvalds 	int __user *p = argp;
18881da177e4SLinus Torvalds 
18891da177e4SLinus Torvalds 	tu = file->private_data;
18901da177e4SLinus Torvalds 	switch (cmd) {
18911da177e4SLinus Torvalds 	case SNDRV_TIMER_IOCTL_PVERSION:
18921da177e4SLinus Torvalds 		return put_user(SNDRV_TIMER_VERSION, p) ? -EFAULT : 0;
18931da177e4SLinus Torvalds 	case SNDRV_TIMER_IOCTL_NEXT_DEVICE:
18941da177e4SLinus Torvalds 		return snd_timer_user_next_device(argp);
18951da177e4SLinus Torvalds 	case SNDRV_TIMER_IOCTL_TREAD:
18961da177e4SLinus Torvalds 	{
18971da177e4SLinus Torvalds 		int xarg;
18981da177e4SLinus Torvalds 
1899af368027STakashi Iwai 		if (tu->timeri)	/* too late */
19001da177e4SLinus Torvalds 			return -EBUSY;
1901af368027STakashi Iwai 		if (get_user(xarg, p))
19021da177e4SLinus Torvalds 			return -EFAULT;
19031da177e4SLinus Torvalds 		tu->tread = xarg ? 1 : 0;
19041da177e4SLinus Torvalds 		return 0;
19051da177e4SLinus Torvalds 	}
19061da177e4SLinus Torvalds 	case SNDRV_TIMER_IOCTL_GINFO:
19071da177e4SLinus Torvalds 		return snd_timer_user_ginfo(file, argp);
19081da177e4SLinus Torvalds 	case SNDRV_TIMER_IOCTL_GPARAMS:
19091da177e4SLinus Torvalds 		return snd_timer_user_gparams(file, argp);
19101da177e4SLinus Torvalds 	case SNDRV_TIMER_IOCTL_GSTATUS:
19111da177e4SLinus Torvalds 		return snd_timer_user_gstatus(file, argp);
19121da177e4SLinus Torvalds 	case SNDRV_TIMER_IOCTL_SELECT:
19131da177e4SLinus Torvalds 		return snd_timer_user_tselect(file, argp);
19141da177e4SLinus Torvalds 	case SNDRV_TIMER_IOCTL_INFO:
19151da177e4SLinus Torvalds 		return snd_timer_user_info(file, argp);
19161da177e4SLinus Torvalds 	case SNDRV_TIMER_IOCTL_PARAMS:
19171da177e4SLinus Torvalds 		return snd_timer_user_params(file, argp);
19181da177e4SLinus Torvalds 	case SNDRV_TIMER_IOCTL_STATUS:
19191da177e4SLinus Torvalds 		return snd_timer_user_status(file, argp);
19201da177e4SLinus Torvalds 	case SNDRV_TIMER_IOCTL_START:
19218c50b37cSTakashi Iwai 	case SNDRV_TIMER_IOCTL_START_OLD:
19221da177e4SLinus Torvalds 		return snd_timer_user_start(file);
19231da177e4SLinus Torvalds 	case SNDRV_TIMER_IOCTL_STOP:
19248c50b37cSTakashi Iwai 	case SNDRV_TIMER_IOCTL_STOP_OLD:
19251da177e4SLinus Torvalds 		return snd_timer_user_stop(file);
19261da177e4SLinus Torvalds 	case SNDRV_TIMER_IOCTL_CONTINUE:
19278c50b37cSTakashi Iwai 	case SNDRV_TIMER_IOCTL_CONTINUE_OLD:
19281da177e4SLinus Torvalds 		return snd_timer_user_continue(file);
192915790a6bSTakashi Iwai 	case SNDRV_TIMER_IOCTL_PAUSE:
19308c50b37cSTakashi Iwai 	case SNDRV_TIMER_IOCTL_PAUSE_OLD:
193115790a6bSTakashi Iwai 		return snd_timer_user_pause(file);
19321da177e4SLinus Torvalds 	}
19331da177e4SLinus Torvalds 	return -ENOTTY;
19341da177e4SLinus Torvalds }
19351da177e4SLinus Torvalds 
1936af368027STakashi Iwai static long snd_timer_user_ioctl(struct file *file, unsigned int cmd,
1937af368027STakashi Iwai 				 unsigned long arg)
1938af368027STakashi Iwai {
1939af368027STakashi Iwai 	struct snd_timer_user *tu = file->private_data;
1940af368027STakashi Iwai 	long ret;
1941af368027STakashi Iwai 
1942af368027STakashi Iwai 	mutex_lock(&tu->ioctl_lock);
1943af368027STakashi Iwai 	ret = __snd_timer_user_ioctl(file, cmd, arg);
1944af368027STakashi Iwai 	mutex_unlock(&tu->ioctl_lock);
1945af368027STakashi Iwai 	return ret;
1946af368027STakashi Iwai }
1947af368027STakashi Iwai 
19481da177e4SLinus Torvalds static int snd_timer_user_fasync(int fd, struct file * file, int on)
19491da177e4SLinus Torvalds {
195053d2f744STakashi Iwai 	struct snd_timer_user *tu;
19511da177e4SLinus Torvalds 
19521da177e4SLinus Torvalds 	tu = file->private_data;
195360aa4924SJonathan Corbet 	return fasync_helper(fd, file, on, &tu->fasync);
19541da177e4SLinus Torvalds }
19551da177e4SLinus Torvalds 
19566b172a85SClemens Ladisch static ssize_t snd_timer_user_read(struct file *file, char __user *buffer,
19576b172a85SClemens Ladisch 				   size_t count, loff_t *offset)
19581da177e4SLinus Torvalds {
195953d2f744STakashi Iwai 	struct snd_timer_user *tu;
19601da177e4SLinus Torvalds 	long result = 0, unit;
19614dff5c7bSTakashi Iwai 	int qhead;
19621da177e4SLinus Torvalds 	int err = 0;
19631da177e4SLinus Torvalds 
19641da177e4SLinus Torvalds 	tu = file->private_data;
196553d2f744STakashi Iwai 	unit = tu->tread ? sizeof(struct snd_timer_tread) : sizeof(struct snd_timer_read);
19661da177e4SLinus Torvalds 	spin_lock_irq(&tu->qlock);
19671da177e4SLinus Torvalds 	while ((long)count - result >= unit) {
19681da177e4SLinus Torvalds 		while (!tu->qused) {
19691da177e4SLinus Torvalds 			wait_queue_t wait;
19701da177e4SLinus Torvalds 
19711da177e4SLinus Torvalds 			if ((file->f_flags & O_NONBLOCK) != 0 || result > 0) {
19721da177e4SLinus Torvalds 				err = -EAGAIN;
19734dff5c7bSTakashi Iwai 				goto _error;
19741da177e4SLinus Torvalds 			}
19751da177e4SLinus Torvalds 
19761da177e4SLinus Torvalds 			set_current_state(TASK_INTERRUPTIBLE);
19771da177e4SLinus Torvalds 			init_waitqueue_entry(&wait, current);
19781da177e4SLinus Torvalds 			add_wait_queue(&tu->qchange_sleep, &wait);
19791da177e4SLinus Torvalds 
19801da177e4SLinus Torvalds 			spin_unlock_irq(&tu->qlock);
19811da177e4SLinus Torvalds 			schedule();
19821da177e4SLinus Torvalds 			spin_lock_irq(&tu->qlock);
19831da177e4SLinus Torvalds 
19841da177e4SLinus Torvalds 			remove_wait_queue(&tu->qchange_sleep, &wait);
19851da177e4SLinus Torvalds 
1986230323daSTakashi Iwai 			if (tu->disconnected) {
1987230323daSTakashi Iwai 				err = -ENODEV;
19884dff5c7bSTakashi Iwai 				goto _error;
1989230323daSTakashi Iwai 			}
19901da177e4SLinus Torvalds 			if (signal_pending(current)) {
19911da177e4SLinus Torvalds 				err = -ERESTARTSYS;
19924dff5c7bSTakashi Iwai 				goto _error;
19931da177e4SLinus Torvalds 			}
19941da177e4SLinus Torvalds 		}
19951da177e4SLinus Torvalds 
19964dff5c7bSTakashi Iwai 		qhead = tu->qhead++;
19974dff5c7bSTakashi Iwai 		tu->qhead %= tu->queue_size;
19983fa6993fSTakashi Iwai 		tu->qused--;
19991da177e4SLinus Torvalds 		spin_unlock_irq(&tu->qlock);
20001da177e4SLinus Torvalds 
200111749e08SVegard Nossum 		mutex_lock(&tu->ioctl_lock);
20021da177e4SLinus Torvalds 		if (tu->tread) {
20034dff5c7bSTakashi Iwai 			if (copy_to_user(buffer, &tu->tqueue[qhead],
20044dff5c7bSTakashi Iwai 					 sizeof(struct snd_timer_tread)))
20051da177e4SLinus Torvalds 				err = -EFAULT;
20061da177e4SLinus Torvalds 		} else {
20074dff5c7bSTakashi Iwai 			if (copy_to_user(buffer, &tu->queue[qhead],
20084dff5c7bSTakashi Iwai 					 sizeof(struct snd_timer_read)))
20091da177e4SLinus Torvalds 				err = -EFAULT;
20101da177e4SLinus Torvalds 		}
201111749e08SVegard Nossum 		mutex_unlock(&tu->ioctl_lock);
20121da177e4SLinus Torvalds 
20131da177e4SLinus Torvalds 		spin_lock_irq(&tu->qlock);
20144dff5c7bSTakashi Iwai 		if (err < 0)
20154dff5c7bSTakashi Iwai 			goto _error;
20164dff5c7bSTakashi Iwai 		result += unit;
20174dff5c7bSTakashi Iwai 		buffer += unit;
20181da177e4SLinus Torvalds 	}
20191da177e4SLinus Torvalds  _error:
20204dff5c7bSTakashi Iwai 	spin_unlock_irq(&tu->qlock);
20211da177e4SLinus Torvalds 	return result > 0 ? result : err;
20221da177e4SLinus Torvalds }
20231da177e4SLinus Torvalds 
20241da177e4SLinus Torvalds static unsigned int snd_timer_user_poll(struct file *file, poll_table * wait)
20251da177e4SLinus Torvalds {
20261da177e4SLinus Torvalds         unsigned int mask;
202753d2f744STakashi Iwai         struct snd_timer_user *tu;
20281da177e4SLinus Torvalds 
20291da177e4SLinus Torvalds         tu = file->private_data;
20301da177e4SLinus Torvalds 
20311da177e4SLinus Torvalds         poll_wait(file, &tu->qchange_sleep, wait);
20321da177e4SLinus Torvalds 
20331da177e4SLinus Torvalds 	mask = 0;
20341da177e4SLinus Torvalds 	if (tu->qused)
20351da177e4SLinus Torvalds 		mask |= POLLIN | POLLRDNORM;
2036230323daSTakashi Iwai 	if (tu->disconnected)
2037230323daSTakashi Iwai 		mask |= POLLERR;
20381da177e4SLinus Torvalds 
20391da177e4SLinus Torvalds 	return mask;
20401da177e4SLinus Torvalds }
20411da177e4SLinus Torvalds 
20421da177e4SLinus Torvalds #ifdef CONFIG_COMPAT
20431da177e4SLinus Torvalds #include "timer_compat.c"
20441da177e4SLinus Torvalds #else
20451da177e4SLinus Torvalds #define snd_timer_user_ioctl_compat	NULL
20461da177e4SLinus Torvalds #endif
20471da177e4SLinus Torvalds 
20489c2e08c5SArjan van de Ven static const struct file_operations snd_timer_f_ops =
20491da177e4SLinus Torvalds {
20501da177e4SLinus Torvalds 	.owner =	THIS_MODULE,
20511da177e4SLinus Torvalds 	.read =		snd_timer_user_read,
20521da177e4SLinus Torvalds 	.open =		snd_timer_user_open,
20531da177e4SLinus Torvalds 	.release =	snd_timer_user_release,
205402f4865fSTakashi Iwai 	.llseek =	no_llseek,
20551da177e4SLinus Torvalds 	.poll =		snd_timer_user_poll,
20561da177e4SLinus Torvalds 	.unlocked_ioctl =	snd_timer_user_ioctl,
20571da177e4SLinus Torvalds 	.compat_ioctl =	snd_timer_user_ioctl_compat,
20581da177e4SLinus Torvalds 	.fasync = 	snd_timer_user_fasync,
20591da177e4SLinus Torvalds };
20601da177e4SLinus Torvalds 
20617c35860dSTakashi Iwai /* unregister the system timer */
20627c35860dSTakashi Iwai static void snd_timer_free_all(void)
20637c35860dSTakashi Iwai {
20647c35860dSTakashi Iwai 	struct snd_timer *timer, *n;
20657c35860dSTakashi Iwai 
20667c35860dSTakashi Iwai 	list_for_each_entry_safe(timer, n, &snd_timer_list, device_list)
20677c35860dSTakashi Iwai 		snd_timer_free(timer);
20687c35860dSTakashi Iwai }
20697c35860dSTakashi Iwai 
207089da061fSTakashi Iwai static struct device timer_dev;
207189da061fSTakashi Iwai 
20721da177e4SLinus Torvalds /*
20731da177e4SLinus Torvalds  *  ENTRY functions
20741da177e4SLinus Torvalds  */
20751da177e4SLinus Torvalds 
20761da177e4SLinus Torvalds static int __init alsa_timer_init(void)
20771da177e4SLinus Torvalds {
20781da177e4SLinus Torvalds 	int err;
20791da177e4SLinus Torvalds 
208089da061fSTakashi Iwai 	snd_device_initialize(&timer_dev, NULL);
208189da061fSTakashi Iwai 	dev_set_name(&timer_dev, "timer");
208289da061fSTakashi Iwai 
20831da177e4SLinus Torvalds #ifdef SNDRV_OSS_INFO_DEV_TIMERS
20846b172a85SClemens Ladisch 	snd_oss_info_register(SNDRV_OSS_INFO_DEV_TIMERS, SNDRV_CARDS - 1,
20856b172a85SClemens Ladisch 			      "system timer");
20861da177e4SLinus Torvalds #endif
2087e28563ccSTakashi Iwai 
20887c35860dSTakashi Iwai 	err = snd_timer_register_system();
20897c35860dSTakashi Iwai 	if (err < 0) {
2090cf74dcf3STakashi Iwai 		pr_err("ALSA: unable to register system timer (%i)\n", err);
209189da061fSTakashi Iwai 		put_device(&timer_dev);
20927c35860dSTakashi Iwai 		return err;
20937c35860dSTakashi Iwai 	}
20947c35860dSTakashi Iwai 
209540a4b263STakashi Iwai 	err = snd_register_device(SNDRV_DEVICE_TYPE_TIMER, NULL, 0,
209640a4b263STakashi Iwai 				  &snd_timer_f_ops, NULL, &timer_dev);
20977c35860dSTakashi Iwai 	if (err < 0) {
2098cf74dcf3STakashi Iwai 		pr_err("ALSA: unable to register timer device (%i)\n", err);
20997c35860dSTakashi Iwai 		snd_timer_free_all();
210089da061fSTakashi Iwai 		put_device(&timer_dev);
21017c35860dSTakashi Iwai 		return err;
21027c35860dSTakashi Iwai 	}
21037c35860dSTakashi Iwai 
2104e28563ccSTakashi Iwai 	snd_timer_proc_init();
21051da177e4SLinus Torvalds 	return 0;
21061da177e4SLinus Torvalds }
21071da177e4SLinus Torvalds 
21081da177e4SLinus Torvalds static void __exit alsa_timer_exit(void)
21091da177e4SLinus Torvalds {
211040a4b263STakashi Iwai 	snd_unregister_device(&timer_dev);
21117c35860dSTakashi Iwai 	snd_timer_free_all();
211289da061fSTakashi Iwai 	put_device(&timer_dev);
2113e28563ccSTakashi Iwai 	snd_timer_proc_done();
21141da177e4SLinus Torvalds #ifdef SNDRV_OSS_INFO_DEV_TIMERS
21151da177e4SLinus Torvalds 	snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_TIMERS, SNDRV_CARDS - 1);
21161da177e4SLinus Torvalds #endif
21171da177e4SLinus Torvalds }
21181da177e4SLinus Torvalds 
21191da177e4SLinus Torvalds module_init(alsa_timer_init)
21201da177e4SLinus Torvalds module_exit(alsa_timer_exit)
21211da177e4SLinus Torvalds 
21221da177e4SLinus Torvalds EXPORT_SYMBOL(snd_timer_open);
21231da177e4SLinus Torvalds EXPORT_SYMBOL(snd_timer_close);
21241da177e4SLinus Torvalds EXPORT_SYMBOL(snd_timer_resolution);
21251da177e4SLinus Torvalds EXPORT_SYMBOL(snd_timer_start);
21261da177e4SLinus Torvalds EXPORT_SYMBOL(snd_timer_stop);
21271da177e4SLinus Torvalds EXPORT_SYMBOL(snd_timer_continue);
21281da177e4SLinus Torvalds EXPORT_SYMBOL(snd_timer_pause);
21291da177e4SLinus Torvalds EXPORT_SYMBOL(snd_timer_new);
21301da177e4SLinus Torvalds EXPORT_SYMBOL(snd_timer_notify);
21311da177e4SLinus Torvalds EXPORT_SYMBOL(snd_timer_global_new);
21321da177e4SLinus Torvalds EXPORT_SYMBOL(snd_timer_global_free);
21331da177e4SLinus Torvalds EXPORT_SYMBOL(snd_timer_global_register);
21341da177e4SLinus Torvalds EXPORT_SYMBOL(snd_timer_interrupt);
2135