summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/sound/timer.h1
-rw-r--r--sound/core/timer.c123
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);