From a65d629ceb4cff5e7d5edadfd6bf1f64c370a517 Mon Sep 17 00:00:00 2001 From: Takashi Iwai Date: Mon, 23 Feb 2009 16:57:04 +0100 Subject: [PATCH] ALSA: hda - Add pseudo device-locking for clear/reconfig Added the pseudo device-locking using card->shutdown flag to avoid the crash via clear/reconfig during operations. Signed-off-by: Takashi Iwai --- sound/pci/hda/hda_codec.c | 54 ++++++++++++++++++++++++++++++++++++--- sound/pci/hda/hda_hwdep.c | 15 +++++++++-- sound/pci/hda/hda_local.h | 2 +- 3 files changed, 64 insertions(+), 7 deletions(-) diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index a13480fa8e74..5dceee8a113b 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -1445,9 +1445,52 @@ void snd_hda_ctls_clear(struct hda_codec *codec) snd_array_free(&codec->mixers); } -void snd_hda_codec_reset(struct hda_codec *codec) +/* pseudo device locking + * toggle card->shutdown to allow/disallow the device access (as a hack) + */ +static int hda_lock_devices(struct snd_card *card) { - int i; + spin_lock(&card->files_lock); + if (card->shutdown) { + spin_unlock(&card->files_lock); + return -EINVAL; + } + card->shutdown = 1; + spin_unlock(&card->files_lock); + return 0; +} + +static void hda_unlock_devices(struct snd_card *card) +{ + spin_lock(&card->files_lock); + card->shutdown = 0; + spin_unlock(&card->files_lock); +} + +int snd_hda_codec_reset(struct hda_codec *codec) +{ + struct snd_card *card = codec->bus->card; + int i, pcm; + + if (hda_lock_devices(card) < 0) + return -EBUSY; + /* check whether the codec isn't used by any mixer or PCM streams */ + if (!list_empty(&card->ctl_files)) { + hda_unlock_devices(card); + return -EBUSY; + } + for (pcm = 0; pcm < codec->num_pcms; pcm++) { + struct hda_pcm *cpcm = &codec->pcm_info[pcm]; + if (!cpcm->pcm) + continue; + if (cpcm->pcm->streams[0].substream_opened || + cpcm->pcm->streams[1].substream_opened) { + hda_unlock_devices(card); + return -EBUSY; + } + } + + /* OK, let it free */ #ifdef CONFIG_SND_HDA_POWER_SAVE cancel_delayed_work(&codec->power_work); @@ -1457,8 +1500,7 @@ void snd_hda_codec_reset(struct hda_codec *codec) /* relase PCMs */ for (i = 0; i < codec->num_pcms; i++) { if (codec->pcm_info[i].pcm) { - snd_device_free(codec->bus->card, - codec->pcm_info[i].pcm); + snd_device_free(card, codec->pcm_info[i].pcm); clear_bit(codec->pcm_info[i].device, codec->bus->pcm_dev_bits); } @@ -1479,6 +1521,10 @@ void snd_hda_codec_reset(struct hda_codec *codec) codec->preset = NULL; module_put(codec->owner); codec->owner = NULL; + + /* allow device access again */ + hda_unlock_devices(card); + return 0; } #endif /* CONFIG_SND_HDA_RECONFIG */ diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c index c660383ef381..4af484b8240c 100644 --- a/sound/pci/hda/hda_hwdep.c +++ b/sound/pci/hda/hda_hwdep.c @@ -155,7 +155,13 @@ int /*__devinit*/ snd_hda_create_hwdep(struct hda_codec *codec) static int clear_codec(struct hda_codec *codec) { - snd_hda_codec_reset(codec); + int err; + + err = snd_hda_codec_reset(codec); + if (err < 0) { + snd_printk(KERN_ERR "The codec is being used, can't free.\n"); + return err; + } clear_hwdep_elements(codec); return 0; } @@ -165,7 +171,12 @@ static int reconfig_codec(struct hda_codec *codec) int err; snd_printk(KERN_INFO "hda-codec: reconfiguring\n"); - snd_hda_codec_reset(codec); + err = snd_hda_codec_reset(codec); + if (err < 0) { + snd_printk(KERN_ERR + "The codec is being used, can't reconfigure.\n"); + return err; + } err = snd_hda_codec_configure(codec); if (err < 0) return err; diff --git a/sound/pci/hda/hda_local.h b/sound/pci/hda/hda_local.h index 84e2cf644fd7..4bd82a37a4c8 100644 --- a/sound/pci/hda/hda_local.h +++ b/sound/pci/hda/hda_local.h @@ -98,7 +98,7 @@ struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec, const char *name); int snd_hda_add_vmaster(struct hda_codec *codec, char *name, unsigned int *tlv, const char **slaves); -void snd_hda_codec_reset(struct hda_codec *codec); +int snd_hda_codec_reset(struct hda_codec *codec); int snd_hda_codec_configure(struct hda_codec *codec); /* amp value bits */