diff options
Diffstat (limited to 'sound/core/timer.c')
-rw-r--r-- | sound/core/timer.c | 182 |
1 files changed, 93 insertions, 89 deletions
diff --git a/sound/core/timer.c b/sound/core/timer.c index 59ae21b0bb93..24fed5c78273 100644 --- a/sound/core/timer.c +++ b/sound/core/timer.c @@ -74,6 +74,9 @@ static LIST_HEAD(snd_timer_slave_list); /* lock for slave active lists */ static DEFINE_SPINLOCK(slave_active_lock); +#define MAX_SLAVE_INSTANCES 1000 +static int num_slaves; + static DEFINE_MUTEX(register_mutex); static int snd_timer_free(struct snd_timer *timer); @@ -85,12 +88,11 @@ static void snd_timer_reschedule(struct snd_timer * timer, unsigned long ticks_l /* * create a timer instance with the given owner string. - * when timer is not NULL, increments the module counter */ -static struct snd_timer_instance *snd_timer_instance_new(char *owner, - struct snd_timer *timer) +struct snd_timer_instance *snd_timer_instance_new(const char *owner) { struct snd_timer_instance *timeri; + timeri = kzalloc(sizeof(*timeri), GFP_KERNEL); if (timeri == NULL) return NULL; @@ -105,15 +107,20 @@ static struct snd_timer_instance *snd_timer_instance_new(char *owner, INIT_LIST_HEAD(&timeri->slave_list_head); INIT_LIST_HEAD(&timeri->slave_active_head); - timeri->timer = timer; - if (timer && !try_module_get(timer->module)) { + return timeri; +} +EXPORT_SYMBOL(snd_timer_instance_new); + +void snd_timer_instance_free(struct snd_timer_instance *timeri) +{ + if (timeri) { + if (timeri->private_free) + timeri->private_free(timeri); kfree(timeri->owner); kfree(timeri); - return NULL; } - - return timeri; } +EXPORT_SYMBOL(snd_timer_instance_free); /* * find a timer instance from the given timer id @@ -160,6 +167,28 @@ static void snd_timer_request(struct snd_timer_id *tid) #endif +/* move the slave if it belongs to the master; return 1 if match */ +static int check_matching_master_slave(struct snd_timer_instance *master, + struct snd_timer_instance *slave) +{ + if (slave->slave_class != master->slave_class || + slave->slave_id != master->slave_id) + return 0; + 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; + slave->timer = master->timer; + if (slave->flags & SNDRV_TIMER_IFLG_RUNNING) + list_add_tail(&slave->active_list, &master->slave_active_head); + spin_unlock(&master->timer->lock); + spin_unlock_irq(&slave_active_lock); + return 1; +} + /* * look for a master instance matching with the slave id of the given slave. * when found, relink the open_link of the slave. @@ -170,27 +199,18 @@ static int snd_timer_check_slave(struct snd_timer_instance *slave) { struct snd_timer *timer; struct snd_timer_instance *master; + int err = 0; /* FIXME: it's really dumb to look up all entries.. */ list_for_each_entry(timer, &snd_timer_list, device_list) { 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 0; - } + err = check_matching_master_slave(master, slave); + if (err != 0) /* match found or error */ + goto out; } } - return 0; + out: + return err < 0 ? err : 0; } /* @@ -202,43 +222,29 @@ static int snd_timer_check_slave(struct snd_timer_instance *slave) static int snd_timer_check_master(struct snd_timer_instance *master) { struct snd_timer_instance *slave, *tmp; + int err = 0; /* check all pending slaves */ 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; - slave->timer = master->timer; - if (slave->flags & SNDRV_TIMER_IFLG_RUNNING) - list_add_tail(&slave->active_list, - &master->slave_active_head); - spin_unlock(&master->timer->lock); - spin_unlock_irq(&slave_active_lock); - } + err = check_matching_master_slave(master, slave); + if (err < 0) + break; } - return 0; + return err < 0 ? err : 0; } -static int snd_timer_close_locked(struct snd_timer_instance *timeri, - struct device **card_devp_to_put); +static void snd_timer_close_locked(struct snd_timer_instance *timeri, + struct device **card_devp_to_put); /* * open a timer instance * when opening a master, the slave id must be here given. */ -int snd_timer_open(struct snd_timer_instance **ti, - char *owner, struct snd_timer_id *tid, +int snd_timer_open(struct snd_timer_instance *timeri, + struct snd_timer_id *tid, unsigned int slave_id) { struct snd_timer *timer; - struct snd_timer_instance *timeri = NULL; struct device *card_dev_to_put = NULL; int err; @@ -252,21 +258,17 @@ int snd_timer_open(struct snd_timer_instance **ti, err = -EINVAL; goto unlock; } - timeri = snd_timer_instance_new(owner, NULL); - if (!timeri) { - err = -ENOMEM; + if (num_slaves >= MAX_SLAVE_INSTANCES) { + err = -EBUSY; goto unlock; } timeri->slave_class = tid->dev_sclass; timeri->slave_id = tid->device; timeri->flags |= SNDRV_TIMER_IFLG_SLAVE; list_add_tail(&timeri->open_list, &snd_timer_slave_list); + num_slaves++; err = snd_timer_check_slave(timeri); - if (err < 0) { - snd_timer_close_locked(timeri, &card_dev_to_put); - timeri = NULL; - } - goto unlock; + goto list_added; } /* open a master instance */ @@ -296,45 +298,40 @@ int snd_timer_open(struct snd_timer_instance **ti, err = -EBUSY; goto unlock; } - timeri = snd_timer_instance_new(owner, timer); - if (!timeri) { - err = -ENOMEM; + if (!try_module_get(timer->module)) { + err = -EBUSY; goto unlock; } /* take a card refcount for safe disconnection */ - if (timer->card) + if (timer->card) { get_device(&timer->card->card_dev); - timeri->slave_class = tid->dev_sclass; - timeri->slave_id = slave_id; + card_dev_to_put = &timer->card->card_dev; + } if (list_empty(&timer->open_list_head) && timer->hw.open) { err = timer->hw.open(timer); if (err) { - kfree(timeri->owner); - kfree(timeri); - timeri = NULL; - - if (timer->card) - card_dev_to_put = &timer->card->card_dev; module_put(timer->module); goto unlock; } } + timeri->timer = timer; + timeri->slave_class = tid->dev_sclass; + timeri->slave_id = slave_id; + list_add_tail(&timeri->open_list, &timer->open_list_head); timer->num_instances++; err = snd_timer_check_master(timeri); - if (err < 0) { +list_added: + if (err < 0) snd_timer_close_locked(timeri, &card_dev_to_put); - timeri = NULL; - } unlock: mutex_unlock(®ister_mutex); /* put_device() is called after unlock for avoiding deadlock */ - if (card_dev_to_put) + if (err < 0 && card_dev_to_put) put_device(card_dev_to_put); - *ti = timeri; return err; } EXPORT_SYMBOL(snd_timer_open); @@ -343,8 +340,8 @@ EXPORT_SYMBOL(snd_timer_open); * close a timer instance * call this with register_mutex down. */ -static int snd_timer_close_locked(struct snd_timer_instance *timeri, - struct device **card_devp_to_put) +static void snd_timer_close_locked(struct snd_timer_instance *timeri, + struct device **card_devp_to_put) { struct snd_timer *timer = timeri->timer; struct snd_timer_instance *slave, *tmp; @@ -355,7 +352,11 @@ static int snd_timer_close_locked(struct snd_timer_instance *timeri, spin_unlock_irq(&timer->lock); } - list_del(&timeri->open_list); + if (!list_empty(&timeri->open_list)) { + list_del_init(&timeri->open_list); + if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) + num_slaves--; + } /* force to stop the timer */ snd_timer_stop(timeri); @@ -374,6 +375,7 @@ static int snd_timer_close_locked(struct snd_timer_instance *timeri, /* remove slave links */ spin_lock_irq(&slave_active_lock); spin_lock(&timer->lock); + timeri->timer = NULL; list_for_each_entry_safe(slave, tmp, &timeri->slave_list_head, open_list) { list_move_tail(&slave->open_list, &snd_timer_slave_list); @@ -391,11 +393,6 @@ static int snd_timer_close_locked(struct snd_timer_instance *timeri, timer = NULL; } - if (timeri->private_free) - timeri->private_free(timeri); - kfree(timeri->owner); - kfree(timeri); - if (timer) { if (list_empty(&timer->open_list_head) && timer->hw.close) timer->hw.close(timer); @@ -404,28 +401,24 @@ static int snd_timer_close_locked(struct snd_timer_instance *timeri, *card_devp_to_put = &timer->card->card_dev; module_put(timer->module); } - - return 0; } /* * close a timer instance */ -int snd_timer_close(struct snd_timer_instance *timeri) +void snd_timer_close(struct snd_timer_instance *timeri) { struct device *card_dev_to_put = NULL; - int err; if (snd_BUG_ON(!timeri)) - return -ENXIO; + return; mutex_lock(®ister_mutex); - err = snd_timer_close_locked(timeri, &card_dev_to_put); + snd_timer_close_locked(timeri, &card_dev_to_put); mutex_unlock(®ister_mutex); /* put_device() is called after unlock for avoiding deadlock */ if (card_dev_to_put) put_device(card_dev_to_put); - return err; } EXPORT_SYMBOL(snd_timer_close); @@ -1474,8 +1467,10 @@ static int snd_timer_user_release(struct inode *inode, struct file *file) tu = file->private_data; file->private_data = NULL; mutex_lock(&tu->ioctl_lock); - if (tu->timeri) + if (tu->timeri) { snd_timer_close(tu->timeri); + snd_timer_instance_free(tu->timeri); + } mutex_unlock(&tu->ioctl_lock); kfree(tu->queue); kfree(tu->tqueue); @@ -1716,6 +1711,7 @@ static int snd_timer_user_tselect(struct file *file, tu = file->private_data; if (tu->timeri) { snd_timer_close(tu->timeri); + snd_timer_instance_free(tu->timeri); tu->timeri = NULL; } if (copy_from_user(&tselect, _tselect, sizeof(tselect))) { @@ -1725,9 +1721,11 @@ static int snd_timer_user_tselect(struct file *file, sprintf(str, "application %i", current->pid); if (tselect.id.dev_class != SNDRV_TIMER_CLASS_SLAVE) tselect.id.dev_sclass = SNDRV_TIMER_SCLASS_APPLICATION; - err = snd_timer_open(&tu->timeri, str, &tselect.id, current->pid); - if (err < 0) + tu->timeri = snd_timer_instance_new(str); + if (!tu->timeri) { + err = -ENOMEM; goto __err; + } tu->timeri->flags |= SNDRV_TIMER_IFLG_FAST; tu->timeri->callback = tu->tread @@ -1736,6 +1734,12 @@ static int snd_timer_user_tselect(struct file *file, tu->timeri->callback_data = (void *)tu; tu->timeri->disconnect = snd_timer_user_disconnect; + err = snd_timer_open(tu->timeri, &tselect.id, current->pid); + if (err < 0) { + snd_timer_instance_free(tu->timeri); + tu->timeri = NULL; + } + __err: return err; } |