11da177e4SLinus Torvalds /* 21da177e4SLinus Torvalds * Timers abstract layer 31da177e4SLinus Torvalds * Copyright (c) by Jaroslav Kysela <perex@suse.cz> 41da177e4SLinus Torvalds * 51da177e4SLinus Torvalds * 61da177e4SLinus Torvalds * This program is free software; you can redistribute it and/or modify 71da177e4SLinus Torvalds * it under the terms of the GNU General Public License as published by 81da177e4SLinus Torvalds * the Free Software Foundation; either version 2 of the License, or 91da177e4SLinus Torvalds * (at your option) any later version. 101da177e4SLinus Torvalds * 111da177e4SLinus Torvalds * This program is distributed in the hope that it will be useful, 121da177e4SLinus Torvalds * but WITHOUT ANY WARRANTY; without even the implied warranty of 131da177e4SLinus Torvalds * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 141da177e4SLinus Torvalds * GNU General Public License for more details. 151da177e4SLinus Torvalds * 161da177e4SLinus Torvalds * You should have received a copy of the GNU General Public License 171da177e4SLinus Torvalds * along with this program; if not, write to the Free Software 181da177e4SLinus Torvalds * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 191da177e4SLinus Torvalds * 201da177e4SLinus Torvalds */ 211da177e4SLinus Torvalds 221da177e4SLinus Torvalds #include <sound/driver.h> 231da177e4SLinus Torvalds #include <linux/delay.h> 241da177e4SLinus Torvalds #include <linux/init.h> 251da177e4SLinus Torvalds #include <linux/smp_lock.h> 261da177e4SLinus Torvalds #include <linux/slab.h> 271da177e4SLinus Torvalds #include <linux/time.h> 281da177e4SLinus Torvalds #include <linux/moduleparam.h> 291da177e4SLinus Torvalds #include <sound/core.h> 301da177e4SLinus Torvalds #include <sound/timer.h> 311da177e4SLinus Torvalds #include <sound/control.h> 321da177e4SLinus Torvalds #include <sound/info.h> 331da177e4SLinus Torvalds #include <sound/minors.h> 341da177e4SLinus Torvalds #include <sound/initval.h> 351da177e4SLinus Torvalds #include <linux/kmod.h> 361da177e4SLinus Torvalds #ifdef CONFIG_KERNELD 371da177e4SLinus Torvalds #include <linux/kerneld.h> 381da177e4SLinus Torvalds #endif 391da177e4SLinus Torvalds 401da177e4SLinus Torvalds #if defined(CONFIG_SND_HPET) || defined(CONFIG_SND_HPET_MODULE) 411da177e4SLinus Torvalds #define DEFAULT_TIMER_LIMIT 3 421da177e4SLinus Torvalds #elif defined(CONFIG_SND_RTCTIMER) || defined(CONFIG_SND_RTCTIMER_MODULE) 431da177e4SLinus Torvalds #define DEFAULT_TIMER_LIMIT 2 441da177e4SLinus Torvalds #else 451da177e4SLinus Torvalds #define DEFAULT_TIMER_LIMIT 1 461da177e4SLinus Torvalds #endif 471da177e4SLinus Torvalds 481da177e4SLinus Torvalds static int timer_limit = DEFAULT_TIMER_LIMIT; 491da177e4SLinus Torvalds MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>, Takashi Iwai <tiwai@suse.de>"); 501da177e4SLinus Torvalds MODULE_DESCRIPTION("ALSA timer interface"); 511da177e4SLinus Torvalds MODULE_LICENSE("GPL"); 521da177e4SLinus Torvalds module_param(timer_limit, int, 0444); 531da177e4SLinus Torvalds MODULE_PARM_DESC(timer_limit, "Maximum global timers in system."); 541da177e4SLinus Torvalds 551da177e4SLinus Torvalds typedef struct { 561da177e4SLinus Torvalds snd_timer_instance_t *timeri; 571da177e4SLinus Torvalds int tread; /* enhanced read with timestamps and events */ 581da177e4SLinus Torvalds unsigned long ticks; 591da177e4SLinus Torvalds unsigned long overrun; 601da177e4SLinus Torvalds int qhead; 611da177e4SLinus Torvalds int qtail; 621da177e4SLinus Torvalds int qused; 631da177e4SLinus Torvalds int queue_size; 641da177e4SLinus Torvalds snd_timer_read_t *queue; 651da177e4SLinus Torvalds snd_timer_tread_t *tqueue; 661da177e4SLinus Torvalds spinlock_t qlock; 671da177e4SLinus Torvalds unsigned long last_resolution; 681da177e4SLinus Torvalds unsigned int filter; 691da177e4SLinus Torvalds struct timespec tstamp; /* trigger tstamp */ 701da177e4SLinus Torvalds wait_queue_head_t qchange_sleep; 711da177e4SLinus Torvalds struct fasync_struct *fasync; 72c1935b4dSJaroslav Kysela struct semaphore tread_sem; 731da177e4SLinus Torvalds } snd_timer_user_t; 741da177e4SLinus Torvalds 751da177e4SLinus Torvalds /* list of timers */ 761da177e4SLinus Torvalds static LIST_HEAD(snd_timer_list); 771da177e4SLinus Torvalds 781da177e4SLinus Torvalds /* list of slave instances */ 791da177e4SLinus Torvalds static LIST_HEAD(snd_timer_slave_list); 801da177e4SLinus Torvalds 811da177e4SLinus Torvalds /* lock for slave active lists */ 821da177e4SLinus Torvalds static DEFINE_SPINLOCK(slave_active_lock); 831da177e4SLinus Torvalds 841da177e4SLinus Torvalds static DECLARE_MUTEX(register_mutex); 851da177e4SLinus Torvalds 861da177e4SLinus Torvalds static int snd_timer_free(snd_timer_t *timer); 871da177e4SLinus Torvalds static int snd_timer_dev_free(snd_device_t *device); 881da177e4SLinus Torvalds static int snd_timer_dev_register(snd_device_t *device); 891da177e4SLinus Torvalds static int snd_timer_dev_unregister(snd_device_t *device); 901da177e4SLinus Torvalds 911da177e4SLinus Torvalds static void snd_timer_reschedule(snd_timer_t * timer, unsigned long ticks_left); 921da177e4SLinus Torvalds 931da177e4SLinus Torvalds /* 941da177e4SLinus Torvalds * create a timer instance with the given owner string. 951da177e4SLinus Torvalds * when timer is not NULL, increments the module counter 961da177e4SLinus Torvalds */ 971da177e4SLinus Torvalds static snd_timer_instance_t *snd_timer_instance_new(char *owner, snd_timer_t *timer) 981da177e4SLinus Torvalds { 991da177e4SLinus Torvalds snd_timer_instance_t *timeri; 1001da177e4SLinus Torvalds timeri = kcalloc(1, sizeof(*timeri), GFP_KERNEL); 1011da177e4SLinus Torvalds if (timeri == NULL) 1021da177e4SLinus Torvalds return NULL; 1031da177e4SLinus Torvalds timeri->owner = snd_kmalloc_strdup(owner, GFP_KERNEL); 1041da177e4SLinus Torvalds if (! timeri->owner) { 1051da177e4SLinus Torvalds kfree(timeri); 1061da177e4SLinus Torvalds return NULL; 1071da177e4SLinus Torvalds } 1081da177e4SLinus Torvalds INIT_LIST_HEAD(&timeri->open_list); 1091da177e4SLinus Torvalds INIT_LIST_HEAD(&timeri->active_list); 1101da177e4SLinus Torvalds INIT_LIST_HEAD(&timeri->ack_list); 1111da177e4SLinus Torvalds INIT_LIST_HEAD(&timeri->slave_list_head); 1121da177e4SLinus Torvalds INIT_LIST_HEAD(&timeri->slave_active_head); 1131da177e4SLinus Torvalds 1141da177e4SLinus Torvalds timeri->timer = timer; 1151da177e4SLinus Torvalds if (timer && timer->card && !try_module_get(timer->card->module)) { 1161da177e4SLinus Torvalds kfree(timeri->owner); 1171da177e4SLinus Torvalds kfree(timeri); 1181da177e4SLinus Torvalds return NULL; 1191da177e4SLinus Torvalds } 1201da177e4SLinus Torvalds 1211da177e4SLinus Torvalds return timeri; 1221da177e4SLinus Torvalds } 1231da177e4SLinus Torvalds 1241da177e4SLinus Torvalds /* 1251da177e4SLinus Torvalds * find a timer instance from the given timer id 1261da177e4SLinus Torvalds */ 1271da177e4SLinus Torvalds static snd_timer_t *snd_timer_find(snd_timer_id_t *tid) 1281da177e4SLinus Torvalds { 1291da177e4SLinus Torvalds snd_timer_t *timer = NULL; 1301da177e4SLinus Torvalds struct list_head *p; 1311da177e4SLinus Torvalds 1321da177e4SLinus Torvalds list_for_each(p, &snd_timer_list) { 1331da177e4SLinus Torvalds timer = (snd_timer_t *)list_entry(p, snd_timer_t, device_list); 1341da177e4SLinus Torvalds 1351da177e4SLinus Torvalds if (timer->tmr_class != tid->dev_class) 1361da177e4SLinus Torvalds continue; 1371da177e4SLinus Torvalds if ((timer->tmr_class == SNDRV_TIMER_CLASS_CARD || 1381da177e4SLinus Torvalds timer->tmr_class == SNDRV_TIMER_CLASS_PCM) && 1391da177e4SLinus Torvalds (timer->card == NULL || 1401da177e4SLinus Torvalds timer->card->number != tid->card)) 1411da177e4SLinus Torvalds continue; 1421da177e4SLinus Torvalds if (timer->tmr_device != tid->device) 1431da177e4SLinus Torvalds continue; 1441da177e4SLinus Torvalds if (timer->tmr_subdevice != tid->subdevice) 1451da177e4SLinus Torvalds continue; 1461da177e4SLinus Torvalds return timer; 1471da177e4SLinus Torvalds } 1481da177e4SLinus Torvalds return NULL; 1491da177e4SLinus Torvalds } 1501da177e4SLinus Torvalds 1511da177e4SLinus Torvalds #ifdef CONFIG_KMOD 1521da177e4SLinus Torvalds 1531da177e4SLinus Torvalds static void snd_timer_request(snd_timer_id_t *tid) 1541da177e4SLinus Torvalds { 1551da177e4SLinus Torvalds if (! current->fs->root) 1561da177e4SLinus Torvalds return; 1571da177e4SLinus Torvalds switch (tid->dev_class) { 1581da177e4SLinus Torvalds case SNDRV_TIMER_CLASS_GLOBAL: 1591da177e4SLinus Torvalds if (tid->device < timer_limit) 1601da177e4SLinus Torvalds request_module("snd-timer-%i", tid->device); 1611da177e4SLinus Torvalds break; 1621da177e4SLinus Torvalds case SNDRV_TIMER_CLASS_CARD: 1631da177e4SLinus Torvalds case SNDRV_TIMER_CLASS_PCM: 1641da177e4SLinus Torvalds if (tid->card < snd_ecards_limit) 1651da177e4SLinus Torvalds request_module("snd-card-%i", tid->card); 1661da177e4SLinus Torvalds break; 1671da177e4SLinus Torvalds default: 1681da177e4SLinus Torvalds break; 1691da177e4SLinus Torvalds } 1701da177e4SLinus Torvalds } 1711da177e4SLinus Torvalds 1721da177e4SLinus Torvalds #endif 1731da177e4SLinus Torvalds 1741da177e4SLinus Torvalds /* 1751da177e4SLinus Torvalds * look for a master instance matching with the slave id of the given slave. 1761da177e4SLinus Torvalds * when found, relink the open_link of the slave. 1771da177e4SLinus Torvalds * 1781da177e4SLinus Torvalds * call this with register_mutex down. 1791da177e4SLinus Torvalds */ 1801da177e4SLinus Torvalds static void snd_timer_check_slave(snd_timer_instance_t *slave) 1811da177e4SLinus Torvalds { 1821da177e4SLinus Torvalds snd_timer_t *timer; 1831da177e4SLinus Torvalds snd_timer_instance_t *master; 1841da177e4SLinus Torvalds struct list_head *p, *q; 1851da177e4SLinus Torvalds 1861da177e4SLinus Torvalds /* FIXME: it's really dumb to look up all entries.. */ 1871da177e4SLinus Torvalds list_for_each(p, &snd_timer_list) { 1881da177e4SLinus Torvalds timer = (snd_timer_t *)list_entry(p, snd_timer_t, device_list); 1891da177e4SLinus Torvalds list_for_each(q, &timer->open_list_head) { 1901da177e4SLinus Torvalds master = (snd_timer_instance_t *)list_entry(q, snd_timer_instance_t, open_list); 1911da177e4SLinus Torvalds if (slave->slave_class == master->slave_class && 1921da177e4SLinus Torvalds slave->slave_id == master->slave_id) { 1931da177e4SLinus Torvalds list_del(&slave->open_list); 1941da177e4SLinus Torvalds list_add_tail(&slave->open_list, &master->slave_list_head); 1951da177e4SLinus Torvalds spin_lock_irq(&slave_active_lock); 1961da177e4SLinus Torvalds slave->master = master; 1971da177e4SLinus Torvalds slave->timer = master->timer; 1981da177e4SLinus Torvalds spin_unlock_irq(&slave_active_lock); 1991da177e4SLinus Torvalds return; 2001da177e4SLinus Torvalds } 2011da177e4SLinus Torvalds } 2021da177e4SLinus Torvalds } 2031da177e4SLinus Torvalds } 2041da177e4SLinus Torvalds 2051da177e4SLinus Torvalds /* 2061da177e4SLinus Torvalds * look for slave instances matching with the slave id of the given master. 2071da177e4SLinus Torvalds * when found, relink the open_link of slaves. 2081da177e4SLinus Torvalds * 2091da177e4SLinus Torvalds * call this with register_mutex down. 2101da177e4SLinus Torvalds */ 2111da177e4SLinus Torvalds static void snd_timer_check_master(snd_timer_instance_t *master) 2121da177e4SLinus Torvalds { 2131da177e4SLinus Torvalds snd_timer_instance_t *slave; 2141da177e4SLinus Torvalds struct list_head *p, *n; 2151da177e4SLinus Torvalds 2161da177e4SLinus Torvalds /* check all pending slaves */ 2171da177e4SLinus Torvalds list_for_each_safe(p, n, &snd_timer_slave_list) { 2181da177e4SLinus Torvalds slave = (snd_timer_instance_t *)list_entry(p, snd_timer_instance_t, open_list); 2191da177e4SLinus Torvalds if (slave->slave_class == master->slave_class && 2201da177e4SLinus Torvalds slave->slave_id == master->slave_id) { 2211da177e4SLinus Torvalds list_del(p); 2221da177e4SLinus Torvalds list_add_tail(p, &master->slave_list_head); 2231da177e4SLinus Torvalds spin_lock_irq(&slave_active_lock); 2241da177e4SLinus Torvalds slave->master = master; 2251da177e4SLinus Torvalds slave->timer = master->timer; 2261da177e4SLinus Torvalds if (slave->flags & SNDRV_TIMER_IFLG_RUNNING) 2271da177e4SLinus Torvalds list_add_tail(&slave->active_list, &master->slave_active_head); 2281da177e4SLinus Torvalds spin_unlock_irq(&slave_active_lock); 2291da177e4SLinus Torvalds } 2301da177e4SLinus Torvalds } 2311da177e4SLinus Torvalds } 2321da177e4SLinus Torvalds 2331da177e4SLinus Torvalds /* 2341da177e4SLinus Torvalds * open a timer instance 2351da177e4SLinus Torvalds * when opening a master, the slave id must be here given. 2361da177e4SLinus Torvalds */ 2371da177e4SLinus Torvalds int snd_timer_open(snd_timer_instance_t **ti, 2381da177e4SLinus Torvalds char *owner, snd_timer_id_t *tid, 2391da177e4SLinus Torvalds unsigned int slave_id) 2401da177e4SLinus Torvalds { 2411da177e4SLinus Torvalds snd_timer_t *timer; 2421da177e4SLinus Torvalds snd_timer_instance_t *timeri = NULL; 2431da177e4SLinus Torvalds 2441da177e4SLinus Torvalds if (tid->dev_class == SNDRV_TIMER_CLASS_SLAVE) { 2451da177e4SLinus Torvalds /* open a slave instance */ 2461da177e4SLinus Torvalds if (tid->dev_sclass <= SNDRV_TIMER_SCLASS_NONE || 2471da177e4SLinus Torvalds tid->dev_sclass > SNDRV_TIMER_SCLASS_OSS_SEQUENCER) { 2481da177e4SLinus Torvalds snd_printd("invalid slave class %i\n", tid->dev_sclass); 2491da177e4SLinus Torvalds return -EINVAL; 2501da177e4SLinus Torvalds } 2511da177e4SLinus Torvalds down(®ister_mutex); 2521da177e4SLinus Torvalds timeri = snd_timer_instance_new(owner, NULL); 2531da177e4SLinus Torvalds timeri->slave_class = tid->dev_sclass; 2541da177e4SLinus Torvalds timeri->slave_id = tid->device; 2551da177e4SLinus Torvalds timeri->flags |= SNDRV_TIMER_IFLG_SLAVE; 2561da177e4SLinus Torvalds list_add_tail(&timeri->open_list, &snd_timer_slave_list); 2571da177e4SLinus Torvalds snd_timer_check_slave(timeri); 2581da177e4SLinus Torvalds up(®ister_mutex); 2591da177e4SLinus Torvalds *ti = timeri; 2601da177e4SLinus Torvalds return 0; 2611da177e4SLinus Torvalds } 2621da177e4SLinus Torvalds 2631da177e4SLinus Torvalds /* open a master instance */ 2641da177e4SLinus Torvalds down(®ister_mutex); 2651da177e4SLinus Torvalds timer = snd_timer_find(tid); 2661da177e4SLinus Torvalds #ifdef CONFIG_KMOD 2671da177e4SLinus Torvalds if (timer == NULL) { 2681da177e4SLinus Torvalds up(®ister_mutex); 2691da177e4SLinus Torvalds snd_timer_request(tid); 2701da177e4SLinus Torvalds down(®ister_mutex); 2711da177e4SLinus Torvalds timer = snd_timer_find(tid); 2721da177e4SLinus Torvalds } 2731da177e4SLinus Torvalds #endif 2741da177e4SLinus Torvalds if (timer) { 2751da177e4SLinus Torvalds if (!list_empty(&timer->open_list_head)) { 2761da177e4SLinus Torvalds timeri = (snd_timer_instance_t *)list_entry(timer->open_list_head.next, snd_timer_instance_t, open_list); 2771da177e4SLinus Torvalds if (timeri->flags & SNDRV_TIMER_IFLG_EXCLUSIVE) { 2781da177e4SLinus Torvalds up(®ister_mutex); 2791da177e4SLinus Torvalds return -EBUSY; 2801da177e4SLinus Torvalds } 2811da177e4SLinus Torvalds } 2821da177e4SLinus Torvalds timeri = snd_timer_instance_new(owner, timer); 2831da177e4SLinus Torvalds if (timeri) { 2841da177e4SLinus Torvalds timeri->slave_class = tid->dev_sclass; 2851da177e4SLinus Torvalds timeri->slave_id = slave_id; 2861da177e4SLinus Torvalds if (list_empty(&timer->open_list_head) && timer->hw.open) 2871da177e4SLinus Torvalds timer->hw.open(timer); 2881da177e4SLinus Torvalds list_add_tail(&timeri->open_list, &timer->open_list_head); 2891da177e4SLinus Torvalds snd_timer_check_master(timeri); 2901da177e4SLinus Torvalds } 2911da177e4SLinus Torvalds } else { 2921da177e4SLinus Torvalds up(®ister_mutex); 2931da177e4SLinus Torvalds return -ENODEV; 2941da177e4SLinus Torvalds } 2951da177e4SLinus Torvalds up(®ister_mutex); 2961da177e4SLinus Torvalds *ti = timeri; 2971da177e4SLinus Torvalds return 0; 2981da177e4SLinus Torvalds } 2991da177e4SLinus Torvalds 3001da177e4SLinus Torvalds static int _snd_timer_stop(snd_timer_instance_t * timeri, int keep_flag, enum sndrv_timer_event event); 3011da177e4SLinus Torvalds 3021da177e4SLinus Torvalds /* 3031da177e4SLinus Torvalds * close a timer instance 3041da177e4SLinus Torvalds */ 3051da177e4SLinus Torvalds int snd_timer_close(snd_timer_instance_t * timeri) 3061da177e4SLinus Torvalds { 3071da177e4SLinus Torvalds snd_timer_t *timer = NULL; 3081da177e4SLinus Torvalds struct list_head *p, *n; 3091da177e4SLinus Torvalds snd_timer_instance_t *slave; 3101da177e4SLinus Torvalds 3111da177e4SLinus Torvalds snd_assert(timeri != NULL, return -ENXIO); 3121da177e4SLinus Torvalds 3131da177e4SLinus Torvalds /* force to stop the timer */ 3141da177e4SLinus Torvalds snd_timer_stop(timeri); 3151da177e4SLinus Torvalds 3161da177e4SLinus Torvalds if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) { 3171da177e4SLinus Torvalds /* wait, until the active callback is finished */ 3181da177e4SLinus Torvalds spin_lock_irq(&slave_active_lock); 3191da177e4SLinus Torvalds while (timeri->flags & SNDRV_TIMER_IFLG_CALLBACK) { 3201da177e4SLinus Torvalds spin_unlock_irq(&slave_active_lock); 3211da177e4SLinus Torvalds udelay(10); 3221da177e4SLinus Torvalds spin_lock_irq(&slave_active_lock); 3231da177e4SLinus Torvalds } 3241da177e4SLinus Torvalds spin_unlock_irq(&slave_active_lock); 3251da177e4SLinus Torvalds down(®ister_mutex); 3261da177e4SLinus Torvalds list_del(&timeri->open_list); 3271da177e4SLinus Torvalds up(®ister_mutex); 3281da177e4SLinus Torvalds } else { 3291da177e4SLinus Torvalds timer = timeri->timer; 3301da177e4SLinus Torvalds /* wait, until the active callback is finished */ 3311da177e4SLinus Torvalds spin_lock_irq(&timer->lock); 3321da177e4SLinus Torvalds while (timeri->flags & SNDRV_TIMER_IFLG_CALLBACK) { 3331da177e4SLinus Torvalds spin_unlock_irq(&timer->lock); 3341da177e4SLinus Torvalds udelay(10); 3351da177e4SLinus Torvalds spin_lock_irq(&timer->lock); 3361da177e4SLinus Torvalds } 3371da177e4SLinus Torvalds spin_unlock_irq(&timer->lock); 3381da177e4SLinus Torvalds down(®ister_mutex); 3391da177e4SLinus Torvalds list_del(&timeri->open_list); 3401da177e4SLinus Torvalds if (timer && list_empty(&timer->open_list_head) && timer->hw.close) 3411da177e4SLinus Torvalds timer->hw.close(timer); 3421da177e4SLinus Torvalds /* remove slave links */ 3431da177e4SLinus Torvalds list_for_each_safe(p, n, &timeri->slave_list_head) { 3441da177e4SLinus Torvalds slave = (snd_timer_instance_t *)list_entry(p, snd_timer_instance_t, open_list); 3451da177e4SLinus Torvalds spin_lock_irq(&slave_active_lock); 3461da177e4SLinus Torvalds _snd_timer_stop(slave, 1, SNDRV_TIMER_EVENT_RESOLUTION); 3471da177e4SLinus Torvalds list_del(p); 3481da177e4SLinus Torvalds list_add_tail(p, &snd_timer_slave_list); 3491da177e4SLinus Torvalds slave->master = NULL; 3501da177e4SLinus Torvalds slave->timer = NULL; 3511da177e4SLinus Torvalds spin_unlock_irq(&slave_active_lock); 3521da177e4SLinus Torvalds } 3531da177e4SLinus Torvalds up(®ister_mutex); 3541da177e4SLinus Torvalds } 3551da177e4SLinus Torvalds if (timeri->private_free) 3561da177e4SLinus Torvalds timeri->private_free(timeri); 3571da177e4SLinus Torvalds kfree(timeri->owner); 3581da177e4SLinus Torvalds kfree(timeri); 3591da177e4SLinus Torvalds if (timer && timer->card) 3601da177e4SLinus Torvalds module_put(timer->card->module); 3611da177e4SLinus Torvalds return 0; 3621da177e4SLinus Torvalds } 3631da177e4SLinus Torvalds 3641da177e4SLinus Torvalds unsigned long snd_timer_resolution(snd_timer_instance_t * timeri) 3651da177e4SLinus Torvalds { 3661da177e4SLinus Torvalds snd_timer_t * timer; 3671da177e4SLinus Torvalds 3681da177e4SLinus Torvalds if (timeri == NULL) 3691da177e4SLinus Torvalds return 0; 3701da177e4SLinus Torvalds if ((timer = timeri->timer) != NULL) { 3711da177e4SLinus Torvalds if (timer->hw.c_resolution) 3721da177e4SLinus Torvalds return timer->hw.c_resolution(timer); 3731da177e4SLinus Torvalds return timer->hw.resolution; 3741da177e4SLinus Torvalds } 3751da177e4SLinus Torvalds return 0; 3761da177e4SLinus Torvalds } 3771da177e4SLinus Torvalds 3781da177e4SLinus Torvalds static void snd_timer_notify1(snd_timer_instance_t *ti, enum sndrv_timer_event event) 3791da177e4SLinus Torvalds { 3801da177e4SLinus Torvalds snd_timer_t *timer; 3811da177e4SLinus Torvalds unsigned long flags; 3821da177e4SLinus Torvalds unsigned long resolution = 0; 3831da177e4SLinus Torvalds snd_timer_instance_t *ts; 3841da177e4SLinus Torvalds struct list_head *n; 3851da177e4SLinus Torvalds struct timespec tstamp; 3861da177e4SLinus Torvalds 3871da177e4SLinus Torvalds snd_timestamp_now(&tstamp, 1); 3881da177e4SLinus Torvalds snd_assert(event >= SNDRV_TIMER_EVENT_START && event <= SNDRV_TIMER_EVENT_PAUSE, return); 3891da177e4SLinus Torvalds if (event == SNDRV_TIMER_EVENT_START || event == SNDRV_TIMER_EVENT_CONTINUE) 3901da177e4SLinus Torvalds resolution = snd_timer_resolution(ti); 3911da177e4SLinus Torvalds if (ti->ccallback) 3921da177e4SLinus Torvalds ti->ccallback(ti, SNDRV_TIMER_EVENT_START, &tstamp, resolution); 3931da177e4SLinus Torvalds if (ti->flags & SNDRV_TIMER_IFLG_SLAVE) 3941da177e4SLinus Torvalds return; 3951da177e4SLinus Torvalds timer = ti->timer; 3961da177e4SLinus Torvalds if (timer == NULL) 3971da177e4SLinus Torvalds return; 3981da177e4SLinus Torvalds if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE) 3991da177e4SLinus Torvalds return; 4001da177e4SLinus Torvalds spin_lock_irqsave(&timer->lock, flags); 4011da177e4SLinus Torvalds list_for_each(n, &ti->slave_active_head) { 4021da177e4SLinus Torvalds ts = (snd_timer_instance_t *)list_entry(n, snd_timer_instance_t, active_list); 4031da177e4SLinus Torvalds if (ts->ccallback) 4041da177e4SLinus Torvalds ts->ccallback(ti, event + 100, &tstamp, resolution); 4051da177e4SLinus Torvalds } 4061da177e4SLinus Torvalds spin_unlock_irqrestore(&timer->lock, flags); 4071da177e4SLinus Torvalds } 4081da177e4SLinus Torvalds 4091da177e4SLinus Torvalds static int snd_timer_start1(snd_timer_t *timer, snd_timer_instance_t *timeri, unsigned long sticks) 4101da177e4SLinus Torvalds { 4111da177e4SLinus Torvalds list_del(&timeri->active_list); 4121da177e4SLinus Torvalds list_add_tail(&timeri->active_list, &timer->active_list_head); 4131da177e4SLinus Torvalds if (timer->running) { 4141da177e4SLinus Torvalds if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE) 4151da177e4SLinus Torvalds goto __start_now; 4161da177e4SLinus Torvalds timer->flags |= SNDRV_TIMER_FLG_RESCHED; 4171da177e4SLinus Torvalds timeri->flags |= SNDRV_TIMER_IFLG_START; 4181da177e4SLinus Torvalds return 1; /* delayed start */ 4191da177e4SLinus Torvalds } else { 4201da177e4SLinus Torvalds timer->sticks = sticks; 4211da177e4SLinus Torvalds timer->hw.start(timer); 4221da177e4SLinus Torvalds __start_now: 4231da177e4SLinus Torvalds timer->running++; 4241da177e4SLinus Torvalds timeri->flags |= SNDRV_TIMER_IFLG_RUNNING; 4251da177e4SLinus Torvalds return 0; 4261da177e4SLinus Torvalds } 4271da177e4SLinus Torvalds } 4281da177e4SLinus Torvalds 4291da177e4SLinus Torvalds static int snd_timer_start_slave(snd_timer_instance_t *timeri) 4301da177e4SLinus Torvalds { 4311da177e4SLinus Torvalds unsigned long flags; 4321da177e4SLinus Torvalds 4331da177e4SLinus Torvalds spin_lock_irqsave(&slave_active_lock, flags); 4341da177e4SLinus Torvalds timeri->flags |= SNDRV_TIMER_IFLG_RUNNING; 4351da177e4SLinus Torvalds if (timeri->master) 4361da177e4SLinus Torvalds list_add_tail(&timeri->active_list, &timeri->master->slave_active_head); 4371da177e4SLinus Torvalds spin_unlock_irqrestore(&slave_active_lock, flags); 4381da177e4SLinus Torvalds return 1; /* delayed start */ 4391da177e4SLinus Torvalds } 4401da177e4SLinus Torvalds 4411da177e4SLinus Torvalds /* 4421da177e4SLinus Torvalds * start the timer instance 4431da177e4SLinus Torvalds */ 4441da177e4SLinus Torvalds int snd_timer_start(snd_timer_instance_t * timeri, unsigned int ticks) 4451da177e4SLinus Torvalds { 4461da177e4SLinus Torvalds snd_timer_t *timer; 4471da177e4SLinus Torvalds int result = -EINVAL; 4481da177e4SLinus Torvalds unsigned long flags; 4491da177e4SLinus Torvalds 4501da177e4SLinus Torvalds if (timeri == NULL || ticks < 1) 4511da177e4SLinus Torvalds return -EINVAL; 4521da177e4SLinus Torvalds if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) { 4531da177e4SLinus Torvalds result = snd_timer_start_slave(timeri); 4541da177e4SLinus Torvalds snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_START); 4551da177e4SLinus Torvalds return result; 4561da177e4SLinus Torvalds } 4571da177e4SLinus Torvalds timer = timeri->timer; 4581da177e4SLinus Torvalds if (timer == NULL) 4591da177e4SLinus Torvalds return -EINVAL; 4601da177e4SLinus Torvalds spin_lock_irqsave(&timer->lock, flags); 4611da177e4SLinus Torvalds timeri->ticks = timeri->cticks = ticks; 4621da177e4SLinus Torvalds timeri->pticks = 0; 4631da177e4SLinus Torvalds result = snd_timer_start1(timer, timeri, ticks); 4641da177e4SLinus Torvalds spin_unlock_irqrestore(&timer->lock, flags); 4651da177e4SLinus Torvalds snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_START); 4661da177e4SLinus Torvalds return result; 4671da177e4SLinus Torvalds } 4681da177e4SLinus Torvalds 4691da177e4SLinus Torvalds static int _snd_timer_stop(snd_timer_instance_t * timeri, int keep_flag, enum sndrv_timer_event event) 4701da177e4SLinus Torvalds { 4711da177e4SLinus Torvalds snd_timer_t *timer; 4721da177e4SLinus Torvalds unsigned long flags; 4731da177e4SLinus Torvalds 4741da177e4SLinus Torvalds snd_assert(timeri != NULL, return -ENXIO); 4751da177e4SLinus Torvalds 4761da177e4SLinus Torvalds if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) { 4771da177e4SLinus Torvalds if (!keep_flag) { 4781da177e4SLinus Torvalds spin_lock_irqsave(&slave_active_lock, flags); 4791da177e4SLinus Torvalds timeri->flags &= ~SNDRV_TIMER_IFLG_RUNNING; 4801da177e4SLinus Torvalds spin_unlock_irqrestore(&slave_active_lock, flags); 4811da177e4SLinus Torvalds } 4821da177e4SLinus Torvalds goto __end; 4831da177e4SLinus Torvalds } 4841da177e4SLinus Torvalds timer = timeri->timer; 4851da177e4SLinus Torvalds if (!timer) 4861da177e4SLinus Torvalds return -EINVAL; 4871da177e4SLinus Torvalds spin_lock_irqsave(&timer->lock, flags); 4881da177e4SLinus Torvalds list_del_init(&timeri->ack_list); 4891da177e4SLinus Torvalds list_del_init(&timeri->active_list); 4901da177e4SLinus Torvalds if ((timeri->flags & SNDRV_TIMER_IFLG_RUNNING) && 4911da177e4SLinus Torvalds !(--timer->running)) { 4921da177e4SLinus Torvalds timer->hw.stop(timer); 4931da177e4SLinus Torvalds if (timer->flags & SNDRV_TIMER_FLG_RESCHED) { 4941da177e4SLinus Torvalds timer->flags &= ~SNDRV_TIMER_FLG_RESCHED; 4951da177e4SLinus Torvalds snd_timer_reschedule(timer, 0); 4961da177e4SLinus Torvalds if (timer->flags & SNDRV_TIMER_FLG_CHANGE) { 4971da177e4SLinus Torvalds timer->flags &= ~SNDRV_TIMER_FLG_CHANGE; 4981da177e4SLinus Torvalds timer->hw.start(timer); 4991da177e4SLinus Torvalds } 5001da177e4SLinus Torvalds } 5011da177e4SLinus Torvalds } 5021da177e4SLinus Torvalds if (!keep_flag) 5031da177e4SLinus Torvalds timeri->flags &= ~(SNDRV_TIMER_IFLG_RUNNING|SNDRV_TIMER_IFLG_START); 5041da177e4SLinus Torvalds spin_unlock_irqrestore(&timer->lock, flags); 5051da177e4SLinus Torvalds __end: 5061da177e4SLinus Torvalds if (event != SNDRV_TIMER_EVENT_RESOLUTION) 5071da177e4SLinus Torvalds snd_timer_notify1(timeri, event); 5081da177e4SLinus Torvalds return 0; 5091da177e4SLinus Torvalds } 5101da177e4SLinus Torvalds 5111da177e4SLinus Torvalds /* 5121da177e4SLinus Torvalds * stop the timer instance. 5131da177e4SLinus Torvalds * 5141da177e4SLinus Torvalds * do not call this from the timer callback! 5151da177e4SLinus Torvalds */ 5161da177e4SLinus Torvalds int snd_timer_stop(snd_timer_instance_t * timeri) 5171da177e4SLinus Torvalds { 5181da177e4SLinus Torvalds snd_timer_t *timer; 5191da177e4SLinus Torvalds unsigned long flags; 5201da177e4SLinus Torvalds int err; 5211da177e4SLinus Torvalds 5221da177e4SLinus Torvalds err = _snd_timer_stop(timeri, 0, SNDRV_TIMER_EVENT_STOP); 5231da177e4SLinus Torvalds if (err < 0) 5241da177e4SLinus Torvalds return err; 5251da177e4SLinus Torvalds timer = timeri->timer; 5261da177e4SLinus Torvalds spin_lock_irqsave(&timer->lock, flags); 5271da177e4SLinus Torvalds timeri->cticks = timeri->ticks; 5281da177e4SLinus Torvalds timeri->pticks = 0; 5291da177e4SLinus Torvalds spin_unlock_irqrestore(&timer->lock, flags); 5301da177e4SLinus Torvalds return 0; 5311da177e4SLinus Torvalds } 5321da177e4SLinus Torvalds 5331da177e4SLinus Torvalds /* 5341da177e4SLinus Torvalds * start again.. the tick is kept. 5351da177e4SLinus Torvalds */ 5361da177e4SLinus Torvalds int snd_timer_continue(snd_timer_instance_t * timeri) 5371da177e4SLinus Torvalds { 5381da177e4SLinus Torvalds snd_timer_t *timer; 5391da177e4SLinus Torvalds int result = -EINVAL; 5401da177e4SLinus Torvalds unsigned long flags; 5411da177e4SLinus Torvalds 5421da177e4SLinus Torvalds if (timeri == NULL) 5431da177e4SLinus Torvalds return result; 5441da177e4SLinus Torvalds if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) 5451da177e4SLinus Torvalds return snd_timer_start_slave(timeri); 5461da177e4SLinus Torvalds timer = timeri->timer; 5471da177e4SLinus Torvalds if (! timer) 5481da177e4SLinus Torvalds return -EINVAL; 5491da177e4SLinus Torvalds spin_lock_irqsave(&timer->lock, flags); 5501da177e4SLinus Torvalds if (!timeri->cticks) 5511da177e4SLinus Torvalds timeri->cticks = 1; 5521da177e4SLinus Torvalds timeri->pticks = 0; 5531da177e4SLinus Torvalds result = snd_timer_start1(timer, timeri, timer->sticks); 5541da177e4SLinus Torvalds spin_unlock_irqrestore(&timer->lock, flags); 5551da177e4SLinus Torvalds snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_CONTINUE); 5561da177e4SLinus Torvalds return result; 5571da177e4SLinus Torvalds } 5581da177e4SLinus Torvalds 5591da177e4SLinus Torvalds /* 5601da177e4SLinus Torvalds * pause.. remember the ticks left 5611da177e4SLinus Torvalds */ 5621da177e4SLinus Torvalds int snd_timer_pause(snd_timer_instance_t * timeri) 5631da177e4SLinus Torvalds { 5641da177e4SLinus Torvalds return _snd_timer_stop(timeri, 0, SNDRV_TIMER_EVENT_PAUSE); 5651da177e4SLinus Torvalds } 5661da177e4SLinus Torvalds 5671da177e4SLinus Torvalds /* 5681da177e4SLinus Torvalds * reschedule the timer 5691da177e4SLinus Torvalds * 5701da177e4SLinus Torvalds * start pending instances and check the scheduling ticks. 5711da177e4SLinus Torvalds * when the scheduling ticks is changed set CHANGE flag to reprogram the timer. 5721da177e4SLinus Torvalds */ 5731da177e4SLinus Torvalds static void snd_timer_reschedule(snd_timer_t * timer, unsigned long ticks_left) 5741da177e4SLinus Torvalds { 5751da177e4SLinus Torvalds snd_timer_instance_t *ti; 5761da177e4SLinus Torvalds unsigned long ticks = ~0UL; 5771da177e4SLinus Torvalds struct list_head *p; 5781da177e4SLinus Torvalds 5791da177e4SLinus Torvalds list_for_each(p, &timer->active_list_head) { 5801da177e4SLinus Torvalds ti = (snd_timer_instance_t *)list_entry(p, snd_timer_instance_t, active_list); 5811da177e4SLinus Torvalds if (ti->flags & SNDRV_TIMER_IFLG_START) { 5821da177e4SLinus Torvalds ti->flags &= ~SNDRV_TIMER_IFLG_START; 5831da177e4SLinus Torvalds ti->flags |= SNDRV_TIMER_IFLG_RUNNING; 5841da177e4SLinus Torvalds timer->running++; 5851da177e4SLinus Torvalds } 5861da177e4SLinus Torvalds if (ti->flags & SNDRV_TIMER_IFLG_RUNNING) { 5871da177e4SLinus Torvalds if (ticks > ti->cticks) 5881da177e4SLinus Torvalds ticks = ti->cticks; 5891da177e4SLinus Torvalds } 5901da177e4SLinus Torvalds } 5911da177e4SLinus Torvalds if (ticks == ~0UL) { 5921da177e4SLinus Torvalds timer->flags &= ~SNDRV_TIMER_FLG_RESCHED; 5931da177e4SLinus Torvalds return; 5941da177e4SLinus Torvalds } 5951da177e4SLinus Torvalds if (ticks > timer->hw.ticks) 5961da177e4SLinus Torvalds ticks = timer->hw.ticks; 5971da177e4SLinus Torvalds if (ticks_left != ticks) 5981da177e4SLinus Torvalds timer->flags |= SNDRV_TIMER_FLG_CHANGE; 5991da177e4SLinus Torvalds timer->sticks = ticks; 6001da177e4SLinus Torvalds } 6011da177e4SLinus Torvalds 6021da177e4SLinus Torvalds /* 6031da177e4SLinus Torvalds * timer tasklet 6041da177e4SLinus Torvalds * 6051da177e4SLinus Torvalds */ 6061da177e4SLinus Torvalds static void snd_timer_tasklet(unsigned long arg) 6071da177e4SLinus Torvalds { 6081da177e4SLinus Torvalds snd_timer_t *timer = (snd_timer_t *) arg; 6091da177e4SLinus Torvalds snd_timer_instance_t *ti; 6101da177e4SLinus Torvalds struct list_head *p; 6111da177e4SLinus Torvalds unsigned long resolution, ticks; 6121da177e4SLinus Torvalds 6131da177e4SLinus Torvalds spin_lock(&timer->lock); 6141da177e4SLinus Torvalds /* now process all callbacks */ 6151da177e4SLinus Torvalds while (!list_empty(&timer->sack_list_head)) { 6161da177e4SLinus Torvalds p = timer->sack_list_head.next; /* get first item */ 6171da177e4SLinus Torvalds ti = (snd_timer_instance_t *)list_entry(p, snd_timer_instance_t, ack_list); 6181da177e4SLinus Torvalds 6191da177e4SLinus Torvalds /* remove from ack_list and make empty */ 6201da177e4SLinus Torvalds list_del_init(p); 6211da177e4SLinus Torvalds 6221da177e4SLinus Torvalds ticks = ti->pticks; 6231da177e4SLinus Torvalds ti->pticks = 0; 6241da177e4SLinus Torvalds resolution = ti->resolution; 6251da177e4SLinus Torvalds 6261da177e4SLinus Torvalds ti->flags |= SNDRV_TIMER_IFLG_CALLBACK; 6271da177e4SLinus Torvalds spin_unlock(&timer->lock); 6281da177e4SLinus Torvalds if (ti->callback) 6291da177e4SLinus Torvalds ti->callback(ti, resolution, ticks); 6301da177e4SLinus Torvalds spin_lock(&timer->lock); 6311da177e4SLinus Torvalds ti->flags &= ~SNDRV_TIMER_IFLG_CALLBACK; 6321da177e4SLinus Torvalds } 6331da177e4SLinus Torvalds spin_unlock(&timer->lock); 6341da177e4SLinus Torvalds } 6351da177e4SLinus Torvalds 6361da177e4SLinus Torvalds /* 6371da177e4SLinus Torvalds * timer interrupt 6381da177e4SLinus Torvalds * 6391da177e4SLinus Torvalds * ticks_left is usually equal to timer->sticks. 6401da177e4SLinus Torvalds * 6411da177e4SLinus Torvalds */ 6421da177e4SLinus Torvalds void snd_timer_interrupt(snd_timer_t * timer, unsigned long ticks_left) 6431da177e4SLinus Torvalds { 6441da177e4SLinus Torvalds snd_timer_instance_t *ti, *ts; 6451da177e4SLinus Torvalds unsigned long resolution, ticks; 6461da177e4SLinus Torvalds struct list_head *p, *q, *n; 6471da177e4SLinus Torvalds int use_tasklet = 0; 6481da177e4SLinus Torvalds 6491da177e4SLinus Torvalds if (timer == NULL) 6501da177e4SLinus Torvalds return; 6511da177e4SLinus Torvalds 6521da177e4SLinus Torvalds spin_lock(&timer->lock); 6531da177e4SLinus Torvalds 6541da177e4SLinus Torvalds /* remember the current resolution */ 6551da177e4SLinus Torvalds if (timer->hw.c_resolution) 6561da177e4SLinus Torvalds resolution = timer->hw.c_resolution(timer); 6571da177e4SLinus Torvalds else 6581da177e4SLinus Torvalds resolution = timer->hw.resolution; 6591da177e4SLinus Torvalds 6601da177e4SLinus Torvalds /* loop for all active instances 6611da177e4SLinus Torvalds * here we cannot use list_for_each because the active_list of a processed 6621da177e4SLinus Torvalds * instance is relinked to done_list_head before callback is called. 6631da177e4SLinus Torvalds */ 6641da177e4SLinus Torvalds list_for_each_safe(p, n, &timer->active_list_head) { 6651da177e4SLinus Torvalds ti = (snd_timer_instance_t *)list_entry(p, snd_timer_instance_t, active_list); 6661da177e4SLinus Torvalds if (!(ti->flags & SNDRV_TIMER_IFLG_RUNNING)) 6671da177e4SLinus Torvalds continue; 6681da177e4SLinus Torvalds ti->pticks += ticks_left; 6691da177e4SLinus Torvalds ti->resolution = resolution; 6701da177e4SLinus Torvalds if (ti->cticks < ticks_left) 6711da177e4SLinus Torvalds ti->cticks = 0; 6721da177e4SLinus Torvalds else 6731da177e4SLinus Torvalds ti->cticks -= ticks_left; 6741da177e4SLinus Torvalds if (ti->cticks) /* not expired */ 6751da177e4SLinus Torvalds continue; 6761da177e4SLinus Torvalds if (ti->flags & SNDRV_TIMER_IFLG_AUTO) { 6771da177e4SLinus Torvalds ti->cticks = ti->ticks; 6781da177e4SLinus Torvalds } else { 6791da177e4SLinus Torvalds ti->flags &= ~SNDRV_TIMER_IFLG_RUNNING; 6801da177e4SLinus Torvalds if (--timer->running) 6811da177e4SLinus Torvalds list_del(p); 6821da177e4SLinus Torvalds } 6831da177e4SLinus Torvalds if (list_empty(&ti->ack_list)) { 6841da177e4SLinus Torvalds if ((timer->hw.flags & SNDRV_TIMER_HW_TASKLET) || 6851da177e4SLinus Torvalds (ti->flags & SNDRV_TIMER_IFLG_FAST)) { 6861da177e4SLinus Torvalds list_add_tail(&ti->ack_list, &timer->ack_list_head); 6871da177e4SLinus Torvalds } else { 6881da177e4SLinus Torvalds list_add_tail(&ti->ack_list, &timer->sack_list_head); 6891da177e4SLinus Torvalds } 6901da177e4SLinus Torvalds } 6911da177e4SLinus Torvalds list_for_each(q, &ti->slave_active_head) { 6921da177e4SLinus Torvalds ts = (snd_timer_instance_t *)list_entry(q, snd_timer_instance_t, active_list); 6931da177e4SLinus Torvalds ts->pticks = ti->pticks; 6941da177e4SLinus Torvalds ts->resolution = resolution; 6951da177e4SLinus Torvalds if (list_empty(&ts->ack_list)) { 6961da177e4SLinus Torvalds if ((timer->hw.flags & SNDRV_TIMER_HW_TASKLET) || 6971da177e4SLinus Torvalds (ti->flags & SNDRV_TIMER_IFLG_FAST)) { 6981da177e4SLinus Torvalds list_add_tail(&ts->ack_list, &timer->ack_list_head); 6991da177e4SLinus Torvalds } else { 7001da177e4SLinus Torvalds list_add_tail(&ts->ack_list, &timer->sack_list_head); 7011da177e4SLinus Torvalds } 7021da177e4SLinus Torvalds } 7031da177e4SLinus Torvalds } 7041da177e4SLinus Torvalds } 7051da177e4SLinus Torvalds if (timer->flags & SNDRV_TIMER_FLG_RESCHED) 7061da177e4SLinus Torvalds snd_timer_reschedule(timer, ticks_left); 7071da177e4SLinus Torvalds if (timer->running) { 7081da177e4SLinus Torvalds if (timer->hw.flags & SNDRV_TIMER_HW_STOP) { 7091da177e4SLinus Torvalds timer->hw.stop(timer); 7101da177e4SLinus Torvalds timer->flags |= SNDRV_TIMER_FLG_CHANGE; 7111da177e4SLinus Torvalds } 7121da177e4SLinus Torvalds if (!(timer->hw.flags & SNDRV_TIMER_HW_AUTO) || 7131da177e4SLinus Torvalds (timer->flags & SNDRV_TIMER_FLG_CHANGE)) { 7141da177e4SLinus Torvalds /* restart timer */ 7151da177e4SLinus Torvalds timer->flags &= ~SNDRV_TIMER_FLG_CHANGE; 7161da177e4SLinus Torvalds timer->hw.start(timer); 7171da177e4SLinus Torvalds } 7181da177e4SLinus Torvalds } else { 7191da177e4SLinus Torvalds timer->hw.stop(timer); 7201da177e4SLinus Torvalds } 7211da177e4SLinus Torvalds 7221da177e4SLinus Torvalds /* now process all fast callbacks */ 7231da177e4SLinus Torvalds while (!list_empty(&timer->ack_list_head)) { 7241da177e4SLinus Torvalds p = timer->ack_list_head.next; /* get first item */ 7251da177e4SLinus Torvalds ti = (snd_timer_instance_t *)list_entry(p, snd_timer_instance_t, ack_list); 7261da177e4SLinus Torvalds 7271da177e4SLinus Torvalds /* remove from ack_list and make empty */ 7281da177e4SLinus Torvalds list_del_init(p); 7291da177e4SLinus Torvalds 7301da177e4SLinus Torvalds ticks = ti->pticks; 7311da177e4SLinus Torvalds ti->pticks = 0; 7321da177e4SLinus Torvalds 7331da177e4SLinus Torvalds ti->flags |= SNDRV_TIMER_IFLG_CALLBACK; 7341da177e4SLinus Torvalds spin_unlock(&timer->lock); 7351da177e4SLinus Torvalds if (ti->callback) 7361da177e4SLinus Torvalds ti->callback(ti, resolution, ticks); 7371da177e4SLinus Torvalds spin_lock(&timer->lock); 7381da177e4SLinus Torvalds ti->flags &= ~SNDRV_TIMER_IFLG_CALLBACK; 7391da177e4SLinus Torvalds } 7401da177e4SLinus Torvalds 7411da177e4SLinus Torvalds /* do we have any slow callbacks? */ 7421da177e4SLinus Torvalds use_tasklet = !list_empty(&timer->sack_list_head); 7431da177e4SLinus Torvalds spin_unlock(&timer->lock); 7441da177e4SLinus Torvalds 7451da177e4SLinus Torvalds if (use_tasklet) 7461da177e4SLinus Torvalds tasklet_hi_schedule(&timer->task_queue); 7471da177e4SLinus Torvalds } 7481da177e4SLinus Torvalds 7491da177e4SLinus Torvalds /* 7501da177e4SLinus Torvalds 7511da177e4SLinus Torvalds */ 7521da177e4SLinus Torvalds 7531da177e4SLinus Torvalds int snd_timer_new(snd_card_t *card, char *id, snd_timer_id_t *tid, snd_timer_t ** rtimer) 7541da177e4SLinus Torvalds { 7551da177e4SLinus Torvalds snd_timer_t *timer; 7561da177e4SLinus Torvalds int err; 7571da177e4SLinus Torvalds static snd_device_ops_t ops = { 7581da177e4SLinus Torvalds .dev_free = snd_timer_dev_free, 7591da177e4SLinus Torvalds .dev_register = snd_timer_dev_register, 7601da177e4SLinus Torvalds .dev_unregister = snd_timer_dev_unregister 7611da177e4SLinus Torvalds }; 7621da177e4SLinus Torvalds 7631da177e4SLinus Torvalds snd_assert(tid != NULL, return -EINVAL); 7641da177e4SLinus Torvalds snd_assert(rtimer != NULL, return -EINVAL); 7651da177e4SLinus Torvalds *rtimer = NULL; 7661da177e4SLinus Torvalds timer = kcalloc(1, sizeof(*timer), GFP_KERNEL); 7671da177e4SLinus Torvalds if (timer == NULL) 7681da177e4SLinus Torvalds return -ENOMEM; 7691da177e4SLinus Torvalds timer->tmr_class = tid->dev_class; 7701da177e4SLinus Torvalds timer->card = card; 7711da177e4SLinus Torvalds timer->tmr_device = tid->device; 7721da177e4SLinus Torvalds timer->tmr_subdevice = tid->subdevice; 7731da177e4SLinus Torvalds if (id) 7741da177e4SLinus Torvalds strlcpy(timer->id, id, sizeof(timer->id)); 7751da177e4SLinus Torvalds INIT_LIST_HEAD(&timer->device_list); 7761da177e4SLinus Torvalds INIT_LIST_HEAD(&timer->open_list_head); 7771da177e4SLinus Torvalds INIT_LIST_HEAD(&timer->active_list_head); 7781da177e4SLinus Torvalds INIT_LIST_HEAD(&timer->ack_list_head); 7791da177e4SLinus Torvalds INIT_LIST_HEAD(&timer->sack_list_head); 7801da177e4SLinus Torvalds spin_lock_init(&timer->lock); 7811da177e4SLinus Torvalds tasklet_init(&timer->task_queue, snd_timer_tasklet, (unsigned long)timer); 7821da177e4SLinus Torvalds if (card != NULL) { 7831da177e4SLinus Torvalds if ((err = snd_device_new(card, SNDRV_DEV_TIMER, timer, &ops)) < 0) { 7841da177e4SLinus Torvalds snd_timer_free(timer); 7851da177e4SLinus Torvalds return err; 7861da177e4SLinus Torvalds } 7871da177e4SLinus Torvalds } 7881da177e4SLinus Torvalds *rtimer = timer; 7891da177e4SLinus Torvalds return 0; 7901da177e4SLinus Torvalds } 7911da177e4SLinus Torvalds 7921da177e4SLinus Torvalds static int snd_timer_free(snd_timer_t *timer) 7931da177e4SLinus Torvalds { 7941da177e4SLinus Torvalds snd_assert(timer != NULL, return -ENXIO); 7951da177e4SLinus Torvalds if (timer->private_free) 7961da177e4SLinus Torvalds timer->private_free(timer); 7971da177e4SLinus Torvalds kfree(timer); 7981da177e4SLinus Torvalds return 0; 7991da177e4SLinus Torvalds } 8001da177e4SLinus Torvalds 8011da177e4SLinus Torvalds int snd_timer_dev_free(snd_device_t *device) 8021da177e4SLinus Torvalds { 8031da177e4SLinus Torvalds snd_timer_t *timer = device->device_data; 8041da177e4SLinus Torvalds return snd_timer_free(timer); 8051da177e4SLinus Torvalds } 8061da177e4SLinus Torvalds 8071da177e4SLinus Torvalds int snd_timer_dev_register(snd_device_t *dev) 8081da177e4SLinus Torvalds { 8091da177e4SLinus Torvalds snd_timer_t *timer = dev->device_data; 8101da177e4SLinus Torvalds snd_timer_t *timer1; 8111da177e4SLinus Torvalds struct list_head *p; 8121da177e4SLinus Torvalds 8131da177e4SLinus Torvalds snd_assert(timer != NULL && timer->hw.start != NULL && timer->hw.stop != NULL, return -ENXIO); 8141da177e4SLinus Torvalds if (!(timer->hw.flags & SNDRV_TIMER_HW_SLAVE) && 8151da177e4SLinus Torvalds !timer->hw.resolution && timer->hw.c_resolution == NULL) 8161da177e4SLinus Torvalds return -EINVAL; 8171da177e4SLinus Torvalds 8181da177e4SLinus Torvalds down(®ister_mutex); 8191da177e4SLinus Torvalds list_for_each(p, &snd_timer_list) { 8201da177e4SLinus Torvalds timer1 = (snd_timer_t *)list_entry(p, snd_timer_t, device_list); 8211da177e4SLinus Torvalds if (timer1->tmr_class > timer->tmr_class) 8221da177e4SLinus Torvalds break; 8231da177e4SLinus Torvalds if (timer1->tmr_class < timer->tmr_class) 8241da177e4SLinus Torvalds continue; 8251da177e4SLinus Torvalds if (timer1->card && timer->card) { 8261da177e4SLinus Torvalds if (timer1->card->number > timer->card->number) 8271da177e4SLinus Torvalds break; 8281da177e4SLinus Torvalds if (timer1->card->number < timer->card->number) 8291da177e4SLinus Torvalds continue; 8301da177e4SLinus Torvalds } 8311da177e4SLinus Torvalds if (timer1->tmr_device > timer->tmr_device) 8321da177e4SLinus Torvalds break; 8331da177e4SLinus Torvalds if (timer1->tmr_device < timer->tmr_device) 8341da177e4SLinus Torvalds continue; 8351da177e4SLinus Torvalds if (timer1->tmr_subdevice > timer->tmr_subdevice) 8361da177e4SLinus Torvalds break; 8371da177e4SLinus Torvalds if (timer1->tmr_subdevice < timer->tmr_subdevice) 8381da177e4SLinus Torvalds continue; 8391da177e4SLinus Torvalds /* conflicts.. */ 8401da177e4SLinus Torvalds up(®ister_mutex); 8411da177e4SLinus Torvalds return -EBUSY; 8421da177e4SLinus Torvalds } 8431da177e4SLinus Torvalds list_add_tail(&timer->device_list, p); 8441da177e4SLinus Torvalds up(®ister_mutex); 8451da177e4SLinus Torvalds return 0; 8461da177e4SLinus Torvalds } 8471da177e4SLinus Torvalds 848*123992f7SAdrian Bunk static int snd_timer_unregister(snd_timer_t *timer) 8491da177e4SLinus Torvalds { 8501da177e4SLinus Torvalds struct list_head *p, *n; 8511da177e4SLinus Torvalds snd_timer_instance_t *ti; 8521da177e4SLinus Torvalds 8531da177e4SLinus Torvalds snd_assert(timer != NULL, return -ENXIO); 8541da177e4SLinus Torvalds down(®ister_mutex); 8551da177e4SLinus Torvalds if (! list_empty(&timer->open_list_head)) { 8561da177e4SLinus Torvalds snd_printk(KERN_WARNING "timer 0x%lx is busy?\n", (long)timer); 8571da177e4SLinus Torvalds list_for_each_safe(p, n, &timer->open_list_head) { 8581da177e4SLinus Torvalds list_del_init(p); 8591da177e4SLinus Torvalds ti = (snd_timer_instance_t *)list_entry(p, snd_timer_instance_t, open_list); 8601da177e4SLinus Torvalds ti->timer = NULL; 8611da177e4SLinus Torvalds } 8621da177e4SLinus Torvalds } 8631da177e4SLinus Torvalds list_del(&timer->device_list); 8641da177e4SLinus Torvalds up(®ister_mutex); 8651da177e4SLinus Torvalds return snd_timer_free(timer); 8661da177e4SLinus Torvalds } 8671da177e4SLinus Torvalds 8681da177e4SLinus Torvalds static int snd_timer_dev_unregister(snd_device_t *device) 8691da177e4SLinus Torvalds { 8701da177e4SLinus Torvalds snd_timer_t *timer = device->device_data; 8711da177e4SLinus Torvalds return snd_timer_unregister(timer); 8721da177e4SLinus Torvalds } 8731da177e4SLinus Torvalds 8741da177e4SLinus Torvalds void snd_timer_notify(snd_timer_t *timer, enum sndrv_timer_event event, struct timespec *tstamp) 8751da177e4SLinus Torvalds { 8761da177e4SLinus Torvalds unsigned long flags; 8771da177e4SLinus Torvalds unsigned long resolution = 0; 8781da177e4SLinus Torvalds snd_timer_instance_t *ti, *ts; 8791da177e4SLinus Torvalds struct list_head *p, *n; 8801da177e4SLinus Torvalds 8811da177e4SLinus Torvalds snd_runtime_check(timer->hw.flags & SNDRV_TIMER_HW_SLAVE, return); 8821da177e4SLinus Torvalds snd_assert(event >= SNDRV_TIMER_EVENT_MSTART && event <= SNDRV_TIMER_EVENT_MPAUSE, return); 8831da177e4SLinus Torvalds spin_lock_irqsave(&timer->lock, flags); 8841da177e4SLinus Torvalds if (event == SNDRV_TIMER_EVENT_MSTART || event == SNDRV_TIMER_EVENT_MCONTINUE) { 8851da177e4SLinus Torvalds if (timer->hw.c_resolution) 8861da177e4SLinus Torvalds resolution = timer->hw.c_resolution(timer); 8871da177e4SLinus Torvalds else 8881da177e4SLinus Torvalds resolution = timer->hw.resolution; 8891da177e4SLinus Torvalds } 8901da177e4SLinus Torvalds list_for_each(p, &timer->active_list_head) { 8911da177e4SLinus Torvalds ti = (snd_timer_instance_t *)list_entry(p, snd_timer_instance_t, active_list); 8921da177e4SLinus Torvalds if (ti->ccallback) 8931da177e4SLinus Torvalds ti->ccallback(ti, event, tstamp, resolution); 8941da177e4SLinus Torvalds list_for_each(n, &ti->slave_active_head) { 8951da177e4SLinus Torvalds ts = (snd_timer_instance_t *)list_entry(n, snd_timer_instance_t, active_list); 8961da177e4SLinus Torvalds if (ts->ccallback) 8971da177e4SLinus Torvalds ts->ccallback(ts, event, tstamp, resolution); 8981da177e4SLinus Torvalds } 8991da177e4SLinus Torvalds } 9001da177e4SLinus Torvalds spin_unlock_irqrestore(&timer->lock, flags); 9011da177e4SLinus Torvalds } 9021da177e4SLinus Torvalds 9031da177e4SLinus Torvalds /* 9041da177e4SLinus Torvalds * exported functions for global timers 9051da177e4SLinus Torvalds */ 9061da177e4SLinus Torvalds int snd_timer_global_new(char *id, int device, snd_timer_t **rtimer) 9071da177e4SLinus Torvalds { 9081da177e4SLinus Torvalds snd_timer_id_t tid; 9091da177e4SLinus Torvalds 9101da177e4SLinus Torvalds tid.dev_class = SNDRV_TIMER_CLASS_GLOBAL; 9111da177e4SLinus Torvalds tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE; 9121da177e4SLinus Torvalds tid.card = -1; 9131da177e4SLinus Torvalds tid.device = device; 9141da177e4SLinus Torvalds tid.subdevice = 0; 9151da177e4SLinus Torvalds return snd_timer_new(NULL, id, &tid, rtimer); 9161da177e4SLinus Torvalds } 9171da177e4SLinus Torvalds 9181da177e4SLinus Torvalds int snd_timer_global_free(snd_timer_t *timer) 9191da177e4SLinus Torvalds { 9201da177e4SLinus Torvalds return snd_timer_free(timer); 9211da177e4SLinus Torvalds } 9221da177e4SLinus Torvalds 9231da177e4SLinus Torvalds int snd_timer_global_register(snd_timer_t *timer) 9241da177e4SLinus Torvalds { 9251da177e4SLinus Torvalds snd_device_t dev; 9261da177e4SLinus Torvalds 9271da177e4SLinus Torvalds memset(&dev, 0, sizeof(dev)); 9281da177e4SLinus Torvalds dev.device_data = timer; 9291da177e4SLinus Torvalds return snd_timer_dev_register(&dev); 9301da177e4SLinus Torvalds } 9311da177e4SLinus Torvalds 9321da177e4SLinus Torvalds int snd_timer_global_unregister(snd_timer_t *timer) 9331da177e4SLinus Torvalds { 9341da177e4SLinus Torvalds return snd_timer_unregister(timer); 9351da177e4SLinus Torvalds } 9361da177e4SLinus Torvalds 9371da177e4SLinus Torvalds /* 9381da177e4SLinus Torvalds * System timer 9391da177e4SLinus Torvalds */ 9401da177e4SLinus Torvalds 9411da177e4SLinus Torvalds struct snd_timer_system_private { 9421da177e4SLinus Torvalds struct timer_list tlist; 9431da177e4SLinus Torvalds struct timer * timer; 9441da177e4SLinus Torvalds unsigned long last_expires; 9451da177e4SLinus Torvalds unsigned long last_jiffies; 9461da177e4SLinus Torvalds unsigned long correction; 9471da177e4SLinus Torvalds }; 9481da177e4SLinus Torvalds 9491da177e4SLinus Torvalds static void snd_timer_s_function(unsigned long data) 9501da177e4SLinus Torvalds { 9511da177e4SLinus Torvalds snd_timer_t *timer = (snd_timer_t *)data; 9521da177e4SLinus Torvalds struct snd_timer_system_private *priv = timer->private_data; 9531da177e4SLinus Torvalds unsigned long jiff = jiffies; 9541da177e4SLinus Torvalds if (time_after(jiff, priv->last_expires)) 9551da177e4SLinus Torvalds priv->correction = (long)jiff - (long)priv->last_expires; 9561da177e4SLinus Torvalds snd_timer_interrupt(timer, (long)jiff - (long)priv->last_jiffies); 9571da177e4SLinus Torvalds } 9581da177e4SLinus Torvalds 9591da177e4SLinus Torvalds static int snd_timer_s_start(snd_timer_t * timer) 9601da177e4SLinus Torvalds { 9611da177e4SLinus Torvalds struct snd_timer_system_private *priv; 9621da177e4SLinus Torvalds unsigned long njiff; 9631da177e4SLinus Torvalds 9641da177e4SLinus Torvalds priv = (struct snd_timer_system_private *) timer->private_data; 9651da177e4SLinus Torvalds njiff = (priv->last_jiffies = jiffies); 9661da177e4SLinus Torvalds if (priv->correction > timer->sticks - 1) { 9671da177e4SLinus Torvalds priv->correction -= timer->sticks - 1; 9681da177e4SLinus Torvalds njiff++; 9691da177e4SLinus Torvalds } else { 9701da177e4SLinus Torvalds njiff += timer->sticks - priv->correction; 9711da177e4SLinus Torvalds priv->correction -= timer->sticks; 9721da177e4SLinus Torvalds } 9731da177e4SLinus Torvalds priv->last_expires = priv->tlist.expires = njiff; 9741da177e4SLinus Torvalds add_timer(&priv->tlist); 9751da177e4SLinus Torvalds return 0; 9761da177e4SLinus Torvalds } 9771da177e4SLinus Torvalds 9781da177e4SLinus Torvalds static int snd_timer_s_stop(snd_timer_t * timer) 9791da177e4SLinus Torvalds { 9801da177e4SLinus Torvalds struct snd_timer_system_private *priv; 9811da177e4SLinus Torvalds unsigned long jiff; 9821da177e4SLinus Torvalds 9831da177e4SLinus Torvalds priv = (struct snd_timer_system_private *) timer->private_data; 9841da177e4SLinus Torvalds del_timer(&priv->tlist); 9851da177e4SLinus Torvalds jiff = jiffies; 9861da177e4SLinus Torvalds if (time_before(jiff, priv->last_expires)) 9871da177e4SLinus Torvalds timer->sticks = priv->last_expires - jiff; 9881da177e4SLinus Torvalds else 9891da177e4SLinus Torvalds timer->sticks = 1; 9901da177e4SLinus Torvalds return 0; 9911da177e4SLinus Torvalds } 9921da177e4SLinus Torvalds 9931da177e4SLinus Torvalds static struct _snd_timer_hardware snd_timer_system = 9941da177e4SLinus Torvalds { 9951da177e4SLinus Torvalds .flags = SNDRV_TIMER_HW_FIRST | SNDRV_TIMER_HW_TASKLET, 9961da177e4SLinus Torvalds .resolution = 1000000000L / HZ, 9971da177e4SLinus Torvalds .ticks = 10000000L, 9981da177e4SLinus Torvalds .start = snd_timer_s_start, 9991da177e4SLinus Torvalds .stop = snd_timer_s_stop 10001da177e4SLinus Torvalds }; 10011da177e4SLinus Torvalds 10021da177e4SLinus Torvalds static void snd_timer_free_system(snd_timer_t *timer) 10031da177e4SLinus Torvalds { 10041da177e4SLinus Torvalds kfree(timer->private_data); 10051da177e4SLinus Torvalds } 10061da177e4SLinus Torvalds 10071da177e4SLinus Torvalds static int snd_timer_register_system(void) 10081da177e4SLinus Torvalds { 10091da177e4SLinus Torvalds snd_timer_t *timer; 10101da177e4SLinus Torvalds struct snd_timer_system_private *priv; 10111da177e4SLinus Torvalds int err; 10121da177e4SLinus Torvalds 10131da177e4SLinus Torvalds if ((err = snd_timer_global_new("system", SNDRV_TIMER_GLOBAL_SYSTEM, &timer)) < 0) 10141da177e4SLinus Torvalds return err; 10151da177e4SLinus Torvalds strcpy(timer->name, "system timer"); 10161da177e4SLinus Torvalds timer->hw = snd_timer_system; 10171da177e4SLinus Torvalds priv = kcalloc(1, sizeof(*priv), GFP_KERNEL); 10181da177e4SLinus Torvalds if (priv == NULL) { 10191da177e4SLinus Torvalds snd_timer_free(timer); 10201da177e4SLinus Torvalds return -ENOMEM; 10211da177e4SLinus Torvalds } 10221da177e4SLinus Torvalds init_timer(&priv->tlist); 10231da177e4SLinus Torvalds priv->tlist.function = snd_timer_s_function; 10241da177e4SLinus Torvalds priv->tlist.data = (unsigned long) timer; 10251da177e4SLinus Torvalds timer->private_data = priv; 10261da177e4SLinus Torvalds timer->private_free = snd_timer_free_system; 10271da177e4SLinus Torvalds return snd_timer_global_register(timer); 10281da177e4SLinus Torvalds } 10291da177e4SLinus Torvalds 10301da177e4SLinus Torvalds /* 10311da177e4SLinus Torvalds * Info interface 10321da177e4SLinus Torvalds */ 10331da177e4SLinus Torvalds 10341da177e4SLinus Torvalds static void snd_timer_proc_read(snd_info_entry_t *entry, 10351da177e4SLinus Torvalds snd_info_buffer_t * buffer) 10361da177e4SLinus Torvalds { 10371da177e4SLinus Torvalds unsigned long flags; 10381da177e4SLinus Torvalds snd_timer_t *timer; 10391da177e4SLinus Torvalds snd_timer_instance_t *ti; 10401da177e4SLinus Torvalds struct list_head *p, *q; 10411da177e4SLinus Torvalds 10421da177e4SLinus Torvalds down(®ister_mutex); 10431da177e4SLinus Torvalds list_for_each(p, &snd_timer_list) { 10441da177e4SLinus Torvalds timer = (snd_timer_t *)list_entry(p, snd_timer_t, device_list); 10451da177e4SLinus Torvalds switch (timer->tmr_class) { 10461da177e4SLinus Torvalds case SNDRV_TIMER_CLASS_GLOBAL: 10471da177e4SLinus Torvalds snd_iprintf(buffer, "G%i: ", timer->tmr_device); 10481da177e4SLinus Torvalds break; 10491da177e4SLinus Torvalds case SNDRV_TIMER_CLASS_CARD: 10501da177e4SLinus Torvalds snd_iprintf(buffer, "C%i-%i: ", timer->card->number, timer->tmr_device); 10511da177e4SLinus Torvalds break; 10521da177e4SLinus Torvalds case SNDRV_TIMER_CLASS_PCM: 10531da177e4SLinus Torvalds snd_iprintf(buffer, "P%i-%i-%i: ", timer->card->number, timer->tmr_device, timer->tmr_subdevice); 10541da177e4SLinus Torvalds break; 10551da177e4SLinus Torvalds default: 10561da177e4SLinus Torvalds snd_iprintf(buffer, "?%i-%i-%i-%i: ", timer->tmr_class, timer->card ? timer->card->number : -1, timer->tmr_device, timer->tmr_subdevice); 10571da177e4SLinus Torvalds } 10581da177e4SLinus Torvalds snd_iprintf(buffer, "%s :", timer->name); 10591da177e4SLinus Torvalds if (timer->hw.resolution) 10601da177e4SLinus Torvalds snd_iprintf(buffer, " %lu.%03luus (%lu ticks)", timer->hw.resolution / 1000, timer->hw.resolution % 1000, timer->hw.ticks); 10611da177e4SLinus Torvalds if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE) 10621da177e4SLinus Torvalds snd_iprintf(buffer, " SLAVE"); 10631da177e4SLinus Torvalds snd_iprintf(buffer, "\n"); 10641da177e4SLinus Torvalds spin_lock_irqsave(&timer->lock, flags); 10651da177e4SLinus Torvalds list_for_each(q, &timer->open_list_head) { 10661da177e4SLinus Torvalds ti = (snd_timer_instance_t *)list_entry(q, snd_timer_instance_t, open_list); 10671da177e4SLinus Torvalds snd_iprintf(buffer, " Client %s : %s : lost interrupts %li\n", 10681da177e4SLinus Torvalds ti->owner ? ti->owner : "unknown", 10691da177e4SLinus Torvalds ti->flags & (SNDRV_TIMER_IFLG_START|SNDRV_TIMER_IFLG_RUNNING) ? "running" : "stopped", 10701da177e4SLinus Torvalds ti->lost); 10711da177e4SLinus Torvalds } 10721da177e4SLinus Torvalds spin_unlock_irqrestore(&timer->lock, flags); 10731da177e4SLinus Torvalds } 10741da177e4SLinus Torvalds up(®ister_mutex); 10751da177e4SLinus Torvalds } 10761da177e4SLinus Torvalds 10771da177e4SLinus Torvalds /* 10781da177e4SLinus Torvalds * USER SPACE interface 10791da177e4SLinus Torvalds */ 10801da177e4SLinus Torvalds 10811da177e4SLinus Torvalds static void snd_timer_user_interrupt(snd_timer_instance_t *timeri, 10821da177e4SLinus Torvalds unsigned long resolution, 10831da177e4SLinus Torvalds unsigned long ticks) 10841da177e4SLinus Torvalds { 10851da177e4SLinus Torvalds snd_timer_user_t *tu = timeri->callback_data; 10861da177e4SLinus Torvalds snd_timer_read_t *r; 10871da177e4SLinus Torvalds int prev; 10881da177e4SLinus Torvalds 10891da177e4SLinus Torvalds spin_lock(&tu->qlock); 10901da177e4SLinus Torvalds if (tu->qused > 0) { 10911da177e4SLinus Torvalds prev = tu->qtail == 0 ? tu->queue_size - 1 : tu->qtail - 1; 10921da177e4SLinus Torvalds r = &tu->queue[prev]; 10931da177e4SLinus Torvalds if (r->resolution == resolution) { 10941da177e4SLinus Torvalds r->ticks += ticks; 10951da177e4SLinus Torvalds goto __wake; 10961da177e4SLinus Torvalds } 10971da177e4SLinus Torvalds } 10981da177e4SLinus Torvalds if (tu->qused >= tu->queue_size) { 10991da177e4SLinus Torvalds tu->overrun++; 11001da177e4SLinus Torvalds } else { 11011da177e4SLinus Torvalds r = &tu->queue[tu->qtail++]; 11021da177e4SLinus Torvalds tu->qtail %= tu->queue_size; 11031da177e4SLinus Torvalds r->resolution = resolution; 11041da177e4SLinus Torvalds r->ticks = ticks; 11051da177e4SLinus Torvalds tu->qused++; 11061da177e4SLinus Torvalds } 11071da177e4SLinus Torvalds __wake: 11081da177e4SLinus Torvalds spin_unlock(&tu->qlock); 11091da177e4SLinus Torvalds kill_fasync(&tu->fasync, SIGIO, POLL_IN); 11101da177e4SLinus Torvalds wake_up(&tu->qchange_sleep); 11111da177e4SLinus Torvalds } 11121da177e4SLinus Torvalds 11131da177e4SLinus Torvalds static void snd_timer_user_append_to_tqueue(snd_timer_user_t *tu, snd_timer_tread_t *tread) 11141da177e4SLinus Torvalds { 11151da177e4SLinus Torvalds if (tu->qused >= tu->queue_size) { 11161da177e4SLinus Torvalds tu->overrun++; 11171da177e4SLinus Torvalds } else { 11181da177e4SLinus Torvalds memcpy(&tu->tqueue[tu->qtail++], tread, sizeof(*tread)); 11191da177e4SLinus Torvalds tu->qtail %= tu->queue_size; 11201da177e4SLinus Torvalds tu->qused++; 11211da177e4SLinus Torvalds } 11221da177e4SLinus Torvalds } 11231da177e4SLinus Torvalds 11241da177e4SLinus Torvalds static void snd_timer_user_ccallback(snd_timer_instance_t *timeri, 11251da177e4SLinus Torvalds enum sndrv_timer_event event, 11261da177e4SLinus Torvalds struct timespec *tstamp, 11271da177e4SLinus Torvalds unsigned long resolution) 11281da177e4SLinus Torvalds { 11291da177e4SLinus Torvalds snd_timer_user_t *tu = timeri->callback_data; 11301da177e4SLinus Torvalds snd_timer_tread_t r1; 11311da177e4SLinus Torvalds 11321da177e4SLinus Torvalds if (event >= SNDRV_TIMER_EVENT_START && event <= SNDRV_TIMER_EVENT_PAUSE) 11331da177e4SLinus Torvalds tu->tstamp = *tstamp; 11341da177e4SLinus Torvalds if ((tu->filter & (1 << event)) == 0 || !tu->tread) 11351da177e4SLinus Torvalds return; 11361da177e4SLinus Torvalds r1.event = event; 11371da177e4SLinus Torvalds r1.tstamp = *tstamp; 11381da177e4SLinus Torvalds r1.val = resolution; 11391da177e4SLinus Torvalds spin_lock(&tu->qlock); 11401da177e4SLinus Torvalds snd_timer_user_append_to_tqueue(tu, &r1); 11411da177e4SLinus Torvalds spin_unlock(&tu->qlock); 11421da177e4SLinus Torvalds kill_fasync(&tu->fasync, SIGIO, POLL_IN); 11431da177e4SLinus Torvalds wake_up(&tu->qchange_sleep); 11441da177e4SLinus Torvalds } 11451da177e4SLinus Torvalds 11461da177e4SLinus Torvalds static void snd_timer_user_tinterrupt(snd_timer_instance_t *timeri, 11471da177e4SLinus Torvalds unsigned long resolution, 11481da177e4SLinus Torvalds unsigned long ticks) 11491da177e4SLinus Torvalds { 11501da177e4SLinus Torvalds snd_timer_user_t *tu = timeri->callback_data; 11511da177e4SLinus Torvalds snd_timer_tread_t *r, r1; 11521da177e4SLinus Torvalds struct timespec tstamp; 11531da177e4SLinus Torvalds int prev, append = 0; 11541da177e4SLinus Torvalds 11551da177e4SLinus Torvalds snd_timestamp_zero(&tstamp); 11561da177e4SLinus Torvalds spin_lock(&tu->qlock); 11571da177e4SLinus Torvalds if ((tu->filter & ((1 << SNDRV_TIMER_EVENT_RESOLUTION)|(1 << SNDRV_TIMER_EVENT_TICK))) == 0) { 11581da177e4SLinus Torvalds spin_unlock(&tu->qlock); 11591da177e4SLinus Torvalds return; 11601da177e4SLinus Torvalds } 11611da177e4SLinus Torvalds if (tu->last_resolution != resolution || ticks > 0) 11621da177e4SLinus Torvalds snd_timestamp_now(&tstamp, 1); 11631da177e4SLinus Torvalds if ((tu->filter & (1 << SNDRV_TIMER_EVENT_RESOLUTION)) && tu->last_resolution != resolution) { 11641da177e4SLinus Torvalds r1.event = SNDRV_TIMER_EVENT_RESOLUTION; 11651da177e4SLinus Torvalds r1.tstamp = tstamp; 11661da177e4SLinus Torvalds r1.val = resolution; 11671da177e4SLinus Torvalds snd_timer_user_append_to_tqueue(tu, &r1); 11681da177e4SLinus Torvalds tu->last_resolution = resolution; 11691da177e4SLinus Torvalds append++; 11701da177e4SLinus Torvalds } 11711da177e4SLinus Torvalds if ((tu->filter & (1 << SNDRV_TIMER_EVENT_TICK)) == 0) 11721da177e4SLinus Torvalds goto __wake; 11731da177e4SLinus Torvalds if (ticks == 0) 11741da177e4SLinus Torvalds goto __wake; 11751da177e4SLinus Torvalds if (tu->qused > 0) { 11761da177e4SLinus Torvalds prev = tu->qtail == 0 ? tu->queue_size - 1 : tu->qtail - 1; 11771da177e4SLinus Torvalds r = &tu->tqueue[prev]; 11781da177e4SLinus Torvalds if (r->event == SNDRV_TIMER_EVENT_TICK) { 11791da177e4SLinus Torvalds r->tstamp = tstamp; 11801da177e4SLinus Torvalds r->val += ticks; 11811da177e4SLinus Torvalds append++; 11821da177e4SLinus Torvalds goto __wake; 11831da177e4SLinus Torvalds } 11841da177e4SLinus Torvalds } 11851da177e4SLinus Torvalds r1.event = SNDRV_TIMER_EVENT_TICK; 11861da177e4SLinus Torvalds r1.tstamp = tstamp; 11871da177e4SLinus Torvalds r1.val = ticks; 11881da177e4SLinus Torvalds snd_timer_user_append_to_tqueue(tu, &r1); 11891da177e4SLinus Torvalds append++; 11901da177e4SLinus Torvalds __wake: 11911da177e4SLinus Torvalds spin_unlock(&tu->qlock); 11921da177e4SLinus Torvalds if (append == 0) 11931da177e4SLinus Torvalds return; 11941da177e4SLinus Torvalds kill_fasync(&tu->fasync, SIGIO, POLL_IN); 11951da177e4SLinus Torvalds wake_up(&tu->qchange_sleep); 11961da177e4SLinus Torvalds } 11971da177e4SLinus Torvalds 11981da177e4SLinus Torvalds static int snd_timer_user_open(struct inode *inode, struct file *file) 11991da177e4SLinus Torvalds { 12001da177e4SLinus Torvalds snd_timer_user_t *tu; 12011da177e4SLinus Torvalds 12021da177e4SLinus Torvalds tu = kcalloc(1, sizeof(*tu), GFP_KERNEL); 12031da177e4SLinus Torvalds if (tu == NULL) 12041da177e4SLinus Torvalds return -ENOMEM; 12051da177e4SLinus Torvalds spin_lock_init(&tu->qlock); 12061da177e4SLinus Torvalds init_waitqueue_head(&tu->qchange_sleep); 1207c1935b4dSJaroslav Kysela init_MUTEX(&tu->tread_sem); 12081da177e4SLinus Torvalds tu->ticks = 1; 12091da177e4SLinus Torvalds tu->queue_size = 128; 12101da177e4SLinus Torvalds tu->queue = (snd_timer_read_t *)kmalloc(tu->queue_size * sizeof(snd_timer_read_t), GFP_KERNEL); 12111da177e4SLinus Torvalds if (tu->queue == NULL) { 12121da177e4SLinus Torvalds kfree(tu); 12131da177e4SLinus Torvalds return -ENOMEM; 12141da177e4SLinus Torvalds } 12151da177e4SLinus Torvalds file->private_data = tu; 12161da177e4SLinus Torvalds return 0; 12171da177e4SLinus Torvalds } 12181da177e4SLinus Torvalds 12191da177e4SLinus Torvalds static int snd_timer_user_release(struct inode *inode, struct file *file) 12201da177e4SLinus Torvalds { 12211da177e4SLinus Torvalds snd_timer_user_t *tu; 12221da177e4SLinus Torvalds 12231da177e4SLinus Torvalds if (file->private_data) { 12241da177e4SLinus Torvalds tu = file->private_data; 12251da177e4SLinus Torvalds file->private_data = NULL; 12261da177e4SLinus Torvalds fasync_helper(-1, file, 0, &tu->fasync); 12271da177e4SLinus Torvalds if (tu->timeri) 12281da177e4SLinus Torvalds snd_timer_close(tu->timeri); 12291da177e4SLinus Torvalds kfree(tu->queue); 12301da177e4SLinus Torvalds kfree(tu->tqueue); 12311da177e4SLinus Torvalds kfree(tu); 12321da177e4SLinus Torvalds } 12331da177e4SLinus Torvalds return 0; 12341da177e4SLinus Torvalds } 12351da177e4SLinus Torvalds 12361da177e4SLinus Torvalds static void snd_timer_user_zero_id(snd_timer_id_t *id) 12371da177e4SLinus Torvalds { 12381da177e4SLinus Torvalds id->dev_class = SNDRV_TIMER_CLASS_NONE; 12391da177e4SLinus Torvalds id->dev_sclass = SNDRV_TIMER_SCLASS_NONE; 12401da177e4SLinus Torvalds id->card = -1; 12411da177e4SLinus Torvalds id->device = -1; 12421da177e4SLinus Torvalds id->subdevice = -1; 12431da177e4SLinus Torvalds } 12441da177e4SLinus Torvalds 12451da177e4SLinus Torvalds static void snd_timer_user_copy_id(snd_timer_id_t *id, snd_timer_t *timer) 12461da177e4SLinus Torvalds { 12471da177e4SLinus Torvalds id->dev_class = timer->tmr_class; 12481da177e4SLinus Torvalds id->dev_sclass = SNDRV_TIMER_SCLASS_NONE; 12491da177e4SLinus Torvalds id->card = timer->card ? timer->card->number : -1; 12501da177e4SLinus Torvalds id->device = timer->tmr_device; 12511da177e4SLinus Torvalds id->subdevice = timer->tmr_subdevice; 12521da177e4SLinus Torvalds } 12531da177e4SLinus Torvalds 12541da177e4SLinus Torvalds static int snd_timer_user_next_device(snd_timer_id_t __user *_tid) 12551da177e4SLinus Torvalds { 12561da177e4SLinus Torvalds snd_timer_id_t id; 12571da177e4SLinus Torvalds snd_timer_t *timer; 12581da177e4SLinus Torvalds struct list_head *p; 12591da177e4SLinus Torvalds 12601da177e4SLinus Torvalds if (copy_from_user(&id, _tid, sizeof(id))) 12611da177e4SLinus Torvalds return -EFAULT; 12621da177e4SLinus Torvalds down(®ister_mutex); 12631da177e4SLinus Torvalds if (id.dev_class < 0) { /* first item */ 12641da177e4SLinus Torvalds if (list_empty(&snd_timer_list)) 12651da177e4SLinus Torvalds snd_timer_user_zero_id(&id); 12661da177e4SLinus Torvalds else { 12671da177e4SLinus Torvalds timer = (snd_timer_t *)list_entry(snd_timer_list.next, snd_timer_t, device_list); 12681da177e4SLinus Torvalds snd_timer_user_copy_id(&id, timer); 12691da177e4SLinus Torvalds } 12701da177e4SLinus Torvalds } else { 12711da177e4SLinus Torvalds switch (id.dev_class) { 12721da177e4SLinus Torvalds case SNDRV_TIMER_CLASS_GLOBAL: 12731da177e4SLinus Torvalds id.device = id.device < 0 ? 0 : id.device + 1; 12741da177e4SLinus Torvalds list_for_each(p, &snd_timer_list) { 12751da177e4SLinus Torvalds timer = (snd_timer_t *)list_entry(p, snd_timer_t, device_list); 12761da177e4SLinus Torvalds if (timer->tmr_class > SNDRV_TIMER_CLASS_GLOBAL) { 12771da177e4SLinus Torvalds snd_timer_user_copy_id(&id, timer); 12781da177e4SLinus Torvalds break; 12791da177e4SLinus Torvalds } 12801da177e4SLinus Torvalds if (timer->tmr_device >= id.device) { 12811da177e4SLinus Torvalds snd_timer_user_copy_id(&id, timer); 12821da177e4SLinus Torvalds break; 12831da177e4SLinus Torvalds } 12841da177e4SLinus Torvalds } 12851da177e4SLinus Torvalds if (p == &snd_timer_list) 12861da177e4SLinus Torvalds snd_timer_user_zero_id(&id); 12871da177e4SLinus Torvalds break; 12881da177e4SLinus Torvalds case SNDRV_TIMER_CLASS_CARD: 12891da177e4SLinus Torvalds case SNDRV_TIMER_CLASS_PCM: 12901da177e4SLinus Torvalds if (id.card < 0) { 12911da177e4SLinus Torvalds id.card = 0; 12921da177e4SLinus Torvalds } else { 12931da177e4SLinus Torvalds if (id.card < 0) { 12941da177e4SLinus Torvalds id.card = 0; 12951da177e4SLinus Torvalds } else { 12961da177e4SLinus Torvalds if (id.device < 0) { 12971da177e4SLinus Torvalds id.device = 0; 12981da177e4SLinus Torvalds } else { 12991da177e4SLinus Torvalds id.subdevice = id.subdevice < 0 ? 0 : id.subdevice + 1; 13001da177e4SLinus Torvalds } 13011da177e4SLinus Torvalds } 13021da177e4SLinus Torvalds } 13031da177e4SLinus Torvalds list_for_each(p, &snd_timer_list) { 13041da177e4SLinus Torvalds timer = (snd_timer_t *)list_entry(p, snd_timer_t, device_list); 13051da177e4SLinus Torvalds if (timer->tmr_class > id.dev_class) { 13061da177e4SLinus Torvalds snd_timer_user_copy_id(&id, timer); 13071da177e4SLinus Torvalds break; 13081da177e4SLinus Torvalds } 13091da177e4SLinus Torvalds if (timer->tmr_class < id.dev_class) 13101da177e4SLinus Torvalds continue; 13111da177e4SLinus Torvalds if (timer->card->number > id.card) { 13121da177e4SLinus Torvalds snd_timer_user_copy_id(&id, timer); 13131da177e4SLinus Torvalds break; 13141da177e4SLinus Torvalds } 13151da177e4SLinus Torvalds if (timer->card->number < id.card) 13161da177e4SLinus Torvalds continue; 13171da177e4SLinus Torvalds if (timer->tmr_device > id.device) { 13181da177e4SLinus Torvalds snd_timer_user_copy_id(&id, timer); 13191da177e4SLinus Torvalds break; 13201da177e4SLinus Torvalds } 13211da177e4SLinus Torvalds if (timer->tmr_device < id.device) 13221da177e4SLinus Torvalds continue; 13231da177e4SLinus Torvalds if (timer->tmr_subdevice > id.subdevice) { 13241da177e4SLinus Torvalds snd_timer_user_copy_id(&id, timer); 13251da177e4SLinus Torvalds break; 13261da177e4SLinus Torvalds } 13271da177e4SLinus Torvalds if (timer->tmr_subdevice < id.subdevice) 13281da177e4SLinus Torvalds continue; 13291da177e4SLinus Torvalds snd_timer_user_copy_id(&id, timer); 13301da177e4SLinus Torvalds break; 13311da177e4SLinus Torvalds } 13321da177e4SLinus Torvalds if (p == &snd_timer_list) 13331da177e4SLinus Torvalds snd_timer_user_zero_id(&id); 13341da177e4SLinus Torvalds break; 13351da177e4SLinus Torvalds default: 13361da177e4SLinus Torvalds snd_timer_user_zero_id(&id); 13371da177e4SLinus Torvalds } 13381da177e4SLinus Torvalds } 13391da177e4SLinus Torvalds up(®ister_mutex); 13401da177e4SLinus Torvalds if (copy_to_user(_tid, &id, sizeof(*_tid))) 13411da177e4SLinus Torvalds return -EFAULT; 13421da177e4SLinus Torvalds return 0; 13431da177e4SLinus Torvalds } 13441da177e4SLinus Torvalds 13451da177e4SLinus Torvalds static int snd_timer_user_ginfo(struct file *file, snd_timer_ginfo_t __user *_ginfo) 13461da177e4SLinus Torvalds { 13471da177e4SLinus Torvalds snd_timer_ginfo_t *ginfo; 13481da177e4SLinus Torvalds snd_timer_id_t tid; 13491da177e4SLinus Torvalds snd_timer_t *t; 13501da177e4SLinus Torvalds struct list_head *p; 13511da177e4SLinus Torvalds int err = 0; 13521da177e4SLinus Torvalds 13531da177e4SLinus Torvalds ginfo = kmalloc(sizeof(*ginfo), GFP_KERNEL); 13541da177e4SLinus Torvalds if (! ginfo) 13551da177e4SLinus Torvalds return -ENOMEM; 13561da177e4SLinus Torvalds if (copy_from_user(ginfo, _ginfo, sizeof(*ginfo))) { 13571da177e4SLinus Torvalds kfree(ginfo); 13581da177e4SLinus Torvalds return -EFAULT; 13591da177e4SLinus Torvalds } 13601da177e4SLinus Torvalds tid = ginfo->tid; 13611da177e4SLinus Torvalds memset(ginfo, 0, sizeof(*ginfo)); 13621da177e4SLinus Torvalds ginfo->tid = tid; 13631da177e4SLinus Torvalds down(®ister_mutex); 13641da177e4SLinus Torvalds t = snd_timer_find(&tid); 13651da177e4SLinus Torvalds if (t != NULL) { 13661da177e4SLinus Torvalds ginfo->card = t->card ? t->card->number : -1; 13671da177e4SLinus Torvalds if (t->hw.flags & SNDRV_TIMER_HW_SLAVE) 13681da177e4SLinus Torvalds ginfo->flags |= SNDRV_TIMER_FLG_SLAVE; 13691da177e4SLinus Torvalds strlcpy(ginfo->id, t->id, sizeof(ginfo->id)); 13701da177e4SLinus Torvalds strlcpy(ginfo->name, t->name, sizeof(ginfo->name)); 13711da177e4SLinus Torvalds ginfo->resolution = t->hw.resolution; 13721da177e4SLinus Torvalds if (t->hw.resolution_min > 0) { 13731da177e4SLinus Torvalds ginfo->resolution_min = t->hw.resolution_min; 13741da177e4SLinus Torvalds ginfo->resolution_max = t->hw.resolution_max; 13751da177e4SLinus Torvalds } 13761da177e4SLinus Torvalds list_for_each(p, &t->open_list_head) { 13771da177e4SLinus Torvalds ginfo->clients++; 13781da177e4SLinus Torvalds } 13791da177e4SLinus Torvalds } else { 13801da177e4SLinus Torvalds err = -ENODEV; 13811da177e4SLinus Torvalds } 13821da177e4SLinus Torvalds up(®ister_mutex); 13831da177e4SLinus Torvalds if (err >= 0 && copy_to_user(_ginfo, ginfo, sizeof(*ginfo))) 13841da177e4SLinus Torvalds err = -EFAULT; 13851da177e4SLinus Torvalds kfree(ginfo); 13861da177e4SLinus Torvalds return err; 13871da177e4SLinus Torvalds } 13881da177e4SLinus Torvalds 13891da177e4SLinus Torvalds static int snd_timer_user_gparams(struct file *file, snd_timer_gparams_t __user *_gparams) 13901da177e4SLinus Torvalds { 13911da177e4SLinus Torvalds snd_timer_gparams_t gparams; 13921da177e4SLinus Torvalds snd_timer_t *t; 13931da177e4SLinus Torvalds int err; 13941da177e4SLinus Torvalds 13951da177e4SLinus Torvalds if (copy_from_user(&gparams, _gparams, sizeof(gparams))) 13961da177e4SLinus Torvalds return -EFAULT; 13971da177e4SLinus Torvalds down(®ister_mutex); 13981da177e4SLinus Torvalds t = snd_timer_find(&gparams.tid); 13991da177e4SLinus Torvalds if (t != NULL) { 14001da177e4SLinus Torvalds if (list_empty(&t->open_list_head)) { 14011da177e4SLinus Torvalds if (t->hw.set_period) 14021da177e4SLinus Torvalds err = t->hw.set_period(t, gparams.period_num, gparams.period_den); 14031da177e4SLinus Torvalds else 14041da177e4SLinus Torvalds err = -ENOSYS; 14051da177e4SLinus Torvalds } else { 14061da177e4SLinus Torvalds err = -EBUSY; 14071da177e4SLinus Torvalds } 14081da177e4SLinus Torvalds } else { 14091da177e4SLinus Torvalds err = -ENODEV; 14101da177e4SLinus Torvalds } 14111da177e4SLinus Torvalds up(®ister_mutex); 14121da177e4SLinus Torvalds return err; 14131da177e4SLinus Torvalds } 14141da177e4SLinus Torvalds 14151da177e4SLinus Torvalds static int snd_timer_user_gstatus(struct file *file, snd_timer_gstatus_t __user *_gstatus) 14161da177e4SLinus Torvalds { 14171da177e4SLinus Torvalds snd_timer_gstatus_t gstatus; 14181da177e4SLinus Torvalds snd_timer_id_t tid; 14191da177e4SLinus Torvalds snd_timer_t *t; 14201da177e4SLinus Torvalds int err = 0; 14211da177e4SLinus Torvalds 14221da177e4SLinus Torvalds if (copy_from_user(&gstatus, _gstatus, sizeof(gstatus))) 14231da177e4SLinus Torvalds return -EFAULT; 14241da177e4SLinus Torvalds tid = gstatus.tid; 14251da177e4SLinus Torvalds memset(&gstatus, 0, sizeof(gstatus)); 14261da177e4SLinus Torvalds gstatus.tid = tid; 14271da177e4SLinus Torvalds down(®ister_mutex); 14281da177e4SLinus Torvalds t = snd_timer_find(&tid); 14291da177e4SLinus Torvalds if (t != NULL) { 14301da177e4SLinus Torvalds if (t->hw.c_resolution) 14311da177e4SLinus Torvalds gstatus.resolution = t->hw.c_resolution(t); 14321da177e4SLinus Torvalds else 14331da177e4SLinus Torvalds gstatus.resolution = t->hw.resolution; 14341da177e4SLinus Torvalds if (t->hw.precise_resolution) { 14351da177e4SLinus Torvalds t->hw.precise_resolution(t, &gstatus.resolution_num, &gstatus.resolution_den); 14361da177e4SLinus Torvalds } else { 14371da177e4SLinus Torvalds gstatus.resolution_num = gstatus.resolution; 14381da177e4SLinus Torvalds gstatus.resolution_den = 1000000000uL; 14391da177e4SLinus Torvalds } 14401da177e4SLinus Torvalds } else { 14411da177e4SLinus Torvalds err = -ENODEV; 14421da177e4SLinus Torvalds } 14431da177e4SLinus Torvalds up(®ister_mutex); 14441da177e4SLinus Torvalds if (err >= 0 && copy_to_user(_gstatus, &gstatus, sizeof(gstatus))) 14451da177e4SLinus Torvalds err = -EFAULT; 14461da177e4SLinus Torvalds return err; 14471da177e4SLinus Torvalds } 14481da177e4SLinus Torvalds 14491da177e4SLinus Torvalds static int snd_timer_user_tselect(struct file *file, snd_timer_select_t __user *_tselect) 14501da177e4SLinus Torvalds { 14511da177e4SLinus Torvalds snd_timer_user_t *tu; 14521da177e4SLinus Torvalds snd_timer_select_t tselect; 14531da177e4SLinus Torvalds char str[32]; 1454c1935b4dSJaroslav Kysela int err = 0; 14551da177e4SLinus Torvalds 14561da177e4SLinus Torvalds tu = file->private_data; 1457c1935b4dSJaroslav Kysela down(&tu->tread_sem); 1458c1935b4dSJaroslav Kysela if (tu->timeri) { 14591da177e4SLinus Torvalds snd_timer_close(tu->timeri); 1460c1935b4dSJaroslav Kysela tu->timeri = NULL; 1461c1935b4dSJaroslav Kysela } 1462c1935b4dSJaroslav Kysela if (copy_from_user(&tselect, _tselect, sizeof(tselect))) { 1463c1935b4dSJaroslav Kysela err = -EFAULT; 1464c1935b4dSJaroslav Kysela goto __err; 1465c1935b4dSJaroslav Kysela } 14661da177e4SLinus Torvalds sprintf(str, "application %i", current->pid); 14671da177e4SLinus Torvalds if (tselect.id.dev_class != SNDRV_TIMER_CLASS_SLAVE) 14681da177e4SLinus Torvalds tselect.id.dev_sclass = SNDRV_TIMER_SCLASS_APPLICATION; 14691da177e4SLinus Torvalds if ((err = snd_timer_open(&tu->timeri, str, &tselect.id, current->pid)) < 0) 1470c1935b4dSJaroslav Kysela goto __err; 14711da177e4SLinus Torvalds 14721da177e4SLinus Torvalds if (tu->queue) { 14731da177e4SLinus Torvalds kfree(tu->queue); 14741da177e4SLinus Torvalds tu->queue = NULL; 14751da177e4SLinus Torvalds } 14761da177e4SLinus Torvalds if (tu->tqueue) { 14771da177e4SLinus Torvalds kfree(tu->tqueue); 14781da177e4SLinus Torvalds tu->tqueue = NULL; 14791da177e4SLinus Torvalds } 14801da177e4SLinus Torvalds if (tu->tread) { 14811da177e4SLinus Torvalds tu->tqueue = (snd_timer_tread_t *)kmalloc(tu->queue_size * sizeof(snd_timer_tread_t), GFP_KERNEL); 1482c1935b4dSJaroslav Kysela if (tu->tqueue == NULL) 1483c1935b4dSJaroslav Kysela err = -ENOMEM; 14841da177e4SLinus Torvalds } else { 14851da177e4SLinus Torvalds tu->queue = (snd_timer_read_t *)kmalloc(tu->queue_size * sizeof(snd_timer_read_t), GFP_KERNEL); 1486c1935b4dSJaroslav Kysela if (tu->queue == NULL) 1487c1935b4dSJaroslav Kysela err = -ENOMEM; 14881da177e4SLinus Torvalds } 14891da177e4SLinus Torvalds 1490c1935b4dSJaroslav Kysela if (err < 0) { 1491c1935b4dSJaroslav Kysela snd_timer_close(tu->timeri); 1492c1935b4dSJaroslav Kysela tu->timeri = NULL; 1493c1935b4dSJaroslav Kysela } else { 14941da177e4SLinus Torvalds tu->timeri->flags |= SNDRV_TIMER_IFLG_FAST; 14951da177e4SLinus Torvalds tu->timeri->callback = tu->tread ? snd_timer_user_tinterrupt : snd_timer_user_interrupt; 14961da177e4SLinus Torvalds tu->timeri->ccallback = snd_timer_user_ccallback; 14971da177e4SLinus Torvalds tu->timeri->callback_data = (void *)tu; 1498c1935b4dSJaroslav Kysela } 1499c1935b4dSJaroslav Kysela 1500c1935b4dSJaroslav Kysela __err: 1501c1935b4dSJaroslav Kysela up(&tu->tread_sem); 1502c1935b4dSJaroslav Kysela return err; 15031da177e4SLinus Torvalds } 15041da177e4SLinus Torvalds 15051da177e4SLinus Torvalds static int snd_timer_user_info(struct file *file, snd_timer_info_t __user *_info) 15061da177e4SLinus Torvalds { 15071da177e4SLinus Torvalds snd_timer_user_t *tu; 15081da177e4SLinus Torvalds snd_timer_info_t *info; 15091da177e4SLinus Torvalds snd_timer_t *t; 15101da177e4SLinus Torvalds int err = 0; 15111da177e4SLinus Torvalds 15121da177e4SLinus Torvalds tu = file->private_data; 15131da177e4SLinus Torvalds snd_assert(tu->timeri != NULL, return -ENXIO); 15141da177e4SLinus Torvalds t = tu->timeri->timer; 15151da177e4SLinus Torvalds snd_assert(t != NULL, return -ENXIO); 15161da177e4SLinus Torvalds 15171da177e4SLinus Torvalds info = kcalloc(1, sizeof(*info), GFP_KERNEL); 15181da177e4SLinus Torvalds if (! info) 15191da177e4SLinus Torvalds return -ENOMEM; 15201da177e4SLinus Torvalds info->card = t->card ? t->card->number : -1; 15211da177e4SLinus Torvalds if (t->hw.flags & SNDRV_TIMER_HW_SLAVE) 15221da177e4SLinus Torvalds info->flags |= SNDRV_TIMER_FLG_SLAVE; 15231da177e4SLinus Torvalds strlcpy(info->id, t->id, sizeof(info->id)); 15241da177e4SLinus Torvalds strlcpy(info->name, t->name, sizeof(info->name)); 15251da177e4SLinus Torvalds info->resolution = t->hw.resolution; 15261da177e4SLinus Torvalds if (copy_to_user(_info, info, sizeof(*_info))) 15271da177e4SLinus Torvalds err = -EFAULT; 15281da177e4SLinus Torvalds kfree(info); 15291da177e4SLinus Torvalds return err; 15301da177e4SLinus Torvalds } 15311da177e4SLinus Torvalds 15321da177e4SLinus Torvalds static int snd_timer_user_params(struct file *file, snd_timer_params_t __user *_params) 15331da177e4SLinus Torvalds { 15341da177e4SLinus Torvalds snd_timer_user_t *tu; 15351da177e4SLinus Torvalds snd_timer_params_t params; 15361da177e4SLinus Torvalds snd_timer_t *t; 15371da177e4SLinus Torvalds snd_timer_read_t *tr; 15381da177e4SLinus Torvalds snd_timer_tread_t *ttr; 15391da177e4SLinus Torvalds int err; 15401da177e4SLinus Torvalds 15411da177e4SLinus Torvalds tu = file->private_data; 15421da177e4SLinus Torvalds snd_assert(tu->timeri != NULL, return -ENXIO); 15431da177e4SLinus Torvalds t = tu->timeri->timer; 15441da177e4SLinus Torvalds snd_assert(t != NULL, return -ENXIO); 15451da177e4SLinus Torvalds if (copy_from_user(¶ms, _params, sizeof(params))) 15461da177e4SLinus Torvalds return -EFAULT; 15471da177e4SLinus Torvalds if (!(t->hw.flags & SNDRV_TIMER_HW_SLAVE) && params.ticks < 1) { 15481da177e4SLinus Torvalds err = -EINVAL; 15491da177e4SLinus Torvalds goto _end; 15501da177e4SLinus Torvalds } 15511da177e4SLinus Torvalds if (params.queue_size > 0 && (params.queue_size < 32 || params.queue_size > 1024)) { 15521da177e4SLinus Torvalds err = -EINVAL; 15531da177e4SLinus Torvalds goto _end; 15541da177e4SLinus Torvalds } 15551da177e4SLinus Torvalds if (params.filter & ~((1<<SNDRV_TIMER_EVENT_RESOLUTION)| 15561da177e4SLinus Torvalds (1<<SNDRV_TIMER_EVENT_TICK)| 15571da177e4SLinus Torvalds (1<<SNDRV_TIMER_EVENT_START)| 15581da177e4SLinus Torvalds (1<<SNDRV_TIMER_EVENT_STOP)| 15591da177e4SLinus Torvalds (1<<SNDRV_TIMER_EVENT_CONTINUE)| 15601da177e4SLinus Torvalds (1<<SNDRV_TIMER_EVENT_PAUSE)| 15611da177e4SLinus Torvalds (1<<SNDRV_TIMER_EVENT_MSTART)| 15621da177e4SLinus Torvalds (1<<SNDRV_TIMER_EVENT_MSTOP)| 15631da177e4SLinus Torvalds (1<<SNDRV_TIMER_EVENT_MCONTINUE)| 15641da177e4SLinus Torvalds (1<<SNDRV_TIMER_EVENT_MPAUSE))) { 15651da177e4SLinus Torvalds err = -EINVAL; 15661da177e4SLinus Torvalds goto _end; 15671da177e4SLinus Torvalds } 15681da177e4SLinus Torvalds snd_timer_stop(tu->timeri); 15691da177e4SLinus Torvalds spin_lock_irq(&t->lock); 15701da177e4SLinus Torvalds tu->timeri->flags &= ~(SNDRV_TIMER_IFLG_AUTO| 15711da177e4SLinus Torvalds SNDRV_TIMER_IFLG_EXCLUSIVE| 15721da177e4SLinus Torvalds SNDRV_TIMER_IFLG_EARLY_EVENT); 15731da177e4SLinus Torvalds if (params.flags & SNDRV_TIMER_PSFLG_AUTO) 15741da177e4SLinus Torvalds tu->timeri->flags |= SNDRV_TIMER_IFLG_AUTO; 15751da177e4SLinus Torvalds if (params.flags & SNDRV_TIMER_PSFLG_EXCLUSIVE) 15761da177e4SLinus Torvalds tu->timeri->flags |= SNDRV_TIMER_IFLG_EXCLUSIVE; 15771da177e4SLinus Torvalds if (params.flags & SNDRV_TIMER_PSFLG_EARLY_EVENT) 15781da177e4SLinus Torvalds tu->timeri->flags |= SNDRV_TIMER_IFLG_EARLY_EVENT; 15791da177e4SLinus Torvalds spin_unlock_irq(&t->lock); 15801da177e4SLinus Torvalds if (params.queue_size > 0 && (unsigned int)tu->queue_size != params.queue_size) { 15811da177e4SLinus Torvalds if (tu->tread) { 15821da177e4SLinus Torvalds ttr = (snd_timer_tread_t *)kmalloc(params.queue_size * sizeof(snd_timer_tread_t), GFP_KERNEL); 15831da177e4SLinus Torvalds if (ttr) { 15841da177e4SLinus Torvalds kfree(tu->tqueue); 15851da177e4SLinus Torvalds tu->queue_size = params.queue_size; 15861da177e4SLinus Torvalds tu->tqueue = ttr; 15871da177e4SLinus Torvalds } 15881da177e4SLinus Torvalds } else { 15891da177e4SLinus Torvalds tr = (snd_timer_read_t *)kmalloc(params.queue_size * sizeof(snd_timer_read_t), GFP_KERNEL); 15901da177e4SLinus Torvalds if (tr) { 15911da177e4SLinus Torvalds kfree(tu->queue); 15921da177e4SLinus Torvalds tu->queue_size = params.queue_size; 15931da177e4SLinus Torvalds tu->queue = tr; 15941da177e4SLinus Torvalds } 15951da177e4SLinus Torvalds } 15961da177e4SLinus Torvalds } 15971da177e4SLinus Torvalds tu->qhead = tu->qtail = tu->qused = 0; 15981da177e4SLinus Torvalds if (tu->timeri->flags & SNDRV_TIMER_IFLG_EARLY_EVENT) { 15991da177e4SLinus Torvalds if (tu->tread) { 16001da177e4SLinus Torvalds snd_timer_tread_t tread; 16011da177e4SLinus Torvalds tread.event = SNDRV_TIMER_EVENT_EARLY; 16021da177e4SLinus Torvalds tread.tstamp.tv_sec = 0; 16031da177e4SLinus Torvalds tread.tstamp.tv_nsec = 0; 16041da177e4SLinus Torvalds tread.val = 0; 16051da177e4SLinus Torvalds snd_timer_user_append_to_tqueue(tu, &tread); 16061da177e4SLinus Torvalds } else { 16071da177e4SLinus Torvalds snd_timer_read_t *r = &tu->queue[0]; 16081da177e4SLinus Torvalds r->resolution = 0; 16091da177e4SLinus Torvalds r->ticks = 0; 16101da177e4SLinus Torvalds tu->qused++; 16111da177e4SLinus Torvalds tu->qtail++; 16121da177e4SLinus Torvalds } 16131da177e4SLinus Torvalds 16141da177e4SLinus Torvalds } 16151da177e4SLinus Torvalds tu->filter = params.filter; 16161da177e4SLinus Torvalds tu->ticks = params.ticks; 16171da177e4SLinus Torvalds err = 0; 16181da177e4SLinus Torvalds _end: 16191da177e4SLinus Torvalds if (copy_to_user(_params, ¶ms, sizeof(params))) 16201da177e4SLinus Torvalds return -EFAULT; 16211da177e4SLinus Torvalds return err; 16221da177e4SLinus Torvalds } 16231da177e4SLinus Torvalds 16241da177e4SLinus Torvalds static int snd_timer_user_status(struct file *file, snd_timer_status_t __user *_status) 16251da177e4SLinus Torvalds { 16261da177e4SLinus Torvalds snd_timer_user_t *tu; 16271da177e4SLinus Torvalds snd_timer_status_t status; 16281da177e4SLinus Torvalds 16291da177e4SLinus Torvalds tu = file->private_data; 16301da177e4SLinus Torvalds snd_assert(tu->timeri != NULL, return -ENXIO); 16311da177e4SLinus Torvalds memset(&status, 0, sizeof(status)); 16321da177e4SLinus Torvalds status.tstamp = tu->tstamp; 16331da177e4SLinus Torvalds status.resolution = snd_timer_resolution(tu->timeri); 16341da177e4SLinus Torvalds status.lost = tu->timeri->lost; 16351da177e4SLinus Torvalds status.overrun = tu->overrun; 16361da177e4SLinus Torvalds spin_lock_irq(&tu->qlock); 16371da177e4SLinus Torvalds status.queue = tu->qused; 16381da177e4SLinus Torvalds spin_unlock_irq(&tu->qlock); 16391da177e4SLinus Torvalds if (copy_to_user(_status, &status, sizeof(status))) 16401da177e4SLinus Torvalds return -EFAULT; 16411da177e4SLinus Torvalds return 0; 16421da177e4SLinus Torvalds } 16431da177e4SLinus Torvalds 16441da177e4SLinus Torvalds static int snd_timer_user_start(struct file *file) 16451da177e4SLinus Torvalds { 16461da177e4SLinus Torvalds int err; 16471da177e4SLinus Torvalds snd_timer_user_t *tu; 16481da177e4SLinus Torvalds 16491da177e4SLinus Torvalds tu = file->private_data; 16501da177e4SLinus Torvalds snd_assert(tu->timeri != NULL, return -ENXIO); 16511da177e4SLinus Torvalds snd_timer_stop(tu->timeri); 16521da177e4SLinus Torvalds tu->timeri->lost = 0; 16531da177e4SLinus Torvalds tu->last_resolution = 0; 16541da177e4SLinus Torvalds return (err = snd_timer_start(tu->timeri, tu->ticks)) < 0 ? err : 0; 16551da177e4SLinus Torvalds } 16561da177e4SLinus Torvalds 16571da177e4SLinus Torvalds static int snd_timer_user_stop(struct file *file) 16581da177e4SLinus Torvalds { 16591da177e4SLinus Torvalds int err; 16601da177e4SLinus Torvalds snd_timer_user_t *tu; 16611da177e4SLinus Torvalds 16621da177e4SLinus Torvalds tu = file->private_data; 16631da177e4SLinus Torvalds snd_assert(tu->timeri != NULL, return -ENXIO); 16641da177e4SLinus Torvalds return (err = snd_timer_stop(tu->timeri)) < 0 ? err : 0; 16651da177e4SLinus Torvalds } 16661da177e4SLinus Torvalds 16671da177e4SLinus Torvalds static int snd_timer_user_continue(struct file *file) 16681da177e4SLinus Torvalds { 16691da177e4SLinus Torvalds int err; 16701da177e4SLinus Torvalds snd_timer_user_t *tu; 16711da177e4SLinus Torvalds 16721da177e4SLinus Torvalds tu = file->private_data; 16731da177e4SLinus Torvalds snd_assert(tu->timeri != NULL, return -ENXIO); 16741da177e4SLinus Torvalds tu->timeri->lost = 0; 16751da177e4SLinus Torvalds return (err = snd_timer_continue(tu->timeri)) < 0 ? err : 0; 16761da177e4SLinus Torvalds } 16771da177e4SLinus Torvalds 167815790a6bSTakashi Iwai static int snd_timer_user_pause(struct file *file) 167915790a6bSTakashi Iwai { 168015790a6bSTakashi Iwai int err; 168115790a6bSTakashi Iwai snd_timer_user_t *tu; 168215790a6bSTakashi Iwai 168315790a6bSTakashi Iwai tu = file->private_data; 168415790a6bSTakashi Iwai snd_assert(tu->timeri != NULL, return -ENXIO); 1685d138b445SJaroslav Kysela return (err = snd_timer_pause(tu->timeri)) < 0 ? err : 0; 168615790a6bSTakashi Iwai } 168715790a6bSTakashi Iwai 16888c50b37cSTakashi Iwai enum { 16898c50b37cSTakashi Iwai SNDRV_TIMER_IOCTL_START_OLD = _IO('T', 0x20), 16908c50b37cSTakashi Iwai SNDRV_TIMER_IOCTL_STOP_OLD = _IO('T', 0x21), 16918c50b37cSTakashi Iwai SNDRV_TIMER_IOCTL_CONTINUE_OLD = _IO('T', 0x22), 16928c50b37cSTakashi Iwai SNDRV_TIMER_IOCTL_PAUSE_OLD = _IO('T', 0x23), 16938c50b37cSTakashi Iwai }; 16948c50b37cSTakashi Iwai 16951da177e4SLinus Torvalds static long snd_timer_user_ioctl(struct file *file, unsigned int cmd, unsigned long arg) 16961da177e4SLinus Torvalds { 16971da177e4SLinus Torvalds snd_timer_user_t *tu; 16981da177e4SLinus Torvalds void __user *argp = (void __user *)arg; 16991da177e4SLinus Torvalds int __user *p = argp; 17001da177e4SLinus Torvalds 17011da177e4SLinus Torvalds tu = file->private_data; 17021da177e4SLinus Torvalds switch (cmd) { 17031da177e4SLinus Torvalds case SNDRV_TIMER_IOCTL_PVERSION: 17041da177e4SLinus Torvalds return put_user(SNDRV_TIMER_VERSION, p) ? -EFAULT : 0; 17051da177e4SLinus Torvalds case SNDRV_TIMER_IOCTL_NEXT_DEVICE: 17061da177e4SLinus Torvalds return snd_timer_user_next_device(argp); 17071da177e4SLinus Torvalds case SNDRV_TIMER_IOCTL_TREAD: 17081da177e4SLinus Torvalds { 17091da177e4SLinus Torvalds int xarg; 17101da177e4SLinus Torvalds 1711c1935b4dSJaroslav Kysela down(&tu->tread_sem); 1712c1935b4dSJaroslav Kysela if (tu->timeri) { /* too late */ 1713c1935b4dSJaroslav Kysela up(&tu->tread_sem); 17141da177e4SLinus Torvalds return -EBUSY; 1715c1935b4dSJaroslav Kysela } 1716c1935b4dSJaroslav Kysela if (get_user(xarg, p)) { 1717c1935b4dSJaroslav Kysela up(&tu->tread_sem); 17181da177e4SLinus Torvalds return -EFAULT; 1719c1935b4dSJaroslav Kysela } 17201da177e4SLinus Torvalds tu->tread = xarg ? 1 : 0; 1721c1935b4dSJaroslav Kysela up(&tu->tread_sem); 17221da177e4SLinus Torvalds return 0; 17231da177e4SLinus Torvalds } 17241da177e4SLinus Torvalds case SNDRV_TIMER_IOCTL_GINFO: 17251da177e4SLinus Torvalds return snd_timer_user_ginfo(file, argp); 17261da177e4SLinus Torvalds case SNDRV_TIMER_IOCTL_GPARAMS: 17271da177e4SLinus Torvalds return snd_timer_user_gparams(file, argp); 17281da177e4SLinus Torvalds case SNDRV_TIMER_IOCTL_GSTATUS: 17291da177e4SLinus Torvalds return snd_timer_user_gstatus(file, argp); 17301da177e4SLinus Torvalds case SNDRV_TIMER_IOCTL_SELECT: 17311da177e4SLinus Torvalds return snd_timer_user_tselect(file, argp); 17321da177e4SLinus Torvalds case SNDRV_TIMER_IOCTL_INFO: 17331da177e4SLinus Torvalds return snd_timer_user_info(file, argp); 17341da177e4SLinus Torvalds case SNDRV_TIMER_IOCTL_PARAMS: 17351da177e4SLinus Torvalds return snd_timer_user_params(file, argp); 17361da177e4SLinus Torvalds case SNDRV_TIMER_IOCTL_STATUS: 17371da177e4SLinus Torvalds return snd_timer_user_status(file, argp); 17381da177e4SLinus Torvalds case SNDRV_TIMER_IOCTL_START: 17398c50b37cSTakashi Iwai case SNDRV_TIMER_IOCTL_START_OLD: 17401da177e4SLinus Torvalds return snd_timer_user_start(file); 17411da177e4SLinus Torvalds case SNDRV_TIMER_IOCTL_STOP: 17428c50b37cSTakashi Iwai case SNDRV_TIMER_IOCTL_STOP_OLD: 17431da177e4SLinus Torvalds return snd_timer_user_stop(file); 17441da177e4SLinus Torvalds case SNDRV_TIMER_IOCTL_CONTINUE: 17458c50b37cSTakashi Iwai case SNDRV_TIMER_IOCTL_CONTINUE_OLD: 17461da177e4SLinus Torvalds return snd_timer_user_continue(file); 174715790a6bSTakashi Iwai case SNDRV_TIMER_IOCTL_PAUSE: 17488c50b37cSTakashi Iwai case SNDRV_TIMER_IOCTL_PAUSE_OLD: 174915790a6bSTakashi Iwai return snd_timer_user_pause(file); 17501da177e4SLinus Torvalds } 17511da177e4SLinus Torvalds return -ENOTTY; 17521da177e4SLinus Torvalds } 17531da177e4SLinus Torvalds 17541da177e4SLinus Torvalds static int snd_timer_user_fasync(int fd, struct file * file, int on) 17551da177e4SLinus Torvalds { 17561da177e4SLinus Torvalds snd_timer_user_t *tu; 17571da177e4SLinus Torvalds int err; 17581da177e4SLinus Torvalds 17591da177e4SLinus Torvalds tu = file->private_data; 17601da177e4SLinus Torvalds err = fasync_helper(fd, file, on, &tu->fasync); 17611da177e4SLinus Torvalds if (err < 0) 17621da177e4SLinus Torvalds return err; 17631da177e4SLinus Torvalds return 0; 17641da177e4SLinus Torvalds } 17651da177e4SLinus Torvalds 17661da177e4SLinus Torvalds static ssize_t snd_timer_user_read(struct file *file, char __user *buffer, size_t count, loff_t *offset) 17671da177e4SLinus Torvalds { 17681da177e4SLinus Torvalds snd_timer_user_t *tu; 17691da177e4SLinus Torvalds long result = 0, unit; 17701da177e4SLinus Torvalds int err = 0; 17711da177e4SLinus Torvalds 17721da177e4SLinus Torvalds tu = file->private_data; 17731da177e4SLinus Torvalds unit = tu->tread ? sizeof(snd_timer_tread_t) : sizeof(snd_timer_read_t); 17741da177e4SLinus Torvalds spin_lock_irq(&tu->qlock); 17751da177e4SLinus Torvalds while ((long)count - result >= unit) { 17761da177e4SLinus Torvalds while (!tu->qused) { 17771da177e4SLinus Torvalds wait_queue_t wait; 17781da177e4SLinus Torvalds 17791da177e4SLinus Torvalds if ((file->f_flags & O_NONBLOCK) != 0 || result > 0) { 17801da177e4SLinus Torvalds err = -EAGAIN; 17811da177e4SLinus Torvalds break; 17821da177e4SLinus Torvalds } 17831da177e4SLinus Torvalds 17841da177e4SLinus Torvalds set_current_state(TASK_INTERRUPTIBLE); 17851da177e4SLinus Torvalds init_waitqueue_entry(&wait, current); 17861da177e4SLinus Torvalds add_wait_queue(&tu->qchange_sleep, &wait); 17871da177e4SLinus Torvalds 17881da177e4SLinus Torvalds spin_unlock_irq(&tu->qlock); 17891da177e4SLinus Torvalds schedule(); 17901da177e4SLinus Torvalds spin_lock_irq(&tu->qlock); 17911da177e4SLinus Torvalds 17921da177e4SLinus Torvalds remove_wait_queue(&tu->qchange_sleep, &wait); 17931da177e4SLinus Torvalds 17941da177e4SLinus Torvalds if (signal_pending(current)) { 17951da177e4SLinus Torvalds err = -ERESTARTSYS; 17961da177e4SLinus Torvalds break; 17971da177e4SLinus Torvalds } 17981da177e4SLinus Torvalds } 17991da177e4SLinus Torvalds 18001da177e4SLinus Torvalds spin_unlock_irq(&tu->qlock); 18011da177e4SLinus Torvalds if (err < 0) 18021da177e4SLinus Torvalds goto _error; 18031da177e4SLinus Torvalds 18041da177e4SLinus Torvalds if (tu->tread) { 18051da177e4SLinus Torvalds if (copy_to_user(buffer, &tu->tqueue[tu->qhead++], sizeof(snd_timer_tread_t))) { 18061da177e4SLinus Torvalds err = -EFAULT; 18071da177e4SLinus Torvalds goto _error; 18081da177e4SLinus Torvalds } 18091da177e4SLinus Torvalds } else { 18101da177e4SLinus Torvalds if (copy_to_user(buffer, &tu->queue[tu->qhead++], sizeof(snd_timer_read_t))) { 18111da177e4SLinus Torvalds err = -EFAULT; 18121da177e4SLinus Torvalds goto _error; 18131da177e4SLinus Torvalds } 18141da177e4SLinus Torvalds } 18151da177e4SLinus Torvalds 18161da177e4SLinus Torvalds tu->qhead %= tu->queue_size; 18171da177e4SLinus Torvalds 18181da177e4SLinus Torvalds result += unit; 18191da177e4SLinus Torvalds buffer += unit; 18201da177e4SLinus Torvalds 18211da177e4SLinus Torvalds spin_lock_irq(&tu->qlock); 18221da177e4SLinus Torvalds tu->qused--; 18231da177e4SLinus Torvalds } 18241da177e4SLinus Torvalds spin_unlock_irq(&tu->qlock); 18251da177e4SLinus Torvalds _error: 18261da177e4SLinus Torvalds return result > 0 ? result : err; 18271da177e4SLinus Torvalds } 18281da177e4SLinus Torvalds 18291da177e4SLinus Torvalds static unsigned int snd_timer_user_poll(struct file *file, poll_table * wait) 18301da177e4SLinus Torvalds { 18311da177e4SLinus Torvalds unsigned int mask; 18321da177e4SLinus Torvalds snd_timer_user_t *tu; 18331da177e4SLinus Torvalds 18341da177e4SLinus Torvalds tu = file->private_data; 18351da177e4SLinus Torvalds 18361da177e4SLinus Torvalds poll_wait(file, &tu->qchange_sleep, wait); 18371da177e4SLinus Torvalds 18381da177e4SLinus Torvalds mask = 0; 18391da177e4SLinus Torvalds if (tu->qused) 18401da177e4SLinus Torvalds mask |= POLLIN | POLLRDNORM; 18411da177e4SLinus Torvalds 18421da177e4SLinus Torvalds return mask; 18431da177e4SLinus Torvalds } 18441da177e4SLinus Torvalds 18451da177e4SLinus Torvalds #ifdef CONFIG_COMPAT 18461da177e4SLinus Torvalds #include "timer_compat.c" 18471da177e4SLinus Torvalds #else 18481da177e4SLinus Torvalds #define snd_timer_user_ioctl_compat NULL 18491da177e4SLinus Torvalds #endif 18501da177e4SLinus Torvalds 18511da177e4SLinus Torvalds static struct file_operations snd_timer_f_ops = 18521da177e4SLinus Torvalds { 18531da177e4SLinus Torvalds .owner = THIS_MODULE, 18541da177e4SLinus Torvalds .read = snd_timer_user_read, 18551da177e4SLinus Torvalds .open = snd_timer_user_open, 18561da177e4SLinus Torvalds .release = snd_timer_user_release, 18571da177e4SLinus Torvalds .poll = snd_timer_user_poll, 18581da177e4SLinus Torvalds .unlocked_ioctl = snd_timer_user_ioctl, 18591da177e4SLinus Torvalds .compat_ioctl = snd_timer_user_ioctl_compat, 18601da177e4SLinus Torvalds .fasync = snd_timer_user_fasync, 18611da177e4SLinus Torvalds }; 18621da177e4SLinus Torvalds 18631da177e4SLinus Torvalds static snd_minor_t snd_timer_reg = 18641da177e4SLinus Torvalds { 18651da177e4SLinus Torvalds .comment = "timer", 18661da177e4SLinus Torvalds .f_ops = &snd_timer_f_ops, 18671da177e4SLinus Torvalds }; 18681da177e4SLinus Torvalds 18691da177e4SLinus Torvalds /* 18701da177e4SLinus Torvalds * ENTRY functions 18711da177e4SLinus Torvalds */ 18721da177e4SLinus Torvalds 18731da177e4SLinus Torvalds static snd_info_entry_t *snd_timer_proc_entry = NULL; 18741da177e4SLinus Torvalds 18751da177e4SLinus Torvalds static int __init alsa_timer_init(void) 18761da177e4SLinus Torvalds { 18771da177e4SLinus Torvalds int err; 18781da177e4SLinus Torvalds snd_info_entry_t *entry; 18791da177e4SLinus Torvalds 18801da177e4SLinus Torvalds #ifdef SNDRV_OSS_INFO_DEV_TIMERS 18811da177e4SLinus Torvalds snd_oss_info_register(SNDRV_OSS_INFO_DEV_TIMERS, SNDRV_CARDS - 1, "system timer"); 18821da177e4SLinus Torvalds #endif 18831da177e4SLinus Torvalds if ((entry = snd_info_create_module_entry(THIS_MODULE, "timers", NULL)) != NULL) { 18841da177e4SLinus Torvalds entry->c.text.read_size = SNDRV_TIMER_DEVICES * 128; 18851da177e4SLinus Torvalds entry->c.text.read = snd_timer_proc_read; 18861da177e4SLinus Torvalds if (snd_info_register(entry) < 0) { 18871da177e4SLinus Torvalds snd_info_free_entry(entry); 18881da177e4SLinus Torvalds entry = NULL; 18891da177e4SLinus Torvalds } 18901da177e4SLinus Torvalds } 18911da177e4SLinus Torvalds snd_timer_proc_entry = entry; 18921da177e4SLinus Torvalds if ((err = snd_timer_register_system()) < 0) 18931da177e4SLinus Torvalds snd_printk(KERN_ERR "unable to register system timer (%i)\n", err); 18941da177e4SLinus Torvalds if ((err = snd_register_device(SNDRV_DEVICE_TYPE_TIMER, 18951da177e4SLinus Torvalds NULL, 0, &snd_timer_reg, "timer"))<0) 18961da177e4SLinus Torvalds snd_printk(KERN_ERR "unable to register timer device (%i)\n", err); 18971da177e4SLinus Torvalds return 0; 18981da177e4SLinus Torvalds } 18991da177e4SLinus Torvalds 19001da177e4SLinus Torvalds static void __exit alsa_timer_exit(void) 19011da177e4SLinus Torvalds { 19021da177e4SLinus Torvalds struct list_head *p, *n; 19031da177e4SLinus Torvalds 19041da177e4SLinus Torvalds snd_unregister_device(SNDRV_DEVICE_TYPE_TIMER, NULL, 0); 19051da177e4SLinus Torvalds /* unregister the system timer */ 19061da177e4SLinus Torvalds list_for_each_safe(p, n, &snd_timer_list) { 19071da177e4SLinus Torvalds snd_timer_t *timer = (snd_timer_t *)list_entry(p, snd_timer_t, device_list); 19081da177e4SLinus Torvalds snd_timer_unregister(timer); 19091da177e4SLinus Torvalds } 19101da177e4SLinus Torvalds if (snd_timer_proc_entry) { 19111da177e4SLinus Torvalds snd_info_unregister(snd_timer_proc_entry); 19121da177e4SLinus Torvalds snd_timer_proc_entry = NULL; 19131da177e4SLinus Torvalds } 19141da177e4SLinus Torvalds #ifdef SNDRV_OSS_INFO_DEV_TIMERS 19151da177e4SLinus Torvalds snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_TIMERS, SNDRV_CARDS - 1); 19161da177e4SLinus Torvalds #endif 19171da177e4SLinus Torvalds } 19181da177e4SLinus Torvalds 19191da177e4SLinus Torvalds module_init(alsa_timer_init) 19201da177e4SLinus Torvalds module_exit(alsa_timer_exit) 19211da177e4SLinus Torvalds 19221da177e4SLinus Torvalds EXPORT_SYMBOL(snd_timer_open); 19231da177e4SLinus Torvalds EXPORT_SYMBOL(snd_timer_close); 19241da177e4SLinus Torvalds EXPORT_SYMBOL(snd_timer_resolution); 19251da177e4SLinus Torvalds EXPORT_SYMBOL(snd_timer_start); 19261da177e4SLinus Torvalds EXPORT_SYMBOL(snd_timer_stop); 19271da177e4SLinus Torvalds EXPORT_SYMBOL(snd_timer_continue); 19281da177e4SLinus Torvalds EXPORT_SYMBOL(snd_timer_pause); 19291da177e4SLinus Torvalds EXPORT_SYMBOL(snd_timer_new); 19301da177e4SLinus Torvalds EXPORT_SYMBOL(snd_timer_notify); 19311da177e4SLinus Torvalds EXPORT_SYMBOL(snd_timer_global_new); 19321da177e4SLinus Torvalds EXPORT_SYMBOL(snd_timer_global_free); 19331da177e4SLinus Torvalds EXPORT_SYMBOL(snd_timer_global_register); 19341da177e4SLinus Torvalds EXPORT_SYMBOL(snd_timer_global_unregister); 19351da177e4SLinus Torvalds EXPORT_SYMBOL(snd_timer_interrupt); 1936