⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 timer.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
/* *  Timers abstract layer *  Copyright (c) by Jaroslav Kysela <perex@perex.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/slab.h>#include <linux/time.h>#include <linux/mutex.h>#include <linux/moduleparam.h>#include <linux/string.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>#if defined(CONFIG_SND_HPET) || defined(CONFIG_SND_HPET_MODULE)#define DEFAULT_TIMER_LIMIT 3#elif defined(CONFIG_SND_RTCTIMER) || defined(CONFIG_SND_RTCTIMER_MODULE)#define DEFAULT_TIMER_LIMIT 2#else#define DEFAULT_TIMER_LIMIT 1#endifstatic int timer_limit = DEFAULT_TIMER_LIMIT;MODULE_AUTHOR("Jaroslav Kysela <perex@perex.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.");struct snd_timer_user {	struct snd_timer_instance *timeri;	int tread;		/* enhanced read with timestamps and events */	unsigned long ticks;	unsigned long overrun;	int qhead;	int qtail;	int qused;	int queue_size;	struct snd_timer_read *queue;	struct snd_timer_tread *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;	struct mutex tread_sem;};/* 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 DEFINE_SPINLOCK(slave_active_lock);static DEFINE_MUTEX(register_mutex);static int snd_timer_free(struct snd_timer *timer);static int snd_timer_dev_free(struct snd_device *device);static int snd_timer_dev_register(struct snd_device *device);static int snd_timer_dev_disconnect(struct snd_device *device);static void snd_timer_reschedule(struct snd_timer * timer, unsigned long ticks_left);/* * create a timer instance with the given owner string. * when timer is not NULL, increments the module counter */static struct snd_timer_instance *snd_timer_instance_new(char *owner,							 struct snd_timer *timer){	struct snd_timer_instance *timeri;	timeri = kzalloc(sizeof(*timeri), GFP_KERNEL);	if (timeri == NULL)		return NULL;	timeri->owner = kstrdup(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 && !try_module_get(timer->module)) {		kfree(timeri->owner);		kfree(timeri);		return NULL;	}	return timeri;}/* * find a timer instance from the given timer id */static struct snd_timer *snd_timer_find(struct snd_timer_id *tid){	struct snd_timer *timer = NULL;	list_for_each_entry(timer, &snd_timer_list, 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(struct snd_timer_id *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(struct snd_timer_instance *slave){	struct snd_timer *timer;	struct snd_timer_instance *master;	/* FIXME: it's really dumb to look up all entries.. */	list_for_each_entry(timer, &snd_timer_list, device_list) {		list_for_each_entry(master, &timer->open_list_head, 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(struct snd_timer_instance *master){	struct snd_timer_instance *slave, *tmp;	/* check all pending slaves */	list_for_each_entry_safe(slave, tmp, &snd_timer_slave_list, open_list) {		if (slave->slave_class == master->slave_class &&		    slave->slave_id == master->slave_id) {			list_move_tail(&slave->open_list, &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(struct snd_timer_instance **ti,		   char *owner, struct snd_timer_id *tid,		   unsigned int slave_id){	struct snd_timer *timer;	struct snd_timer_instance *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;		}		mutex_lock(&register_mutex);		timeri = snd_timer_instance_new(owner, NULL);		if (!timeri) {			mutex_unlock(&register_mutex);			return -ENOMEM;		}		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);		mutex_unlock(&register_mutex);		*ti = timeri;		return 0;	}	/* open a master instance */	mutex_lock(&register_mutex);	timer = snd_timer_find(tid);#ifdef CONFIG_KMOD	if (timer == NULL) {		mutex_unlock(&register_mutex);		snd_timer_request(tid);		mutex_lock(&register_mutex);		timer = snd_timer_find(tid);	}#endif	if (!timer) {		mutex_unlock(&register_mutex);		return -ENODEV;	}	if (!list_empty(&timer->open_list_head)) {		timeri = list_entry(timer->open_list_head.next,				    struct snd_timer_instance, open_list);		if (timeri->flags & SNDRV_TIMER_IFLG_EXCLUSIVE) {			mutex_unlock(&register_mutex);			return -EBUSY;		}	}	timeri = snd_timer_instance_new(owner, timer);	if (!timeri) {		mutex_unlock(&register_mutex);		return -ENOMEM;	}	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);	mutex_unlock(&register_mutex);	*ti = timeri;	return 0;}static int _snd_timer_stop(struct snd_timer_instance *timeri,			   int keep_flag, int event);/* * close a timer instance */int snd_timer_close(struct snd_timer_instance *timeri){	struct snd_timer *timer = NULL;	struct snd_timer_instance *slave, *tmp;	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);		mutex_lock(&register_mutex);		list_del(&timeri->open_list);		mutex_unlock(&register_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);		mutex_lock(&register_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_entry_safe(slave, tmp, &timeri->slave_list_head,					 open_list) {			spin_lock_irq(&slave_active_lock);			_snd_timer_stop(slave, 1, SNDRV_TIMER_EVENT_RESOLUTION);			list_move_tail(&slave->open_list, &snd_timer_slave_list);			slave->master = NULL;			slave->timer = NULL;			spin_unlock_irq(&slave_active_lock);		}		mutex_unlock(&register_mutex);	}	if (timeri->private_free)		timeri->private_free(timeri);	kfree(timeri->owner);	kfree(timeri);	if (timer)		module_put(timer->module);	return 0;}unsigned long snd_timer_resolution(struct snd_timer_instance *timeri){	struct snd_timer * 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(struct snd_timer_instance *ti, int event){	struct snd_timer *timer;	unsigned long flags;	unsigned long resolution = 0;	struct snd_timer_instance *ts;	struct timespec tstamp;	getnstimeofday(&tstamp);	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_entry(ts, &ti->slave_active_head, active_list)		if (ts->ccallback)			ts->ccallback(ti, event + 100, &tstamp, resolution);	spin_unlock_irqrestore(&timer->lock, flags);}static int snd_timer_start1(struct snd_timer *timer, struct snd_timer_instance *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(struct snd_timer_instance *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(struct snd_timer_instance *timeri, unsigned int ticks){	struct snd_timer *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(struct snd_timer_instance * timeri,			   int keep_flag, int event){	struct snd_timer *timer;	unsigned long flags;	snd_assert(timeri != NULL, return -ENXIO);	if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) {		if (!keep_flag) {			spin_lock_irqsave(&slave_active_lock, flags);			timeri->flags &= ~SNDRV_TIMER_IFLG_RUNNING;			spin_unlock_irqrestore(&slave_active_lock, flags);		}		goto __end;	}	timer = timeri->timer;	if (!timer)		return -EINVAL;	spin_lock_irqsave(&timer->lock, flags);	list_del_init(&timeri->ack_list);	list_del_init(&timeri->active_list);	if ((timeri->flags & SNDRV_TIMER_IFLG_RUNNING) &&	    !(--timer->running)) {		timer->hw.stop(timer);		if (timer->flags & SNDRV_TIMER_FLG_RESCHED) {			timer->flags &= ~SNDRV_TIMER_FLG_RESCHED;			snd_timer_reschedule(timer, 0);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -