ALSA: timer: Limit max instances per timer
[pandora-kernel.git] / sound / core / timer.c
index 80ca958..69c801a 100644 (file)
@@ -179,7 +179,7 @@ static void snd_timer_request(struct snd_timer_id *tid)
  *
  * call this with register_mutex down.
  */
-static void snd_timer_check_slave(struct snd_timer_instance *slave)
+static int snd_timer_check_slave(struct snd_timer_instance *slave)
 {
        struct snd_timer *timer;
        struct snd_timer_instance *master;
@@ -189,16 +189,21 @@ static void snd_timer_check_slave(struct snd_timer_instance *slave)
                list_for_each_entry(master, &timer->open_list_head, open_list) {
                        if (slave->slave_class == master->slave_class &&
                            slave->slave_id == master->slave_id) {
+                               if (master->timer->num_instances >=
+                                   master->timer->max_instances)
+                                       return -EBUSY;
                                list_move_tail(&slave->open_list,
                                               &master->slave_list_head);
+                               master->timer->num_instances++;
                                spin_lock_irq(&slave_active_lock);
                                slave->master = master;
                                slave->timer = master->timer;
                                spin_unlock_irq(&slave_active_lock);
-                               return;
+                               return 0;
                        }
                }
        }
+       return 0;
 }
 
 /*
@@ -207,7 +212,7 @@ static void snd_timer_check_slave(struct snd_timer_instance *slave)
  *
  * call this with register_mutex down.
  */
-static void snd_timer_check_master(struct snd_timer_instance *master)
+static int snd_timer_check_master(struct snd_timer_instance *master)
 {
        struct snd_timer_instance *slave, *tmp;
 
@@ -215,7 +220,11 @@ static void snd_timer_check_master(struct snd_timer_instance *master)
        list_for_each_entry_safe(slave, tmp, &snd_timer_slave_list, open_list) {
                if (slave->slave_class == master->slave_class &&
                    slave->slave_id == master->slave_id) {
+                       if (master->timer->num_instances >=
+                           master->timer->max_instances)
+                               return -EBUSY;
                        list_move_tail(&slave->open_list, &master->slave_list_head);
+                       master->timer->num_instances++;
                        spin_lock_irq(&slave_active_lock);
                        spin_lock(&master->timer->lock);
                        slave->master = master;
@@ -227,8 +236,11 @@ static void snd_timer_check_master(struct snd_timer_instance *master)
                        spin_unlock_irq(&slave_active_lock);
                }
        }
+       return 0;
 }
 
+static int snd_timer_close_locked(struct snd_timer_instance *timeri);
+
 /*
  * open a timer instance
  * when opening a master, the slave id must be here given.
@@ -239,6 +251,7 @@ int snd_timer_open(struct snd_timer_instance **ti,
 {
        struct snd_timer *timer;
        struct snd_timer_instance *timeri = NULL;
+       int err;
 
        if (tid->dev_class == SNDRV_TIMER_CLASS_SLAVE) {
                /* open a slave instance */
@@ -257,10 +270,14 @@ int snd_timer_open(struct snd_timer_instance **ti,
                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);
+               err = snd_timer_check_slave(timeri);
+               if (err < 0) {
+                       snd_timer_close_locked(timeri);
+                       timeri = NULL;
+               }
                mutex_unlock(&register_mutex);
                *ti = timeri;
-               return 0;
+               return err;
        }
 
        /* open a master instance */
@@ -286,6 +303,10 @@ int snd_timer_open(struct snd_timer_instance **ti,
                        return -EBUSY;
                }
        }
+       if (timer->num_instances >= timer->max_instances) {
+               mutex_unlock(&register_mutex);
+               return -EBUSY;
+       }
        timeri = snd_timer_instance_new(owner, timer);
        if (!timeri) {
                mutex_unlock(&register_mutex);
@@ -307,26 +328,28 @@ int snd_timer_open(struct snd_timer_instance **ti,
        }
 
        list_add_tail(&timeri->open_list, &timer->open_list_head);
-       snd_timer_check_master(timeri);
+       timer->num_instances++;
+       err = snd_timer_check_master(timeri);
+       if (err < 0) {
+               snd_timer_close_locked(timeri);
+               timeri = NULL;
+       }
        mutex_unlock(&register_mutex);
        *ti = timeri;
-       return 0;
+       return err;
 }
 
 static int _snd_timer_stop(struct snd_timer_instance *timeri, int event);
 
 /*
  * close a timer instance
+ * call this with register_mutex down.
  */
-int snd_timer_close(struct snd_timer_instance *timeri)
+static int snd_timer_close_locked(struct snd_timer_instance *timeri)
 {
        struct snd_timer *timer = NULL;
        struct snd_timer_instance *slave, *tmp;
 
-       if (snd_BUG_ON(!timeri))
-               return -ENXIO;
-
-       mutex_lock(&register_mutex);
        list_del(&timeri->open_list);
 
        /* force to stop the timer */
@@ -334,6 +357,7 @@ int snd_timer_close(struct snd_timer_instance *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) {
@@ -349,6 +373,7 @@ int snd_timer_close(struct snd_timer_instance *timeri)
                list_for_each_entry_safe(slave, tmp, &timeri->slave_list_head,
                                         open_list) {
                        list_move_tail(&slave->open_list, &snd_timer_slave_list);
+                       timer->num_instances--;
                        slave->master = NULL;
                        slave->timer = NULL;
                        list_del_init(&slave->ack_list);
@@ -373,10 +398,25 @@ int snd_timer_close(struct snd_timer_instance *timeri)
                module_put(timer->module);
        }
 
-       mutex_unlock(&register_mutex);
        return 0;
 }
 
+/*
+ * close a timer instance
+ */
+int snd_timer_close(struct snd_timer_instance *timeri)
+{
+       int err;
+
+       if (snd_BUG_ON(!timeri))
+               return -ENXIO;
+
+       mutex_lock(&register_mutex);
+       err = snd_timer_close_locked(timeri);
+       mutex_unlock(&register_mutex);
+       return err;
+}
+
 unsigned long snd_timer_resolution(struct snd_timer_instance *timeri)
 {
        struct snd_timer * timer;
@@ -841,6 +881,7 @@ int snd_timer_new(struct snd_card *card, char *id, struct snd_timer_id *tid,
        spin_lock_init(&timer->lock);
        tasklet_init(&timer->task_queue, snd_timer_tasklet,
                     (unsigned long)timer);
+       timer->max_instances = 1000; /* default limit per timer */
        if (card != NULL) {
                timer->module = card->module;
                err = snd_device_new(card, SNDRV_DEV_TIMER, timer, &ops);