timer.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,895 行 · 第 1/4 页
C
1,895 行
/* * Timers abstract layer * Copyright (c) by Jaroslav Kysela <perex@suse.cz> * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */#include <sound/driver.h>#include <linux/delay.h>#include <linux/init.h>#include <linux/smp_lock.h>#include <linux/slab.h>#include <linux/time.h>#include <linux/moduleparam.h>#include <sound/core.h>#include <sound/timer.h>#include <sound/control.h>#include <sound/info.h>#include <sound/minors.h>#include <sound/initval.h>#include <linux/kmod.h>#ifdef CONFIG_KERNELD#include <linux/kerneld.h>#endif#if !defined(CONFIG_SND_RTCTIMER) && !defined(CONFIG_SND_RTCTIMER_MODULE)#define DEFAULT_TIMER_LIMIT 1#else#define DEFAULT_TIMER_LIMIT 2#endifstatic int timer_limit = DEFAULT_TIMER_LIMIT;MODULE_AUTHOR("Jaroslav Kysela <perex@suse.cz>, Takashi Iwai <tiwai@suse.de>");MODULE_DESCRIPTION("ALSA timer interface");MODULE_LICENSE("GPL");module_param(timer_limit, int, 0444);MODULE_PARM_DESC(timer_limit, "Maximum global timers in system.");typedef struct { snd_timer_instance_t *timeri; int tread; /* enhanced read with timestamps and events */ unsigned long ticks; unsigned long overrun; int qhead; int qtail; int qused; int queue_size; snd_timer_read_t *queue; snd_timer_tread_t *tqueue; spinlock_t qlock; unsigned long last_resolution; unsigned int filter; struct timespec tstamp; /* trigger tstamp */ wait_queue_head_t qchange_sleep; struct fasync_struct *fasync;} snd_timer_user_t;/* list of timers */static LIST_HEAD(snd_timer_list);/* list of slave instances */static LIST_HEAD(snd_timer_slave_list);/* lock for slave active lists */static spinlock_t slave_active_lock = SPIN_LOCK_UNLOCKED;static DECLARE_MUTEX(register_mutex);static int snd_timer_free(snd_timer_t *timer);static int snd_timer_dev_free(snd_device_t *device);static int snd_timer_dev_register(snd_device_t *device);static int snd_timer_dev_unregister(snd_device_t *device);static void snd_timer_reschedule(snd_timer_t * timer, unsigned long ticks_left);/* * create a timer instance with the given owner string. * when timer is not NULL, increments the module counter */static snd_timer_instance_t *snd_timer_instance_new(char *owner, snd_timer_t *timer){ snd_timer_instance_t *timeri; timeri = kcalloc(1, sizeof(*timeri), GFP_KERNEL); if (timeri == NULL) return NULL; timeri->owner = snd_kmalloc_strdup(owner, GFP_KERNEL); if (! timeri->owner) { kfree(timeri); return NULL; } INIT_LIST_HEAD(&timeri->open_list); INIT_LIST_HEAD(&timeri->active_list); INIT_LIST_HEAD(&timeri->ack_list); INIT_LIST_HEAD(&timeri->slave_list_head); INIT_LIST_HEAD(&timeri->slave_active_head); timeri->timer = timer; if (timer && timer->card && !try_module_get(timer->card->module)) { kfree(timeri->owner); kfree(timeri); return NULL; } return timeri;}/* * find a timer instance from the given timer id */static snd_timer_t *snd_timer_find(snd_timer_id_t *tid){ snd_timer_t *timer = NULL; struct list_head *p; list_for_each(p, &snd_timer_list) { timer = (snd_timer_t *)list_entry(p, snd_timer_t, device_list); if (timer->tmr_class != tid->dev_class) continue; if ((timer->tmr_class == SNDRV_TIMER_CLASS_CARD || timer->tmr_class == SNDRV_TIMER_CLASS_PCM) && (timer->card == NULL || timer->card->number != tid->card)) continue; if (timer->tmr_device != tid->device) continue; if (timer->tmr_subdevice != tid->subdevice) continue; return timer; } return NULL;}#ifdef CONFIG_KMODstatic void snd_timer_request(snd_timer_id_t *tid){ if (! current->fs->root) return; switch (tid->dev_class) { case SNDRV_TIMER_CLASS_GLOBAL: if (tid->device < timer_limit) request_module("snd-timer-%i", tid->device); break; case SNDRV_TIMER_CLASS_CARD: case SNDRV_TIMER_CLASS_PCM: if (tid->card < snd_ecards_limit) request_module("snd-card-%i", tid->card); break; default: break; }}#endif/* * look for a master instance matching with the slave id of the given slave. * when found, relink the open_link of the slave. * * call this with register_mutex down. */static void snd_timer_check_slave(snd_timer_instance_t *slave){ snd_timer_t *timer; snd_timer_instance_t *master; struct list_head *p, *q; /* FIXME: it's really dumb to look up all entries.. */ list_for_each(p, &snd_timer_list) { timer = (snd_timer_t *)list_entry(p, snd_timer_t, device_list); list_for_each(q, &timer->open_list_head) { master = (snd_timer_instance_t *)list_entry(q, snd_timer_instance_t, open_list); if (slave->slave_class == master->slave_class && slave->slave_id == master->slave_id) { list_del(&slave->open_list); list_add_tail(&slave->open_list, &master->slave_list_head); spin_lock_irq(&slave_active_lock); slave->master = master; slave->timer = master->timer; spin_unlock_irq(&slave_active_lock); return; } } }}/* * look for slave instances matching with the slave id of the given master. * when found, relink the open_link of slaves. * * call this with register_mutex down. */static void snd_timer_check_master(snd_timer_instance_t *master){ snd_timer_instance_t *slave; struct list_head *p, *n; /* check all pending slaves */ list_for_each_safe(p, n, &snd_timer_slave_list) { slave = (snd_timer_instance_t *)list_entry(p, snd_timer_instance_t, open_list); if (slave->slave_class == master->slave_class && slave->slave_id == master->slave_id) { list_del(p); list_add_tail(p, &master->slave_list_head); spin_lock_irq(&slave_active_lock); slave->master = master; slave->timer = master->timer; if (slave->flags & SNDRV_TIMER_IFLG_RUNNING) list_add_tail(&slave->active_list, &master->slave_active_head); spin_unlock_irq(&slave_active_lock); } }}/* * open a timer instance * when opening a master, the slave id must be here given. */int snd_timer_open(snd_timer_instance_t **ti, char *owner, snd_timer_id_t *tid, unsigned int slave_id){ snd_timer_t *timer; snd_timer_instance_t *timeri = NULL; if (tid->dev_class == SNDRV_TIMER_CLASS_SLAVE) { /* open a slave instance */ if (tid->dev_sclass <= SNDRV_TIMER_SCLASS_NONE || tid->dev_sclass > SNDRV_TIMER_SCLASS_OSS_SEQUENCER) { snd_printd("invalid slave class %i\n", tid->dev_sclass); return -EINVAL; } down(®ister_mutex); timeri = snd_timer_instance_new(owner, NULL); timeri->slave_class = tid->dev_sclass; timeri->slave_id = tid->device; timeri->flags |= SNDRV_TIMER_IFLG_SLAVE; list_add_tail(&timeri->open_list, &snd_timer_slave_list); snd_timer_check_slave(timeri); up(®ister_mutex); *ti = timeri; return 0; } /* open a master instance */ down(®ister_mutex); timer = snd_timer_find(tid);#ifdef CONFIG_KMOD if (timer == NULL) { up(®ister_mutex); snd_timer_request(tid); down(®ister_mutex); timer = snd_timer_find(tid); }#endif if (timer) { if (!list_empty(&timer->open_list_head)) { timeri = (snd_timer_instance_t *)list_entry(timer->open_list_head.next, snd_timer_instance_t, open_list); if (timeri->flags & SNDRV_TIMER_IFLG_EXCLUSIVE) { up(®ister_mutex); return -EBUSY; } } timeri = snd_timer_instance_new(owner, timer); if (timeri) { timeri->slave_class = tid->dev_sclass; timeri->slave_id = slave_id; if (list_empty(&timer->open_list_head) && timer->hw.open) timer->hw.open(timer); list_add_tail(&timeri->open_list, &timer->open_list_head); snd_timer_check_master(timeri); } } else { up(®ister_mutex); return -ENODEV; } up(®ister_mutex); *ti = timeri; return 0;}static int _snd_timer_stop(snd_timer_instance_t * timeri, int keep_flag, enum sndrv_timer_event event);/* * close a timer instance */int snd_timer_close(snd_timer_instance_t * timeri){ snd_timer_t *timer = NULL; struct list_head *p, *n; snd_timer_instance_t *slave; snd_assert(timeri != NULL, return -ENXIO); /* force to stop the timer */ snd_timer_stop(timeri); if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) { /* wait, until the active callback is finished */ spin_lock_irq(&slave_active_lock); while (timeri->flags & SNDRV_TIMER_IFLG_CALLBACK) { spin_unlock_irq(&slave_active_lock); udelay(10); spin_lock_irq(&slave_active_lock); } spin_unlock_irq(&slave_active_lock); down(®ister_mutex); list_del(&timeri->open_list); up(®ister_mutex); } else { timer = timeri->timer; /* wait, until the active callback is finished */ spin_lock_irq(&timer->lock); while (timeri->flags & SNDRV_TIMER_IFLG_CALLBACK) { spin_unlock_irq(&timer->lock); udelay(10); spin_lock_irq(&timer->lock); } spin_unlock_irq(&timer->lock); down(®ister_mutex); list_del(&timeri->open_list); if (timer && list_empty(&timer->open_list_head) && timer->hw.close) timer->hw.close(timer); /* remove slave links */ list_for_each_safe(p, n, &timeri->slave_list_head) { slave = (snd_timer_instance_t *)list_entry(p, snd_timer_instance_t, open_list); spin_lock_irq(&slave_active_lock); _snd_timer_stop(slave, 1, SNDRV_TIMER_EVENT_RESOLUTION); list_del(p); list_add_tail(p, &snd_timer_slave_list); slave->master = NULL; slave->timer = NULL; spin_unlock_irq(&slave_active_lock); } up(®ister_mutex); } if (timeri->private_free) timeri->private_free(timeri); if (timeri->owner) kfree(timeri->owner); kfree(timeri); if (timer && timer->card) module_put(timer->card->module); return 0;}unsigned long snd_timer_resolution(snd_timer_instance_t * timeri){ snd_timer_t * timer; if (timeri == NULL) return 0; if ((timer = timeri->timer) != NULL) { if (timer->hw.c_resolution) return timer->hw.c_resolution(timer); return timer->hw.resolution; } return 0;}static void snd_timer_notify1(snd_timer_instance_t *ti, enum sndrv_timer_event event){ snd_timer_t *timer; unsigned long flags; unsigned long resolution = 0; snd_timer_instance_t *ts; struct list_head *n; struct timespec tstamp; snd_timestamp_now(&tstamp, 1); snd_assert(event >= SNDRV_TIMER_EVENT_START && event <= SNDRV_TIMER_EVENT_PAUSE, return); if (event == SNDRV_TIMER_EVENT_START || event == SNDRV_TIMER_EVENT_CONTINUE) resolution = snd_timer_resolution(ti); if (ti->ccallback) ti->ccallback(ti, SNDRV_TIMER_EVENT_START, &tstamp, resolution); if (ti->flags & SNDRV_TIMER_IFLG_SLAVE) return; timer = ti->timer; if (timer == NULL) return; if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE) return; spin_lock_irqsave(&timer->lock, flags); list_for_each(n, &ti->slave_active_head) { ts = (snd_timer_instance_t *)list_entry(n, snd_timer_instance_t, active_list); if (ts->ccallback) ts->ccallback(ti, event + 100, &tstamp, resolution); } spin_unlock_irqrestore(&timer->lock, flags);}static int snd_timer_start1(snd_timer_t *timer, snd_timer_instance_t *timeri, unsigned long sticks){ list_del(&timeri->active_list); list_add_tail(&timeri->active_list, &timer->active_list_head); if (timer->running) { if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE) goto __start_now; timer->flags |= SNDRV_TIMER_FLG_RESCHED; timeri->flags |= SNDRV_TIMER_IFLG_START; return 1; /* delayed start */ } else { timer->sticks = sticks; timer->hw.start(timer); __start_now: timer->running++; timeri->flags |= SNDRV_TIMER_IFLG_RUNNING; return 0; }}static int snd_timer_start_slave(snd_timer_instance_t *timeri){ unsigned long flags; spin_lock_irqsave(&slave_active_lock, flags); timeri->flags |= SNDRV_TIMER_IFLG_RUNNING; if (timeri->master) list_add_tail(&timeri->active_list, &timeri->master->slave_active_head); spin_unlock_irqrestore(&slave_active_lock, flags); return 1; /* delayed start */}/* * start the timer instance */ int snd_timer_start(snd_timer_instance_t * timeri, unsigned int ticks){ snd_timer_t *timer; int result = -EINVAL; unsigned long flags; if (timeri == NULL || ticks < 1) return -EINVAL; if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) { result = snd_timer_start_slave(timeri); snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_START); return result; } timer = timeri->timer; if (timer == NULL) return -EINVAL; spin_lock_irqsave(&timer->lock, flags); timeri->ticks = timeri->cticks = ticks; timeri->pticks = 0; result = snd_timer_start1(timer, timeri, ticks); spin_unlock_irqrestore(&timer->lock, flags); snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_START); return result;}static int _snd_timer_stop(snd_timer_instance_t * timeri, int keep_flag, enum sndrv_timer_event event){ snd_timer_t *timer; unsigned long flags; snd_assert(timeri != NULL, return -ENXIO); if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) {
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?