aboutsummaryrefslogtreecommitdiff
path: root/sound/pci/hda
diff options
context:
space:
mode:
authorTakashi Iwai <tiwai@suse.de>2009-02-23 16:57:04 +0100
committerTakashi Iwai <tiwai@suse.de>2009-02-23 16:57:04 +0100
commita65d629ceb4cff5e7d5edadfd6bf1f64c370a517 (patch)
treeafe9fc6ca83c7e2261ec3032eca34739376514cf /sound/pci/hda
parent209b14033652f0509912da97fb4a5c8001e64ec0 (diff)
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 <tiwai@suse.de>
Diffstat (limited to 'sound/pci/hda')
-rw-r--r--sound/pci/hda/hda_codec.c54
-rw-r--r--sound/pci/hda/hda_hwdep.c15
-rw-r--r--sound/pci/hda/hda_local.h2
3 files changed, 64 insertions, 7 deletions
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index a13480fa8e7..5dceee8a113 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 c660383ef38..4af484b8240 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 84e2cf644fd..4bd82a37a4c 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 */