Commit a65d629c authored by Takashi Iwai's avatar Takashi Iwai

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: default avatarTakashi Iwai <tiwai@suse.de>
parent 209b1403
...@@ -1445,9 +1445,52 @@ void snd_hda_ctls_clear(struct hda_codec *codec) ...@@ -1445,9 +1445,52 @@ void snd_hda_ctls_clear(struct hda_codec *codec)
snd_array_free(&codec->mixers); 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 #ifdef CONFIG_SND_HDA_POWER_SAVE
cancel_delayed_work(&codec->power_work); cancel_delayed_work(&codec->power_work);
...@@ -1457,8 +1500,7 @@ void snd_hda_codec_reset(struct hda_codec *codec) ...@@ -1457,8 +1500,7 @@ void snd_hda_codec_reset(struct hda_codec *codec)
/* relase PCMs */ /* relase PCMs */
for (i = 0; i < codec->num_pcms; i++) { for (i = 0; i < codec->num_pcms; i++) {
if (codec->pcm_info[i].pcm) { if (codec->pcm_info[i].pcm) {
snd_device_free(codec->bus->card, snd_device_free(card, codec->pcm_info[i].pcm);
codec->pcm_info[i].pcm);
clear_bit(codec->pcm_info[i].device, clear_bit(codec->pcm_info[i].device,
codec->bus->pcm_dev_bits); codec->bus->pcm_dev_bits);
} }
...@@ -1479,6 +1521,10 @@ void snd_hda_codec_reset(struct hda_codec *codec) ...@@ -1479,6 +1521,10 @@ void snd_hda_codec_reset(struct hda_codec *codec)
codec->preset = NULL; codec->preset = NULL;
module_put(codec->owner); module_put(codec->owner);
codec->owner = NULL; codec->owner = NULL;
/* allow device access again */
hda_unlock_devices(card);
return 0;
} }
#endif /* CONFIG_SND_HDA_RECONFIG */ #endif /* CONFIG_SND_HDA_RECONFIG */
......
...@@ -155,7 +155,13 @@ int /*__devinit*/ snd_hda_create_hwdep(struct hda_codec *codec) ...@@ -155,7 +155,13 @@ int /*__devinit*/ snd_hda_create_hwdep(struct hda_codec *codec)
static int clear_codec(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); clear_hwdep_elements(codec);
return 0; return 0;
} }
...@@ -165,7 +171,12 @@ static int reconfig_codec(struct hda_codec *codec) ...@@ -165,7 +171,12 @@ static int reconfig_codec(struct hda_codec *codec)
int err; int err;
snd_printk(KERN_INFO "hda-codec: reconfiguring\n"); 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); err = snd_hda_codec_configure(codec);
if (err < 0) if (err < 0)
return err; return err;
......
...@@ -98,7 +98,7 @@ struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec, ...@@ -98,7 +98,7 @@ struct snd_kcontrol *snd_hda_find_mixer_ctl(struct hda_codec *codec,
const char *name); const char *name);
int snd_hda_add_vmaster(struct hda_codec *codec, char *name, int snd_hda_add_vmaster(struct hda_codec *codec, char *name,
unsigned int *tlv, const char **slaves); 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); int snd_hda_codec_configure(struct hda_codec *codec);
/* amp value bits */ /* amp value bits */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment