Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a65d629c authored by Takashi Iwai's avatar Takashi Iwai
Browse files

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
Loading
Loading
Loading
Loading
+50 −4
Original line number Diff line number Diff line
@@ -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 */

+13 −2
Original line number Diff line number Diff line
@@ -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;
+1 −1
Original line number Diff line number Diff line
@@ -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 */