X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=sound%2Fpci%2Fes1938.c;h=4cd9a1faaecc186732b1ad911ebfd74e3c59e77e;hb=24c0996a6b73e2554104961afcc8659534503e0d;hp=fc686db59ce8d97fed580769b275dfba98a1f095;hpb=a5ce88909d3007caa7b65996a8f6784350beb2a6;p=safe%2Fjmp%2Flinux-2.6 diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c index fc686db..4cd9a1f 100644 --- a/sound/pci/es1938.c +++ b/sound/pci/es1938.c @@ -1,7 +1,7 @@ /* * Driver for ESS Solo-1 (ES1938, ES1946, ES1969) soundcard * Copyright (c) by Jaromir Koutek , - * Jaroslav Kysela , + * Jaroslav Kysela , * Thomas Sailer , * Abramo Bagnara , * Markus Gruber @@ -47,7 +47,6 @@ */ -#include #include #include #include @@ -227,6 +226,7 @@ struct es1938 { unsigned int dma2_start; unsigned int dma1_shift; unsigned int dma2_shift; + unsigned int last_capture_dmaaddr; unsigned int active; spinlock_t reg_lock; @@ -529,6 +529,7 @@ static void snd_es1938_capture_setdma(struct es1938 *chip) outb(1, SLDM_REG(chip, DMAMASK)); outb(0x14, SLDM_REG(chip, DMAMODE)); outl(chip->dma1_start, SLDM_REG(chip, DMAADDR)); + chip->last_capture_dmaaddr = chip->dma1_start; outw(chip->dma1_size - 1, SLDM_REG(chip, DMACOUNT)); /* 3. Unmask DMA */ outb(0, SLDM_REG(chip, DMAMASK)); @@ -770,19 +771,40 @@ static int snd_es1938_playback_prepare(struct snd_pcm_substream *substream) return -EINVAL; } +/* during the incrementing of dma counters the DMA register reads sometimes + returns garbage. To ensure a valid hw pointer, the following checks which + should be very unlikely to fail are used: + - is the current DMA address in the valid DMA range ? + - is the sum of DMA address and DMA counter pointing to the last DMA byte ? + One can argue this could differ by one byte depending on which register is + updated first, so the implementation below allows for that. +*/ static snd_pcm_uframes_t snd_es1938_capture_pointer(struct snd_pcm_substream *substream) { struct es1938 *chip = snd_pcm_substream_chip(substream); size_t ptr; +#if 0 size_t old, new; -#if 1 /* This stuff is *needed*, don't ask why - AB */ old = inw(SLDM_REG(chip, DMACOUNT)); while ((new = inw(SLDM_REG(chip, DMACOUNT))) != old) old = new; ptr = chip->dma1_size - 1 - new; #else - ptr = inl(SLDM_REG(chip, DMAADDR)) - chip->dma1_start; + size_t count; + unsigned int diff; + + ptr = inl(SLDM_REG(chip, DMAADDR)); + count = inw(SLDM_REG(chip, DMACOUNT)); + diff = chip->dma1_start + chip->dma1_size - ptr - count; + + if (diff > 3 || ptr < chip->dma1_start + || ptr >= chip->dma1_start+chip->dma1_size) + ptr = chip->last_capture_dmaaddr; /* bad, use last saved */ + else + chip->last_capture_dmaaddr = ptr; /* good, remember it */ + + ptr -= chip->dma1_start; #endif return ptr >> chip->dma1_shift; } @@ -838,7 +860,8 @@ static int snd_es1938_capture_copy(struct snd_pcm_substream *substream, struct es1938 *chip = snd_pcm_substream_chip(substream); pos <<= chip->dma1_shift; count <<= chip->dma1_shift; - snd_assert(pos + count <= chip->dma1_size, return -EINVAL); + if (snd_BUG_ON(pos + count > chip->dma1_size)) + return -EINVAL; if (pos + count < chip->dma1_size) { if (copy_to_user(dst, runtime->dma_area + pos + 1, count)) return -EFAULT; @@ -1466,7 +1489,6 @@ static int es1938_suspend(struct pci_dev *pci, pm_message_t state) outb(0x00, SLIO_REG(chip, IRQCONTROL)); /* disable irqs */ if (chip->irq >= 0) { - synchronize_irq(chip->irq); free_irq(chip->irq, chip); chip->irq = -1; } @@ -1556,10 +1578,8 @@ static int snd_es1938_free(struct es1938 *chip) snd_es1938_free_gameport(chip); - if (chip->irq >= 0) { - synchronize_irq(chip->irq); + if (chip->irq >= 0) free_irq(chip->irq, chip); - } pci_release_regions(chip->pci); pci_disable_device(chip->pci); kfree(chip);