On 6/16/06, Takashi Iwai <tiwai@xxxxxxx> wrote:
The patch looks fine to me (as I also wanted to implement the same stuff right now :) Did you check whether it works with usb-audio actually? If it's confirmed to work, we can commit it together with the changes for other components. Then snd_card_free_in_thread() should be replaced with snd_card_free_when_closed().
Apparently the last patch had some problems tearing down the /proc/asound/cardX/pcmYZ entries at disconnect time. The attached one should fix that. The original one was developed on the driver bundled with a recent -mm kernel that didn't seem to create those entries. Anyway, it seems to work as advertised with the usb-audio driver, at least in my configuration. The whole /proc/asound/cardX tree goes away immediately when the card is unplugged without hanging khubd, and with an old handle held open, playback seems to work through the new device instance on the same slot when the card is plugged back in. Now, there has got to be some way to make usb-audio retain its snd_card structure at disconnect time. Perhaps it could optionally leave things hanging for some period of time before calling snd_card_disconnect(), leaving a window for the same device to be reconnected...? Does this sound too kludgy? Thanks.. -Sam Revitch
diff -r 08577d0b45ef core/control.c --- a/core/control.c Tue Jun 13 12:01:14 2006 +0200 +++ b/core/control.c Sat Jun 17 01:12:46 2006 -0700 @@ -1375,6 +1375,11 @@ static int snd_ctl_dev_disconnect(struct struct snd_card *card = device->device_data; struct list_head *flist; struct snd_ctl_file *ctl; + int err, cardnum; + + snd_assert(card != NULL, return -ENXIO); + cardnum = card->number; + snd_assert(cardnum >= 0 && cardnum < SNDRV_CARDS, return -ENXIO); down_read(&card->controls_rwsem); list_for_each(flist, &card->ctl_files) { @@ -1383,6 +1388,10 @@ static int snd_ctl_dev_disconnect(struct kill_fasync(&ctl->fasync, SIGIO, POLL_ERR); } up_read(&card->controls_rwsem); + + if ((err = snd_unregister_device(SNDRV_DEVICE_TYPE_CONTROL, + card, -1)) < 0) + return err; return 0; } @@ -1408,15 +1417,8 @@ static int snd_ctl_dev_free(struct snd_d */ static int snd_ctl_dev_unregister(struct snd_device *device) { - struct snd_card *card = device->device_data; - int err, cardnum; - - snd_assert(card != NULL, return -ENXIO); - cardnum = card->number; - snd_assert(cardnum >= 0 && cardnum < SNDRV_CARDS, return -ENXIO); - if ((err = snd_unregister_device(SNDRV_DEVICE_TYPE_CONTROL, - card, -1)) < 0) - return err; + if (device->state == SNDRV_DEV_REGISTERED) + (void) snd_ctl_dev_disconnect(device); return snd_ctl_dev_free(device); } diff -r 08577d0b45ef core/hwdep.c --- a/core/hwdep.c Tue Jun 13 12:01:14 2006 +0200 +++ b/core/hwdep.c Sat Jun 17 01:12:46 2006 -0700 @@ -42,6 +42,7 @@ static int snd_hwdep_free(struct snd_hwd static int snd_hwdep_free(struct snd_hwdep *hwdep); static int snd_hwdep_dev_free(struct snd_device *device); static int snd_hwdep_dev_register(struct snd_device *device); +static int snd_hwdep_dev_disconnect(struct snd_device *device); static int snd_hwdep_dev_unregister(struct snd_device *device); @@ -353,6 +354,7 @@ int snd_hwdep_new(struct snd_card *card, static struct snd_device_ops ops = { .dev_free = snd_hwdep_dev_free, .dev_register = snd_hwdep_dev_register, + .dev_disconnect = snd_hwdep_dev_disconnect, .dev_unregister = snd_hwdep_dev_unregister }; @@ -439,7 +441,7 @@ static int snd_hwdep_dev_register(struct return 0; } -static int snd_hwdep_dev_unregister(struct snd_device *device) +static int snd_hwdep_dev_disconnect(struct snd_device *device) { struct snd_hwdep *hwdep = device->device_data; @@ -454,8 +456,16 @@ static int snd_hwdep_dev_unregister(stru snd_unregister_oss_device(hwdep->oss_type, hwdep->card, hwdep->device); #endif snd_unregister_device(SNDRV_DEVICE_TYPE_HWDEP, hwdep->card, hwdep->device); - list_del(&hwdep->list); + list_del_init(&hwdep->list); mutex_unlock(®ister_mutex); + return 0; +} + +static int snd_hwdep_dev_unregister(struct snd_device *device) +{ + struct snd_hwdep *hwdep = device->device_data; + snd_assert(hwdep != NULL, return -ENXIO); + (void) snd_hwdep_dev_disconnect(device); return snd_hwdep_free(hwdep); } diff -r 08577d0b45ef core/info.c --- a/core/info.c Tue Jun 13 12:01:14 2006 +0200 +++ b/core/info.c Sat Jun 17 01:12:46 2006 -0700 @@ -820,6 +820,23 @@ struct snd_info_entry *snd_info_create_c EXPORT_SYMBOL(snd_info_create_card_entry); +int snd_info_disconnect(struct snd_info_entry *entry) +{ + struct proc_dir_entry *root; + if (!entry->disconnected) { + snd_assert(entry->p != NULL, return -ENXIO); + root = entry->parent == NULL ? snd_proc_root : entry->parent->p; + snd_assert(root, return -ENXIO); + mutex_lock(&info_mutex); + snd_remove_proc_entry(root, entry->p); + mutex_unlock(&info_mutex); + entry->disconnected = 1; + } + return 0; +} + +EXPORT_SYMBOL(snd_info_disconnect); + static int snd_info_dev_free_entry(struct snd_device *device) { struct snd_info_entry *entry = device->device_data; @@ -836,8 +853,7 @@ static int snd_info_dev_disconnect_entry static int snd_info_dev_disconnect_entry(struct snd_device *device) { struct snd_info_entry *entry = device->device_data; - entry->disconnected = 1; - return 0; + return snd_info_disconnect(entry); } static int snd_info_dev_unregister_entry(struct snd_device *device) @@ -952,16 +968,9 @@ EXPORT_SYMBOL(snd_info_register); */ int snd_info_unregister(struct snd_info_entry * entry) { - struct proc_dir_entry *root; - if (! entry) return 0; - snd_assert(entry->p != NULL, return -ENXIO); - root = entry->parent == NULL ? snd_proc_root : entry->parent->p; - snd_assert(root, return -ENXIO); - mutex_lock(&info_mutex); - snd_remove_proc_entry(root, entry->p); - mutex_unlock(&info_mutex); + (void) snd_info_disconnect(entry); snd_info_free_entry(entry); return 0; } diff -r 08577d0b45ef core/init.c --- a/core/init.c Tue Jun 13 12:01:14 2006 +0200 +++ b/core/init.c Sat Jun 17 01:12:46 2006 -0700 @@ -310,70 +310,104 @@ int snd_card_disconnect(struct snd_card if (err < 0) snd_printk(KERN_ERR "not all devices for card %i can be disconnected\n", card->number); - return 0; -} - -EXPORT_SYMBOL(snd_card_disconnect); - -/** - * snd_card_free - frees given soundcard structure - * @card: soundcard structure - * - * This function releases the soundcard structure and the all assigned - * devices automatically. That is, you don't have to release the devices - * by yourself. - * - * Returns zero. Frees all associated devices and frees the control - * interface associated to given soundcard. - */ -int snd_card_free(struct snd_card *card) -{ - struct snd_shutdown_f_ops *s_f_ops; - - if (card == NULL) - return -EINVAL; - mutex_lock(&snd_card_mutex); - snd_cards[card->number] = NULL; - mutex_unlock(&snd_card_mutex); - -#ifdef CONFIG_PM - wake_up(&card->power_sleep); -#endif - /* wait, until all devices are ready for the free operation */ - wait_event(card->shutdown_sleep, card->files == NULL); - -#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) - if (snd_mixer_oss_notify_callback) - snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_FREE); -#endif - if (snd_device_free_all(card, SNDRV_DEV_CMD_PRE) < 0) { - snd_printk(KERN_ERR "unable to free all devices (pre)\n"); - /* Fatal, but this situation should never occur */ - } - if (snd_device_free_all(card, SNDRV_DEV_CMD_NORMAL) < 0) { - snd_printk(KERN_ERR "unable to free all devices (normal)\n"); - /* Fatal, but this situation should never occur */ - } - if (snd_device_free_all(card, SNDRV_DEV_CMD_POST) < 0) { - snd_printk(KERN_ERR "unable to free all devices (post)\n"); - /* Fatal, but this situation should never occur */ - } - if (card->private_free) - card->private_free(card); snd_info_unregister(card->proc_id); if (snd_info_card_free(card) < 0) { snd_printk(KERN_WARNING "unable to free card info\n"); /* Not fatal error */ } + + return 0; +} + +EXPORT_SYMBOL(snd_card_disconnect); + +/** + * snd_card_free - frees given soundcard structure + * @card: soundcard structure + * + * This function releases the soundcard structure and the all assigned + * devices automatically. That is, you don't have to release the devices + * by yourself. + * + * Returns zero. Frees all associated devices and frees the control + * interface associated to given soundcard. + */ +static int snd_card_do_free(struct snd_card *card) +{ + struct snd_shutdown_f_ops *s_f_ops; + +#if defined(CONFIG_SND_MIXER_OSS) || defined(CONFIG_SND_MIXER_OSS_MODULE) + if (snd_mixer_oss_notify_callback) + snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_FREE); +#endif + if (snd_device_free_all(card, SNDRV_DEV_CMD_PRE) < 0) { + snd_printk(KERN_ERR "unable to free all devices (pre)\n"); + /* Fatal, but this situation should never occur */ + } + if (snd_device_free_all(card, SNDRV_DEV_CMD_NORMAL) < 0) { + snd_printk(KERN_ERR "unable to free all devices (normal)\n"); + /* Fatal, but this situation should never occur */ + } + if (snd_device_free_all(card, SNDRV_DEV_CMD_POST) < 0) { + snd_printk(KERN_ERR "unable to free all devices (post)\n"); + /* Fatal, but this situation should never occur */ + } + if (card->private_free) + card->private_free(card); while (card->s_f_ops) { s_f_ops = card->s_f_ops; card->s_f_ops = s_f_ops->next; kfree(s_f_ops); } + kfree(card); + return 0; +} + +static int snd_card_free_prepare(struct snd_card *card) +{ + if (card == NULL) + return -EINVAL; + (void) snd_card_disconnect(card); mutex_lock(&snd_card_mutex); + snd_cards[card->number] = NULL; snd_cards_lock &= ~(1 << card->number); mutex_unlock(&snd_card_mutex); - kfree(card); +#ifdef CONFIG_PM + wake_up(&card->power_sleep); +#endif + return 0; +} + +int snd_card_free_when_closed(struct snd_card *card) +{ + int free_now = 0; + int ret = snd_card_free_prepare(card); + if (ret) + return ret; + + spin_lock(&card->files_lock); + if (card->files == NULL) + free_now = 1; + else + card->free_on_last_close = 1; + spin_unlock(&card->files_lock); + + if (free_now) + snd_card_do_free(card); + return 0; +} + +EXPORT_SYMBOL(snd_card_free_when_closed); + +int snd_card_free(struct snd_card *card) +{ + int ret = snd_card_free_prepare(card); + if (ret) + return ret; + + /* wait, until all devices are ready for the free operation */ + wait_event(card->shutdown_sleep, card->files == NULL); + snd_card_do_free(card); return 0; } @@ -717,6 +751,7 @@ int snd_card_file_remove(struct snd_card int snd_card_file_remove(struct snd_card *card, struct file *file) { struct snd_monitor_file *mfile, *pfile = NULL; + int last_close = 0; spin_lock(&card->files_lock); mfile = card->files; @@ -731,9 +766,14 @@ int snd_card_file_remove(struct snd_card pfile = mfile; mfile = mfile->next; } + if (card->files == NULL) + last_close = 1; spin_unlock(&card->files_lock); - if (card->files == NULL) + if (last_close) { wake_up(&card->shutdown_sleep); + if (card->free_on_last_close) + snd_card_do_free(card); + } if (!mfile) { snd_printk(KERN_ERR "ALSA card file remove problem (%p)\n", file); return -ENOENT; diff -r 08577d0b45ef core/oss/mixer_oss.c --- a/core/oss/mixer_oss.c Tue Jun 13 12:01:14 2006 +0200 +++ b/core/oss/mixer_oss.c Sat Jun 17 01:12:46 2006 -0700 @@ -1313,22 +1313,20 @@ static int snd_mixer_oss_notify_handler( card->mixer_oss = mixer; snd_mixer_oss_build(mixer); snd_mixer_oss_proc_init(mixer); - } else if (cmd == SND_MIXER_OSS_NOTIFY_DISCONNECT) { - mixer = card->mixer_oss; - if (mixer == NULL || !mixer->oss_dev_alloc) - return 0; - snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MIXER, mixer->card, 0); - mixer->oss_dev_alloc = 0; - } else { /* free */ + } else { mixer = card->mixer_oss; if (mixer == NULL) return 0; + if (mixer->oss_dev_alloc) { #ifdef SNDRV_OSS_INFO_DEV_MIXERS - snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_MIXERS, mixer->card->number); + snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_MIXERS, mixer->card->number); #endif - if (mixer->oss_dev_alloc) snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_MIXER, mixer->card, 0); - snd_mixer_oss_proc_done(mixer); + snd_mixer_oss_proc_done(mixer); + mixer->oss_dev_alloc = 0; + } + if (cmd == SND_MIXER_OSS_NOTIFY_DISCONNECT) + return 0; return snd_mixer_oss_free1(mixer); } return 0; diff -r 08577d0b45ef core/oss/pcm_oss.c --- a/core/oss/pcm_oss.c Tue Jun 13 12:01:14 2006 +0200 +++ b/core/oss/pcm_oss.c Sat Jun 17 01:12:46 2006 -0700 @@ -2929,14 +2929,6 @@ static int snd_pcm_oss_disconnect_minor( snd_unregister_oss_device(SNDRV_OSS_DEVICE_TYPE_PCM, pcm->card, 1); } - } - return 0; -} - -static int snd_pcm_oss_unregister_minor(struct snd_pcm *pcm) -{ - snd_pcm_oss_disconnect_minor(pcm); - if (pcm->oss.reg) { if (dsp_map[pcm->card->number] == (int)pcm->device) { #ifdef SNDRV_OSS_INFO_DEV_AUDIO snd_oss_info_unregister(SNDRV_OSS_INFO_DEV_AUDIO, pcm->card->number); @@ -2945,6 +2937,12 @@ static int snd_pcm_oss_unregister_minor( pcm->oss.reg = 0; snd_pcm_oss_proc_done(pcm); } + return 0; +} + +static int snd_pcm_oss_unregister_minor(struct snd_pcm *pcm) +{ + snd_pcm_oss_disconnect_minor(pcm); return 0; } diff -r 08577d0b45ef core/pcm.c --- a/core/pcm.c Tue Jun 13 12:01:14 2006 +0200 +++ b/core/pcm.c Sat Jun 17 01:12:46 2006 -0700 @@ -491,6 +491,18 @@ static int snd_pcm_stream_proc_init(stru return 0; } +static void snd_pcm_stream_proc_disconnect(struct snd_pcm_str *pstr) +{ +#ifdef CONFIG_SND_PCM_XRUN_DEBUG + if (pstr->proc_xrun_debug_entry) + snd_info_disconnect(pstr->proc_xrun_debug_entry); +#endif + if (pstr->proc_info_entry) + snd_info_disconnect(pstr->proc_info_entry); + if (pstr->proc_root) + snd_info_disconnect(pstr->proc_root); +} + static int snd_pcm_stream_proc_done(struct snd_pcm_str *pstr) { #ifdef CONFIG_SND_PCM_XRUN_DEBUG @@ -569,6 +581,24 @@ static int snd_pcm_substream_proc_init(s substream->proc_status_entry = entry; return 0; +} + +/* Non-exported utility function defined in pcm_memory.c */ +extern void snd_pcm_lib_disconnect(struct snd_pcm_substream *); + +static void snd_pcm_substream_proc_disconnect(struct snd_pcm_substream *substream) +{ + snd_pcm_lib_disconnect(substream); + if (substream->proc_info_entry) + snd_info_disconnect(substream->proc_info_entry); + if (substream->proc_hw_params_entry) + snd_info_disconnect(substream->proc_hw_params_entry); + if (substream->proc_sw_params_entry) + snd_info_disconnect(substream->proc_sw_params_entry); + if (substream->proc_status_entry) + snd_info_disconnect(substream->proc_status_entry); + if (substream->proc_root) + snd_info_disconnect(substream->proc_root); } static int snd_pcm_substream_proc_done(struct snd_pcm_substream *substream) @@ -968,40 +998,34 @@ static int snd_pcm_dev_register(struct s return 0; } -static int snd_pcm_dev_disconnect(struct snd_device *device) -{ - struct snd_pcm *pcm = device->device_data; +static int snd_pcm_do_disconnect(struct snd_pcm *pcm, int notify) +{ struct list_head *list; struct snd_pcm_substream *substream; - int cidx; - - mutex_lock(®ister_mutex); + int cidx, devtype; + + if (list_empty(&pcm->list)) + return 0; + list_del_init(&pcm->list); for (cidx = 0; cidx < 2; cidx++) for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) if (substream->runtime) substream->runtime->status->state = SNDRV_PCM_STATE_DISCONNECTED; - list_for_each(list, &snd_pcm_notify_list) { - struct snd_pcm_notify *notify; - notify = list_entry(list, struct snd_pcm_notify, list); - notify->n_disconnect(pcm); - } - mutex_unlock(®ister_mutex); - return 0; -} - -static int snd_pcm_dev_unregister(struct snd_device *device) -{ - int cidx, devtype; - struct snd_pcm_substream *substream; - struct list_head *list; - struct snd_pcm *pcm = device->device_data; - - snd_assert(pcm != NULL, return -ENXIO); - mutex_lock(®ister_mutex); - list_del(&pcm->list); + if (notify) { + list_for_each(list, &snd_pcm_notify_list) { + struct snd_pcm_notify *notify; + notify = list_entry(list, struct snd_pcm_notify, list); + notify->n_disconnect(pcm); + } + } for (cidx = 0; cidx < 2; cidx++) { + struct snd_pcm_str *pstr = &pcm->streams[cidx]; + int err; devtype = -1; + for (substream = pstr->substream; substream; substream = substream->next) + snd_pcm_substream_proc_disconnect(substream); + snd_pcm_stream_proc_disconnect(pstr); switch (cidx) { case SNDRV_PCM_STREAM_PLAYBACK: devtype = SNDRV_DEVICE_TYPE_PCM_PLAYBACK; @@ -1010,10 +1034,38 @@ static int snd_pcm_dev_unregister(struct devtype = SNDRV_DEVICE_TYPE_PCM_CAPTURE; break; } - snd_unregister_device(devtype, pcm->card, pcm->device); + err = snd_unregister_device(devtype, pcm->card, pcm->device); + if (err < 0) + snd_printk(KERN_ERR "snd_unregister_device %d/%d = %i\n", + pcm->card->number, devtype, err); + } + return 0; +} + +static int snd_pcm_dev_disconnect(struct snd_device *device) +{ + struct snd_pcm *pcm = device->device_data; + int err = 0; + + mutex_lock(®ister_mutex); + err = snd_pcm_do_disconnect(pcm, 1); + mutex_unlock(®ister_mutex); + return err; +} + +static int snd_pcm_dev_unregister(struct snd_device *device) +{ + int cidx; + struct snd_pcm_substream *substream; + struct list_head *list; + struct snd_pcm *pcm = device->device_data; + + snd_assert(pcm != NULL, return -ENXIO); + mutex_lock(®ister_mutex); + snd_pcm_do_disconnect(pcm, 0); + for (cidx = 0; cidx < 2; cidx++) for (substream = pcm->streams[cidx].substream; substream; substream = substream->next) snd_pcm_timer_done(substream); - } list_for_each(list, &snd_pcm_notify_list) { struct snd_pcm_notify *notify; notify = list_entry(list, struct snd_pcm_notify, list); diff -r 08577d0b45ef core/pcm_memory.c --- a/core/pcm_memory.c Tue Jun 13 12:01:14 2006 +0200 +++ b/core/pcm_memory.c Sat Jun 17 01:12:46 2006 -0700 @@ -255,6 +255,16 @@ int snd_pcm_lib_preallocate_pages(struct EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages); + +void snd_pcm_lib_disconnect(struct snd_pcm_substream *substream) +{ +#ifdef CONFIG_SND_VERBOSE_PROCFS + if (substream->proc_prealloc_entry) + snd_info_disconnect(substream->proc_prealloc_entry); +#endif +} + + /** * snd_pcm_lib_preallocate_pages_for_all - pre-allocation for continous memory type (all substreams) * @pcm: the pcm instance diff -r 08577d0b45ef core/timer.c --- a/core/timer.c Tue Jun 13 12:01:14 2006 +0200 +++ b/core/timer.c Sat Jun 17 01:12:46 2006 -0700 @@ -88,6 +88,7 @@ static int snd_timer_free(struct snd_tim static int snd_timer_free(struct snd_timer *timer); static int snd_timer_dev_free(struct snd_device *device); static int snd_timer_dev_register(struct snd_device *device); +static int snd_timer_dev_disconnect(struct snd_device *device); static int snd_timer_dev_unregister(struct snd_device *device); static void snd_timer_reschedule(struct snd_timer * timer, unsigned long ticks_left); @@ -772,6 +773,7 @@ int snd_timer_new(struct snd_card *card, static struct snd_device_ops ops = { .dev_free = snd_timer_dev_free, .dev_register = snd_timer_dev_register, + .dev_disconnect = snd_timer_dev_disconnect, .dev_unregister = snd_timer_dev_unregister }; @@ -884,6 +886,15 @@ static int snd_timer_unregister(struct s list_del(&timer->device_list); mutex_unlock(®ister_mutex); return snd_timer_free(timer); +} + +static int snd_timer_dev_disconnect(struct snd_device *device) +{ + struct snd_timer *timer = device->device_data; + mutex_lock(®ister_mutex); + list_del_init(&timer->device_list); + mutex_unlock(®ister_mutex); + return 0; } static int snd_timer_dev_unregister(struct snd_device *device) diff -r 08577d0b45ef include/core.h --- a/include/core.h Tue Jun 13 12:01:14 2006 +0200 +++ b/include/core.h Sat Jun 17 01:12:46 2006 -0700 @@ -131,6 +131,7 @@ struct snd_card { state */ spinlock_t files_lock; /* lock the files for this card */ int shutdown; /* this card is going down */ + int free_on_last_close; /* free in context of file_release */ wait_queue_head_t shutdown_sleep; struct work_struct free_workq; /* for free in workqueue */ struct device *dev; @@ -246,6 +247,7 @@ struct snd_card *snd_card_new(int idx, c struct module *module, int extra_size); int snd_card_disconnect(struct snd_card *card); int snd_card_free(struct snd_card *card); +int snd_card_free_when_closed(struct snd_card *card); int snd_card_free_in_thread(struct snd_card *card); int snd_card_register(struct snd_card *card); int snd_card_info_init(void); diff -r 08577d0b45ef include/info.h --- a/include/info.h Tue Jun 13 12:01:14 2006 +0200 +++ b/include/info.h Sat Jun 17 01:12:46 2006 -0700 @@ -123,6 +123,7 @@ int snd_info_card_register(struct snd_ca int snd_info_card_register(struct snd_card * card); int snd_info_card_free(struct snd_card * card); int snd_info_register(struct snd_info_entry * entry); +int snd_info_disconnect(struct snd_info_entry * entry); int snd_info_unregister(struct snd_info_entry * entry); /* for card drivers */ diff -r 08577d0b45ef usb/usbaudio.c --- a/usb/usbaudio.c Tue Jun 13 12:01:14 2006 +0200 +++ b/usb/usbaudio.c Sat Jun 17 01:12:46 2006 -0700 @@ -3469,7 +3469,7 @@ static void snd_usb_audio_disconnect(str } usb_chip[chip->index] = NULL; mutex_unlock(®ister_mutex); - snd_card_free(card); + snd_card_free_when_closed(card); } else { mutex_unlock(®ister_mutex); }
_______________________________________________ Alsa-devel mailing list Alsa-devel@xxxxxxxxxxxxxxxxxxxxx https://lists.sourceforge.net/lists/listinfo/alsa-devel