Commit c1f06161 authored by Takashi Iwai's avatar Takashi Iwai

ALSA: intel8x0: Don't update period unless prepared

The interrupt handler of intel8x0 calls snd_intel8x0_update() whenever
the hardware sets the corresponding status bit for each stream.  This
works fine for most cases as long as the hardware behaves properly.
But when the hardware gives a wrong bit set, this leads to a zero-
division Oops, and reportedly, this seems what happened on a VM.

For fixing the crash, this patch adds a internal flag indicating that
the stream is ready to be updated, and check it (as well as the flag
being in suspended) to ignore such spurious update.

Cc: <stable@vger.kernel.org>
Reported-and-tested-by: default avatarSergey Senozhatsky <senozhatsky@chromium.org>
Link: https://lore.kernel.org/r/s5h5yzi7uh0.wl-tiwai@suse.deSigned-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent 1d5cfca2
...@@ -331,6 +331,7 @@ struct ichdev { ...@@ -331,6 +331,7 @@ struct ichdev {
unsigned int ali_slot; /* ALI DMA slot */ unsigned int ali_slot; /* ALI DMA slot */
struct ac97_pcm *pcm; struct ac97_pcm *pcm;
int pcm_open_flag; int pcm_open_flag;
unsigned int prepared:1;
unsigned int suspended: 1; unsigned int suspended: 1;
}; };
...@@ -691,6 +692,9 @@ static inline void snd_intel8x0_update(struct intel8x0 *chip, struct ichdev *ich ...@@ -691,6 +692,9 @@ static inline void snd_intel8x0_update(struct intel8x0 *chip, struct ichdev *ich
int status, civ, i, step; int status, civ, i, step;
int ack = 0; int ack = 0;
if (!ichdev->prepared || ichdev->suspended)
return;
spin_lock_irqsave(&chip->reg_lock, flags); spin_lock_irqsave(&chip->reg_lock, flags);
status = igetbyte(chip, port + ichdev->roff_sr); status = igetbyte(chip, port + ichdev->roff_sr);
civ = igetbyte(chip, port + ICH_REG_OFF_CIV); civ = igetbyte(chip, port + ICH_REG_OFF_CIV);
...@@ -881,6 +885,7 @@ static int snd_intel8x0_hw_params(struct snd_pcm_substream *substream, ...@@ -881,6 +885,7 @@ static int snd_intel8x0_hw_params(struct snd_pcm_substream *substream,
if (ichdev->pcm_open_flag) { if (ichdev->pcm_open_flag) {
snd_ac97_pcm_close(ichdev->pcm); snd_ac97_pcm_close(ichdev->pcm);
ichdev->pcm_open_flag = 0; ichdev->pcm_open_flag = 0;
ichdev->prepared = 0;
} }
err = snd_ac97_pcm_open(ichdev->pcm, params_rate(hw_params), err = snd_ac97_pcm_open(ichdev->pcm, params_rate(hw_params),
params_channels(hw_params), params_channels(hw_params),
...@@ -902,6 +907,7 @@ static int snd_intel8x0_hw_free(struct snd_pcm_substream *substream) ...@@ -902,6 +907,7 @@ static int snd_intel8x0_hw_free(struct snd_pcm_substream *substream)
if (ichdev->pcm_open_flag) { if (ichdev->pcm_open_flag) {
snd_ac97_pcm_close(ichdev->pcm); snd_ac97_pcm_close(ichdev->pcm);
ichdev->pcm_open_flag = 0; ichdev->pcm_open_flag = 0;
ichdev->prepared = 0;
} }
return 0; return 0;
} }
...@@ -976,6 +982,7 @@ static int snd_intel8x0_pcm_prepare(struct snd_pcm_substream *substream) ...@@ -976,6 +982,7 @@ static int snd_intel8x0_pcm_prepare(struct snd_pcm_substream *substream)
ichdev->pos_shift = (runtime->sample_bits > 16) ? 2 : 1; ichdev->pos_shift = (runtime->sample_bits > 16) ? 2 : 1;
} }
snd_intel8x0_setup_periods(chip, ichdev); snd_intel8x0_setup_periods(chip, ichdev);
ichdev->prepared = 1;
return 0; return 0;
} }
......
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