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