ALSA: intel8x0: Don't update period unless prepared
commit c1f0616124
upstream.
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: Sergey Senozhatsky <senozhatsky@chromium.org>
Link: https://lore.kernel.org/r/s5h5yzi7uh0.wl-tiwai@suse.de
Signed-off-by: Takashi Iwai <tiwai@suse.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
pull/356/head^2
parent
e227c60aa9
commit
1e94ffd074
|
@ -354,6 +354,7 @@ struct ichdev {
|
|||
unsigned int ali_slot; /* ALI DMA slot */
|
||||
struct ac97_pcm *pcm;
|
||||
int pcm_open_flag;
|
||||
unsigned int prepared:1;
|
||||
unsigned int suspended: 1;
|
||||
};
|
||||
|
||||
|
@ -714,6 +715,9 @@ static inline void snd_intel8x0_update(struct intel8x0 *chip, struct ichdev *ich
|
|||
int status, civ, i, step;
|
||||
int ack = 0;
|
||||
|
||||
if (!ichdev->prepared || ichdev->suspended)
|
||||
return;
|
||||
|
||||
spin_lock_irqsave(&chip->reg_lock, flags);
|
||||
status = igetbyte(chip, port + ichdev->roff_sr);
|
||||
civ = igetbyte(chip, port + ICH_REG_OFF_CIV);
|
||||
|
@ -904,6 +908,7 @@ static int snd_intel8x0_hw_params(struct snd_pcm_substream *substream,
|
|||
if (ichdev->pcm_open_flag) {
|
||||
snd_ac97_pcm_close(ichdev->pcm);
|
||||
ichdev->pcm_open_flag = 0;
|
||||
ichdev->prepared = 0;
|
||||
}
|
||||
err = snd_ac97_pcm_open(ichdev->pcm, params_rate(hw_params),
|
||||
params_channels(hw_params),
|
||||
|
@ -925,6 +930,7 @@ static int snd_intel8x0_hw_free(struct snd_pcm_substream *substream)
|
|||
if (ichdev->pcm_open_flag) {
|
||||
snd_ac97_pcm_close(ichdev->pcm);
|
||||
ichdev->pcm_open_flag = 0;
|
||||
ichdev->prepared = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -999,6 +1005,7 @@ static int snd_intel8x0_pcm_prepare(struct snd_pcm_substream *substream)
|
|||
ichdev->pos_shift = (runtime->sample_bits > 16) ? 2 : 1;
|
||||
}
|
||||
snd_intel8x0_setup_periods(chip, ichdev);
|
||||
ichdev->prepared = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue