diff options
-rw-r--r-- | include/sound/timer.h | 1 | ||||
-rw-r--r-- | sound/core/timer.c | 123 |
2 files changed, 74 insertions, 50 deletions
diff --git a/include/sound/timer.h b/include/sound/timer.h index 7ae226ab6990..bcfee20ea226 100644 --- a/include/sound/timer.h +++ b/include/sound/timer.h @@ -43,7 +43,6 @@ #define SNDRV_TIMER_IFLG_START 0x00000004 #define SNDRV_TIMER_IFLG_AUTO 0x00000008 /* auto restart */ #define SNDRV_TIMER_IFLG_FAST 0x00000010 /* fast callback (do not use tasklet) */ -#define SNDRV_TIMER_IFLG_CALLBACK 0x00000020 /* timer callback is active */ #define SNDRV_TIMER_IFLG_EXCLUSIVE 0x00000040 /* exclusive owner - no more instances */ #define SNDRV_TIMER_IFLG_EARLY_EVENT 0x00000080 /* write early event to the poll queue */ diff --git a/sound/core/timer.c b/sound/core/timer.c index 61a0cec6e1f6..bb7e90ab90f8 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -38,6 +38,7 @@ /* internal flags */ #define SNDRV_TIMER_IFLG_PAUSED 0x00010000 +#define SNDRV_TIMER_IFLG_DEAD 0x00020000 #if IS_ENABLED(CONFIG_SND_HRTIMER) #define DEFAULT_TIMER_LIMIT 4 @@ -353,20 +354,25 @@ EXPORT_SYMBOL(snd_timer_open); */ static int snd_timer_close_locked(struct snd_timer_instance *timeri) { - struct snd_timer *timer = NULL; + struct snd_timer *timer = timeri->timer; struct snd_timer_instance *slave, *tmp; + if (timer) { + spin_lock_irq(&timer->lock); + timeri->flags |= SNDRV_TIMER_IFLG_DEAD; + spin_unlock_irq(&timer->lock); + } + list_del(&timeri->open_list); /* force to stop the timer */ snd_timer_stop(timeri); - timer = timeri->timer; if (timer) { timer->num_instances--; /* wait, until the active callback is finished */ spin_lock_irq(&timer->lock); - while (timeri->flags & SNDRV_TIMER_IFLG_CALLBACK) { + while (!list_empty(&timeri->ack_list)) { spin_unlock_irq(&timer->lock); udelay(10); spin_lock_irq(&timer->lock); @@ -497,6 +503,10 @@ static int snd_timer_start1(struct snd_timer_instance *timeri, return -EINVAL; spin_lock_irqsave(&timer->lock, flags); + if (timeri->flags & SNDRV_TIMER_IFLG_DEAD) { + result = -EINVAL; + goto unlock; + } if (timer->card && timer->card->shutdown) { result = -ENODEV; goto unlock; @@ -541,11 +551,16 @@ static int snd_timer_start_slave(struct snd_timer_instance *timeri, bool start) { unsigned long flags; + int err; spin_lock_irqsave(&slave_active_lock, flags); + if (timeri->flags & SNDRV_TIMER_IFLG_DEAD) { + err = -EINVAL; + goto unlock; + } if (timeri->flags & SNDRV_TIMER_IFLG_RUNNING) { - spin_unlock_irqrestore(&slave_active_lock, flags); - return -EBUSY; + err = -EBUSY; + goto unlock; } timeri->flags |= SNDRV_TIMER_IFLG_RUNNING; if (timeri->master && timeri->timer) { @@ -556,8 +571,10 @@ static int snd_timer_start_slave(struct snd_timer_instance *timeri, SNDRV_TIMER_EVENT_CONTINUE); spin_unlock(&timeri->timer->lock); } + err = 1; /* delayed start */ + unlock: spin_unlock_irqrestore(&slave_active_lock, flags); - return 1; /* delayed start */ + return err; } /* stop/pause a master timer */ @@ -720,6 +737,45 @@ static void snd_timer_reschedule(struct snd_timer * timer, unsigned long ticks_l timer->sticks = ticks; } +/* call callbacks in timer ack list */ +static void snd_timer_process_callbacks(struct snd_timer *timer, + struct list_head *head) +{ + struct snd_timer_instance *ti; + unsigned long resolution, ticks; + + while (!list_empty(head)) { + ti = list_first_entry(head, struct snd_timer_instance, + ack_list); + + if (!(ti->flags & SNDRV_TIMER_IFLG_DEAD)) { + ticks = ti->pticks; + ti->pticks = 0; + resolution = ti->resolution; + + spin_unlock(&timer->lock); + if (ti->callback) + ti->callback(ti, resolution, ticks); + spin_lock(&timer->lock); + } + + /* remove from ack_list and make empty */ + list_del_init(&ti->ack_list); + } +} + +/* clear pending instances from ack list */ +static void snd_timer_clear_callbacks(struct snd_timer *timer, + struct list_head *head) +{ + unsigned long flags; + + spin_lock_irqsave(&timer->lock, flags); + while (!list_empty(head)) + list_del_init(head->next); + spin_unlock_irqrestore(&timer->lock, flags); +} + /* * timer tasklet * @@ -727,34 +783,15 @@ static void snd_timer_reschedule(struct snd_timer * timer, unsigned long ticks_l 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; - if (timer->card && timer->card->shutdown) + if (timer->card && timer->card->shutdown) { + snd_timer_clear_callbacks(timer, &timer->sack_list_head); return; + } 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; - } + snd_timer_process_callbacks(timer, &timer->sack_list_head); spin_unlock_irqrestore(&timer->lock, flags); } @@ -767,16 +804,18 @@ static void snd_timer_tasklet(unsigned long arg) 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 resolution; + struct list_head *ack_list_head; unsigned long flags; int use_tasklet = 0; if (timer == NULL) return; - if (timer->card && timer->card->shutdown) + if (timer->card && timer->card->shutdown) { + snd_timer_clear_callbacks(timer, &timer->ack_list_head); return; + } spin_lock_irqsave(&timer->lock, flags); @@ -790,6 +829,8 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left) */ list_for_each_entry_safe(ti, tmp, &timer->active_list_head, active_list) { + if (ti->flags & SNDRV_TIMER_IFLG_DEAD) + continue; if (!(ti->flags & SNDRV_TIMER_IFLG_RUNNING)) continue; ti->pticks += ticks_left; @@ -839,23 +880,7 @@ void snd_timer_interrupt(struct snd_timer * timer, unsigned long ticks_left) } /* 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; - } + snd_timer_process_callbacks(timer, &timer->ack_list_head); /* do we have any slow callbacks? */ use_tasklet = !list_empty(&timer->sack_list_head); |