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>
This commit is contained in:
Родитель
209b140336
Коммит
a65d629ceb
|
@ -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 */
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
|
|
Загрузка…
Ссылка в новой задаче