]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - sound/core/timer.c
Merge tag 'for-v5.2' of git://git.kernel.org/pub/scm/linux/kernel/git/sre/linux-power...
[linux.git] / sound / core / timer.c
index b842b61f66c285a6b1a3af28d60add8a6c8dc0c6..e3973957b39273e50b04feb5a588a496c4d5f14a 100644 (file)
@@ -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
@@ -254,19 +255,20 @@ int snd_timer_open(struct snd_timer_instance **ti,
        struct snd_timer_instance *timeri = NULL;
        int err;
 
+       mutex_lock(&register_mutex);
        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) {
                        pr_debug("ALSA: timer: invalid slave class %i\n",
                                 tid->dev_sclass);
-                       return -EINVAL;
+                       err = -EINVAL;
+                       goto unlock;
                }
-               mutex_lock(&register_mutex);
                timeri = snd_timer_instance_new(owner, NULL);
                if (!timeri) {
-                       mutex_unlock(&register_mutex);
-                       return -ENOMEM;
+                       err = -ENOMEM;
+                       goto unlock;
                }
                timeri->slave_class = tid->dev_sclass;
                timeri->slave_id = tid->device;
@@ -277,13 +279,10 @@ int snd_timer_open(struct snd_timer_instance **ti,
                        snd_timer_close_locked(timeri);
                        timeri = NULL;
                }
-               mutex_unlock(&register_mutex);
-               *ti = timeri;
-               return err;
+               goto unlock;
        }
 
        /* open a master instance */
-       mutex_lock(&register_mutex);
        timer = snd_timer_find(tid);
 #ifdef CONFIG_MODULES
        if (!timer) {
@@ -294,25 +293,26 @@ int snd_timer_open(struct snd_timer_instance **ti,
        }
 #endif
        if (!timer) {
-               mutex_unlock(&register_mutex);
-               return -ENODEV;
+               err = -ENODEV;
+               goto unlock;
        }
        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;
+                       err = -EBUSY;
+                       timeri = NULL;
+                       goto unlock;
                }
        }
        if (timer->num_instances >= timer->max_instances) {
-               mutex_unlock(&register_mutex);
-               return -EBUSY;
+               err = -EBUSY;
+               goto unlock;
        }
        timeri = snd_timer_instance_new(owner, timer);
        if (!timeri) {
-               mutex_unlock(&register_mutex);
-               return -ENOMEM;
+               err = -ENOMEM;
+               goto unlock;
        }
        /* take a card refcount for safe disconnection */
        if (timer->card)
@@ -321,16 +321,16 @@ int snd_timer_open(struct snd_timer_instance **ti,
        timeri->slave_id = slave_id;
 
        if (list_empty(&timer->open_list_head) && timer->hw.open) {
-               int err = timer->hw.open(timer);
+               err = timer->hw.open(timer);
                if (err) {
                        kfree(timeri->owner);
                        kfree(timeri);
+                       timeri = NULL;
 
                        if (timer->card)
                                put_device(&timer->card->card_dev);
                        module_put(timer->module);
-                       mutex_unlock(&register_mutex);
-                       return err;
+                       goto unlock;
                }
        }
 
@@ -341,6 +341,8 @@ int snd_timer_open(struct snd_timer_instance **ti,
                snd_timer_close_locked(timeri);
                timeri = NULL;
        }
+
+ unlock:
        mutex_unlock(&register_mutex);
        *ti = timeri;
        return err;
@@ -353,15 +355,20 @@ 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 */
@@ -497,6 +504,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 +552,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 +572,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 +738,46 @@ 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);
+
+               /* remove from ack_list and make empty */
+               list_del_init(&ti->ack_list);
+
+               if (!(ti->flags & SNDRV_TIMER_IFLG_DEAD)) {
+                       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;
+               }
+       }
+}
+
+/* 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 +785,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 +806,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 +831,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 +882,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);
@@ -1882,7 +1909,10 @@ static int snd_timer_user_start(struct file *file)
        snd_timer_stop(tu->timeri);
        tu->timeri->lost = 0;
        tu->last_resolution = 0;
-       return (err = snd_timer_start(tu->timeri, tu->ticks)) < 0 ? err : 0;
+       err = snd_timer_start(tu->timeri, tu->ticks);
+       if (err < 0)
+               return err;
+       return 0;
 }
 
 static int snd_timer_user_stop(struct file *file)
@@ -1893,7 +1923,10 @@ static int snd_timer_user_stop(struct file *file)
        tu = file->private_data;
        if (!tu->timeri)
                return -EBADFD;
-       return (err = snd_timer_stop(tu->timeri)) < 0 ? err : 0;
+       err = snd_timer_stop(tu->timeri);
+       if (err < 0)
+               return err;
+       return 0;
 }
 
 static int snd_timer_user_continue(struct file *file)
@@ -1908,7 +1941,10 @@ static int snd_timer_user_continue(struct file *file)
        if (!(tu->timeri->flags & SNDRV_TIMER_IFLG_PAUSED))
                return snd_timer_user_start(file);
        tu->timeri->lost = 0;
-       return (err = snd_timer_continue(tu->timeri)) < 0 ? err : 0;
+       err = snd_timer_continue(tu->timeri);
+       if (err < 0)
+               return err;
+       return 0;
 }
 
 static int snd_timer_user_pause(struct file *file)
@@ -1919,7 +1955,10 @@ static int snd_timer_user_pause(struct file *file)
        tu = file->private_data;
        if (!tu->timeri)
                return -EBADFD;
-       return (err = snd_timer_pause(tu->timeri)) < 0 ? err : 0;
+       err = snd_timer_pause(tu->timeri);
+       if (err < 0)
+               return err;
+       return 0;
 }
 
 enum {