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(&register_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(&register_mutex);		*ti = timeri;		return 0;	}	/* open a master instance */	down(&register_mutex);	timer = snd_timer_find(tid);#ifdef CONFIG_KMOD	if (timer == NULL) {		up(&register_mutex);		snd_timer_request(tid);		down(&register_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(&register_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(&register_mutex);		return -ENODEV;	}	up(&register_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(&register_mutex);		list_del(&timeri->open_list);		up(&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);		down(&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_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(&register_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 + -
显示快捷键?