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

📄 timer.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
			if (timer->flags & SNDRV_TIMER_FLG_CHANGE) {				timer->flags &= ~SNDRV_TIMER_FLG_CHANGE;				timer->hw.start(timer);			}		}	}	if (!keep_flag)		timeri->flags &=			~(SNDRV_TIMER_IFLG_RUNNING | SNDRV_TIMER_IFLG_START);	spin_unlock_irqrestore(&timer->lock, flags);      __end:	if (event != SNDRV_TIMER_EVENT_RESOLUTION)		snd_timer_notify1(timeri, event);	return 0;}/* * stop the timer instance. * * do not call this from the timer callback! */int snd_timer_stop(struct snd_timer_instance *timeri){	struct snd_timer *timer;	unsigned long flags;	int err;	err = _snd_timer_stop(timeri, 0, SNDRV_TIMER_EVENT_STOP);	if (err < 0)		return err;	timer = timeri->timer;	spin_lock_irqsave(&timer->lock, flags);	timeri->cticks = timeri->ticks;	timeri->pticks = 0;	spin_unlock_irqrestore(&timer->lock, flags);	return 0;}/* * start again..  the tick is kept. */int snd_timer_continue(struct snd_timer_instance *timeri){	struct snd_timer *timer;	int result = -EINVAL;	unsigned long flags;	if (timeri == NULL)		return result;	if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)		return snd_timer_start_slave(timeri);	timer = timeri->timer;	if (! timer)		return -EINVAL;	spin_lock_irqsave(&timer->lock, flags);	if (!timeri->cticks)		timeri->cticks = 1;	timeri->pticks = 0;	result = snd_timer_start1(timer, timeri, timer->sticks);	spin_unlock_irqrestore(&timer->lock, flags);	snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_CONTINUE);	return result;}/* * pause.. remember the ticks left */int snd_timer_pause(struct snd_timer_instance * timeri){	return _snd_timer_stop(timeri, 0, SNDRV_TIMER_EVENT_PAUSE);}/* * reschedule the timer * * start pending instances and check the scheduling ticks. * when the scheduling ticks is changed set CHANGE flag to reprogram the timer. */static void snd_timer_reschedule(struct snd_timer * timer, unsigned long ticks_left){	struct snd_timer_instance *ti;	unsigned long ticks = ~0UL;	list_for_each_entry(ti, &timer->active_list_head, active_list) {		if (ti->flags & SNDRV_TIMER_IFLG_START) {			ti->flags &= ~SNDRV_TIMER_IFLG_START;			ti->flags |= SNDRV_TIMER_IFLG_RUNNING;			timer->running++;		}		if (ti->flags & SNDRV_TIMER_IFLG_RUNNING) {			if (ticks > ti->cticks)				ticks = ti->cticks;		}	}	if (ticks == ~0UL) {		timer->flags &= ~SNDRV_TIMER_FLG_RESCHED;		return;	}	if (ticks > timer->hw.ticks)		ticks = timer->hw.ticks;	if (ticks_left != ticks)		timer->flags |= SNDRV_TIMER_FLG_CHANGE;	timer->sticks = ticks;}/* * timer tasklet * */static void snd_timer_tasklet(unsigned long arg){	struct snd_timer *timer = (struct snd_timer *) arg;	struct snd_timer_instance *ti;	struct list_head *p;	unsigned long resolution, ticks;	unsigned long flags;	spin_lock_irqsave(&timer->lock, flags);	/* now process all callbacks */	while (!list_empty(&timer->sack_list_head)) {		p = timer->sack_list_head.next;		/* get first item */		ti = list_entry(p, struct snd_timer_instance, ack_list);		/* remove from ack_list and make empty */		list_del_init(p);		ticks = ti->pticks;		ti->pticks = 0;		resolution = ti->resolution;		ti->flags |= SNDRV_TIMER_IFLG_CALLBACK;		spin_unlock(&timer->lock);		if (ti->callback)			ti->callback(ti, resolution, ticks);		spin_lock(&timer->lock);		ti->flags &= ~SNDRV_TIMER_IFLG_CALLBACK;	}	spin_unlock_irqrestore(&timer->lock, flags);}/* * timer interrupt * * ticks_left is usually equal to timer->sticks. * */void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left){	struct snd_timer_instance *ti, *ts, *tmp;	unsigned long resolution, ticks;	struct list_head *p, *ack_list_head;	unsigned long flags;	int use_tasklet = 0;	if (timer == NULL)		return;	spin_lock_irqsave(&timer->lock, flags);	/* remember the current resolution */	if (timer->hw.c_resolution)		resolution = timer->hw.c_resolution(timer);	else		resolution = timer->hw.resolution;	/* loop for all active instances	 * Here we cannot use list_for_each_entry because the active_list of a	 * processed instance is relinked to done_list_head before the callback	 * is called.	 */	list_for_each_entry_safe(ti, tmp, &timer->active_list_head,				 active_list) {		if (!(ti->flags & SNDRV_TIMER_IFLG_RUNNING))			continue;		ti->pticks += ticks_left;		ti->resolution = resolution;		if (ti->cticks < ticks_left)			ti->cticks = 0;		else			ti->cticks -= ticks_left;		if (ti->cticks) /* not expired */			continue;		if (ti->flags & SNDRV_TIMER_IFLG_AUTO) {			ti->cticks = ti->ticks;		} else {			ti->flags &= ~SNDRV_TIMER_IFLG_RUNNING;			if (--timer->running)				list_del(&ti->active_list);		}		if ((timer->hw.flags & SNDRV_TIMER_HW_TASKLET) ||		    (ti->flags & SNDRV_TIMER_IFLG_FAST))			ack_list_head = &timer->ack_list_head;		else			ack_list_head = &timer->sack_list_head;		if (list_empty(&ti->ack_list))			list_add_tail(&ti->ack_list, ack_list_head);		list_for_each_entry(ts, &ti->slave_active_head, active_list) {			ts->pticks = ti->pticks;			ts->resolution = resolution;			if (list_empty(&ts->ack_list))				list_add_tail(&ts->ack_list, ack_list_head);		}	}	if (timer->flags & SNDRV_TIMER_FLG_RESCHED)		snd_timer_reschedule(timer, timer->sticks);	if (timer->running) {		if (timer->hw.flags & SNDRV_TIMER_HW_STOP) {			timer->hw.stop(timer);			timer->flags |= SNDRV_TIMER_FLG_CHANGE;		}		if (!(timer->hw.flags & SNDRV_TIMER_HW_AUTO) ||		    (timer->flags & SNDRV_TIMER_FLG_CHANGE)) {			/* restart timer */			timer->flags &= ~SNDRV_TIMER_FLG_CHANGE;			timer->hw.start(timer);		}	} else {		timer->hw.stop(timer);	}	/* now process all fast callbacks */	while (!list_empty(&timer->ack_list_head)) {		p = timer->ack_list_head.next;		/* get first item */		ti = list_entry(p, struct snd_timer_instance, ack_list);		/* remove from ack_list and make empty */		list_del_init(p);		ticks = ti->pticks;		ti->pticks = 0;		ti->flags |= SNDRV_TIMER_IFLG_CALLBACK;		spin_unlock(&timer->lock);		if (ti->callback)			ti->callback(ti, resolution, ticks);		spin_lock(&timer->lock);		ti->flags &= ~SNDRV_TIMER_IFLG_CALLBACK;	}	/* do we have any slow callbacks? */	use_tasklet = !list_empty(&timer->sack_list_head);	spin_unlock_irqrestore(&timer->lock, flags);	if (use_tasklet)		tasklet_hi_schedule(&timer->task_queue);}/* */int snd_timer_new(struct snd_card *card, char *id, struct snd_timer_id *tid,		  struct snd_timer **rtimer){	struct snd_timer *timer;	int err;	static struct snd_device_ops ops = {		.dev_free = snd_timer_dev_free,		.dev_register = snd_timer_dev_register,		.dev_disconnect = snd_timer_dev_disconnect,	};	snd_assert(tid != NULL, return -EINVAL);	snd_assert(rtimer != NULL, return -EINVAL);	*rtimer = NULL;	timer = kzalloc(sizeof(*timer), GFP_KERNEL);	if (timer == NULL) {		snd_printk(KERN_ERR "timer: cannot allocate\n");		return -ENOMEM;	}	timer->tmr_class = tid->dev_class;	timer->card = card;	timer->tmr_device = tid->device;	timer->tmr_subdevice = tid->subdevice;	if (id)		strlcpy(timer->id, id, sizeof(timer->id));	INIT_LIST_HEAD(&timer->device_list);	INIT_LIST_HEAD(&timer->open_list_head);	INIT_LIST_HEAD(&timer->active_list_head);	INIT_LIST_HEAD(&timer->ack_list_head);	INIT_LIST_HEAD(&timer->sack_list_head);	spin_lock_init(&timer->lock);	tasklet_init(&timer->task_queue, snd_timer_tasklet,		     (unsigned long)timer);	if (card != NULL) {		timer->module = card->module;		err = snd_device_new(card, SNDRV_DEV_TIMER, timer, &ops);		if (err < 0) {			snd_timer_free(timer);			return err;		}	}	*rtimer = timer;	return 0;}static int snd_timer_free(struct snd_timer *timer){	snd_assert(timer != NULL, return -ENXIO);	mutex_lock(&register_mutex);	if (! list_empty(&timer->open_list_head)) {		struct list_head *p, *n;		struct snd_timer_instance *ti;		snd_printk(KERN_WARNING "timer %p is busy?\n", timer);		list_for_each_safe(p, n, &timer->open_list_head) {			list_del_init(p);			ti = list_entry(p, struct snd_timer_instance, open_list);			ti->timer = NULL;		}	}	list_del(&timer->device_list);	mutex_unlock(&register_mutex);	if (timer->private_free)		timer->private_free(timer);	kfree(timer);	return 0;}static int snd_timer_dev_free(struct snd_device *device){	struct snd_timer *timer = device->device_data;	return snd_timer_free(timer);}static int snd_timer_dev_register(struct snd_device *dev){	struct snd_timer *timer = dev->device_data;	struct snd_timer *timer1;	snd_assert(timer != NULL && timer->hw.start != NULL &&		   timer->hw.stop != NULL, return -ENXIO);	if (!(timer->hw.flags & SNDRV_TIMER_HW_SLAVE) &&	    !timer->hw.resolution && timer->hw.c_resolution == NULL)	    	return -EINVAL;	mutex_lock(&register_mutex);	list_for_each_entry(timer1, &snd_timer_list, device_list) {		if (timer1->tmr_class > timer->tmr_class)			break;		if (timer1->tmr_class < timer->tmr_class)			continue;		if (timer1->card && timer->card) {			if (timer1->card->number > timer->card->number)				break;			if (timer1->card->number < timer->card->number)				continue;		}		if (timer1->tmr_device > timer->tmr_device)			break;		if (timer1->tmr_device < timer->tmr_device)			continue;		if (timer1->tmr_subdevice > timer->tmr_subdevice)			break;		if (timer1->tmr_subdevice < timer->tmr_subdevice)			continue;		/* conflicts.. */		mutex_unlock(&register_mutex);		return -EBUSY;	}	list_add_tail(&timer->device_list, &timer1->device_list);	mutex_unlock(&register_mutex);	return 0;}static int snd_timer_dev_disconnect(struct snd_device *device){	struct snd_timer *timer = device->device_data;	mutex_lock(&register_mutex);	list_del_init(&timer->device_list);	mutex_unlock(&register_mutex);	return 0;}void snd_timer_notify(struct snd_timer *timer, int event, struct timespec *tstamp){	unsigned long flags;	unsigned long resolution = 0;	struct snd_timer_instance *ti, *ts;	if (! (timer->hw.flags & SNDRV_TIMER_HW_SLAVE))		return;	snd_assert(event >= SNDRV_TIMER_EVENT_MSTART &&		   event <= SNDRV_TIMER_EVENT_MRESUME, return);	spin_lock_irqsave(&timer->lock, flags);	if (event == SNDRV_TIMER_EVENT_MSTART ||	    event == SNDRV_TIMER_EVENT_MCONTINUE ||	    event == SNDRV_TIMER_EVENT_MRESUME) {		if (timer->hw.c_resolution)			resolution = timer->hw.c_resolution(timer);		else			resolution = timer->hw.resolution;	}	list_for_each_entry(ti, &timer->active_list_head, active_list) {		if (ti->ccallback)			ti->ccallback(ti, event, tstamp, resolution);		list_for_each_entry(ts, &ti->slave_active_head, active_list)			if (ts->ccallback)				ts->ccallback(ts, event, tstamp, resolution);	}	spin_unlock_irqrestore(&timer->lock, flags);}/* * exported functions for global timers */int snd_timer_global_new(char *id, int device, struct snd_timer **rtimer){	struct snd_timer_id tid;	tid.dev_class = SNDRV_TIMER_CLASS_GLOBAL;	tid.dev_sclass = SNDRV_TIMER_SCLASS_NONE;	tid.card = -1;	tid.device = device;	tid.subdevice = 0;	return snd_timer_new(NULL, id, &tid, rtimer);}int snd_timer_global_free(struct snd_timer *timer){	return snd_timer_free(timer);}int snd_timer_global_register(struct snd_timer *timer){	struct snd_device dev;	memset(&dev, 0, sizeof(dev));	dev.device_data = timer;	return snd_timer_dev_register(&dev);}/* *  System timer */struct snd_timer_system_private {	struct timer_list tlist;	unsigned long last_expires;	unsigned long last_jiffies;	unsigned long correction;};static void snd_timer_s_function(unsigned long data){	struct snd_timer *timer = (struct snd_timer *)data;	struct snd_timer_system_private *priv = timer->private_data;	unsigned long jiff = jiffies;	if (time_after(jiff, priv->last_expires))		priv->correction += (long)jiff - (long)priv->last_expires;	snd_timer_interrupt(timer, (long)jiff - (long)priv->last_jiffies);}static int snd_timer_s_start(struct snd_timer * timer){	struct snd_timer_system_private *priv;	unsigned long njiff;	priv = (struct snd_timer_system_private *) timer->private_data;	njiff = (priv->last_jiffies = jiffies);	if (priv->correction > timer->sticks - 1) {		priv->correction -= timer->sticks - 1;		njiff++;	} else {		njiff += timer->sticks - priv->correction;		priv->correction = 0;	}	priv->last_expires = priv->tlist.expires = njiff;	add_timer(&priv->tlist);	return 0;}static int snd_timer_s_stop(struct snd_timer * timer){	struct snd_timer_system_private *priv;	unsigned long jiff;	priv = (struct snd_timer_system_private *) timer->private_data;	del_timer(&priv->tlist);	jiff = jiffies;	if (time_before(jiff, priv->last_expires))		timer->sticks = priv->last_expires - jiff;	else		timer->sticks = 1;	priv->correction = 0;	return 0;}static struct snd_timer_hardware snd_timer_system ={	.flags =	SNDRV_TIMER_HW_FIRST | SNDRV_TIMER_HW_TASKLET,	.resolution =	1000000000L / HZ,	.ticks =	10000000L,	.start =	snd_timer_s_start,

⌨️ 快捷键说明

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