What a co-incidence. I just hit this problem a few days ago.
The problem here is that:
1. the dma buffer drains to zero.
2. interrupt handler set LVI to CIV.
3. app write more than a buffer size of data to dma buffer.
4. LVI is un-changed!
There's a kludgey work-around I used, (never use more than
31 segments of the DMA buffer). (I.e. never use the last
dmabuf->fragsize of the dma buffer). This cures the hang
but it isn't an optimal solutions.
--- i810_audio.c.old Tue Jun 19 11:22:56 2001
+++ i810_audio.c Tue Jun 19 11:24:02 2001
@@ -1194,6 +1194,10 @@
cnt = dmabuf->dmasize - swptr;
if(cnt > (dmabuf->dmasize - dmabuf->count))
cnt = dmabuf->dmasize - dmabuf->count;
+
+ if (cnt >= dmabuf->fragsize && (dmabuf->count + cnt) >= dmabuf->dmasize)
+ cnt -= dmabuf->fragsize;
+
spin_unlock_irqrestore(&state->card->lock, flags);
if (cnt > count)
A better fix _may_ be to set CIV to LVI instead of the other way
around. (This assumes CIV is writeable). No testing at all;
may not be a fix.
Something like:
diff -u i810_audio.c.old i810_audio.c
--- i810_audio.c.old Tue Jun 19 11:22:56 2001
+++ i810_audio.c Tue Jun 19 11:26:14 2001
@@ -807,11 +807,11 @@
* means no data on read, handle appropriately
*/
if(!rec && dmabuf->count == 0) {
- outb(inb(port+OFF_CIV),port+OFF_LVI);
+ outb(inb(port+OFF_LVI),port+OFF_CIV);
return;
}
if(rec && dmabuf->count == dmabuf->dmasize) {
- outb(inb(port+OFF_CIV),port+OFF_LVI);
+ outb(inb(port+OFF_LVI),port+OFF_CIV);
return;
}
/* swptr - 1 is the tail of our transfer */
but with testing and a glance at the docs. :)
Michael.
-
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/