11a59d1b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
21da177e4SLinus Torvalds /*
31da177e4SLinus Torvalds * Timers abstract layer
4c1017a4cSJaroslav Kysela * Copyright (c) by Jaroslav Kysela <perex@perex.cz>
51da177e4SLinus Torvalds */
61da177e4SLinus Torvalds
71da177e4SLinus Torvalds #include <linux/delay.h>
81da177e4SLinus Torvalds #include <linux/init.h>
91da177e4SLinus Torvalds #include <linux/slab.h>
101da177e4SLinus Torvalds #include <linux/time.h>
111a60d4c5SIngo Molnar #include <linux/mutex.h>
1251990e82SPaul Gortmaker #include <linux/device.h>
1365a77217SPaul Gortmaker #include <linux/module.h>
14543537bdSPaulo Marques #include <linux/string.h>
15174cd4b1SIngo Molnar #include <linux/sched/signal.h>
161da177e4SLinus Torvalds #include <sound/core.h>
171da177e4SLinus Torvalds #include <sound/timer.h>
181da177e4SLinus Torvalds #include <sound/control.h>
191da177e4SLinus Torvalds #include <sound/info.h>
201da177e4SLinus Torvalds #include <sound/minors.h>
211da177e4SLinus Torvalds #include <sound/initval.h>
221da177e4SLinus Torvalds #include <linux/kmod.h>
231da177e4SLinus Torvalds
249f8a7658STakashi Iwai /* internal flags */
259f8a7658STakashi Iwai #define SNDRV_TIMER_IFLG_PAUSED 0x00010000
26fe1b26c9STakashi Iwai #define SNDRV_TIMER_IFLG_DEAD 0x00020000
279f8a7658STakashi Iwai
288eeaa2f9STakashi Iwai #if IS_ENABLED(CONFIG_SND_HRTIMER)
29109fef9eSClemens Ladisch #define DEFAULT_TIMER_LIMIT 4
301da177e4SLinus Torvalds #else
311da177e4SLinus Torvalds #define DEFAULT_TIMER_LIMIT 1
321da177e4SLinus Torvalds #endif
331da177e4SLinus Torvalds
341da177e4SLinus Torvalds static int timer_limit = DEFAULT_TIMER_LIMIT;
35b751eef1SJaroslav Kysela static int timer_tstamp_monotonic = 1;
36c1017a4cSJaroslav Kysela MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>, Takashi Iwai <tiwai@suse.de>");
371da177e4SLinus Torvalds MODULE_DESCRIPTION("ALSA timer interface");
381da177e4SLinus Torvalds MODULE_LICENSE("GPL");
391da177e4SLinus Torvalds module_param(timer_limit, int, 0444);
401da177e4SLinus Torvalds MODULE_PARM_DESC(timer_limit, "Maximum global timers in system.");
41b751eef1SJaroslav Kysela module_param(timer_tstamp_monotonic, int, 0444);
42b751eef1SJaroslav Kysela MODULE_PARM_DESC(timer_tstamp_monotonic, "Use posix monotonic clock source for timestamps (default).");
431da177e4SLinus Torvalds
4403cfe6f5SKay Sievers MODULE_ALIAS_CHARDEV(CONFIG_SND_MAJOR, SNDRV_MINOR_TIMER);
4503cfe6f5SKay Sievers MODULE_ALIAS("devname:snd/timer");
4603cfe6f5SKay Sievers
4707094ae6SBaolin Wang enum timer_tread_format {
4807094ae6SBaolin Wang TREAD_FORMAT_NONE = 0,
4907094ae6SBaolin Wang TREAD_FORMAT_TIME64,
5007094ae6SBaolin Wang TREAD_FORMAT_TIME32,
5107094ae6SBaolin Wang };
5207094ae6SBaolin Wang
5307094ae6SBaolin Wang struct snd_timer_tread32 {
5407094ae6SBaolin Wang int event;
5507094ae6SBaolin Wang s32 tstamp_sec;
5607094ae6SBaolin Wang s32 tstamp_nsec;
5707094ae6SBaolin Wang unsigned int val;
5807094ae6SBaolin Wang };
5907094ae6SBaolin Wang
6007094ae6SBaolin Wang struct snd_timer_tread64 {
6107094ae6SBaolin Wang int event;
6207094ae6SBaolin Wang u8 pad1[4];
6307094ae6SBaolin Wang s64 tstamp_sec;
6407094ae6SBaolin Wang s64 tstamp_nsec;
6507094ae6SBaolin Wang unsigned int val;
6607094ae6SBaolin Wang u8 pad2[4];
6707094ae6SBaolin Wang };
6807094ae6SBaolin Wang
6953d2f744STakashi Iwai struct snd_timer_user {
7053d2f744STakashi Iwai struct snd_timer_instance *timeri;
711da177e4SLinus Torvalds int tread; /* enhanced read with timestamps and events */
721da177e4SLinus Torvalds unsigned long ticks;
731da177e4SLinus Torvalds unsigned long overrun;
741da177e4SLinus Torvalds int qhead;
751da177e4SLinus Torvalds int qtail;
761da177e4SLinus Torvalds int qused;
771da177e4SLinus Torvalds int queue_size;
78230323daSTakashi Iwai bool disconnected;
7953d2f744STakashi Iwai struct snd_timer_read *queue;
8007094ae6SBaolin Wang struct snd_timer_tread64 *tqueue;
811da177e4SLinus Torvalds spinlock_t qlock;
821da177e4SLinus Torvalds unsigned long last_resolution;
831da177e4SLinus Torvalds unsigned int filter;
84fcae40c9SBaolin Wang struct timespec64 tstamp; /* trigger tstamp */
851da177e4SLinus Torvalds wait_queue_head_t qchange_sleep;
8695cc637cSTakashi Iwai struct snd_fasync *fasync;
87af368027STakashi Iwai struct mutex ioctl_lock;
8853d2f744STakashi Iwai };
891da177e4SLinus Torvalds
90a07804ccSBaolin Wang struct snd_timer_status32 {
91a07804ccSBaolin Wang s32 tstamp_sec; /* Timestamp - last update */
92a07804ccSBaolin Wang s32 tstamp_nsec;
93a07804ccSBaolin Wang unsigned int resolution; /* current period resolution in ns */
94a07804ccSBaolin Wang unsigned int lost; /* counter of master tick lost */
95a07804ccSBaolin Wang unsigned int overrun; /* count of read queue overruns */
96a07804ccSBaolin Wang unsigned int queue; /* used queue size */
97a07804ccSBaolin Wang unsigned char reserved[64]; /* reserved */
98a07804ccSBaolin Wang };
99a07804ccSBaolin Wang
100a07804ccSBaolin Wang #define SNDRV_TIMER_IOCTL_STATUS32 _IOR('T', 0x14, struct snd_timer_status32)
101a07804ccSBaolin Wang
102a07804ccSBaolin Wang struct snd_timer_status64 {
103a07804ccSBaolin Wang s64 tstamp_sec; /* Timestamp - last update */
104a07804ccSBaolin Wang s64 tstamp_nsec;
105a07804ccSBaolin Wang unsigned int resolution; /* current period resolution in ns */
106a07804ccSBaolin Wang unsigned int lost; /* counter of master tick lost */
107a07804ccSBaolin Wang unsigned int overrun; /* count of read queue overruns */
108a07804ccSBaolin Wang unsigned int queue; /* used queue size */
109a07804ccSBaolin Wang unsigned char reserved[64]; /* reserved */
110a07804ccSBaolin Wang };
111a07804ccSBaolin Wang
112a07804ccSBaolin Wang #define SNDRV_TIMER_IOCTL_STATUS64 _IOR('T', 0x14, struct snd_timer_status64)
113a07804ccSBaolin Wang
1141da177e4SLinus Torvalds /* list of timers */
1151da177e4SLinus Torvalds static LIST_HEAD(snd_timer_list);
1161da177e4SLinus Torvalds
1171da177e4SLinus Torvalds /* list of slave instances */
1181da177e4SLinus Torvalds static LIST_HEAD(snd_timer_slave_list);
1191da177e4SLinus Torvalds
1201da177e4SLinus Torvalds /* lock for slave active lists */
1211da177e4SLinus Torvalds static DEFINE_SPINLOCK(slave_active_lock);
1221da177e4SLinus Torvalds
123fdea53feSTakashi Iwai #define MAX_SLAVE_INSTANCES 1000
124fdea53feSTakashi Iwai static int num_slaves;
125fdea53feSTakashi Iwai
1261a60d4c5SIngo Molnar static DEFINE_MUTEX(register_mutex);
1271da177e4SLinus Torvalds
12853d2f744STakashi Iwai static int snd_timer_free(struct snd_timer *timer);
12953d2f744STakashi Iwai static int snd_timer_dev_free(struct snd_device *device);
13053d2f744STakashi Iwai static int snd_timer_dev_register(struct snd_device *device);
131c461482cSTakashi Iwai static int snd_timer_dev_disconnect(struct snd_device *device);
1321da177e4SLinus Torvalds
13353d2f744STakashi Iwai static void snd_timer_reschedule(struct snd_timer * timer, unsigned long ticks_left);
1341da177e4SLinus Torvalds
1351da177e4SLinus Torvalds /*
1361da177e4SLinus Torvalds * create a timer instance with the given owner string.
1371da177e4SLinus Torvalds */
snd_timer_instance_new(const char * owner)1386a34367eSTakashi Iwai struct snd_timer_instance *snd_timer_instance_new(const char *owner)
1391da177e4SLinus Torvalds {
14053d2f744STakashi Iwai struct snd_timer_instance *timeri;
1416a34367eSTakashi Iwai
142ca2c0966STakashi Iwai timeri = kzalloc(sizeof(*timeri), GFP_KERNEL);
1431da177e4SLinus Torvalds if (timeri == NULL)
1441da177e4SLinus Torvalds return NULL;
145543537bdSPaulo Marques timeri->owner = kstrdup(owner, GFP_KERNEL);
1461da177e4SLinus Torvalds if (! timeri->owner) {
1471da177e4SLinus Torvalds kfree(timeri);
1481da177e4SLinus Torvalds return NULL;
1491da177e4SLinus Torvalds }
1501da177e4SLinus Torvalds INIT_LIST_HEAD(&timeri->open_list);
1511da177e4SLinus Torvalds INIT_LIST_HEAD(&timeri->active_list);
1521da177e4SLinus Torvalds INIT_LIST_HEAD(&timeri->ack_list);
1531da177e4SLinus Torvalds INIT_LIST_HEAD(&timeri->slave_list_head);
1541da177e4SLinus Torvalds INIT_LIST_HEAD(&timeri->slave_active_head);
1551da177e4SLinus Torvalds
1561da177e4SLinus Torvalds return timeri;
1571da177e4SLinus Torvalds }
1586a34367eSTakashi Iwai EXPORT_SYMBOL(snd_timer_instance_new);
1596a34367eSTakashi Iwai
snd_timer_instance_free(struct snd_timer_instance * timeri)1606a34367eSTakashi Iwai void snd_timer_instance_free(struct snd_timer_instance *timeri)
1616a34367eSTakashi Iwai {
1626a34367eSTakashi Iwai if (timeri) {
1636a34367eSTakashi Iwai if (timeri->private_free)
1646a34367eSTakashi Iwai timeri->private_free(timeri);
1656a34367eSTakashi Iwai kfree(timeri->owner);
1666a34367eSTakashi Iwai kfree(timeri);
1676a34367eSTakashi Iwai }
1686a34367eSTakashi Iwai }
1696a34367eSTakashi Iwai EXPORT_SYMBOL(snd_timer_instance_free);
1701da177e4SLinus Torvalds
1711da177e4SLinus Torvalds /*
1721da177e4SLinus Torvalds * find a timer instance from the given timer id
1731da177e4SLinus Torvalds */
snd_timer_find(struct snd_timer_id * tid)17453d2f744STakashi Iwai static struct snd_timer *snd_timer_find(struct snd_timer_id *tid)
1751da177e4SLinus Torvalds {
17663632563SPierre-Louis Bossart struct snd_timer *timer;
1771da177e4SLinus Torvalds
1789244b2c3SJohannes Berg list_for_each_entry(timer, &snd_timer_list, device_list) {
1791da177e4SLinus Torvalds if (timer->tmr_class != tid->dev_class)
1801da177e4SLinus Torvalds continue;
1811da177e4SLinus Torvalds if ((timer->tmr_class == SNDRV_TIMER_CLASS_CARD ||
1821da177e4SLinus Torvalds timer->tmr_class == SNDRV_TIMER_CLASS_PCM) &&
1831da177e4SLinus Torvalds (timer->card == NULL ||
1841da177e4SLinus Torvalds timer->card->number != tid->card))
1851da177e4SLinus Torvalds continue;
1861da177e4SLinus Torvalds if (timer->tmr_device != tid->device)
1871da177e4SLinus Torvalds continue;
1881da177e4SLinus Torvalds if (timer->tmr_subdevice != tid->subdevice)
1891da177e4SLinus Torvalds continue;
1901da177e4SLinus Torvalds return timer;
1911da177e4SLinus Torvalds }
1921da177e4SLinus Torvalds return NULL;
1931da177e4SLinus Torvalds }
1941da177e4SLinus Torvalds
195ee2da997SJohannes Berg #ifdef CONFIG_MODULES
1961da177e4SLinus Torvalds
snd_timer_request(struct snd_timer_id * tid)19753d2f744STakashi Iwai static void snd_timer_request(struct snd_timer_id *tid)
1981da177e4SLinus Torvalds {
1991da177e4SLinus Torvalds switch (tid->dev_class) {
2001da177e4SLinus Torvalds case SNDRV_TIMER_CLASS_GLOBAL:
2011da177e4SLinus Torvalds if (tid->device < timer_limit)
2021da177e4SLinus Torvalds request_module("snd-timer-%i", tid->device);
2031da177e4SLinus Torvalds break;
2041da177e4SLinus Torvalds case SNDRV_TIMER_CLASS_CARD:
2051da177e4SLinus Torvalds case SNDRV_TIMER_CLASS_PCM:
2061da177e4SLinus Torvalds if (tid->card < snd_ecards_limit)
2071da177e4SLinus Torvalds request_module("snd-card-%i", tid->card);
2081da177e4SLinus Torvalds break;
2091da177e4SLinus Torvalds default:
2101da177e4SLinus Torvalds break;
2111da177e4SLinus Torvalds }
2121da177e4SLinus Torvalds }
2131da177e4SLinus Torvalds
2141da177e4SLinus Torvalds #endif
2151da177e4SLinus Torvalds
216ebfc6de2STakashi Iwai /* move the slave if it belongs to the master; return 1 if match */
check_matching_master_slave(struct snd_timer_instance * master,struct snd_timer_instance * slave)217ebfc6de2STakashi Iwai static int check_matching_master_slave(struct snd_timer_instance *master,
218ebfc6de2STakashi Iwai struct snd_timer_instance *slave)
219ebfc6de2STakashi Iwai {
220ebfc6de2STakashi Iwai if (slave->slave_class != master->slave_class ||
221ebfc6de2STakashi Iwai slave->slave_id != master->slave_id)
222ebfc6de2STakashi Iwai return 0;
223ebfc6de2STakashi Iwai if (master->timer->num_instances >= master->timer->max_instances)
224ebfc6de2STakashi Iwai return -EBUSY;
225ebfc6de2STakashi Iwai list_move_tail(&slave->open_list, &master->slave_list_head);
226ebfc6de2STakashi Iwai master->timer->num_instances++;
227ebfc6de2STakashi Iwai spin_lock_irq(&slave_active_lock);
228ebfc6de2STakashi Iwai spin_lock(&master->timer->lock);
229ebfc6de2STakashi Iwai slave->master = master;
230ebfc6de2STakashi Iwai slave->timer = master->timer;
231ebfc6de2STakashi Iwai if (slave->flags & SNDRV_TIMER_IFLG_RUNNING)
232ebfc6de2STakashi Iwai list_add_tail(&slave->active_list, &master->slave_active_head);
233ebfc6de2STakashi Iwai spin_unlock(&master->timer->lock);
234ebfc6de2STakashi Iwai spin_unlock_irq(&slave_active_lock);
235ebfc6de2STakashi Iwai return 1;
236ebfc6de2STakashi Iwai }
237ebfc6de2STakashi Iwai
2381da177e4SLinus Torvalds /*
2391da177e4SLinus Torvalds * look for a master instance matching with the slave id of the given slave.
2401da177e4SLinus Torvalds * when found, relink the open_link of the slave.
2411da177e4SLinus Torvalds *
2421da177e4SLinus Torvalds * call this with register_mutex down.
2431da177e4SLinus Torvalds */
snd_timer_check_slave(struct snd_timer_instance * slave)2449b7d869eSTakashi Iwai static int snd_timer_check_slave(struct snd_timer_instance *slave)
2451da177e4SLinus Torvalds {
24653d2f744STakashi Iwai struct snd_timer *timer;
24753d2f744STakashi Iwai struct snd_timer_instance *master;
248ebfc6de2STakashi Iwai int err = 0;
2491da177e4SLinus Torvalds
2501da177e4SLinus Torvalds /* FIXME: it's really dumb to look up all entries.. */
2519244b2c3SJohannes Berg list_for_each_entry(timer, &snd_timer_list, device_list) {
2529244b2c3SJohannes Berg list_for_each_entry(master, &timer->open_list_head, open_list) {
253ebfc6de2STakashi Iwai err = check_matching_master_slave(master, slave);
254ebfc6de2STakashi Iwai if (err != 0) /* match found or error */
255ebfc6de2STakashi Iwai goto out;
2561da177e4SLinus Torvalds }
2571da177e4SLinus Torvalds }
258ebfc6de2STakashi Iwai out:
259ebfc6de2STakashi Iwai return err < 0 ? err : 0;
2601da177e4SLinus Torvalds }
2611da177e4SLinus Torvalds
2621da177e4SLinus Torvalds /*
2631da177e4SLinus Torvalds * look for slave instances matching with the slave id of the given master.
2641da177e4SLinus Torvalds * when found, relink the open_link of slaves.
2651da177e4SLinus Torvalds *
2661da177e4SLinus Torvalds * call this with register_mutex down.
2671da177e4SLinus Torvalds */
snd_timer_check_master(struct snd_timer_instance * master)2689b7d869eSTakashi Iwai static int snd_timer_check_master(struct snd_timer_instance *master)
2691da177e4SLinus Torvalds {
2709244b2c3SJohannes Berg struct snd_timer_instance *slave, *tmp;
271ebfc6de2STakashi Iwai int err = 0;
2721da177e4SLinus Torvalds
2731da177e4SLinus Torvalds /* check all pending slaves */
2749244b2c3SJohannes Berg list_for_each_entry_safe(slave, tmp, &snd_timer_slave_list, open_list) {
275ebfc6de2STakashi Iwai err = check_matching_master_slave(master, slave);
276ebfc6de2STakashi Iwai if (err < 0)
277ebfc6de2STakashi Iwai break;
2781da177e4SLinus Torvalds }
279ebfc6de2STakashi Iwai return err < 0 ? err : 0;
2801da177e4SLinus Torvalds }
2811da177e4SLinus Torvalds
28233bbb8a0STakashi Iwai static void snd_timer_close_locked(struct snd_timer_instance *timeri,
283a3933186STakashi Iwai struct device **card_devp_to_put);
2849b7d869eSTakashi Iwai
2851da177e4SLinus Torvalds /*
2861da177e4SLinus Torvalds * open a timer instance
2871da177e4SLinus Torvalds * when opening a master, the slave id must be here given.
2881da177e4SLinus Torvalds */
snd_timer_open(struct snd_timer_instance * timeri,struct snd_timer_id * tid,unsigned int slave_id)2896a34367eSTakashi Iwai int snd_timer_open(struct snd_timer_instance *timeri,
2906a34367eSTakashi Iwai struct snd_timer_id *tid,
2911da177e4SLinus Torvalds unsigned int slave_id)
2921da177e4SLinus Torvalds {
29353d2f744STakashi Iwai struct snd_timer *timer;
294a3933186STakashi Iwai struct device *card_dev_to_put = NULL;
2959b7d869eSTakashi Iwai int err;
2961da177e4SLinus Torvalds
29741672c0cSTakashi Iwai mutex_lock(®ister_mutex);
2981da177e4SLinus Torvalds if (tid->dev_class == SNDRV_TIMER_CLASS_SLAVE) {
2991da177e4SLinus Torvalds /* open a slave instance */
3001da177e4SLinus Torvalds if (tid->dev_sclass <= SNDRV_TIMER_SCLASS_NONE ||
3011da177e4SLinus Torvalds tid->dev_sclass > SNDRV_TIMER_SCLASS_OSS_SEQUENCER) {
302cf74dcf3STakashi Iwai pr_debug("ALSA: timer: invalid slave class %i\n",
303cf74dcf3STakashi Iwai tid->dev_sclass);
30441672c0cSTakashi Iwai err = -EINVAL;
30541672c0cSTakashi Iwai goto unlock;
3061da177e4SLinus Torvalds }
307fdea53feSTakashi Iwai if (num_slaves >= MAX_SLAVE_INSTANCES) {
308fdea53feSTakashi Iwai err = -EBUSY;
309fdea53feSTakashi Iwai goto unlock;
310fdea53feSTakashi Iwai }
3111da177e4SLinus Torvalds timeri->slave_class = tid->dev_sclass;
3121da177e4SLinus Torvalds timeri->slave_id = tid->device;
3131da177e4SLinus Torvalds timeri->flags |= SNDRV_TIMER_IFLG_SLAVE;
3141da177e4SLinus Torvalds list_add_tail(&timeri->open_list, &snd_timer_slave_list);
315fdea53feSTakashi Iwai num_slaves++;
3169b7d869eSTakashi Iwai err = snd_timer_check_slave(timeri);
3170c4f09ceSTakashi Iwai goto list_added;
3181da177e4SLinus Torvalds }
3191da177e4SLinus Torvalds
3201da177e4SLinus Torvalds /* open a master instance */
3211da177e4SLinus Torvalds timer = snd_timer_find(tid);
322ee2da997SJohannes Berg #ifdef CONFIG_MODULES
323ee2da997SJohannes Berg if (!timer) {
3241a60d4c5SIngo Molnar mutex_unlock(®ister_mutex);
3251da177e4SLinus Torvalds snd_timer_request(tid);
3261a60d4c5SIngo Molnar mutex_lock(®ister_mutex);
3271da177e4SLinus Torvalds timer = snd_timer_find(tid);
3281da177e4SLinus Torvalds }
3291da177e4SLinus Torvalds #endif
3302fd43d11SClemens Ladisch if (!timer) {
33141672c0cSTakashi Iwai err = -ENODEV;
33241672c0cSTakashi Iwai goto unlock;
3332fd43d11SClemens Ladisch }
3341da177e4SLinus Torvalds if (!list_empty(&timer->open_list_head)) {
335e7af6307STakashi Iwai struct snd_timer_instance *t =
336e7af6307STakashi Iwai list_entry(timer->open_list_head.next,
33753d2f744STakashi Iwai struct snd_timer_instance, open_list);
338e7af6307STakashi Iwai if (t->flags & SNDRV_TIMER_IFLG_EXCLUSIVE) {
33941672c0cSTakashi Iwai err = -EBUSY;
34041672c0cSTakashi Iwai goto unlock;
3411da177e4SLinus Torvalds }
3421da177e4SLinus Torvalds }
3439b7d869eSTakashi Iwai if (timer->num_instances >= timer->max_instances) {
34441672c0cSTakashi Iwai err = -EBUSY;
34541672c0cSTakashi Iwai goto unlock;
3469b7d869eSTakashi Iwai }
3476a34367eSTakashi Iwai if (!try_module_get(timer->module)) {
3486a34367eSTakashi Iwai err = -EBUSY;
34941672c0cSTakashi Iwai goto unlock;
3502fd43d11SClemens Ladisch }
351230323daSTakashi Iwai /* take a card refcount for safe disconnection */
3526a34367eSTakashi Iwai if (timer->card) {
353230323daSTakashi Iwai get_device(&timer->card->card_dev);
3546a34367eSTakashi Iwai card_dev_to_put = &timer->card->card_dev;
3556a34367eSTakashi Iwai }
3568ddc0563SVegard Nossum
3578ddc0563SVegard Nossum if (list_empty(&timer->open_list_head) && timer->hw.open) {
35841672c0cSTakashi Iwai err = timer->hw.open(timer);
3598ddc0563SVegard Nossum if (err) {
3608ddc0563SVegard Nossum module_put(timer->module);
36141672c0cSTakashi Iwai goto unlock;
3628ddc0563SVegard Nossum }
3638ddc0563SVegard Nossum }
3648ddc0563SVegard Nossum
3656a34367eSTakashi Iwai timeri->timer = timer;
3666a34367eSTakashi Iwai timeri->slave_class = tid->dev_sclass;
3676a34367eSTakashi Iwai timeri->slave_id = slave_id;
3686a34367eSTakashi Iwai
3691da177e4SLinus Torvalds list_add_tail(&timeri->open_list, &timer->open_list_head);
3709b7d869eSTakashi Iwai timer->num_instances++;
3719b7d869eSTakashi Iwai err = snd_timer_check_master(timeri);
3720c4f09ceSTakashi Iwai list_added:
3736a34367eSTakashi Iwai if (err < 0)
374a3933186STakashi Iwai snd_timer_close_locked(timeri, &card_dev_to_put);
37541672c0cSTakashi Iwai
37641672c0cSTakashi Iwai unlock:
3771a60d4c5SIngo Molnar mutex_unlock(®ister_mutex);
378a3933186STakashi Iwai /* put_device() is called after unlock for avoiding deadlock */
3796a34367eSTakashi Iwai if (err < 0 && card_dev_to_put)
380a3933186STakashi Iwai put_device(card_dev_to_put);
3819b7d869eSTakashi Iwai return err;
3821da177e4SLinus Torvalds }
38398856392STakashi Iwai EXPORT_SYMBOL(snd_timer_open);
3841da177e4SLinus Torvalds
3851da177e4SLinus Torvalds /*
3861da177e4SLinus Torvalds * close a timer instance
3879b7d869eSTakashi Iwai * call this with register_mutex down.
3881da177e4SLinus Torvalds */
snd_timer_close_locked(struct snd_timer_instance * timeri,struct device ** card_devp_to_put)38933bbb8a0STakashi Iwai static void snd_timer_close_locked(struct snd_timer_instance *timeri,
390a3933186STakashi Iwai struct device **card_devp_to_put)
3911da177e4SLinus Torvalds {
392fe1b26c9STakashi Iwai struct snd_timer *timer = timeri->timer;
3939244b2c3SJohannes Berg struct snd_timer_instance *slave, *tmp;
3941da177e4SLinus Torvalds
395fe1b26c9STakashi Iwai if (timer) {
396fe1b26c9STakashi Iwai spin_lock_irq(&timer->lock);
397fe1b26c9STakashi Iwai timeri->flags |= SNDRV_TIMER_IFLG_DEAD;
398fe1b26c9STakashi Iwai spin_unlock_irq(&timer->lock);
399fe1b26c9STakashi Iwai }
400fe1b26c9STakashi Iwai
4016a34367eSTakashi Iwai if (!list_empty(&timeri->open_list)) {
4026a34367eSTakashi Iwai list_del_init(&timeri->open_list);
403fdea53feSTakashi Iwai if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
404fdea53feSTakashi Iwai num_slaves--;
4056a34367eSTakashi Iwai }
4069984d1b5STakashi Iwai
4071da177e4SLinus Torvalds /* force to stop the timer */
4081da177e4SLinus Torvalds snd_timer_stop(timeri);
4091da177e4SLinus Torvalds
4109984d1b5STakashi Iwai if (timer) {
4119b7d869eSTakashi Iwai timer->num_instances--;
4121da177e4SLinus Torvalds /* wait, until the active callback is finished */
4131da177e4SLinus Torvalds spin_lock_irq(&timer->lock);
4141da177e4SLinus Torvalds while (timeri->flags & SNDRV_TIMER_IFLG_CALLBACK) {
4151da177e4SLinus Torvalds spin_unlock_irq(&timer->lock);
4161da177e4SLinus Torvalds udelay(10);
4171da177e4SLinus Torvalds spin_lock_irq(&timer->lock);
4181da177e4SLinus Torvalds }
4191da177e4SLinus Torvalds spin_unlock_irq(&timer->lock);
4209984d1b5STakashi Iwai
4211da177e4SLinus Torvalds /* remove slave links */
422b5a663aaSTakashi Iwai spin_lock_irq(&slave_active_lock);
423b5a663aaSTakashi Iwai spin_lock(&timer->lock);
4246a34367eSTakashi Iwai timeri->timer = NULL;
4259244b2c3SJohannes Berg list_for_each_entry_safe(slave, tmp, &timeri->slave_list_head,
4269244b2c3SJohannes Berg open_list) {
4279244b2c3SJohannes Berg list_move_tail(&slave->open_list, &snd_timer_slave_list);
4289b7d869eSTakashi Iwai timer->num_instances--;
4291da177e4SLinus Torvalds slave->master = NULL;
4301da177e4SLinus Torvalds slave->timer = NULL;
431b5a663aaSTakashi Iwai list_del_init(&slave->ack_list);
432b5a663aaSTakashi Iwai list_del_init(&slave->active_list);
4331da177e4SLinus Torvalds }
434b5a663aaSTakashi Iwai spin_unlock(&timer->lock);
435b5a663aaSTakashi Iwai spin_unlock_irq(&slave_active_lock);
4369984d1b5STakashi Iwai
4379984d1b5STakashi Iwai /* slave doesn't need to release timer resources below */
4389984d1b5STakashi Iwai if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
4399984d1b5STakashi Iwai timer = NULL;
4401da177e4SLinus Torvalds }
4419984d1b5STakashi Iwai
4429984d1b5STakashi Iwai if (timer) {
4439984d1b5STakashi Iwai if (list_empty(&timer->open_list_head) && timer->hw.close)
4449984d1b5STakashi Iwai timer->hw.close(timer);
4459984d1b5STakashi Iwai /* release a card refcount for safe disconnection */
4469984d1b5STakashi Iwai if (timer->card)
447a3933186STakashi Iwai *card_devp_to_put = &timer->card->card_dev;
448de24214dSClemens Ladisch module_put(timer->module);
4499984d1b5STakashi Iwai }
4501da177e4SLinus Torvalds }
4519b7d869eSTakashi Iwai
4529b7d869eSTakashi Iwai /*
4539b7d869eSTakashi Iwai * close a timer instance
4549b7d869eSTakashi Iwai */
snd_timer_close(struct snd_timer_instance * timeri)45533bbb8a0STakashi Iwai void snd_timer_close(struct snd_timer_instance *timeri)
4569b7d869eSTakashi Iwai {
457a3933186STakashi Iwai struct device *card_dev_to_put = NULL;
4589b7d869eSTakashi Iwai
4599b7d869eSTakashi Iwai if (snd_BUG_ON(!timeri))
46033bbb8a0STakashi Iwai return;
4619b7d869eSTakashi Iwai
4629b7d869eSTakashi Iwai mutex_lock(®ister_mutex);
46333bbb8a0STakashi Iwai snd_timer_close_locked(timeri, &card_dev_to_put);
4649b7d869eSTakashi Iwai mutex_unlock(®ister_mutex);
465a3933186STakashi Iwai /* put_device() is called after unlock for avoiding deadlock */
466a3933186STakashi Iwai if (card_dev_to_put)
467a3933186STakashi Iwai put_device(card_dev_to_put);
4689b7d869eSTakashi Iwai }
46998856392STakashi Iwai EXPORT_SYMBOL(snd_timer_close);
4701da177e4SLinus Torvalds
snd_timer_hw_resolution(struct snd_timer * timer)471fdcb5761STakashi Iwai static unsigned long snd_timer_hw_resolution(struct snd_timer *timer)
472fdcb5761STakashi Iwai {
473fdcb5761STakashi Iwai if (timer->hw.c_resolution)
474fdcb5761STakashi Iwai return timer->hw.c_resolution(timer);
475fdcb5761STakashi Iwai else
476fdcb5761STakashi Iwai return timer->hw.resolution;
477fdcb5761STakashi Iwai }
478fdcb5761STakashi Iwai
snd_timer_resolution(struct snd_timer_instance * timeri)47953d2f744STakashi Iwai unsigned long snd_timer_resolution(struct snd_timer_instance *timeri)
4801da177e4SLinus Torvalds {
48153d2f744STakashi Iwai struct snd_timer * timer;
4829d4d207dSTakashi Iwai unsigned long ret = 0;
4839d4d207dSTakashi Iwai unsigned long flags;
4841da177e4SLinus Torvalds
4851da177e4SLinus Torvalds if (timeri == NULL)
4861da177e4SLinus Torvalds return 0;
487dd1f7ab8SMarkus Elfring timer = timeri->timer;
4889d4d207dSTakashi Iwai if (timer) {
4899d4d207dSTakashi Iwai spin_lock_irqsave(&timer->lock, flags);
4909d4d207dSTakashi Iwai ret = snd_timer_hw_resolution(timer);
4919d4d207dSTakashi Iwai spin_unlock_irqrestore(&timer->lock, flags);
4929d4d207dSTakashi Iwai }
4939d4d207dSTakashi Iwai return ret;
4941da177e4SLinus Torvalds }
49598856392STakashi Iwai EXPORT_SYMBOL(snd_timer_resolution);
4961da177e4SLinus Torvalds
snd_timer_notify1(struct snd_timer_instance * ti,int event)49753d2f744STakashi Iwai static void snd_timer_notify1(struct snd_timer_instance *ti, int event)
4981da177e4SLinus Torvalds {
4999d4d207dSTakashi Iwai struct snd_timer *timer = ti->timer;
5001da177e4SLinus Torvalds unsigned long resolution = 0;
50153d2f744STakashi Iwai struct snd_timer_instance *ts;
502fcae40c9SBaolin Wang struct timespec64 tstamp;
5031da177e4SLinus Torvalds
504b751eef1SJaroslav Kysela if (timer_tstamp_monotonic)
505fcae40c9SBaolin Wang ktime_get_ts64(&tstamp);
506b751eef1SJaroslav Kysela else
507fcae40c9SBaolin Wang ktime_get_real_ts64(&tstamp);
5087eaa943cSTakashi Iwai if (snd_BUG_ON(event < SNDRV_TIMER_EVENT_START ||
5097eaa943cSTakashi Iwai event > SNDRV_TIMER_EVENT_PAUSE))
5107eaa943cSTakashi Iwai return;
5119d4d207dSTakashi Iwai if (timer &&
5129d4d207dSTakashi Iwai (event == SNDRV_TIMER_EVENT_START ||
5139d4d207dSTakashi Iwai event == SNDRV_TIMER_EVENT_CONTINUE))
5149d4d207dSTakashi Iwai resolution = snd_timer_hw_resolution(timer);
5151da177e4SLinus Torvalds if (ti->ccallback)
516b30477d5SJaroslav Kysela ti->ccallback(ti, event, &tstamp, resolution);
5171da177e4SLinus Torvalds if (ti->flags & SNDRV_TIMER_IFLG_SLAVE)
5181da177e4SLinus Torvalds return;
5191da177e4SLinus Torvalds if (timer == NULL)
5201da177e4SLinus Torvalds return;
5211da177e4SLinus Torvalds if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE)
5221da177e4SLinus Torvalds return;
5239c1fe96bSTakashi Iwai event += 10; /* convert to SNDRV_TIMER_EVENT_MXXX */
5249244b2c3SJohannes Berg list_for_each_entry(ts, &ti->slave_active_head, active_list)
5251da177e4SLinus Torvalds if (ts->ccallback)
5269c1fe96bSTakashi Iwai ts->ccallback(ts, event, &tstamp, resolution);
5271da177e4SLinus Torvalds }
5281da177e4SLinus Torvalds
529f65e0d29STakashi Iwai /* start/continue a master timer */
snd_timer_start1(struct snd_timer_instance * timeri,bool start,unsigned long ticks)530f65e0d29STakashi Iwai static int snd_timer_start1(struct snd_timer_instance *timeri,
531f65e0d29STakashi Iwai bool start, unsigned long ticks)
5321da177e4SLinus Torvalds {
533f65e0d29STakashi Iwai struct snd_timer *timer;
534f65e0d29STakashi Iwai int result;
535f65e0d29STakashi Iwai unsigned long flags;
536f65e0d29STakashi Iwai
537f65e0d29STakashi Iwai timer = timeri->timer;
538f65e0d29STakashi Iwai if (!timer)
539f65e0d29STakashi Iwai return -EINVAL;
540f65e0d29STakashi Iwai
541f65e0d29STakashi Iwai spin_lock_irqsave(&timer->lock, flags);
542fe1b26c9STakashi Iwai if (timeri->flags & SNDRV_TIMER_IFLG_DEAD) {
543fe1b26c9STakashi Iwai result = -EINVAL;
544fe1b26c9STakashi Iwai goto unlock;
545fe1b26c9STakashi Iwai }
546f65e0d29STakashi Iwai if (timer->card && timer->card->shutdown) {
547f65e0d29STakashi Iwai result = -ENODEV;
548f65e0d29STakashi Iwai goto unlock;
549f65e0d29STakashi Iwai }
550f65e0d29STakashi Iwai if (timeri->flags & (SNDRV_TIMER_IFLG_RUNNING |
551f65e0d29STakashi Iwai SNDRV_TIMER_IFLG_START)) {
552f65e0d29STakashi Iwai result = -EBUSY;
553f65e0d29STakashi Iwai goto unlock;
554f65e0d29STakashi Iwai }
555f65e0d29STakashi Iwai
5562c95241aSTakashi Iwai /* check the actual time for the start tick;
5572c95241aSTakashi Iwai * bail out as error if it's way too low (< 100us)
5582c95241aSTakashi Iwai */
559*bb121128STakashi Iwai if (start && !(timer->hw.flags & SNDRV_TIMER_HW_SLAVE)) {
5602c95241aSTakashi Iwai if ((u64)snd_timer_hw_resolution(timer) * ticks < 100000) {
5612c95241aSTakashi Iwai result = -EINVAL;
5622c95241aSTakashi Iwai goto unlock;
5632c95241aSTakashi Iwai }
5642c95241aSTakashi Iwai }
5652c95241aSTakashi Iwai
566f65e0d29STakashi Iwai if (start)
567f65e0d29STakashi Iwai timeri->ticks = timeri->cticks = ticks;
568f65e0d29STakashi Iwai else if (!timeri->cticks)
569f65e0d29STakashi Iwai timeri->cticks = 1;
570f65e0d29STakashi Iwai timeri->pticks = 0;
571f65e0d29STakashi Iwai
5725b7c757dSNicolas Kaiser list_move_tail(&timeri->active_list, &timer->active_list_head);
5731da177e4SLinus Torvalds if (timer->running) {
5741da177e4SLinus Torvalds if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE)
5751da177e4SLinus Torvalds goto __start_now;
5761da177e4SLinus Torvalds timer->flags |= SNDRV_TIMER_FLG_RESCHED;
5771da177e4SLinus Torvalds timeri->flags |= SNDRV_TIMER_IFLG_START;
578f65e0d29STakashi Iwai result = 1; /* delayed start */
5791da177e4SLinus Torvalds } else {
580f65e0d29STakashi Iwai if (start)
581f65e0d29STakashi Iwai timer->sticks = ticks;
5821da177e4SLinus Torvalds timer->hw.start(timer);
5831da177e4SLinus Torvalds __start_now:
5841da177e4SLinus Torvalds timer->running++;
5851da177e4SLinus Torvalds timeri->flags |= SNDRV_TIMER_IFLG_RUNNING;
586f65e0d29STakashi Iwai result = 0;
5871da177e4SLinus Torvalds }
588f65e0d29STakashi Iwai snd_timer_notify1(timeri, start ? SNDRV_TIMER_EVENT_START :
589f65e0d29STakashi Iwai SNDRV_TIMER_EVENT_CONTINUE);
590f65e0d29STakashi Iwai unlock:
591f65e0d29STakashi Iwai spin_unlock_irqrestore(&timer->lock, flags);
592f65e0d29STakashi Iwai return result;
5931da177e4SLinus Torvalds }
5941da177e4SLinus Torvalds
595f65e0d29STakashi Iwai /* start/continue a slave timer */
snd_timer_start_slave(struct snd_timer_instance * timeri,bool start)596f65e0d29STakashi Iwai static int snd_timer_start_slave(struct snd_timer_instance *timeri,
597f65e0d29STakashi Iwai bool start)
5981da177e4SLinus Torvalds {
5991da177e4SLinus Torvalds unsigned long flags;
600fe1b26c9STakashi Iwai int err;
6011da177e4SLinus Torvalds
6021da177e4SLinus Torvalds spin_lock_irqsave(&slave_active_lock, flags);
603fe1b26c9STakashi Iwai if (timeri->flags & SNDRV_TIMER_IFLG_DEAD) {
604fe1b26c9STakashi Iwai err = -EINVAL;
605fe1b26c9STakashi Iwai goto unlock;
606fe1b26c9STakashi Iwai }
607f784beb7STakashi Iwai if (timeri->flags & SNDRV_TIMER_IFLG_RUNNING) {
608fe1b26c9STakashi Iwai err = -EBUSY;
609fe1b26c9STakashi Iwai goto unlock;
610f784beb7STakashi Iwai }
6111da177e4SLinus Torvalds timeri->flags |= SNDRV_TIMER_IFLG_RUNNING;
612b5a663aaSTakashi Iwai if (timeri->master && timeri->timer) {
613b5a663aaSTakashi Iwai spin_lock(&timeri->timer->lock);
6146b172a85SClemens Ladisch list_add_tail(&timeri->active_list,
6156b172a85SClemens Ladisch &timeri->master->slave_active_head);
616f65e0d29STakashi Iwai snd_timer_notify1(timeri, start ? SNDRV_TIMER_EVENT_START :
617f65e0d29STakashi Iwai SNDRV_TIMER_EVENT_CONTINUE);
618b5a663aaSTakashi Iwai spin_unlock(&timeri->timer->lock);
619b5a663aaSTakashi Iwai }
620fe1b26c9STakashi Iwai err = 1; /* delayed start */
621fe1b26c9STakashi Iwai unlock:
6221da177e4SLinus Torvalds spin_unlock_irqrestore(&slave_active_lock, flags);
623fe1b26c9STakashi Iwai return err;
6241da177e4SLinus Torvalds }
6251da177e4SLinus Torvalds
626f65e0d29STakashi Iwai /* stop/pause a master timer */
snd_timer_stop1(struct snd_timer_instance * timeri,bool stop)627f65e0d29STakashi Iwai static int snd_timer_stop1(struct snd_timer_instance *timeri, bool stop)
6281da177e4SLinus Torvalds {
62953d2f744STakashi Iwai struct snd_timer *timer;
630f65e0d29STakashi Iwai int result = 0;
6311da177e4SLinus Torvalds unsigned long flags;
6321da177e4SLinus Torvalds
6331da177e4SLinus Torvalds timer = timeri->timer;
6341da177e4SLinus Torvalds if (!timer)
6351da177e4SLinus Torvalds return -EINVAL;
6361da177e4SLinus Torvalds spin_lock_irqsave(&timer->lock, flags);
637c0317c0eSWang Wensheng list_del_init(&timeri->ack_list);
638c0317c0eSWang Wensheng list_del_init(&timeri->active_list);
639f784beb7STakashi Iwai if (!(timeri->flags & (SNDRV_TIMER_IFLG_RUNNING |
640f784beb7STakashi Iwai SNDRV_TIMER_IFLG_START))) {
641f65e0d29STakashi Iwai result = -EBUSY;
642f65e0d29STakashi Iwai goto unlock;
643f784beb7STakashi Iwai }
644f65e0d29STakashi Iwai if (timer->card && timer->card->shutdown)
645f65e0d29STakashi Iwai goto unlock;
646f65e0d29STakashi Iwai if (stop) {
647f65e0d29STakashi Iwai timeri->cticks = timeri->ticks;
648f65e0d29STakashi Iwai timeri->pticks = 0;
649230323daSTakashi Iwai }
6501da177e4SLinus Torvalds if ((timeri->flags & SNDRV_TIMER_IFLG_RUNNING) &&
6511da177e4SLinus Torvalds !(--timer->running)) {
6521da177e4SLinus Torvalds timer->hw.stop(timer);
6531da177e4SLinus Torvalds if (timer->flags & SNDRV_TIMER_FLG_RESCHED) {
6541da177e4SLinus Torvalds timer->flags &= ~SNDRV_TIMER_FLG_RESCHED;
6551da177e4SLinus Torvalds snd_timer_reschedule(timer, 0);
6561da177e4SLinus Torvalds if (timer->flags & SNDRV_TIMER_FLG_CHANGE) {
6571da177e4SLinus Torvalds timer->flags &= ~SNDRV_TIMER_FLG_CHANGE;
6581da177e4SLinus Torvalds timer->hw.start(timer);
6591da177e4SLinus Torvalds }
6601da177e4SLinus Torvalds }
6611da177e4SLinus Torvalds }
662c3b16813STakashi Iwai timeri->flags &= ~(SNDRV_TIMER_IFLG_RUNNING | SNDRV_TIMER_IFLG_START);
6639f8a7658STakashi Iwai if (stop)
6649f8a7658STakashi Iwai timeri->flags &= ~SNDRV_TIMER_IFLG_PAUSED;
6659f8a7658STakashi Iwai else
6669f8a7658STakashi Iwai timeri->flags |= SNDRV_TIMER_IFLG_PAUSED;
667f65e0d29STakashi Iwai snd_timer_notify1(timeri, stop ? SNDRV_TIMER_EVENT_STOP :
6683ae18097SBen Hutchings SNDRV_TIMER_EVENT_PAUSE);
669f65e0d29STakashi Iwai unlock:
6701da177e4SLinus Torvalds spin_unlock_irqrestore(&timer->lock, flags);
671f65e0d29STakashi Iwai return result;
672f65e0d29STakashi Iwai }
673f65e0d29STakashi Iwai
674f65e0d29STakashi Iwai /* stop/pause a slave timer */
snd_timer_stop_slave(struct snd_timer_instance * timeri,bool stop)675f65e0d29STakashi Iwai static int snd_timer_stop_slave(struct snd_timer_instance *timeri, bool stop)
676f65e0d29STakashi Iwai {
677f65e0d29STakashi Iwai unsigned long flags;
678ffdd9827STakashi Iwai bool running;
679f65e0d29STakashi Iwai
680f65e0d29STakashi Iwai spin_lock_irqsave(&slave_active_lock, flags);
681ffdd9827STakashi Iwai running = timeri->flags & SNDRV_TIMER_IFLG_RUNNING;
682f65e0d29STakashi Iwai timeri->flags &= ~SNDRV_TIMER_IFLG_RUNNING;
683f65e0d29STakashi Iwai if (timeri->timer) {
684f65e0d29STakashi Iwai spin_lock(&timeri->timer->lock);
685f65e0d29STakashi Iwai list_del_init(&timeri->ack_list);
686f65e0d29STakashi Iwai list_del_init(&timeri->active_list);
687ffdd9827STakashi Iwai if (running)
688f65e0d29STakashi Iwai snd_timer_notify1(timeri, stop ? SNDRV_TIMER_EVENT_STOP :
6893ae18097SBen Hutchings SNDRV_TIMER_EVENT_PAUSE);
690f65e0d29STakashi Iwai spin_unlock(&timeri->timer->lock);
691f65e0d29STakashi Iwai }
692f65e0d29STakashi Iwai spin_unlock_irqrestore(&slave_active_lock, flags);
693ffdd9827STakashi Iwai return running ? 0 : -EBUSY;
6941da177e4SLinus Torvalds }
6951da177e4SLinus Torvalds
6961da177e4SLinus Torvalds /*
697f65e0d29STakashi Iwai * start the timer instance
698f65e0d29STakashi Iwai */
snd_timer_start(struct snd_timer_instance * timeri,unsigned int ticks)699f65e0d29STakashi Iwai int snd_timer_start(struct snd_timer_instance *timeri, unsigned int ticks)
700f65e0d29STakashi Iwai {
701f65e0d29STakashi Iwai if (timeri == NULL || ticks < 1)
702f65e0d29STakashi Iwai return -EINVAL;
703f65e0d29STakashi Iwai if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
704f65e0d29STakashi Iwai return snd_timer_start_slave(timeri, true);
705f65e0d29STakashi Iwai else
706f65e0d29STakashi Iwai return snd_timer_start1(timeri, true, ticks);
707f65e0d29STakashi Iwai }
70898856392STakashi Iwai EXPORT_SYMBOL(snd_timer_start);
709f65e0d29STakashi Iwai
710f65e0d29STakashi Iwai /*
7111da177e4SLinus Torvalds * stop the timer instance.
7121da177e4SLinus Torvalds *
7131da177e4SLinus Torvalds * do not call this from the timer callback!
7141da177e4SLinus Torvalds */
snd_timer_stop(struct snd_timer_instance * timeri)71553d2f744STakashi Iwai int snd_timer_stop(struct snd_timer_instance *timeri)
7161da177e4SLinus Torvalds {
717f65e0d29STakashi Iwai if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
718f65e0d29STakashi Iwai return snd_timer_stop_slave(timeri, true);
719f65e0d29STakashi Iwai else
720f65e0d29STakashi Iwai return snd_timer_stop1(timeri, true);
7211da177e4SLinus Torvalds }
72298856392STakashi Iwai EXPORT_SYMBOL(snd_timer_stop);
7231da177e4SLinus Torvalds
7241da177e4SLinus Torvalds /*
7251da177e4SLinus Torvalds * start again.. the tick is kept.
7261da177e4SLinus Torvalds */
snd_timer_continue(struct snd_timer_instance * timeri)72753d2f744STakashi Iwai int snd_timer_continue(struct snd_timer_instance *timeri)
7281da177e4SLinus Torvalds {
7299f8a7658STakashi Iwai /* timer can continue only after pause */
7309f8a7658STakashi Iwai if (!(timeri->flags & SNDRV_TIMER_IFLG_PAUSED))
7319f8a7658STakashi Iwai return -EINVAL;
7329f8a7658STakashi Iwai
7331da177e4SLinus Torvalds if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
734f65e0d29STakashi Iwai return snd_timer_start_slave(timeri, false);
735f65e0d29STakashi Iwai else
736f65e0d29STakashi Iwai return snd_timer_start1(timeri, false, 0);
7371da177e4SLinus Torvalds }
73898856392STakashi Iwai EXPORT_SYMBOL(snd_timer_continue);
7391da177e4SLinus Torvalds
7401da177e4SLinus Torvalds /*
7411da177e4SLinus Torvalds * pause.. remember the ticks left
7421da177e4SLinus Torvalds */
snd_timer_pause(struct snd_timer_instance * timeri)74353d2f744STakashi Iwai int snd_timer_pause(struct snd_timer_instance * timeri)
7441da177e4SLinus Torvalds {
745f65e0d29STakashi Iwai if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
746f65e0d29STakashi Iwai return snd_timer_stop_slave(timeri, false);
747f65e0d29STakashi Iwai else
748f65e0d29STakashi Iwai return snd_timer_stop1(timeri, false);
7491da177e4SLinus Torvalds }
75098856392STakashi Iwai EXPORT_SYMBOL(snd_timer_pause);
7511da177e4SLinus Torvalds
7521da177e4SLinus Torvalds /*
7531da177e4SLinus Torvalds * reschedule the timer
7541da177e4SLinus Torvalds *
7551da177e4SLinus Torvalds * start pending instances and check the scheduling ticks.
7561da177e4SLinus Torvalds * when the scheduling ticks is changed set CHANGE flag to reprogram the timer.
7571da177e4SLinus Torvalds */
snd_timer_reschedule(struct snd_timer * timer,unsigned long ticks_left)75853d2f744STakashi Iwai static void snd_timer_reschedule(struct snd_timer * timer, unsigned long ticks_left)
7591da177e4SLinus Torvalds {
76053d2f744STakashi Iwai struct snd_timer_instance *ti;
7611da177e4SLinus Torvalds unsigned long ticks = ~0UL;
7621da177e4SLinus Torvalds
7639244b2c3SJohannes Berg list_for_each_entry(ti, &timer->active_list_head, active_list) {
7641da177e4SLinus Torvalds if (ti->flags & SNDRV_TIMER_IFLG_START) {
7651da177e4SLinus Torvalds ti->flags &= ~SNDRV_TIMER_IFLG_START;
7661da177e4SLinus Torvalds ti->flags |= SNDRV_TIMER_IFLG_RUNNING;
7671da177e4SLinus Torvalds timer->running++;
7681da177e4SLinus Torvalds }
7691da177e4SLinus Torvalds if (ti->flags & SNDRV_TIMER_IFLG_RUNNING) {
7701da177e4SLinus Torvalds if (ticks > ti->cticks)
7711da177e4SLinus Torvalds ticks = ti->cticks;
7721da177e4SLinus Torvalds }
7731da177e4SLinus Torvalds }
7741da177e4SLinus Torvalds if (ticks == ~0UL) {
7751da177e4SLinus Torvalds timer->flags &= ~SNDRV_TIMER_FLG_RESCHED;
7761da177e4SLinus Torvalds return;
7771da177e4SLinus Torvalds }
7781da177e4SLinus Torvalds if (ticks > timer->hw.ticks)
7791da177e4SLinus Torvalds ticks = timer->hw.ticks;
7801da177e4SLinus Torvalds if (ticks_left != ticks)
7811da177e4SLinus Torvalds timer->flags |= SNDRV_TIMER_FLG_CHANGE;
7821da177e4SLinus Torvalds timer->sticks = ticks;
7831da177e4SLinus Torvalds }
7841da177e4SLinus Torvalds
7858748b850STakashi Iwai /* call callbacks in timer ack list */
snd_timer_process_callbacks(struct snd_timer * timer,struct list_head * head)7868748b850STakashi Iwai static void snd_timer_process_callbacks(struct snd_timer *timer,
7878748b850STakashi Iwai struct list_head *head)
7881da177e4SLinus Torvalds {
78953d2f744STakashi Iwai struct snd_timer_instance *ti;
7901da177e4SLinus Torvalds unsigned long resolution, ticks;
7911da177e4SLinus Torvalds
7928748b850STakashi Iwai while (!list_empty(head)) {
7938748b850STakashi Iwai ti = list_first_entry(head, struct snd_timer_instance,
7948748b850STakashi Iwai ack_list);
7951da177e4SLinus Torvalds
7961da177e4SLinus Torvalds /* remove from ack_list and make empty */
797df55531bSTakashi Iwai list_del_init(&ti->ack_list);
7981da177e4SLinus Torvalds
799fe1b26c9STakashi Iwai if (!(ti->flags & SNDRV_TIMER_IFLG_DEAD)) {
8001da177e4SLinus Torvalds ticks = ti->pticks;
8011da177e4SLinus Torvalds ti->pticks = 0;
8021da177e4SLinus Torvalds resolution = ti->resolution;
8031da177e4SLinus Torvalds ti->flags |= SNDRV_TIMER_IFLG_CALLBACK;
8041da177e4SLinus Torvalds spin_unlock(&timer->lock);
8051da177e4SLinus Torvalds if (ti->callback)
8061da177e4SLinus Torvalds ti->callback(ti, resolution, ticks);
8071da177e4SLinus Torvalds spin_lock(&timer->lock);
8081da177e4SLinus Torvalds ti->flags &= ~SNDRV_TIMER_IFLG_CALLBACK;
8091da177e4SLinus Torvalds }
8101da177e4SLinus Torvalds }
8118748b850STakashi Iwai }
8128748b850STakashi Iwai
8137bb4a8a2STakashi Iwai /* clear pending instances from ack list */
snd_timer_clear_callbacks(struct snd_timer * timer,struct list_head * head)8147bb4a8a2STakashi Iwai static void snd_timer_clear_callbacks(struct snd_timer *timer,
8157bb4a8a2STakashi Iwai struct list_head *head)
8167bb4a8a2STakashi Iwai {
8177bb4a8a2STakashi Iwai unsigned long flags;
8187bb4a8a2STakashi Iwai
8197bb4a8a2STakashi Iwai spin_lock_irqsave(&timer->lock, flags);
8207bb4a8a2STakashi Iwai while (!list_empty(head))
8217bb4a8a2STakashi Iwai list_del_init(head->next);
8227bb4a8a2STakashi Iwai spin_unlock_irqrestore(&timer->lock, flags);
8237bb4a8a2STakashi Iwai }
8247bb4a8a2STakashi Iwai
8258748b850STakashi Iwai /*
826bf083595STakashi Iwai * timer work
8278748b850STakashi Iwai *
8288748b850STakashi Iwai */
snd_timer_work(struct work_struct * work)829bf083595STakashi Iwai static void snd_timer_work(struct work_struct *work)
8308748b850STakashi Iwai {
831bf083595STakashi Iwai struct snd_timer *timer = container_of(work, struct snd_timer, task_work);
8328748b850STakashi Iwai unsigned long flags;
8338748b850STakashi Iwai
8347bb4a8a2STakashi Iwai if (timer->card && timer->card->shutdown) {
8357bb4a8a2STakashi Iwai snd_timer_clear_callbacks(timer, &timer->sack_list_head);
8368748b850STakashi Iwai return;
8377bb4a8a2STakashi Iwai }
8388748b850STakashi Iwai
8398748b850STakashi Iwai spin_lock_irqsave(&timer->lock, flags);
8408748b850STakashi Iwai snd_timer_process_callbacks(timer, &timer->sack_list_head);
8412999ff5bSTakashi Iwai spin_unlock_irqrestore(&timer->lock, flags);
8421da177e4SLinus Torvalds }
8431da177e4SLinus Torvalds
8441da177e4SLinus Torvalds /*
8451da177e4SLinus Torvalds * timer interrupt
8461da177e4SLinus Torvalds *
8471da177e4SLinus Torvalds * ticks_left is usually equal to timer->sticks.
8481da177e4SLinus Torvalds *
8491da177e4SLinus Torvalds */
snd_timer_interrupt(struct snd_timer * timer,unsigned long ticks_left)85053d2f744STakashi Iwai void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left)
8511da177e4SLinus Torvalds {
8529244b2c3SJohannes Berg struct snd_timer_instance *ti, *ts, *tmp;
8538748b850STakashi Iwai unsigned long resolution;
8548748b850STakashi Iwai struct list_head *ack_list_head;
855b32425acSTakashi Iwai unsigned long flags;
856bf083595STakashi Iwai bool use_work = false;
8571da177e4SLinus Torvalds
8581da177e4SLinus Torvalds if (timer == NULL)
8591da177e4SLinus Torvalds return;
8601da177e4SLinus Torvalds
8617bb4a8a2STakashi Iwai if (timer->card && timer->card->shutdown) {
8627bb4a8a2STakashi Iwai snd_timer_clear_callbacks(timer, &timer->ack_list_head);
863230323daSTakashi Iwai return;
8647bb4a8a2STakashi Iwai }
865230323daSTakashi Iwai
866b32425acSTakashi Iwai spin_lock_irqsave(&timer->lock, flags);
8671da177e4SLinus Torvalds
8681da177e4SLinus Torvalds /* remember the current resolution */
869fdcb5761STakashi Iwai resolution = snd_timer_hw_resolution(timer);
8701da177e4SLinus Torvalds
8711da177e4SLinus Torvalds /* loop for all active instances
8729244b2c3SJohannes Berg * Here we cannot use list_for_each_entry because the active_list of a
8736b172a85SClemens Ladisch * processed instance is relinked to done_list_head before the callback
8746b172a85SClemens Ladisch * is called.
8751da177e4SLinus Torvalds */
8769244b2c3SJohannes Berg list_for_each_entry_safe(ti, tmp, &timer->active_list_head,
8779244b2c3SJohannes Berg active_list) {
878fe1b26c9STakashi Iwai if (ti->flags & SNDRV_TIMER_IFLG_DEAD)
879fe1b26c9STakashi Iwai continue;
8801da177e4SLinus Torvalds if (!(ti->flags & SNDRV_TIMER_IFLG_RUNNING))
8811da177e4SLinus Torvalds continue;
8821da177e4SLinus Torvalds ti->pticks += ticks_left;
8831da177e4SLinus Torvalds ti->resolution = resolution;
8841da177e4SLinus Torvalds if (ti->cticks < ticks_left)
8851da177e4SLinus Torvalds ti->cticks = 0;
8861da177e4SLinus Torvalds else
8871da177e4SLinus Torvalds ti->cticks -= ticks_left;
8881da177e4SLinus Torvalds if (ti->cticks) /* not expired */
8891da177e4SLinus Torvalds continue;
8901da177e4SLinus Torvalds if (ti->flags & SNDRV_TIMER_IFLG_AUTO) {
8911da177e4SLinus Torvalds ti->cticks = ti->ticks;
8921da177e4SLinus Torvalds } else {
8931da177e4SLinus Torvalds ti->flags &= ~SNDRV_TIMER_IFLG_RUNNING;
894094fd3beSTakashi Iwai --timer->running;
895ee8413b0STakashi Iwai list_del_init(&ti->active_list);
8961da177e4SLinus Torvalds }
897bf083595STakashi Iwai if ((timer->hw.flags & SNDRV_TIMER_HW_WORK) ||
8986b172a85SClemens Ladisch (ti->flags & SNDRV_TIMER_IFLG_FAST))
8996b172a85SClemens Ladisch ack_list_head = &timer->ack_list_head;
9006b172a85SClemens Ladisch else
9016b172a85SClemens Ladisch ack_list_head = &timer->sack_list_head;
9026b172a85SClemens Ladisch if (list_empty(&ti->ack_list))
9036b172a85SClemens Ladisch list_add_tail(&ti->ack_list, ack_list_head);
9049244b2c3SJohannes Berg list_for_each_entry(ts, &ti->slave_active_head, active_list) {
9051da177e4SLinus Torvalds ts->pticks = ti->pticks;
9061da177e4SLinus Torvalds ts->resolution = resolution;
9076b172a85SClemens Ladisch if (list_empty(&ts->ack_list))
9086b172a85SClemens Ladisch list_add_tail(&ts->ack_list, ack_list_head);
9091da177e4SLinus Torvalds }
9101da177e4SLinus Torvalds }
9111da177e4SLinus Torvalds if (timer->flags & SNDRV_TIMER_FLG_RESCHED)
912cd93fe47SClemens Ladisch snd_timer_reschedule(timer, timer->sticks);
9131da177e4SLinus Torvalds if (timer->running) {
9141da177e4SLinus Torvalds if (timer->hw.flags & SNDRV_TIMER_HW_STOP) {
9151da177e4SLinus Torvalds timer->hw.stop(timer);
9161da177e4SLinus Torvalds timer->flags |= SNDRV_TIMER_FLG_CHANGE;
9171da177e4SLinus Torvalds }
9181da177e4SLinus Torvalds if (!(timer->hw.flags & SNDRV_TIMER_HW_AUTO) ||
9191da177e4SLinus Torvalds (timer->flags & SNDRV_TIMER_FLG_CHANGE)) {
9201da177e4SLinus Torvalds /* restart timer */
9211da177e4SLinus Torvalds timer->flags &= ~SNDRV_TIMER_FLG_CHANGE;
9221da177e4SLinus Torvalds timer->hw.start(timer);
9231da177e4SLinus Torvalds }
9241da177e4SLinus Torvalds } else {
9251da177e4SLinus Torvalds timer->hw.stop(timer);
9261da177e4SLinus Torvalds }
9271da177e4SLinus Torvalds
9281da177e4SLinus Torvalds /* now process all fast callbacks */
9298748b850STakashi Iwai snd_timer_process_callbacks(timer, &timer->ack_list_head);
9301da177e4SLinus Torvalds
9311da177e4SLinus Torvalds /* do we have any slow callbacks? */
932bf083595STakashi Iwai use_work = !list_empty(&timer->sack_list_head);
933b32425acSTakashi Iwai spin_unlock_irqrestore(&timer->lock, flags);
9341da177e4SLinus Torvalds
935bf083595STakashi Iwai if (use_work)
936bf083595STakashi Iwai queue_work(system_highpri_wq, &timer->task_work);
9371da177e4SLinus Torvalds }
93898856392STakashi Iwai EXPORT_SYMBOL(snd_timer_interrupt);
9391da177e4SLinus Torvalds
9401da177e4SLinus Torvalds /*
9411da177e4SLinus Torvalds
9421da177e4SLinus Torvalds */
9431da177e4SLinus Torvalds
snd_timer_new(struct snd_card * card,char * id,struct snd_timer_id * tid,struct snd_timer ** rtimer)94453d2f744STakashi Iwai int snd_timer_new(struct snd_card *card, char *id, struct snd_timer_id *tid,
94553d2f744STakashi Iwai struct snd_timer **rtimer)
9461da177e4SLinus Torvalds {
94753d2f744STakashi Iwai struct snd_timer *timer;
9481da177e4SLinus Torvalds int err;
949f15ee210STakashi Iwai static const struct snd_device_ops ops = {
9501da177e4SLinus Torvalds .dev_free = snd_timer_dev_free,
9511da177e4SLinus Torvalds .dev_register = snd_timer_dev_register,
952c461482cSTakashi Iwai .dev_disconnect = snd_timer_dev_disconnect,
9531da177e4SLinus Torvalds };
9541da177e4SLinus Torvalds
9557eaa943cSTakashi Iwai if (snd_BUG_ON(!tid))
9567eaa943cSTakashi Iwai return -EINVAL;
957d10ee9c5SSrikanth K H if (tid->dev_class == SNDRV_TIMER_CLASS_CARD ||
958d10ee9c5SSrikanth K H tid->dev_class == SNDRV_TIMER_CLASS_PCM) {
959d10ee9c5SSrikanth K H if (WARN_ON(!card))
960d10ee9c5SSrikanth K H return -EINVAL;
961d10ee9c5SSrikanth K H }
9627eaa943cSTakashi Iwai if (rtimer)
9631da177e4SLinus Torvalds *rtimer = NULL;
964ca2c0966STakashi Iwai timer = kzalloc(sizeof(*timer), GFP_KERNEL);
965ec0e9937STakashi Iwai if (!timer)
9661da177e4SLinus Torvalds return -ENOMEM;
9671da177e4SLinus Torvalds timer->tmr_class = tid->dev_class;
9681da177e4SLinus Torvalds timer->card = card;
9691da177e4SLinus Torvalds timer->tmr_device = tid->device;
9701da177e4SLinus Torvalds timer->tmr_subdevice = tid->subdevice;
9711da177e4SLinus Torvalds if (id)
97275b1a8f9SJoe Perches strscpy(timer->id, id, sizeof(timer->id));
9736b760bb2SVegard Nossum timer->sticks = 1;
9741da177e4SLinus Torvalds INIT_LIST_HEAD(&timer->device_list);
9751da177e4SLinus Torvalds INIT_LIST_HEAD(&timer->open_list_head);
9761da177e4SLinus Torvalds INIT_LIST_HEAD(&timer->active_list_head);
9771da177e4SLinus Torvalds INIT_LIST_HEAD(&timer->ack_list_head);
9781da177e4SLinus Torvalds INIT_LIST_HEAD(&timer->sack_list_head);
9791da177e4SLinus Torvalds spin_lock_init(&timer->lock);
980bf083595STakashi Iwai INIT_WORK(&timer->task_work, snd_timer_work);
9819b7d869eSTakashi Iwai timer->max_instances = 1000; /* default limit per timer */
9821da177e4SLinus Torvalds if (card != NULL) {
983de24214dSClemens Ladisch timer->module = card->module;
9846b172a85SClemens Ladisch err = snd_device_new(card, SNDRV_DEV_TIMER, timer, &ops);
9856b172a85SClemens Ladisch if (err < 0) {
9861da177e4SLinus Torvalds snd_timer_free(timer);
9871da177e4SLinus Torvalds return err;
9881da177e4SLinus Torvalds }
9891da177e4SLinus Torvalds }
9907eaa943cSTakashi Iwai if (rtimer)
9911da177e4SLinus Torvalds *rtimer = timer;
9921da177e4SLinus Torvalds return 0;
9931da177e4SLinus Torvalds }
99498856392STakashi Iwai EXPORT_SYMBOL(snd_timer_new);
9951da177e4SLinus Torvalds
snd_timer_free(struct snd_timer * timer)99653d2f744STakashi Iwai static int snd_timer_free(struct snd_timer *timer)
9971da177e4SLinus Torvalds {
9987eaa943cSTakashi Iwai if (!timer)
9997eaa943cSTakashi Iwai return 0;
1000c461482cSTakashi Iwai
1001c461482cSTakashi Iwai mutex_lock(®ister_mutex);
1002c461482cSTakashi Iwai if (! list_empty(&timer->open_list_head)) {
1003c461482cSTakashi Iwai struct list_head *p, *n;
1004c461482cSTakashi Iwai struct snd_timer_instance *ti;
1005cf74dcf3STakashi Iwai pr_warn("ALSA: timer %p is busy?\n", timer);
1006c461482cSTakashi Iwai list_for_each_safe(p, n, &timer->open_list_head) {
1007c461482cSTakashi Iwai list_del_init(p);
1008c461482cSTakashi Iwai ti = list_entry(p, struct snd_timer_instance, open_list);
1009c461482cSTakashi Iwai ti->timer = NULL;
1010c461482cSTakashi Iwai }
1011c461482cSTakashi Iwai }
1012c461482cSTakashi Iwai list_del(&timer->device_list);
1013c461482cSTakashi Iwai mutex_unlock(®ister_mutex);
1014c461482cSTakashi Iwai
10151da177e4SLinus Torvalds if (timer->private_free)
10161da177e4SLinus Torvalds timer->private_free(timer);
10171da177e4SLinus Torvalds kfree(timer);
10181da177e4SLinus Torvalds return 0;
10191da177e4SLinus Torvalds }
10201da177e4SLinus Torvalds
snd_timer_dev_free(struct snd_device * device)102153d2f744STakashi Iwai static int snd_timer_dev_free(struct snd_device *device)
10221da177e4SLinus Torvalds {
102353d2f744STakashi Iwai struct snd_timer *timer = device->device_data;
10241da177e4SLinus Torvalds return snd_timer_free(timer);
10251da177e4SLinus Torvalds }
10261da177e4SLinus Torvalds
snd_timer_dev_register(struct snd_device * dev)102753d2f744STakashi Iwai static int snd_timer_dev_register(struct snd_device *dev)
10281da177e4SLinus Torvalds {
102953d2f744STakashi Iwai struct snd_timer *timer = dev->device_data;
103053d2f744STakashi Iwai struct snd_timer *timer1;
10311da177e4SLinus Torvalds
10327eaa943cSTakashi Iwai if (snd_BUG_ON(!timer || !timer->hw.start || !timer->hw.stop))
10337eaa943cSTakashi Iwai return -ENXIO;
10341da177e4SLinus Torvalds if (!(timer->hw.flags & SNDRV_TIMER_HW_SLAVE) &&
10351da177e4SLinus Torvalds !timer->hw.resolution && timer->hw.c_resolution == NULL)
10361da177e4SLinus Torvalds return -EINVAL;
10371da177e4SLinus Torvalds
10381a60d4c5SIngo Molnar mutex_lock(®ister_mutex);
10399244b2c3SJohannes Berg list_for_each_entry(timer1, &snd_timer_list, device_list) {
10401da177e4SLinus Torvalds if (timer1->tmr_class > timer->tmr_class)
10411da177e4SLinus Torvalds break;
10421da177e4SLinus Torvalds if (timer1->tmr_class < timer->tmr_class)
10431da177e4SLinus Torvalds continue;
10441da177e4SLinus Torvalds if (timer1->card && timer->card) {
10451da177e4SLinus Torvalds if (timer1->card->number > timer->card->number)
10461da177e4SLinus Torvalds break;
10471da177e4SLinus Torvalds if (timer1->card->number < timer->card->number)
10481da177e4SLinus Torvalds continue;
10491da177e4SLinus Torvalds }
10501da177e4SLinus Torvalds if (timer1->tmr_device > timer->tmr_device)
10511da177e4SLinus Torvalds break;
10521da177e4SLinus Torvalds if (timer1->tmr_device < timer->tmr_device)
10531da177e4SLinus Torvalds continue;
10541da177e4SLinus Torvalds if (timer1->tmr_subdevice > timer->tmr_subdevice)
10551da177e4SLinus Torvalds break;
10561da177e4SLinus Torvalds if (timer1->tmr_subdevice < timer->tmr_subdevice)
10571da177e4SLinus Torvalds continue;
10581da177e4SLinus Torvalds /* conflicts.. */
10591a60d4c5SIngo Molnar mutex_unlock(®ister_mutex);
10601da177e4SLinus Torvalds return -EBUSY;
10611da177e4SLinus Torvalds }
10629244b2c3SJohannes Berg list_add_tail(&timer->device_list, &timer1->device_list);
10631a60d4c5SIngo Molnar mutex_unlock(®ister_mutex);
10641da177e4SLinus Torvalds return 0;
10651da177e4SLinus Torvalds }
10661da177e4SLinus Torvalds
snd_timer_dev_disconnect(struct snd_device * device)1067c461482cSTakashi Iwai static int snd_timer_dev_disconnect(struct snd_device *device)
10681da177e4SLinus Torvalds {
106953d2f744STakashi Iwai struct snd_timer *timer = device->device_data;
1070230323daSTakashi Iwai struct snd_timer_instance *ti;
1071230323daSTakashi Iwai
1072c461482cSTakashi Iwai mutex_lock(®ister_mutex);
1073c461482cSTakashi Iwai list_del_init(&timer->device_list);
1074230323daSTakashi Iwai /* wake up pending sleepers */
1075230323daSTakashi Iwai list_for_each_entry(ti, &timer->open_list_head, open_list) {
107640ed9444STakashi Iwai if (ti->disconnect)
107740ed9444STakashi Iwai ti->disconnect(ti);
1078230323daSTakashi Iwai }
1079c461482cSTakashi Iwai mutex_unlock(®ister_mutex);
1080c461482cSTakashi Iwai return 0;
10811da177e4SLinus Torvalds }
10821da177e4SLinus Torvalds
snd_timer_notify(struct snd_timer * timer,int event,struct timespec64 * tstamp)1083fcae40c9SBaolin Wang void snd_timer_notify(struct snd_timer *timer, int event, struct timespec64 *tstamp)
10841da177e4SLinus Torvalds {
10851da177e4SLinus Torvalds unsigned long flags;
10861da177e4SLinus Torvalds unsigned long resolution = 0;
108753d2f744STakashi Iwai struct snd_timer_instance *ti, *ts;
10881da177e4SLinus Torvalds
1089230323daSTakashi Iwai if (timer->card && timer->card->shutdown)
1090230323daSTakashi Iwai return;
10917c22f1aaSTakashi Iwai if (! (timer->hw.flags & SNDRV_TIMER_HW_SLAVE))
10927c22f1aaSTakashi Iwai return;
10937eaa943cSTakashi Iwai if (snd_BUG_ON(event < SNDRV_TIMER_EVENT_MSTART ||
10947eaa943cSTakashi Iwai event > SNDRV_TIMER_EVENT_MRESUME))
10957eaa943cSTakashi Iwai return;
10961da177e4SLinus Torvalds spin_lock_irqsave(&timer->lock, flags);
1097a501dfa3SJaroslav Kysela if (event == SNDRV_TIMER_EVENT_MSTART ||
1098a501dfa3SJaroslav Kysela event == SNDRV_TIMER_EVENT_MCONTINUE ||
1099fdcb5761STakashi Iwai event == SNDRV_TIMER_EVENT_MRESUME)
1100fdcb5761STakashi Iwai resolution = snd_timer_hw_resolution(timer);
11019244b2c3SJohannes Berg list_for_each_entry(ti, &timer->active_list_head, active_list) {
11021da177e4SLinus Torvalds if (ti->ccallback)
11031da177e4SLinus Torvalds ti->ccallback(ti, event, tstamp, resolution);
11049244b2c3SJohannes Berg list_for_each_entry(ts, &ti->slave_active_head, active_list)
11051da177e4SLinus Torvalds if (ts->ccallback)
11061da177e4SLinus Torvalds ts->ccallback(ts, event, tstamp, resolution);
11071da177e4SLinus Torvalds }
11081da177e4SLinus Torvalds spin_unlock_irqrestore(&timer->lock, flags);
11091da177e4SLinus Torvalds }
111098856392STakashi Iwai EXPORT_SYMBOL(snd_timer_notify);
11111da177e4SLinus Torvalds
11121da177e4SLinus Torvalds /*
11131da177e4SLinus Torvalds * exported functions for global timers
11141da177e4SLinus Torvalds */
snd_timer_global_new(char * id,int device,struct snd_timer ** rtimer)111553d2f744STakashi Iwai int snd_timer_global_new(char *id, int device, struct snd_timer **rtimer)
11161da177e4SLinus Torvalds {
111753d2f744STakashi Iwai struct snd_timer_id tid;
11181da177e4SLinus Torvalds
11191da177e4SLinus Torvalds tid.dev_class = SNDRV_TIMER_CLASS_GLOBAL;
11201da177e4SLinus Torvalds tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE;
11211da177e4SLinus Torvalds tid.card = -1;
11221da177e4SLinus Torvalds tid.device = device;
11231da177e4SLinus Torvalds tid.subdevice = 0;
11241da177e4SLinus Torvalds return snd_timer_new(NULL, id, &tid, rtimer);
11251da177e4SLinus Torvalds }
112698856392STakashi Iwai EXPORT_SYMBOL(snd_timer_global_new);
11271da177e4SLinus Torvalds
snd_timer_global_free(struct snd_timer * timer)112853d2f744STakashi Iwai int snd_timer_global_free(struct snd_timer *timer)
11291da177e4SLinus Torvalds {
11301da177e4SLinus Torvalds return snd_timer_free(timer);
11311da177e4SLinus Torvalds }
113298856392STakashi Iwai EXPORT_SYMBOL(snd_timer_global_free);
11331da177e4SLinus Torvalds
snd_timer_global_register(struct snd_timer * timer)113453d2f744STakashi Iwai int snd_timer_global_register(struct snd_timer *timer)
11351da177e4SLinus Torvalds {
113653d2f744STakashi Iwai struct snd_device dev;
11371da177e4SLinus Torvalds
11381da177e4SLinus Torvalds memset(&dev, 0, sizeof(dev));
11391da177e4SLinus Torvalds dev.device_data = timer;
11401da177e4SLinus Torvalds return snd_timer_dev_register(&dev);
11411da177e4SLinus Torvalds }
114298856392STakashi Iwai EXPORT_SYMBOL(snd_timer_global_register);
11431da177e4SLinus Torvalds
11441da177e4SLinus Torvalds /*
11451da177e4SLinus Torvalds * System timer
11461da177e4SLinus Torvalds */
11471da177e4SLinus Torvalds
11481da177e4SLinus Torvalds struct snd_timer_system_private {
11491da177e4SLinus Torvalds struct timer_list tlist;
115038e9a80fSKees Cook struct snd_timer *snd_timer;
11511da177e4SLinus Torvalds unsigned long last_expires;
11521da177e4SLinus Torvalds unsigned long last_jiffies;
11531da177e4SLinus Torvalds unsigned long correction;
11541da177e4SLinus Torvalds };
11551da177e4SLinus Torvalds
snd_timer_s_function(struct timer_list * t)115638e9a80fSKees Cook static void snd_timer_s_function(struct timer_list *t)
11571da177e4SLinus Torvalds {
115838e9a80fSKees Cook struct snd_timer_system_private *priv = from_timer(priv, t,
115938e9a80fSKees Cook tlist);
116038e9a80fSKees Cook struct snd_timer *timer = priv->snd_timer;
11611da177e4SLinus Torvalds unsigned long jiff = jiffies;
11621da177e4SLinus Torvalds if (time_after(jiff, priv->last_expires))
11636ed5eff0SClemens Ladisch priv->correction += (long)jiff - (long)priv->last_expires;
11641da177e4SLinus Torvalds snd_timer_interrupt(timer, (long)jiff - (long)priv->last_jiffies);
11651da177e4SLinus Torvalds }
11661da177e4SLinus Torvalds
snd_timer_s_start(struct snd_timer * timer)116753d2f744STakashi Iwai static int snd_timer_s_start(struct snd_timer * timer)
11681da177e4SLinus Torvalds {
11691da177e4SLinus Torvalds struct snd_timer_system_private *priv;
11701da177e4SLinus Torvalds unsigned long njiff;
11711da177e4SLinus Torvalds
11721da177e4SLinus Torvalds priv = (struct snd_timer_system_private *) timer->private_data;
11731da177e4SLinus Torvalds njiff = (priv->last_jiffies = jiffies);
11741da177e4SLinus Torvalds if (priv->correction > timer->sticks - 1) {
11751da177e4SLinus Torvalds priv->correction -= timer->sticks - 1;
11761da177e4SLinus Torvalds njiff++;
11771da177e4SLinus Torvalds } else {
11781da177e4SLinus Torvalds njiff += timer->sticks - priv->correction;
117917f48ec3SClemens Ladisch priv->correction = 0;
11801da177e4SLinus Torvalds }
11814a07083eSTakashi Iwai priv->last_expires = njiff;
11824a07083eSTakashi Iwai mod_timer(&priv->tlist, njiff);
11831da177e4SLinus Torvalds return 0;
11841da177e4SLinus Torvalds }
11851da177e4SLinus Torvalds
snd_timer_s_stop(struct snd_timer * timer)118653d2f744STakashi Iwai static int snd_timer_s_stop(struct snd_timer * timer)
11871da177e4SLinus Torvalds {
11881da177e4SLinus Torvalds struct snd_timer_system_private *priv;
11891da177e4SLinus Torvalds unsigned long jiff;
11901da177e4SLinus Torvalds
11911da177e4SLinus Torvalds priv = (struct snd_timer_system_private *) timer->private_data;
11921da177e4SLinus Torvalds del_timer(&priv->tlist);
11931da177e4SLinus Torvalds jiff = jiffies;
11941da177e4SLinus Torvalds if (time_before(jiff, priv->last_expires))
11951da177e4SLinus Torvalds timer->sticks = priv->last_expires - jiff;
11961da177e4SLinus Torvalds else
11971da177e4SLinus Torvalds timer->sticks = 1;
1198de2696d8SClemens Ladisch priv->correction = 0;
11991da177e4SLinus Torvalds return 0;
12001da177e4SLinus Torvalds }
12011da177e4SLinus Torvalds
snd_timer_s_close(struct snd_timer * timer)1202f146357fSTakashi Iwai static int snd_timer_s_close(struct snd_timer *timer)
1203f146357fSTakashi Iwai {
1204f146357fSTakashi Iwai struct snd_timer_system_private *priv;
1205f146357fSTakashi Iwai
1206f146357fSTakashi Iwai priv = (struct snd_timer_system_private *)timer->private_data;
1207f146357fSTakashi Iwai del_timer_sync(&priv->tlist);
1208f146357fSTakashi Iwai return 0;
1209f146357fSTakashi Iwai }
1210f146357fSTakashi Iwai
1211df76996aSTakashi Iwai static const struct snd_timer_hardware snd_timer_system =
12121da177e4SLinus Torvalds {
1213bf083595STakashi Iwai .flags = SNDRV_TIMER_HW_FIRST | SNDRV_TIMER_HW_WORK,
12141da177e4SLinus Torvalds .resolution = 1000000000L / HZ,
12151da177e4SLinus Torvalds .ticks = 10000000L,
1216f146357fSTakashi Iwai .close = snd_timer_s_close,
12171da177e4SLinus Torvalds .start = snd_timer_s_start,
12181da177e4SLinus Torvalds .stop = snd_timer_s_stop
12191da177e4SLinus Torvalds };
12201da177e4SLinus Torvalds
snd_timer_free_system(struct snd_timer * timer)122153d2f744STakashi Iwai static void snd_timer_free_system(struct snd_timer *timer)
12221da177e4SLinus Torvalds {
12231da177e4SLinus Torvalds kfree(timer->private_data);
12241da177e4SLinus Torvalds }
12251da177e4SLinus Torvalds
snd_timer_register_system(void)12261da177e4SLinus Torvalds static int snd_timer_register_system(void)
12271da177e4SLinus Torvalds {
122853d2f744STakashi Iwai struct snd_timer *timer;
12291da177e4SLinus Torvalds struct snd_timer_system_private *priv;
12301da177e4SLinus Torvalds int err;
12311da177e4SLinus Torvalds
12326b172a85SClemens Ladisch err = snd_timer_global_new("system", SNDRV_TIMER_GLOBAL_SYSTEM, &timer);
12336b172a85SClemens Ladisch if (err < 0)
12341da177e4SLinus Torvalds return err;
12351da177e4SLinus Torvalds strcpy(timer->name, "system timer");
12361da177e4SLinus Torvalds timer->hw = snd_timer_system;
1237ca2c0966STakashi Iwai priv = kzalloc(sizeof(*priv), GFP_KERNEL);
12381da177e4SLinus Torvalds if (priv == NULL) {
12391da177e4SLinus Torvalds snd_timer_free(timer);
12401da177e4SLinus Torvalds return -ENOMEM;
12411da177e4SLinus Torvalds }
124238e9a80fSKees Cook priv->snd_timer = timer;
124338e9a80fSKees Cook timer_setup(&priv->tlist, snd_timer_s_function, 0);
12441da177e4SLinus Torvalds timer->private_data = priv;
12451da177e4SLinus Torvalds timer->private_free = snd_timer_free_system;
12461da177e4SLinus Torvalds return snd_timer_global_register(timer);
12471da177e4SLinus Torvalds }
12481da177e4SLinus Torvalds
1249cd6a6503SJie Yang #ifdef CONFIG_SND_PROC_FS
12501da177e4SLinus Torvalds /*
12511da177e4SLinus Torvalds * Info interface
12521da177e4SLinus Torvalds */
12531da177e4SLinus Torvalds
snd_timer_proc_read(struct snd_info_entry * entry,struct snd_info_buffer * buffer)125453d2f744STakashi Iwai static void snd_timer_proc_read(struct snd_info_entry *entry,
125553d2f744STakashi Iwai struct snd_info_buffer *buffer)
12561da177e4SLinus Torvalds {
125753d2f744STakashi Iwai struct snd_timer *timer;
125853d2f744STakashi Iwai struct snd_timer_instance *ti;
12596cc84450SOswald Buddenhagen unsigned long resolution;
12601da177e4SLinus Torvalds
12611a60d4c5SIngo Molnar mutex_lock(®ister_mutex);
12629244b2c3SJohannes Berg list_for_each_entry(timer, &snd_timer_list, device_list) {
1263230323daSTakashi Iwai if (timer->card && timer->card->shutdown)
1264230323daSTakashi Iwai continue;
12651da177e4SLinus Torvalds switch (timer->tmr_class) {
12661da177e4SLinus Torvalds case SNDRV_TIMER_CLASS_GLOBAL:
12671da177e4SLinus Torvalds snd_iprintf(buffer, "G%i: ", timer->tmr_device);
12681da177e4SLinus Torvalds break;
12691da177e4SLinus Torvalds case SNDRV_TIMER_CLASS_CARD:
12706b172a85SClemens Ladisch snd_iprintf(buffer, "C%i-%i: ",
12716b172a85SClemens Ladisch timer->card->number, timer->tmr_device);
12721da177e4SLinus Torvalds break;
12731da177e4SLinus Torvalds case SNDRV_TIMER_CLASS_PCM:
12746b172a85SClemens Ladisch snd_iprintf(buffer, "P%i-%i-%i: ", timer->card->number,
12756b172a85SClemens Ladisch timer->tmr_device, timer->tmr_subdevice);
12761da177e4SLinus Torvalds break;
12771da177e4SLinus Torvalds default:
12786b172a85SClemens Ladisch snd_iprintf(buffer, "?%i-%i-%i-%i: ", timer->tmr_class,
12796b172a85SClemens Ladisch timer->card ? timer->card->number : -1,
12806b172a85SClemens Ladisch timer->tmr_device, timer->tmr_subdevice);
12811da177e4SLinus Torvalds }
12821da177e4SLinus Torvalds snd_iprintf(buffer, "%s :", timer->name);
12836cc84450SOswald Buddenhagen spin_lock_irq(&timer->lock);
12846cc84450SOswald Buddenhagen resolution = snd_timer_hw_resolution(timer);
12856cc84450SOswald Buddenhagen spin_unlock_irq(&timer->lock);
12866cc84450SOswald Buddenhagen if (resolution)
12876b172a85SClemens Ladisch snd_iprintf(buffer, " %lu.%03luus (%lu ticks)",
12886cc84450SOswald Buddenhagen resolution / 1000,
12896cc84450SOswald Buddenhagen resolution % 1000,
12906b172a85SClemens Ladisch timer->hw.ticks);
12911da177e4SLinus Torvalds if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE)
12921da177e4SLinus Torvalds snd_iprintf(buffer, " SLAVE");
12931da177e4SLinus Torvalds snd_iprintf(buffer, "\n");
12949244b2c3SJohannes Berg list_for_each_entry(ti, &timer->open_list_head, open_list)
12956b172a85SClemens Ladisch snd_iprintf(buffer, " Client %s : %s\n",
12961da177e4SLinus Torvalds ti->owner ? ti->owner : "unknown",
12973bcf8eebSPierre-Louis Bossart (ti->flags & (SNDRV_TIMER_IFLG_START |
12983bcf8eebSPierre-Louis Bossart SNDRV_TIMER_IFLG_RUNNING))
12996b172a85SClemens Ladisch ? "running" : "stopped");
13001da177e4SLinus Torvalds }
13011a60d4c5SIngo Molnar mutex_unlock(®ister_mutex);
13021da177e4SLinus Torvalds }
13031da177e4SLinus Torvalds
13046581f4e7STakashi Iwai static struct snd_info_entry *snd_timer_proc_entry;
1305e28563ccSTakashi Iwai
snd_timer_proc_init(void)1306e28563ccSTakashi Iwai static void __init snd_timer_proc_init(void)
1307e28563ccSTakashi Iwai {
1308e28563ccSTakashi Iwai struct snd_info_entry *entry;
1309e28563ccSTakashi Iwai
1310e28563ccSTakashi Iwai entry = snd_info_create_module_entry(THIS_MODULE, "timers", NULL);
1311e28563ccSTakashi Iwai if (entry != NULL) {
1312e28563ccSTakashi Iwai entry->c.text.read = snd_timer_proc_read;
1313e28563ccSTakashi Iwai if (snd_info_register(entry) < 0) {
1314e28563ccSTakashi Iwai snd_info_free_entry(entry);
1315e28563ccSTakashi Iwai entry = NULL;
1316e28563ccSTakashi Iwai }
1317e28563ccSTakashi Iwai }
1318e28563ccSTakashi Iwai snd_timer_proc_entry = entry;
1319e28563ccSTakashi Iwai }
1320e28563ccSTakashi Iwai
snd_timer_proc_done(void)1321e28563ccSTakashi Iwai static void __exit snd_timer_proc_done(void)
1322e28563ccSTakashi Iwai {
1323746d4a02STakashi Iwai snd_info_free_entry(snd_timer_proc_entry);
1324e28563ccSTakashi Iwai }
1325cd6a6503SJie Yang #else /* !CONFIG_SND_PROC_FS */
1326e28563ccSTakashi Iwai #define snd_timer_proc_init()
1327e28563ccSTakashi Iwai #define snd_timer_proc_done()
1328e28563ccSTakashi Iwai #endif
1329e28563ccSTakashi Iwai
13301da177e4SLinus Torvalds /*
13311da177e4SLinus Torvalds * USER SPACE interface
13321da177e4SLinus Torvalds */
13331da177e4SLinus Torvalds
snd_timer_user_interrupt(struct snd_timer_instance * timeri,unsigned long resolution,unsigned long ticks)133453d2f744STakashi Iwai static void snd_timer_user_interrupt(struct snd_timer_instance *timeri,
13351da177e4SLinus Torvalds unsigned long resolution,
13361da177e4SLinus Torvalds unsigned long ticks)
13371da177e4SLinus Torvalds {
133853d2f744STakashi Iwai struct snd_timer_user *tu = timeri->callback_data;
133953d2f744STakashi Iwai struct snd_timer_read *r;
13401da177e4SLinus Torvalds int prev;
13411da177e4SLinus Torvalds
13421da177e4SLinus Torvalds spin_lock(&tu->qlock);
13431da177e4SLinus Torvalds if (tu->qused > 0) {
13441da177e4SLinus Torvalds prev = tu->qtail == 0 ? tu->queue_size - 1 : tu->qtail - 1;
13451da177e4SLinus Torvalds r = &tu->queue[prev];
13461da177e4SLinus Torvalds if (r->resolution == resolution) {
13471da177e4SLinus Torvalds r->ticks += ticks;
13481da177e4SLinus Torvalds goto __wake;
13491da177e4SLinus Torvalds }
13501da177e4SLinus Torvalds }
13511da177e4SLinus Torvalds if (tu->qused >= tu->queue_size) {
13521da177e4SLinus Torvalds tu->overrun++;
13531da177e4SLinus Torvalds } else {
13541da177e4SLinus Torvalds r = &tu->queue[tu->qtail++];
13551da177e4SLinus Torvalds tu->qtail %= tu->queue_size;
13561da177e4SLinus Torvalds r->resolution = resolution;
13571da177e4SLinus Torvalds r->ticks = ticks;
13581da177e4SLinus Torvalds tu->qused++;
13591da177e4SLinus Torvalds }
13601da177e4SLinus Torvalds __wake:
13611da177e4SLinus Torvalds spin_unlock(&tu->qlock);
136295cc637cSTakashi Iwai snd_kill_fasync(tu->fasync, SIGIO, POLL_IN);
13631da177e4SLinus Torvalds wake_up(&tu->qchange_sleep);
13641da177e4SLinus Torvalds }
13651da177e4SLinus Torvalds
snd_timer_user_append_to_tqueue(struct snd_timer_user * tu,struct snd_timer_tread64 * tread)136653d2f744STakashi Iwai static void snd_timer_user_append_to_tqueue(struct snd_timer_user *tu,
136707094ae6SBaolin Wang struct snd_timer_tread64 *tread)
13681da177e4SLinus Torvalds {
13691da177e4SLinus Torvalds if (tu->qused >= tu->queue_size) {
13701da177e4SLinus Torvalds tu->overrun++;
13711da177e4SLinus Torvalds } else {
13721da177e4SLinus Torvalds memcpy(&tu->tqueue[tu->qtail++], tread, sizeof(*tread));
13731da177e4SLinus Torvalds tu->qtail %= tu->queue_size;
13741da177e4SLinus Torvalds tu->qused++;
13751da177e4SLinus Torvalds }
13761da177e4SLinus Torvalds }
13771da177e4SLinus Torvalds
snd_timer_user_ccallback(struct snd_timer_instance * timeri,int event,struct timespec64 * tstamp,unsigned long resolution)137853d2f744STakashi Iwai static void snd_timer_user_ccallback(struct snd_timer_instance *timeri,
137953d2f744STakashi Iwai int event,
1380fcae40c9SBaolin Wang struct timespec64 *tstamp,
13811da177e4SLinus Torvalds unsigned long resolution)
13821da177e4SLinus Torvalds {
138353d2f744STakashi Iwai struct snd_timer_user *tu = timeri->callback_data;
138407094ae6SBaolin Wang struct snd_timer_tread64 r1;
1385bfe70783SDan Carpenter unsigned long flags;
13861da177e4SLinus Torvalds
13876b172a85SClemens Ladisch if (event >= SNDRV_TIMER_EVENT_START &&
13886b172a85SClemens Ladisch event <= SNDRV_TIMER_EVENT_PAUSE)
13891da177e4SLinus Torvalds tu->tstamp = *tstamp;
13901da177e4SLinus Torvalds if ((tu->filter & (1 << event)) == 0 || !tu->tread)
13911da177e4SLinus Torvalds return;
13929a47e9cfSKangjie Lu memset(&r1, 0, sizeof(r1));
13931da177e4SLinus Torvalds r1.event = event;
139407094ae6SBaolin Wang r1.tstamp_sec = tstamp->tv_sec;
139507094ae6SBaolin Wang r1.tstamp_nsec = tstamp->tv_nsec;
13961da177e4SLinus Torvalds r1.val = resolution;
1397bfe70783SDan Carpenter spin_lock_irqsave(&tu->qlock, flags);
13981da177e4SLinus Torvalds snd_timer_user_append_to_tqueue(tu, &r1);
1399bfe70783SDan Carpenter spin_unlock_irqrestore(&tu->qlock, flags);
140095cc637cSTakashi Iwai snd_kill_fasync(tu->fasync, SIGIO, POLL_IN);
14011da177e4SLinus Torvalds wake_up(&tu->qchange_sleep);
14021da177e4SLinus Torvalds }
14031da177e4SLinus Torvalds
snd_timer_user_disconnect(struct snd_timer_instance * timeri)140440ed9444STakashi Iwai static void snd_timer_user_disconnect(struct snd_timer_instance *timeri)
140540ed9444STakashi Iwai {
140640ed9444STakashi Iwai struct snd_timer_user *tu = timeri->callback_data;
140740ed9444STakashi Iwai
140840ed9444STakashi Iwai tu->disconnected = true;
140940ed9444STakashi Iwai wake_up(&tu->qchange_sleep);
141040ed9444STakashi Iwai }
141140ed9444STakashi Iwai
snd_timer_user_tinterrupt(struct snd_timer_instance * timeri,unsigned long resolution,unsigned long ticks)141253d2f744STakashi Iwai static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri,
14131da177e4SLinus Torvalds unsigned long resolution,
14141da177e4SLinus Torvalds unsigned long ticks)
14151da177e4SLinus Torvalds {
141653d2f744STakashi Iwai struct snd_timer_user *tu = timeri->callback_data;
141707094ae6SBaolin Wang struct snd_timer_tread64 *r, r1;
1418fcae40c9SBaolin Wang struct timespec64 tstamp;
14191da177e4SLinus Torvalds int prev, append = 0;
14201da177e4SLinus Torvalds
1421a8c006aaSDan Carpenter memset(&r1, 0, sizeof(r1));
142207799e75STakashi Iwai memset(&tstamp, 0, sizeof(tstamp));
14231da177e4SLinus Torvalds spin_lock(&tu->qlock);
14246b172a85SClemens Ladisch if ((tu->filter & ((1 << SNDRV_TIMER_EVENT_RESOLUTION) |
14256b172a85SClemens Ladisch (1 << SNDRV_TIMER_EVENT_TICK))) == 0) {
14261da177e4SLinus Torvalds spin_unlock(&tu->qlock);
14271da177e4SLinus Torvalds return;
14281da177e4SLinus Torvalds }
1429b751eef1SJaroslav Kysela if (tu->last_resolution != resolution || ticks > 0) {
1430b751eef1SJaroslav Kysela if (timer_tstamp_monotonic)
1431fcae40c9SBaolin Wang ktime_get_ts64(&tstamp);
1432b751eef1SJaroslav Kysela else
1433fcae40c9SBaolin Wang ktime_get_real_ts64(&tstamp);
1434b751eef1SJaroslav Kysela }
14356b172a85SClemens Ladisch if ((tu->filter & (1 << SNDRV_TIMER_EVENT_RESOLUTION)) &&
14366b172a85SClemens Ladisch tu->last_resolution != resolution) {
14371da177e4SLinus Torvalds r1.event = SNDRV_TIMER_EVENT_RESOLUTION;
143807094ae6SBaolin Wang r1.tstamp_sec = tstamp.tv_sec;
143907094ae6SBaolin Wang r1.tstamp_nsec = tstamp.tv_nsec;
14401da177e4SLinus Torvalds r1.val = resolution;
14411da177e4SLinus Torvalds snd_timer_user_append_to_tqueue(tu, &r1);
14421da177e4SLinus Torvalds tu->last_resolution = resolution;
14431da177e4SLinus Torvalds append++;
14441da177e4SLinus Torvalds }
14451da177e4SLinus Torvalds if ((tu->filter & (1 << SNDRV_TIMER_EVENT_TICK)) == 0)
14461da177e4SLinus Torvalds goto __wake;
14471da177e4SLinus Torvalds if (ticks == 0)
14481da177e4SLinus Torvalds goto __wake;
14491da177e4SLinus Torvalds if (tu->qused > 0) {
14501da177e4SLinus Torvalds prev = tu->qtail == 0 ? tu->queue_size - 1 : tu->qtail - 1;
14511da177e4SLinus Torvalds r = &tu->tqueue[prev];
14521da177e4SLinus Torvalds if (r->event == SNDRV_TIMER_EVENT_TICK) {
145307094ae6SBaolin Wang r->tstamp_sec = tstamp.tv_sec;
145407094ae6SBaolin Wang r->tstamp_nsec = tstamp.tv_nsec;
14551da177e4SLinus Torvalds r->val += ticks;
14561da177e4SLinus Torvalds append++;
14571da177e4SLinus Torvalds goto __wake;
14581da177e4SLinus Torvalds }
14591da177e4SLinus Torvalds }
14601da177e4SLinus Torvalds r1.event = SNDRV_TIMER_EVENT_TICK;
146107094ae6SBaolin Wang r1.tstamp_sec = tstamp.tv_sec;
146207094ae6SBaolin Wang r1.tstamp_nsec = tstamp.tv_nsec;
14631da177e4SLinus Torvalds r1.val = ticks;
14641da177e4SLinus Torvalds snd_timer_user_append_to_tqueue(tu, &r1);
14651da177e4SLinus Torvalds append++;
14661da177e4SLinus Torvalds __wake:
14671da177e4SLinus Torvalds spin_unlock(&tu->qlock);
14681da177e4SLinus Torvalds if (append == 0)
14691da177e4SLinus Torvalds return;
147095cc637cSTakashi Iwai snd_kill_fasync(tu->fasync, SIGIO, POLL_IN);
14711da177e4SLinus Torvalds wake_up(&tu->qchange_sleep);
14721da177e4SLinus Torvalds }
14731da177e4SLinus Torvalds
realloc_user_queue(struct snd_timer_user * tu,int size)1474890e2cb5STakashi Iwai static int realloc_user_queue(struct snd_timer_user *tu, int size)
1475890e2cb5STakashi Iwai {
1476890e2cb5STakashi Iwai struct snd_timer_read *queue = NULL;
147707094ae6SBaolin Wang struct snd_timer_tread64 *tqueue = NULL;
1478890e2cb5STakashi Iwai
1479890e2cb5STakashi Iwai if (tu->tread) {
1480890e2cb5STakashi Iwai tqueue = kcalloc(size, sizeof(*tqueue), GFP_KERNEL);
1481890e2cb5STakashi Iwai if (!tqueue)
1482890e2cb5STakashi Iwai return -ENOMEM;
1483890e2cb5STakashi Iwai } else {
1484890e2cb5STakashi Iwai queue = kcalloc(size, sizeof(*queue), GFP_KERNEL);
1485890e2cb5STakashi Iwai if (!queue)
1486890e2cb5STakashi Iwai return -ENOMEM;
1487890e2cb5STakashi Iwai }
1488890e2cb5STakashi Iwai
1489890e2cb5STakashi Iwai spin_lock_irq(&tu->qlock);
1490890e2cb5STakashi Iwai kfree(tu->queue);
1491890e2cb5STakashi Iwai kfree(tu->tqueue);
1492890e2cb5STakashi Iwai tu->queue_size = size;
1493890e2cb5STakashi Iwai tu->queue = queue;
1494890e2cb5STakashi Iwai tu->tqueue = tqueue;
1495890e2cb5STakashi Iwai tu->qhead = tu->qtail = tu->qused = 0;
1496890e2cb5STakashi Iwai spin_unlock_irq(&tu->qlock);
1497890e2cb5STakashi Iwai
1498890e2cb5STakashi Iwai return 0;
1499890e2cb5STakashi Iwai }
1500890e2cb5STakashi Iwai
snd_timer_user_open(struct inode * inode,struct file * file)15011da177e4SLinus Torvalds static int snd_timer_user_open(struct inode *inode, struct file *file)
15021da177e4SLinus Torvalds {
150353d2f744STakashi Iwai struct snd_timer_user *tu;
150402f4865fSTakashi Iwai int err;
150502f4865fSTakashi Iwai
1506c5bf68feSKirill Smelkov err = stream_open(inode, file);
150702f4865fSTakashi Iwai if (err < 0)
150802f4865fSTakashi Iwai return err;
15091da177e4SLinus Torvalds
1510ca2c0966STakashi Iwai tu = kzalloc(sizeof(*tu), GFP_KERNEL);
15111da177e4SLinus Torvalds if (tu == NULL)
15121da177e4SLinus Torvalds return -ENOMEM;
15131da177e4SLinus Torvalds spin_lock_init(&tu->qlock);
15141da177e4SLinus Torvalds init_waitqueue_head(&tu->qchange_sleep);
1515af368027STakashi Iwai mutex_init(&tu->ioctl_lock);
15161da177e4SLinus Torvalds tu->ticks = 1;
1517890e2cb5STakashi Iwai if (realloc_user_queue(tu, 128) < 0) {
15181da177e4SLinus Torvalds kfree(tu);
15191da177e4SLinus Torvalds return -ENOMEM;
15201da177e4SLinus Torvalds }
15211da177e4SLinus Torvalds file->private_data = tu;
15221da177e4SLinus Torvalds return 0;
15231da177e4SLinus Torvalds }
15241da177e4SLinus Torvalds
snd_timer_user_release(struct inode * inode,struct file * file)15251da177e4SLinus Torvalds static int snd_timer_user_release(struct inode *inode, struct file *file)
15261da177e4SLinus Torvalds {
152753d2f744STakashi Iwai struct snd_timer_user *tu;
15281da177e4SLinus Torvalds
15291da177e4SLinus Torvalds if (file->private_data) {
15301da177e4SLinus Torvalds tu = file->private_data;
15311da177e4SLinus Torvalds file->private_data = NULL;
1532af368027STakashi Iwai mutex_lock(&tu->ioctl_lock);
15336a34367eSTakashi Iwai if (tu->timeri) {
15341da177e4SLinus Torvalds snd_timer_close(tu->timeri);
15356a34367eSTakashi Iwai snd_timer_instance_free(tu->timeri);
15366a34367eSTakashi Iwai }
1537af368027STakashi Iwai mutex_unlock(&tu->ioctl_lock);
153895cc637cSTakashi Iwai snd_fasync_free(tu->fasync);
15391da177e4SLinus Torvalds kfree(tu->queue);
15401da177e4SLinus Torvalds kfree(tu->tqueue);
15411da177e4SLinus Torvalds kfree(tu);
15421da177e4SLinus Torvalds }
15431da177e4SLinus Torvalds return 0;
15441da177e4SLinus Torvalds }
15451da177e4SLinus Torvalds
snd_timer_user_zero_id(struct snd_timer_id * id)154653d2f744STakashi Iwai static void snd_timer_user_zero_id(struct snd_timer_id *id)
15471da177e4SLinus Torvalds {
15481da177e4SLinus Torvalds id->dev_class = SNDRV_TIMER_CLASS_NONE;
15491da177e4SLinus Torvalds id->dev_sclass = SNDRV_TIMER_SCLASS_NONE;
15501da177e4SLinus Torvalds id->card = -1;
15511da177e4SLinus Torvalds id->device = -1;
15521da177e4SLinus Torvalds id->subdevice = -1;
15531da177e4SLinus Torvalds }
15541da177e4SLinus Torvalds
snd_timer_user_copy_id(struct snd_timer_id * id,struct snd_timer * timer)155553d2f744STakashi Iwai static void snd_timer_user_copy_id(struct snd_timer_id *id, struct snd_timer *timer)
15561da177e4SLinus Torvalds {
15571da177e4SLinus Torvalds id->dev_class = timer->tmr_class;
15581da177e4SLinus Torvalds id->dev_sclass = SNDRV_TIMER_SCLASS_NONE;
15591da177e4SLinus Torvalds id->card = timer->card ? timer->card->number : -1;
15601da177e4SLinus Torvalds id->device = timer->tmr_device;
15611da177e4SLinus Torvalds id->subdevice = timer->tmr_subdevice;
15621da177e4SLinus Torvalds }
15631da177e4SLinus Torvalds
snd_timer_user_next_device(struct snd_timer_id __user * _tid)156453d2f744STakashi Iwai static int snd_timer_user_next_device(struct snd_timer_id __user *_tid)
15651da177e4SLinus Torvalds {
156653d2f744STakashi Iwai struct snd_timer_id id;
156753d2f744STakashi Iwai struct snd_timer *timer;
15681da177e4SLinus Torvalds struct list_head *p;
15691da177e4SLinus Torvalds
15701da177e4SLinus Torvalds if (copy_from_user(&id, _tid, sizeof(id)))
15711da177e4SLinus Torvalds return -EFAULT;
15721a60d4c5SIngo Molnar mutex_lock(®ister_mutex);
15731da177e4SLinus Torvalds if (id.dev_class < 0) { /* first item */
15741da177e4SLinus Torvalds if (list_empty(&snd_timer_list))
15751da177e4SLinus Torvalds snd_timer_user_zero_id(&id);
15761da177e4SLinus Torvalds else {
15779dfba380SClemens Ladisch timer = list_entry(snd_timer_list.next,
157853d2f744STakashi Iwai struct snd_timer, device_list);
15791da177e4SLinus Torvalds snd_timer_user_copy_id(&id, timer);
15801da177e4SLinus Torvalds }
15811da177e4SLinus Torvalds } else {
15821da177e4SLinus Torvalds switch (id.dev_class) {
15831da177e4SLinus Torvalds case SNDRV_TIMER_CLASS_GLOBAL:
15841da177e4SLinus Torvalds id.device = id.device < 0 ? 0 : id.device + 1;
15851da177e4SLinus Torvalds list_for_each(p, &snd_timer_list) {
158653d2f744STakashi Iwai timer = list_entry(p, struct snd_timer, device_list);
15871da177e4SLinus Torvalds if (timer->tmr_class > SNDRV_TIMER_CLASS_GLOBAL) {
15881da177e4SLinus Torvalds snd_timer_user_copy_id(&id, timer);
15891da177e4SLinus Torvalds break;
15901da177e4SLinus Torvalds }
15911da177e4SLinus Torvalds if (timer->tmr_device >= id.device) {
15921da177e4SLinus Torvalds snd_timer_user_copy_id(&id, timer);
15931da177e4SLinus Torvalds break;
15941da177e4SLinus Torvalds }
15951da177e4SLinus Torvalds }
15961da177e4SLinus Torvalds if (p == &snd_timer_list)
15971da177e4SLinus Torvalds snd_timer_user_zero_id(&id);
15981da177e4SLinus Torvalds break;
15991da177e4SLinus Torvalds case SNDRV_TIMER_CLASS_CARD:
16001da177e4SLinus Torvalds case SNDRV_TIMER_CLASS_PCM:
16011da177e4SLinus Torvalds if (id.card < 0) {
16021da177e4SLinus Torvalds id.card = 0;
16031da177e4SLinus Torvalds } else {
16041da177e4SLinus Torvalds if (id.device < 0) {
16051da177e4SLinus Torvalds id.device = 0;
16061da177e4SLinus Torvalds } else {
1607e8ed6820SDan Carpenter if (id.subdevice < 0)
16086b172a85SClemens Ladisch id.subdevice = 0;
1609b41f794fSTakashi Iwai else if (id.subdevice < INT_MAX)
16106b172a85SClemens Ladisch id.subdevice++;
16116b172a85SClemens Ladisch }
16121da177e4SLinus Torvalds }
16131da177e4SLinus Torvalds list_for_each(p, &snd_timer_list) {
161453d2f744STakashi Iwai timer = list_entry(p, struct snd_timer, device_list);
16151da177e4SLinus Torvalds if (timer->tmr_class > id.dev_class) {
16161da177e4SLinus Torvalds snd_timer_user_copy_id(&id, timer);
16171da177e4SLinus Torvalds break;
16181da177e4SLinus Torvalds }
16191da177e4SLinus Torvalds if (timer->tmr_class < id.dev_class)
16201da177e4SLinus Torvalds continue;
16211da177e4SLinus Torvalds if (timer->card->number > id.card) {
16221da177e4SLinus Torvalds snd_timer_user_copy_id(&id, timer);
16231da177e4SLinus Torvalds break;
16241da177e4SLinus Torvalds }
16251da177e4SLinus Torvalds if (timer->card->number < id.card)
16261da177e4SLinus Torvalds continue;
16271da177e4SLinus Torvalds if (timer->tmr_device > id.device) {
16281da177e4SLinus Torvalds snd_timer_user_copy_id(&id, timer);
16291da177e4SLinus Torvalds break;
16301da177e4SLinus Torvalds }
16311da177e4SLinus Torvalds if (timer->tmr_device < id.device)
16321da177e4SLinus Torvalds continue;
16331da177e4SLinus Torvalds if (timer->tmr_subdevice > id.subdevice) {
16341da177e4SLinus Torvalds snd_timer_user_copy_id(&id, timer);
16351da177e4SLinus Torvalds break;
16361da177e4SLinus Torvalds }
16371da177e4SLinus Torvalds if (timer->tmr_subdevice < id.subdevice)
16381da177e4SLinus Torvalds continue;
16391da177e4SLinus Torvalds snd_timer_user_copy_id(&id, timer);
16401da177e4SLinus Torvalds break;
16411da177e4SLinus Torvalds }
16421da177e4SLinus Torvalds if (p == &snd_timer_list)
16431da177e4SLinus Torvalds snd_timer_user_zero_id(&id);
16441da177e4SLinus Torvalds break;
16451da177e4SLinus Torvalds default:
16461da177e4SLinus Torvalds snd_timer_user_zero_id(&id);
16471da177e4SLinus Torvalds }
16481da177e4SLinus Torvalds }
16491a60d4c5SIngo Molnar mutex_unlock(®ister_mutex);
16501da177e4SLinus Torvalds if (copy_to_user(_tid, &id, sizeof(*_tid)))
16511da177e4SLinus Torvalds return -EFAULT;
16521da177e4SLinus Torvalds return 0;
16531da177e4SLinus Torvalds }
16541da177e4SLinus Torvalds
snd_timer_user_ginfo(struct file * file,struct snd_timer_ginfo __user * _ginfo)16556b172a85SClemens Ladisch static int snd_timer_user_ginfo(struct file *file,
165653d2f744STakashi Iwai struct snd_timer_ginfo __user *_ginfo)
16571da177e4SLinus Torvalds {
165853d2f744STakashi Iwai struct snd_timer_ginfo *ginfo;
165953d2f744STakashi Iwai struct snd_timer_id tid;
166053d2f744STakashi Iwai struct snd_timer *t;
16611da177e4SLinus Torvalds struct list_head *p;
16621da177e4SLinus Torvalds int err = 0;
16631da177e4SLinus Torvalds
1664ef44a1ecSLi Zefan ginfo = memdup_user(_ginfo, sizeof(*ginfo));
1665ef44a1ecSLi Zefan if (IS_ERR(ginfo))
1666ef44a1ecSLi Zefan return PTR_ERR(ginfo);
1667ef44a1ecSLi Zefan
16681da177e4SLinus Torvalds tid = ginfo->tid;
16691da177e4SLinus Torvalds memset(ginfo, 0, sizeof(*ginfo));
16701da177e4SLinus Torvalds ginfo->tid = tid;
16711a60d4c5SIngo Molnar mutex_lock(®ister_mutex);
16721da177e4SLinus Torvalds t = snd_timer_find(&tid);
16731da177e4SLinus Torvalds if (t != NULL) {
16741da177e4SLinus Torvalds ginfo->card = t->card ? t->card->number : -1;
16751da177e4SLinus Torvalds if (t->hw.flags & SNDRV_TIMER_HW_SLAVE)
16761da177e4SLinus Torvalds ginfo->flags |= SNDRV_TIMER_FLG_SLAVE;
167775b1a8f9SJoe Perches strscpy(ginfo->id, t->id, sizeof(ginfo->id));
167875b1a8f9SJoe Perches strscpy(ginfo->name, t->name, sizeof(ginfo->name));
16796cc84450SOswald Buddenhagen spin_lock_irq(&t->lock);
16806cc84450SOswald Buddenhagen ginfo->resolution = snd_timer_hw_resolution(t);
16816cc84450SOswald Buddenhagen spin_unlock_irq(&t->lock);
16821da177e4SLinus Torvalds if (t->hw.resolution_min > 0) {
16831da177e4SLinus Torvalds ginfo->resolution_min = t->hw.resolution_min;
16841da177e4SLinus Torvalds ginfo->resolution_max = t->hw.resolution_max;
16851da177e4SLinus Torvalds }
16861da177e4SLinus Torvalds list_for_each(p, &t->open_list_head) {
16871da177e4SLinus Torvalds ginfo->clients++;
16881da177e4SLinus Torvalds }
16891da177e4SLinus Torvalds } else {
16901da177e4SLinus Torvalds err = -ENODEV;
16911da177e4SLinus Torvalds }
16921a60d4c5SIngo Molnar mutex_unlock(®ister_mutex);
16931da177e4SLinus Torvalds if (err >= 0 && copy_to_user(_ginfo, ginfo, sizeof(*ginfo)))
16941da177e4SLinus Torvalds err = -EFAULT;
16951da177e4SLinus Torvalds kfree(ginfo);
16961da177e4SLinus Torvalds return err;
16971da177e4SLinus Torvalds }
16981da177e4SLinus Torvalds
timer_set_gparams(struct snd_timer_gparams * gparams)169991d2178eSTakashi Sakamoto static int timer_set_gparams(struct snd_timer_gparams *gparams)
17001da177e4SLinus Torvalds {
170153d2f744STakashi Iwai struct snd_timer *t;
17021da177e4SLinus Torvalds int err;
17031da177e4SLinus Torvalds
17041a60d4c5SIngo Molnar mutex_lock(®ister_mutex);
170591d2178eSTakashi Sakamoto t = snd_timer_find(&gparams->tid);
17066b172a85SClemens Ladisch if (!t) {
17071da177e4SLinus Torvalds err = -ENODEV;
17086b172a85SClemens Ladisch goto _error;
17091da177e4SLinus Torvalds }
17106b172a85SClemens Ladisch if (!list_empty(&t->open_list_head)) {
17116b172a85SClemens Ladisch err = -EBUSY;
17126b172a85SClemens Ladisch goto _error;
17136b172a85SClemens Ladisch }
17146b172a85SClemens Ladisch if (!t->hw.set_period) {
17156b172a85SClemens Ladisch err = -ENOSYS;
17166b172a85SClemens Ladisch goto _error;
17176b172a85SClemens Ladisch }
171891d2178eSTakashi Sakamoto err = t->hw.set_period(t, gparams->period_num, gparams->period_den);
17196b172a85SClemens Ladisch _error:
17201a60d4c5SIngo Molnar mutex_unlock(®ister_mutex);
17211da177e4SLinus Torvalds return err;
17221da177e4SLinus Torvalds }
17231da177e4SLinus Torvalds
snd_timer_user_gparams(struct file * file,struct snd_timer_gparams __user * _gparams)172491d2178eSTakashi Sakamoto static int snd_timer_user_gparams(struct file *file,
172591d2178eSTakashi Sakamoto struct snd_timer_gparams __user *_gparams)
172691d2178eSTakashi Sakamoto {
172791d2178eSTakashi Sakamoto struct snd_timer_gparams gparams;
172891d2178eSTakashi Sakamoto
172991d2178eSTakashi Sakamoto if (copy_from_user(&gparams, _gparams, sizeof(gparams)))
173091d2178eSTakashi Sakamoto return -EFAULT;
173191d2178eSTakashi Sakamoto return timer_set_gparams(&gparams);
173291d2178eSTakashi Sakamoto }
173391d2178eSTakashi Sakamoto
snd_timer_user_gstatus(struct file * file,struct snd_timer_gstatus __user * _gstatus)17346b172a85SClemens Ladisch static int snd_timer_user_gstatus(struct file *file,
173553d2f744STakashi Iwai struct snd_timer_gstatus __user *_gstatus)
17361da177e4SLinus Torvalds {
173753d2f744STakashi Iwai struct snd_timer_gstatus gstatus;
173853d2f744STakashi Iwai struct snd_timer_id tid;
173953d2f744STakashi Iwai struct snd_timer *t;
17401da177e4SLinus Torvalds int err = 0;
17411da177e4SLinus Torvalds
17421da177e4SLinus Torvalds if (copy_from_user(&gstatus, _gstatus, sizeof(gstatus)))
17431da177e4SLinus Torvalds return -EFAULT;
17441da177e4SLinus Torvalds tid = gstatus.tid;
17451da177e4SLinus Torvalds memset(&gstatus, 0, sizeof(gstatus));
17461da177e4SLinus Torvalds gstatus.tid = tid;
17471a60d4c5SIngo Molnar mutex_lock(®ister_mutex);
17481da177e4SLinus Torvalds t = snd_timer_find(&tid);
17491da177e4SLinus Torvalds if (t != NULL) {
17509d4d207dSTakashi Iwai spin_lock_irq(&t->lock);
1751fdcb5761STakashi Iwai gstatus.resolution = snd_timer_hw_resolution(t);
17521da177e4SLinus Torvalds if (t->hw.precise_resolution) {
17536b172a85SClemens Ladisch t->hw.precise_resolution(t, &gstatus.resolution_num,
17546b172a85SClemens Ladisch &gstatus.resolution_den);
17551da177e4SLinus Torvalds } else {
17561da177e4SLinus Torvalds gstatus.resolution_num = gstatus.resolution;
17571da177e4SLinus Torvalds gstatus.resolution_den = 1000000000uL;
17581da177e4SLinus Torvalds }
17599d4d207dSTakashi Iwai spin_unlock_irq(&t->lock);
17601da177e4SLinus Torvalds } else {
17611da177e4SLinus Torvalds err = -ENODEV;
17621da177e4SLinus Torvalds }
17631a60d4c5SIngo Molnar mutex_unlock(®ister_mutex);
17641da177e4SLinus Torvalds if (err >= 0 && copy_to_user(_gstatus, &gstatus, sizeof(gstatus)))
17651da177e4SLinus Torvalds err = -EFAULT;
17661da177e4SLinus Torvalds return err;
17671da177e4SLinus Torvalds }
17681da177e4SLinus Torvalds
snd_timer_user_tselect(struct file * file,struct snd_timer_select __user * _tselect)17696b172a85SClemens Ladisch static int snd_timer_user_tselect(struct file *file,
177053d2f744STakashi Iwai struct snd_timer_select __user *_tselect)
17711da177e4SLinus Torvalds {
177253d2f744STakashi Iwai struct snd_timer_user *tu;
177353d2f744STakashi Iwai struct snd_timer_select tselect;
17741da177e4SLinus Torvalds char str[32];
1775c1935b4dSJaroslav Kysela int err = 0;
17761da177e4SLinus Torvalds
17771da177e4SLinus Torvalds tu = file->private_data;
1778c1935b4dSJaroslav Kysela if (tu->timeri) {
17791da177e4SLinus Torvalds snd_timer_close(tu->timeri);
17806a34367eSTakashi Iwai snd_timer_instance_free(tu->timeri);
1781c1935b4dSJaroslav Kysela tu->timeri = NULL;
1782c1935b4dSJaroslav Kysela }
1783c1935b4dSJaroslav Kysela if (copy_from_user(&tselect, _tselect, sizeof(tselect))) {
1784c1935b4dSJaroslav Kysela err = -EFAULT;
1785c1935b4dSJaroslav Kysela goto __err;
1786c1935b4dSJaroslav Kysela }
17871da177e4SLinus Torvalds sprintf(str, "application %i", current->pid);
17881da177e4SLinus Torvalds if (tselect.id.dev_class != SNDRV_TIMER_CLASS_SLAVE)
17891da177e4SLinus Torvalds tselect.id.dev_sclass = SNDRV_TIMER_SCLASS_APPLICATION;
17906a34367eSTakashi Iwai tu->timeri = snd_timer_instance_new(str);
17916a34367eSTakashi Iwai if (!tu->timeri) {
17926a34367eSTakashi Iwai err = -ENOMEM;
1793c1935b4dSJaroslav Kysela goto __err;
17946a34367eSTakashi Iwai }
17951da177e4SLinus Torvalds
17961da177e4SLinus Torvalds tu->timeri->flags |= SNDRV_TIMER_IFLG_FAST;
17976b172a85SClemens Ladisch tu->timeri->callback = tu->tread
17986b172a85SClemens Ladisch ? snd_timer_user_tinterrupt : snd_timer_user_interrupt;
17991da177e4SLinus Torvalds tu->timeri->ccallback = snd_timer_user_ccallback;
18001da177e4SLinus Torvalds tu->timeri->callback_data = (void *)tu;
180140ed9444STakashi Iwai tu->timeri->disconnect = snd_timer_user_disconnect;
1802c1935b4dSJaroslav Kysela
18036a34367eSTakashi Iwai err = snd_timer_open(tu->timeri, &tselect.id, current->pid);
18046a34367eSTakashi Iwai if (err < 0) {
18056a34367eSTakashi Iwai snd_timer_instance_free(tu->timeri);
18066a34367eSTakashi Iwai tu->timeri = NULL;
18076a34367eSTakashi Iwai }
18086a34367eSTakashi Iwai
1809c1935b4dSJaroslav Kysela __err:
1810c1935b4dSJaroslav Kysela return err;
18111da177e4SLinus Torvalds }
18121da177e4SLinus Torvalds
snd_timer_user_info(struct file * file,struct snd_timer_info __user * _info)18136b172a85SClemens Ladisch static int snd_timer_user_info(struct file *file,
181453d2f744STakashi Iwai struct snd_timer_info __user *_info)
18151da177e4SLinus Torvalds {
181653d2f744STakashi Iwai struct snd_timer_user *tu;
181753d2f744STakashi Iwai struct snd_timer_info *info;
181853d2f744STakashi Iwai struct snd_timer *t;
18191da177e4SLinus Torvalds int err = 0;
18201da177e4SLinus Torvalds
18211da177e4SLinus Torvalds tu = file->private_data;
18227c64ec34SClemens Ladisch if (!tu->timeri)
18237c64ec34SClemens Ladisch return -EBADFD;
18241da177e4SLinus Torvalds t = tu->timeri->timer;
18257c64ec34SClemens Ladisch if (!t)
18267c64ec34SClemens Ladisch return -EBADFD;
18271da177e4SLinus Torvalds
1828ca2c0966STakashi Iwai info = kzalloc(sizeof(*info), GFP_KERNEL);
18291da177e4SLinus Torvalds if (! info)
18301da177e4SLinus Torvalds return -ENOMEM;
18311da177e4SLinus Torvalds info->card = t->card ? t->card->number : -1;
18321da177e4SLinus Torvalds if (t->hw.flags & SNDRV_TIMER_HW_SLAVE)
18331da177e4SLinus Torvalds info->flags |= SNDRV_TIMER_FLG_SLAVE;
183475b1a8f9SJoe Perches strscpy(info->id, t->id, sizeof(info->id));
183575b1a8f9SJoe Perches strscpy(info->name, t->name, sizeof(info->name));
18366cc84450SOswald Buddenhagen spin_lock_irq(&t->lock);
18376cc84450SOswald Buddenhagen info->resolution = snd_timer_hw_resolution(t);
18386cc84450SOswald Buddenhagen spin_unlock_irq(&t->lock);
18391da177e4SLinus Torvalds if (copy_to_user(_info, info, sizeof(*_info)))
18401da177e4SLinus Torvalds err = -EFAULT;
18411da177e4SLinus Torvalds kfree(info);
18421da177e4SLinus Torvalds return err;
18431da177e4SLinus Torvalds }
18441da177e4SLinus Torvalds
snd_timer_user_params(struct file * file,struct snd_timer_params __user * _params)18456b172a85SClemens Ladisch static int snd_timer_user_params(struct file *file,
184653d2f744STakashi Iwai struct snd_timer_params __user *_params)
18471da177e4SLinus Torvalds {
184853d2f744STakashi Iwai struct snd_timer_user *tu;
184953d2f744STakashi Iwai struct snd_timer_params params;
185053d2f744STakashi Iwai struct snd_timer *t;
18511da177e4SLinus Torvalds int err;
18521da177e4SLinus Torvalds
18531da177e4SLinus Torvalds tu = file->private_data;
18547c64ec34SClemens Ladisch if (!tu->timeri)
18557c64ec34SClemens Ladisch return -EBADFD;
18561da177e4SLinus Torvalds t = tu->timeri->timer;
18577c64ec34SClemens Ladisch if (!t)
18587c64ec34SClemens Ladisch return -EBADFD;
18591da177e4SLinus Torvalds if (copy_from_user(¶ms, _params, sizeof(params)))
18601da177e4SLinus Torvalds return -EFAULT;
186171321eb3STakashi Iwai if (!(t->hw.flags & SNDRV_TIMER_HW_SLAVE)) {
186271321eb3STakashi Iwai u64 resolution;
186371321eb3STakashi Iwai
186471321eb3STakashi Iwai if (params.ticks < 1) {
18651da177e4SLinus Torvalds err = -EINVAL;
18661da177e4SLinus Torvalds goto _end;
18671da177e4SLinus Torvalds }
186871321eb3STakashi Iwai
186971321eb3STakashi Iwai /* Don't allow resolution less than 1ms */
187071321eb3STakashi Iwai resolution = snd_timer_resolution(tu->timeri);
187171321eb3STakashi Iwai resolution *= params.ticks;
187271321eb3STakashi Iwai if (resolution < 1000000) {
187371321eb3STakashi Iwai err = -EINVAL;
187471321eb3STakashi Iwai goto _end;
187571321eb3STakashi Iwai }
187671321eb3STakashi Iwai }
18776b172a85SClemens Ladisch if (params.queue_size > 0 &&
18786b172a85SClemens Ladisch (params.queue_size < 32 || params.queue_size > 1024)) {
18791da177e4SLinus Torvalds err = -EINVAL;
18801da177e4SLinus Torvalds goto _end;
18811da177e4SLinus Torvalds }
18821da177e4SLinus Torvalds if (params.filter & ~((1<<SNDRV_TIMER_EVENT_RESOLUTION)|
18831da177e4SLinus Torvalds (1<<SNDRV_TIMER_EVENT_TICK)|
18841da177e4SLinus Torvalds (1<<SNDRV_TIMER_EVENT_START)|
18851da177e4SLinus Torvalds (1<<SNDRV_TIMER_EVENT_STOP)|
18861da177e4SLinus Torvalds (1<<SNDRV_TIMER_EVENT_CONTINUE)|
18871da177e4SLinus Torvalds (1<<SNDRV_TIMER_EVENT_PAUSE)|
1888a501dfa3SJaroslav Kysela (1<<SNDRV_TIMER_EVENT_SUSPEND)|
1889a501dfa3SJaroslav Kysela (1<<SNDRV_TIMER_EVENT_RESUME)|
18901da177e4SLinus Torvalds (1<<SNDRV_TIMER_EVENT_MSTART)|
18911da177e4SLinus Torvalds (1<<SNDRV_TIMER_EVENT_MSTOP)|
18921da177e4SLinus Torvalds (1<<SNDRV_TIMER_EVENT_MCONTINUE)|
189365d11d95SJaroslav Kysela (1<<SNDRV_TIMER_EVENT_MPAUSE)|
189465d11d95SJaroslav Kysela (1<<SNDRV_TIMER_EVENT_MSUSPEND)|
1895a501dfa3SJaroslav Kysela (1<<SNDRV_TIMER_EVENT_MRESUME))) {
18961da177e4SLinus Torvalds err = -EINVAL;
18971da177e4SLinus Torvalds goto _end;
18981da177e4SLinus Torvalds }
18991da177e4SLinus Torvalds snd_timer_stop(tu->timeri);
19001da177e4SLinus Torvalds spin_lock_irq(&t->lock);
19011da177e4SLinus Torvalds tu->timeri->flags &= ~(SNDRV_TIMER_IFLG_AUTO|
19021da177e4SLinus Torvalds SNDRV_TIMER_IFLG_EXCLUSIVE|
19031da177e4SLinus Torvalds SNDRV_TIMER_IFLG_EARLY_EVENT);
19041da177e4SLinus Torvalds if (params.flags & SNDRV_TIMER_PSFLG_AUTO)
19051da177e4SLinus Torvalds tu->timeri->flags |= SNDRV_TIMER_IFLG_AUTO;
19061da177e4SLinus Torvalds if (params.flags & SNDRV_TIMER_PSFLG_EXCLUSIVE)
19071da177e4SLinus Torvalds tu->timeri->flags |= SNDRV_TIMER_IFLG_EXCLUSIVE;
19081da177e4SLinus Torvalds if (params.flags & SNDRV_TIMER_PSFLG_EARLY_EVENT)
19091da177e4SLinus Torvalds tu->timeri->flags |= SNDRV_TIMER_IFLG_EARLY_EVENT;
19101da177e4SLinus Torvalds spin_unlock_irq(&t->lock);
19116b172a85SClemens Ladisch if (params.queue_size > 0 &&
19126b172a85SClemens Ladisch (unsigned int)tu->queue_size != params.queue_size) {
1913890e2cb5STakashi Iwai err = realloc_user_queue(tu, params.queue_size);
1914890e2cb5STakashi Iwai if (err < 0)
1915890e2cb5STakashi Iwai goto _end;
19161da177e4SLinus Torvalds }
1917d7f910bfSTakashi Iwai spin_lock_irq(&tu->qlock);
19181da177e4SLinus Torvalds tu->qhead = tu->qtail = tu->qused = 0;
19191da177e4SLinus Torvalds if (tu->timeri->flags & SNDRV_TIMER_IFLG_EARLY_EVENT) {
19201da177e4SLinus Torvalds if (tu->tread) {
192107094ae6SBaolin Wang struct snd_timer_tread64 tread;
1922cec8f96eSKangjie Lu memset(&tread, 0, sizeof(tread));
19231da177e4SLinus Torvalds tread.event = SNDRV_TIMER_EVENT_EARLY;
192407094ae6SBaolin Wang tread.tstamp_sec = 0;
192507094ae6SBaolin Wang tread.tstamp_nsec = 0;
19261da177e4SLinus Torvalds tread.val = 0;
19271da177e4SLinus Torvalds snd_timer_user_append_to_tqueue(tu, &tread);
19281da177e4SLinus Torvalds } else {
192953d2f744STakashi Iwai struct snd_timer_read *r = &tu->queue[0];
19301da177e4SLinus Torvalds r->resolution = 0;
19311da177e4SLinus Torvalds r->ticks = 0;
19321da177e4SLinus Torvalds tu->qused++;
19331da177e4SLinus Torvalds tu->qtail++;
19341da177e4SLinus Torvalds }
19351da177e4SLinus Torvalds }
19361da177e4SLinus Torvalds tu->filter = params.filter;
19371da177e4SLinus Torvalds tu->ticks = params.ticks;
1938d7f910bfSTakashi Iwai spin_unlock_irq(&tu->qlock);
19391da177e4SLinus Torvalds err = 0;
19401da177e4SLinus Torvalds _end:
19411da177e4SLinus Torvalds if (copy_to_user(_params, ¶ms, sizeof(params)))
19421da177e4SLinus Torvalds return -EFAULT;
19431da177e4SLinus Torvalds return err;
19441da177e4SLinus Torvalds }
19451da177e4SLinus Torvalds
snd_timer_user_status32(struct file * file,struct snd_timer_status32 __user * _status)1946a07804ccSBaolin Wang static int snd_timer_user_status32(struct file *file,
1947a07804ccSBaolin Wang struct snd_timer_status32 __user *_status)
19481da177e4SLinus Torvalds {
194953d2f744STakashi Iwai struct snd_timer_user *tu;
1950a07804ccSBaolin Wang struct snd_timer_status32 status;
19511da177e4SLinus Torvalds
19521da177e4SLinus Torvalds tu = file->private_data;
19537c64ec34SClemens Ladisch if (!tu->timeri)
19547c64ec34SClemens Ladisch return -EBADFD;
19551da177e4SLinus Torvalds memset(&status, 0, sizeof(status));
1956a07804ccSBaolin Wang status.tstamp_sec = tu->tstamp.tv_sec;
1957a07804ccSBaolin Wang status.tstamp_nsec = tu->tstamp.tv_nsec;
1958a07804ccSBaolin Wang status.resolution = snd_timer_resolution(tu->timeri);
1959a07804ccSBaolin Wang status.lost = tu->timeri->lost;
1960a07804ccSBaolin Wang status.overrun = tu->overrun;
1961a07804ccSBaolin Wang spin_lock_irq(&tu->qlock);
1962a07804ccSBaolin Wang status.queue = tu->qused;
1963a07804ccSBaolin Wang spin_unlock_irq(&tu->qlock);
1964a07804ccSBaolin Wang if (copy_to_user(_status, &status, sizeof(status)))
1965a07804ccSBaolin Wang return -EFAULT;
1966a07804ccSBaolin Wang return 0;
1967a07804ccSBaolin Wang }
1968a07804ccSBaolin Wang
snd_timer_user_status64(struct file * file,struct snd_timer_status64 __user * _status)1969a07804ccSBaolin Wang static int snd_timer_user_status64(struct file *file,
1970a07804ccSBaolin Wang struct snd_timer_status64 __user *_status)
1971a07804ccSBaolin Wang {
1972a07804ccSBaolin Wang struct snd_timer_user *tu;
1973a07804ccSBaolin Wang struct snd_timer_status64 status;
1974a07804ccSBaolin Wang
1975a07804ccSBaolin Wang tu = file->private_data;
1976a07804ccSBaolin Wang if (!tu->timeri)
1977a07804ccSBaolin Wang return -EBADFD;
1978a07804ccSBaolin Wang memset(&status, 0, sizeof(status));
1979a07804ccSBaolin Wang status.tstamp_sec = tu->tstamp.tv_sec;
1980a07804ccSBaolin Wang status.tstamp_nsec = tu->tstamp.tv_nsec;
19811da177e4SLinus Torvalds status.resolution = snd_timer_resolution(tu->timeri);
19821da177e4SLinus Torvalds status.lost = tu->timeri->lost;
19831da177e4SLinus Torvalds status.overrun = tu->overrun;
19841da177e4SLinus Torvalds spin_lock_irq(&tu->qlock);
19851da177e4SLinus Torvalds status.queue = tu->qused;
19861da177e4SLinus Torvalds spin_unlock_irq(&tu->qlock);
19871da177e4SLinus Torvalds if (copy_to_user(_status, &status, sizeof(status)))
19881da177e4SLinus Torvalds return -EFAULT;
19891da177e4SLinus Torvalds return 0;
19901da177e4SLinus Torvalds }
19911da177e4SLinus Torvalds
snd_timer_user_start(struct file * file)19921da177e4SLinus Torvalds static int snd_timer_user_start(struct file *file)
19931da177e4SLinus Torvalds {
19941da177e4SLinus Torvalds int err;
199553d2f744STakashi Iwai struct snd_timer_user *tu;
19961da177e4SLinus Torvalds
19971da177e4SLinus Torvalds tu = file->private_data;
19987c64ec34SClemens Ladisch if (!tu->timeri)
19997c64ec34SClemens Ladisch return -EBADFD;
20001da177e4SLinus Torvalds snd_timer_stop(tu->timeri);
20011da177e4SLinus Torvalds tu->timeri->lost = 0;
20021da177e4SLinus Torvalds tu->last_resolution = 0;
20035d704b0dSTakashi Iwai err = snd_timer_start(tu->timeri, tu->ticks);
20045d704b0dSTakashi Iwai if (err < 0)
20055d704b0dSTakashi Iwai return err;
20065d704b0dSTakashi Iwai return 0;
20071da177e4SLinus Torvalds }
20081da177e4SLinus Torvalds
snd_timer_user_stop(struct file * file)20091da177e4SLinus Torvalds static int snd_timer_user_stop(struct file *file)
20101da177e4SLinus Torvalds {
20111da177e4SLinus Torvalds int err;
201253d2f744STakashi Iwai struct snd_timer_user *tu;
20131da177e4SLinus Torvalds
20141da177e4SLinus Torvalds tu = file->private_data;
20157c64ec34SClemens Ladisch if (!tu->timeri)
20167c64ec34SClemens Ladisch return -EBADFD;
20175d704b0dSTakashi Iwai err = snd_timer_stop(tu->timeri);
20185d704b0dSTakashi Iwai if (err < 0)
20195d704b0dSTakashi Iwai return err;
20205d704b0dSTakashi Iwai return 0;
20211da177e4SLinus Torvalds }
20221da177e4SLinus Torvalds
snd_timer_user_continue(struct file * file)20231da177e4SLinus Torvalds static int snd_timer_user_continue(struct file *file)
20241da177e4SLinus Torvalds {
20251da177e4SLinus Torvalds int err;
202653d2f744STakashi Iwai struct snd_timer_user *tu;
20271da177e4SLinus Torvalds
20281da177e4SLinus Torvalds tu = file->private_data;
20297c64ec34SClemens Ladisch if (!tu->timeri)
20307c64ec34SClemens Ladisch return -EBADFD;
20319f8a7658STakashi Iwai /* start timer instead of continue if it's not used before */
20329f8a7658STakashi Iwai if (!(tu->timeri->flags & SNDRV_TIMER_IFLG_PAUSED))
20339f8a7658STakashi Iwai return snd_timer_user_start(file);
20341da177e4SLinus Torvalds tu->timeri->lost = 0;
20355d704b0dSTakashi Iwai err = snd_timer_continue(tu->timeri);
20365d704b0dSTakashi Iwai if (err < 0)
20375d704b0dSTakashi Iwai return err;
20385d704b0dSTakashi Iwai return 0;
20391da177e4SLinus Torvalds }
20401da177e4SLinus Torvalds
snd_timer_user_pause(struct file * file)204115790a6bSTakashi Iwai static int snd_timer_user_pause(struct file *file)
204215790a6bSTakashi Iwai {
204315790a6bSTakashi Iwai int err;
204453d2f744STakashi Iwai struct snd_timer_user *tu;
204515790a6bSTakashi Iwai
204615790a6bSTakashi Iwai tu = file->private_data;
20477c64ec34SClemens Ladisch if (!tu->timeri)
20487c64ec34SClemens Ladisch return -EBADFD;
20495d704b0dSTakashi Iwai err = snd_timer_pause(tu->timeri);
20505d704b0dSTakashi Iwai if (err < 0)
20515d704b0dSTakashi Iwai return err;
20525d704b0dSTakashi Iwai return 0;
205315790a6bSTakashi Iwai }
205415790a6bSTakashi Iwai
snd_timer_user_tread(void __user * argp,struct snd_timer_user * tu,unsigned int cmd,bool compat)205507094ae6SBaolin Wang static int snd_timer_user_tread(void __user *argp, struct snd_timer_user *tu,
205607094ae6SBaolin Wang unsigned int cmd, bool compat)
205707094ae6SBaolin Wang {
205807094ae6SBaolin Wang int __user *p = argp;
205907094ae6SBaolin Wang int xarg, old_tread;
206007094ae6SBaolin Wang
206107094ae6SBaolin Wang if (tu->timeri) /* too late */
206207094ae6SBaolin Wang return -EBUSY;
206307094ae6SBaolin Wang if (get_user(xarg, p))
206407094ae6SBaolin Wang return -EFAULT;
206507094ae6SBaolin Wang
206607094ae6SBaolin Wang old_tread = tu->tread;
206707094ae6SBaolin Wang
206807094ae6SBaolin Wang if (!xarg)
206907094ae6SBaolin Wang tu->tread = TREAD_FORMAT_NONE;
207007094ae6SBaolin Wang else if (cmd == SNDRV_TIMER_IOCTL_TREAD64 ||
207107094ae6SBaolin Wang (IS_ENABLED(CONFIG_64BIT) && !compat))
207207094ae6SBaolin Wang tu->tread = TREAD_FORMAT_TIME64;
207307094ae6SBaolin Wang else
207407094ae6SBaolin Wang tu->tread = TREAD_FORMAT_TIME32;
207507094ae6SBaolin Wang
207607094ae6SBaolin Wang if (tu->tread != old_tread &&
207707094ae6SBaolin Wang realloc_user_queue(tu, tu->queue_size) < 0) {
207807094ae6SBaolin Wang tu->tread = old_tread;
207907094ae6SBaolin Wang return -ENOMEM;
208007094ae6SBaolin Wang }
208107094ae6SBaolin Wang
208207094ae6SBaolin Wang return 0;
208307094ae6SBaolin Wang }
208407094ae6SBaolin Wang
20858c50b37cSTakashi Iwai enum {
20868c50b37cSTakashi Iwai SNDRV_TIMER_IOCTL_START_OLD = _IO('T', 0x20),
20878c50b37cSTakashi Iwai SNDRV_TIMER_IOCTL_STOP_OLD = _IO('T', 0x21),
20888c50b37cSTakashi Iwai SNDRV_TIMER_IOCTL_CONTINUE_OLD = _IO('T', 0x22),
20898c50b37cSTakashi Iwai SNDRV_TIMER_IOCTL_PAUSE_OLD = _IO('T', 0x23),
20908c50b37cSTakashi Iwai };
20918c50b37cSTakashi Iwai
__snd_timer_user_ioctl(struct file * file,unsigned int cmd,unsigned long arg,bool compat)2092af368027STakashi Iwai static long __snd_timer_user_ioctl(struct file *file, unsigned int cmd,
209307094ae6SBaolin Wang unsigned long arg, bool compat)
20941da177e4SLinus Torvalds {
209553d2f744STakashi Iwai struct snd_timer_user *tu;
20961da177e4SLinus Torvalds void __user *argp = (void __user *)arg;
20971da177e4SLinus Torvalds int __user *p = argp;
20981da177e4SLinus Torvalds
20991da177e4SLinus Torvalds tu = file->private_data;
21001da177e4SLinus Torvalds switch (cmd) {
21011da177e4SLinus Torvalds case SNDRV_TIMER_IOCTL_PVERSION:
21021da177e4SLinus Torvalds return put_user(SNDRV_TIMER_VERSION, p) ? -EFAULT : 0;
21031da177e4SLinus Torvalds case SNDRV_TIMER_IOCTL_NEXT_DEVICE:
21041da177e4SLinus Torvalds return snd_timer_user_next_device(argp);
210507094ae6SBaolin Wang case SNDRV_TIMER_IOCTL_TREAD_OLD:
210607094ae6SBaolin Wang case SNDRV_TIMER_IOCTL_TREAD64:
210707094ae6SBaolin Wang return snd_timer_user_tread(argp, tu, cmd, compat);
21081da177e4SLinus Torvalds case SNDRV_TIMER_IOCTL_GINFO:
21091da177e4SLinus Torvalds return snd_timer_user_ginfo(file, argp);
21101da177e4SLinus Torvalds case SNDRV_TIMER_IOCTL_GPARAMS:
21111da177e4SLinus Torvalds return snd_timer_user_gparams(file, argp);
21121da177e4SLinus Torvalds case SNDRV_TIMER_IOCTL_GSTATUS:
21131da177e4SLinus Torvalds return snd_timer_user_gstatus(file, argp);
21141da177e4SLinus Torvalds case SNDRV_TIMER_IOCTL_SELECT:
21151da177e4SLinus Torvalds return snd_timer_user_tselect(file, argp);
21161da177e4SLinus Torvalds case SNDRV_TIMER_IOCTL_INFO:
21171da177e4SLinus Torvalds return snd_timer_user_info(file, argp);
21181da177e4SLinus Torvalds case SNDRV_TIMER_IOCTL_PARAMS:
21191da177e4SLinus Torvalds return snd_timer_user_params(file, argp);
2120a07804ccSBaolin Wang case SNDRV_TIMER_IOCTL_STATUS32:
2121a07804ccSBaolin Wang return snd_timer_user_status32(file, argp);
2122a07804ccSBaolin Wang case SNDRV_TIMER_IOCTL_STATUS64:
2123a07804ccSBaolin Wang return snd_timer_user_status64(file, argp);
21241da177e4SLinus Torvalds case SNDRV_TIMER_IOCTL_START:
21258c50b37cSTakashi Iwai case SNDRV_TIMER_IOCTL_START_OLD:
21261da177e4SLinus Torvalds return snd_timer_user_start(file);
21271da177e4SLinus Torvalds case SNDRV_TIMER_IOCTL_STOP:
21288c50b37cSTakashi Iwai case SNDRV_TIMER_IOCTL_STOP_OLD:
21291da177e4SLinus Torvalds return snd_timer_user_stop(file);
21301da177e4SLinus Torvalds case SNDRV_TIMER_IOCTL_CONTINUE:
21318c50b37cSTakashi Iwai case SNDRV_TIMER_IOCTL_CONTINUE_OLD:
21321da177e4SLinus Torvalds return snd_timer_user_continue(file);
213315790a6bSTakashi Iwai case SNDRV_TIMER_IOCTL_PAUSE:
21348c50b37cSTakashi Iwai case SNDRV_TIMER_IOCTL_PAUSE_OLD:
213515790a6bSTakashi Iwai return snd_timer_user_pause(file);
21361da177e4SLinus Torvalds }
21371da177e4SLinus Torvalds return -ENOTTY;
21381da177e4SLinus Torvalds }
21391da177e4SLinus Torvalds
snd_timer_user_ioctl(struct file * file,unsigned int cmd,unsigned long arg)2140af368027STakashi Iwai static long snd_timer_user_ioctl(struct file *file, unsigned int cmd,
2141af368027STakashi Iwai unsigned long arg)
2142af368027STakashi Iwai {
2143af368027STakashi Iwai struct snd_timer_user *tu = file->private_data;
2144af368027STakashi Iwai long ret;
2145af368027STakashi Iwai
2146af368027STakashi Iwai mutex_lock(&tu->ioctl_lock);
214707094ae6SBaolin Wang ret = __snd_timer_user_ioctl(file, cmd, arg, false);
2148af368027STakashi Iwai mutex_unlock(&tu->ioctl_lock);
2149af368027STakashi Iwai return ret;
2150af368027STakashi Iwai }
2151af368027STakashi Iwai
snd_timer_user_fasync(int fd,struct file * file,int on)21521da177e4SLinus Torvalds static int snd_timer_user_fasync(int fd, struct file * file, int on)
21531da177e4SLinus Torvalds {
215453d2f744STakashi Iwai struct snd_timer_user *tu;
21551da177e4SLinus Torvalds
21561da177e4SLinus Torvalds tu = file->private_data;
215795cc637cSTakashi Iwai return snd_fasync_helper(fd, file, on, &tu->fasync);
21581da177e4SLinus Torvalds }
21591da177e4SLinus Torvalds
snd_timer_user_read(struct file * file,char __user * buffer,size_t count,loff_t * offset)21606b172a85SClemens Ladisch static ssize_t snd_timer_user_read(struct file *file, char __user *buffer,
21616b172a85SClemens Ladisch size_t count, loff_t *offset)
21621da177e4SLinus Torvalds {
216307094ae6SBaolin Wang struct snd_timer_tread64 *tread;
216407094ae6SBaolin Wang struct snd_timer_tread32 tread32;
216553d2f744STakashi Iwai struct snd_timer_user *tu;
21661da177e4SLinus Torvalds long result = 0, unit;
21674dff5c7bSTakashi Iwai int qhead;
21681da177e4SLinus Torvalds int err = 0;
21691da177e4SLinus Torvalds
21701da177e4SLinus Torvalds tu = file->private_data;
217107094ae6SBaolin Wang switch (tu->tread) {
217207094ae6SBaolin Wang case TREAD_FORMAT_TIME64:
217307094ae6SBaolin Wang unit = sizeof(struct snd_timer_tread64);
217407094ae6SBaolin Wang break;
217507094ae6SBaolin Wang case TREAD_FORMAT_TIME32:
217607094ae6SBaolin Wang unit = sizeof(struct snd_timer_tread32);
217707094ae6SBaolin Wang break;
217807094ae6SBaolin Wang case TREAD_FORMAT_NONE:
217907094ae6SBaolin Wang unit = sizeof(struct snd_timer_read);
218007094ae6SBaolin Wang break;
218107094ae6SBaolin Wang default:
218207094ae6SBaolin Wang WARN_ONCE(1, "Corrupt snd_timer_user\n");
218307094ae6SBaolin Wang return -ENOTSUPP;
218407094ae6SBaolin Wang }
218507094ae6SBaolin Wang
2186d11662f4STakashi Iwai mutex_lock(&tu->ioctl_lock);
21871da177e4SLinus Torvalds spin_lock_irq(&tu->qlock);
21881da177e4SLinus Torvalds while ((long)count - result >= unit) {
21891da177e4SLinus Torvalds while (!tu->qused) {
2190ac6424b9SIngo Molnar wait_queue_entry_t wait;
21911da177e4SLinus Torvalds
21921da177e4SLinus Torvalds if ((file->f_flags & O_NONBLOCK) != 0 || result > 0) {
21931da177e4SLinus Torvalds err = -EAGAIN;
21944dff5c7bSTakashi Iwai goto _error;
21951da177e4SLinus Torvalds }
21961da177e4SLinus Torvalds
21971da177e4SLinus Torvalds set_current_state(TASK_INTERRUPTIBLE);
21981da177e4SLinus Torvalds init_waitqueue_entry(&wait, current);
21991da177e4SLinus Torvalds add_wait_queue(&tu->qchange_sleep, &wait);
22001da177e4SLinus Torvalds
22011da177e4SLinus Torvalds spin_unlock_irq(&tu->qlock);
2202d11662f4STakashi Iwai mutex_unlock(&tu->ioctl_lock);
22031da177e4SLinus Torvalds schedule();
2204d11662f4STakashi Iwai mutex_lock(&tu->ioctl_lock);
22051da177e4SLinus Torvalds spin_lock_irq(&tu->qlock);
22061da177e4SLinus Torvalds
22071da177e4SLinus Torvalds remove_wait_queue(&tu->qchange_sleep, &wait);
22081da177e4SLinus Torvalds
2209230323daSTakashi Iwai if (tu->disconnected) {
2210230323daSTakashi Iwai err = -ENODEV;
22114dff5c7bSTakashi Iwai goto _error;
2212230323daSTakashi Iwai }
22131da177e4SLinus Torvalds if (signal_pending(current)) {
22141da177e4SLinus Torvalds err = -ERESTARTSYS;
22154dff5c7bSTakashi Iwai goto _error;
22161da177e4SLinus Torvalds }
22171da177e4SLinus Torvalds }
22181da177e4SLinus Torvalds
22194dff5c7bSTakashi Iwai qhead = tu->qhead++;
22204dff5c7bSTakashi Iwai tu->qhead %= tu->queue_size;
22213fa6993fSTakashi Iwai tu->qused--;
22221da177e4SLinus Torvalds spin_unlock_irq(&tu->qlock);
22231da177e4SLinus Torvalds
222407094ae6SBaolin Wang tread = &tu->tqueue[qhead];
222507094ae6SBaolin Wang
222607094ae6SBaolin Wang switch (tu->tread) {
222707094ae6SBaolin Wang case TREAD_FORMAT_TIME64:
222807094ae6SBaolin Wang if (copy_to_user(buffer, tread,
222907094ae6SBaolin Wang sizeof(struct snd_timer_tread64)))
22301da177e4SLinus Torvalds err = -EFAULT;
223107094ae6SBaolin Wang break;
223207094ae6SBaolin Wang case TREAD_FORMAT_TIME32:
223307094ae6SBaolin Wang memset(&tread32, 0, sizeof(tread32));
223407094ae6SBaolin Wang tread32 = (struct snd_timer_tread32) {
223507094ae6SBaolin Wang .event = tread->event,
223607094ae6SBaolin Wang .tstamp_sec = tread->tstamp_sec,
2237f9993480SPierre-Louis Bossart .tstamp_nsec = tread->tstamp_nsec,
223807094ae6SBaolin Wang .val = tread->val,
223907094ae6SBaolin Wang };
224007094ae6SBaolin Wang
224107094ae6SBaolin Wang if (copy_to_user(buffer, &tread32, sizeof(tread32)))
224207094ae6SBaolin Wang err = -EFAULT;
224307094ae6SBaolin Wang break;
224407094ae6SBaolin Wang case TREAD_FORMAT_NONE:
22454dff5c7bSTakashi Iwai if (copy_to_user(buffer, &tu->queue[qhead],
22464dff5c7bSTakashi Iwai sizeof(struct snd_timer_read)))
22471da177e4SLinus Torvalds err = -EFAULT;
224807094ae6SBaolin Wang break;
224907094ae6SBaolin Wang default:
225007094ae6SBaolin Wang err = -ENOTSUPP;
225107094ae6SBaolin Wang break;
22521da177e4SLinus Torvalds }
22531da177e4SLinus Torvalds
22541da177e4SLinus Torvalds spin_lock_irq(&tu->qlock);
22554dff5c7bSTakashi Iwai if (err < 0)
22564dff5c7bSTakashi Iwai goto _error;
22574dff5c7bSTakashi Iwai result += unit;
22584dff5c7bSTakashi Iwai buffer += unit;
22591da177e4SLinus Torvalds }
22601da177e4SLinus Torvalds _error:
22614dff5c7bSTakashi Iwai spin_unlock_irq(&tu->qlock);
2262d11662f4STakashi Iwai mutex_unlock(&tu->ioctl_lock);
22631da177e4SLinus Torvalds return result > 0 ? result : err;
22641da177e4SLinus Torvalds }
22651da177e4SLinus Torvalds
snd_timer_user_poll(struct file * file,poll_table * wait)2266680ef72aSAl Viro static __poll_t snd_timer_user_poll(struct file *file, poll_table * wait)
22671da177e4SLinus Torvalds {
2268680ef72aSAl Viro __poll_t mask;
226953d2f744STakashi Iwai struct snd_timer_user *tu;
22701da177e4SLinus Torvalds
22711da177e4SLinus Torvalds tu = file->private_data;
22721da177e4SLinus Torvalds
22731da177e4SLinus Torvalds poll_wait(file, &tu->qchange_sleep, wait);
22741da177e4SLinus Torvalds
22751da177e4SLinus Torvalds mask = 0;
2276d7f910bfSTakashi Iwai spin_lock_irq(&tu->qlock);
22771da177e4SLinus Torvalds if (tu->qused)
2278a9a08845SLinus Torvalds mask |= EPOLLIN | EPOLLRDNORM;
2279230323daSTakashi Iwai if (tu->disconnected)
2280a9a08845SLinus Torvalds mask |= EPOLLERR;
2281d7f910bfSTakashi Iwai spin_unlock_irq(&tu->qlock);
22821da177e4SLinus Torvalds
22831da177e4SLinus Torvalds return mask;
22841da177e4SLinus Torvalds }
22851da177e4SLinus Torvalds
22861da177e4SLinus Torvalds #ifdef CONFIG_COMPAT
22871da177e4SLinus Torvalds #include "timer_compat.c"
22881da177e4SLinus Torvalds #else
22891da177e4SLinus Torvalds #define snd_timer_user_ioctl_compat NULL
22901da177e4SLinus Torvalds #endif
22911da177e4SLinus Torvalds
22929c2e08c5SArjan van de Ven static const struct file_operations snd_timer_f_ops =
22931da177e4SLinus Torvalds {
22941da177e4SLinus Torvalds .owner = THIS_MODULE,
22951da177e4SLinus Torvalds .read = snd_timer_user_read,
22961da177e4SLinus Torvalds .open = snd_timer_user_open,
22971da177e4SLinus Torvalds .release = snd_timer_user_release,
229802f4865fSTakashi Iwai .llseek = no_llseek,
22991da177e4SLinus Torvalds .poll = snd_timer_user_poll,
23001da177e4SLinus Torvalds .unlocked_ioctl = snd_timer_user_ioctl,
23011da177e4SLinus Torvalds .compat_ioctl = snd_timer_user_ioctl_compat,
23021da177e4SLinus Torvalds .fasync = snd_timer_user_fasync,
23031da177e4SLinus Torvalds };
23041da177e4SLinus Torvalds
23057c35860dSTakashi Iwai /* unregister the system timer */
snd_timer_free_all(void)23067c35860dSTakashi Iwai static void snd_timer_free_all(void)
23077c35860dSTakashi Iwai {
23087c35860dSTakashi Iwai struct snd_timer *timer, *n;
23097c35860dSTakashi Iwai
23107c35860dSTakashi Iwai list_for_each_entry_safe(timer, n, &snd_timer_list, device_list)
23117c35860dSTakashi Iwai snd_timer_free(timer);
23127c35860dSTakashi Iwai }
23137c35860dSTakashi Iwai
2314911fcb76STakashi Iwai static struct device *timer_dev;
231589da061fSTakashi Iwai
23161da177e4SLinus Torvalds /*
23171da177e4SLinus Torvalds * ENTRY functions
23181da177e4SLinus Torvalds */
23191da177e4SLinus Torvalds
alsa_timer_init(void)23201da177e4SLinus Torvalds static int __init alsa_timer_init(void)
23211da177e4SLinus Torvalds {
23221da177e4SLinus Torvalds int err;
23231da177e4SLinus Torvalds
2324911fcb76STakashi Iwai err = snd_device_alloc(&timer_dev, NULL);
2325911fcb76STakashi Iwai if (err < 0)
2326911fcb76STakashi Iwai return err;
2327911fcb76STakashi Iwai dev_set_name(timer_dev, "timer");
232889da061fSTakashi Iwai
23291da177e4SLinus Torvalds #ifdef SNDRV_OSS_INFO_DEV_TIMERS
23306b172a85SClemens Ladisch snd_oss_info_register(SNDRV_OSS_INFO_DEV_TIMERS, SNDRV_CARDS - 1,
23316b172a85SClemens Ladisch "system timer");
23321da177e4SLinus Torvalds #endif
2333e28563ccSTakashi Iwai
23347c35860dSTakashi Iwai err = snd_timer_register_system();
23357c35860dSTakashi Iwai if (err < 0) {
2336cf74dcf3STakashi Iwai pr_err("ALSA: unable to register system timer (%i)\n", err);
23371ae0e4ceSMarkus Elfring goto put_timer;
23387c35860dSTakashi Iwai }
23397c35860dSTakashi Iwai
234040a4b263STakashi Iwai err = snd_register_device(SNDRV_DEVICE_TYPE_TIMER, NULL, 0,
2341911fcb76STakashi Iwai &snd_timer_f_ops, NULL, timer_dev);
23427c35860dSTakashi Iwai if (err < 0) {
2343cf74dcf3STakashi Iwai pr_err("ALSA: unable to register timer device (%i)\n", err);
23447c35860dSTakashi Iwai snd_timer_free_all();
23451ae0e4ceSMarkus Elfring goto put_timer;
23467c35860dSTakashi Iwai }
23477c35860dSTakashi Iwai
2348e28563ccSTakashi Iwai snd_timer_proc_init();
23491da177e4SLinus Torvalds return 0;
23501ae0e4ceSMarkus Elfring
23511ae0e4ceSMarkus Elfring put_timer:
2352911fcb76STakashi Iwai put_device(timer_dev);
23531ae0e4ceSMarkus Elfring return err;
23541da177e4SLinus Torvalds }
23551da177e4SLinus Torvalds
alsa_timer_exit(void)23561da177e4SLinus Torvalds static void __exit alsa_timer_exit(void)
23571da177e4SLinus Torvalds {
2358911fcb76STakashi Iwai snd_unregister_device(timer_dev);
23597c35860dSTakashi Iwai snd_timer_free_all();
2360911fcb76STakashi Iwai put_device(timer_dev);
2361e28563ccSTakashi Iwai snd_timer_proc_done();
23621da177e4SLinus Torvalds #ifdef SNDRV_OSS_INFO_DEV_TIMERS
23631da177e4SLinus Torvalds snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_TIMERS, SNDRV_CARDS - 1);
23641da177e4SLinus Torvalds #endif
23651da177e4SLinus Torvalds }
23661da177e4SLinus Torvalds
23671da177e4SLinus Torvalds module_init(alsa_timer_init)
23681da177e4SLinus Torvalds module_exit(alsa_timer_exit)
2369