static void
dmastatus(DMA *dp, int chan, char c)
{
int a, l, s;
ilock(dp);
outb(dp->cbp, 0);
a = inb(dp->addr[chan]);
a |= inb(dp->addr[chan])<<8;
a |= inb(dp->page[chan])<<16;
a |= inb(0x400|dp->page[chan])<<24;
outb(dp->cbp, 0);
l = inb(dp->count[chan]);
l |= inb(dp->count[chan])<<8;
s = inb(dp->cmd);
iunlock(dp);
print("%c: addr %uX len %uX stat %uX\n", c, a, l, s);
}
/*
* DMA must be in the first 16MB. This gets called early by the
* initialisation routines of any devices which require DMA to ensure
* the allocated bounce buffers are below the 16MB limit.
*/
int
dmainit(int chan, int maxtransfer)
{
DMA *dp;
DMAxfer *xp;
static int once;
xp->bva = xspanalloc(maxtransfer, BY2PG, 64*1024);
if(xp->bva == nil)
return 1;
xp->bpa = PADDR(xp->bva);
if(xp->bpa >= 16*MB){
/*
* This will panic with the current
* implementation of xspanalloc().
xfree(xp->bva);
*/
xp->bva = nil;
return 1;
}
xp->blen = maxtransfer;
xp->len = 0;
xp->isread = 0;
return 0;
}
void
xdmastatus(int chan)
{
DMA *dp;
dp = &dma[(chan>>2)&1];
chan = chan & 3;
dmastatus(dp, chan, 'X');
}
/*
* setup a dma transfer. if the destination is not in kernel
* memory, allocate a page for the transfer.
*
* we assume BIOS has set up the command register before we
* are booted.
*
* return the updated transfer length (we can't transfer across 64k
* boundaries)
*/
long
dmasetup(int chan, void *va, long len, int isread)
{
DMA *dp;
ulong pa;
uchar mode;
DMAxfer *xp;
/*
* if this isn't kernel memory or crossing 64k boundary or above 16 meg
* use the bounce buffer.
*/
pa = PADDR(va);
if((((ulong)va)&0xF0000000) != KZERO
|| (pa&0xFFFF0000) != ((pa+len)&0xFFFF0000)
|| pa >= 16*MB) {
if(xp->bva == nil)
return -1;
if(len > xp->blen)
len = xp->blen;
if(!isread)
memmove(xp->bva, va, len);
xp->va = va;
xp->len = len;
xp->isread = isread;
pa = xp->bpa;
}
else
xp->len = 0;
#endif /* notdef */
#ifdef tryISA
pa = ISAWADDR(va);
#endif /* tryISA */
#ifdef tryPCI
pa = PCIWADDR(va);
if((((ulong)va)&0xF0000000) != KZERO){
if(xp->bva == nil)
return -1;
if(len > xp->blen)
len = xp->blen;
if(!isread)
memmove(xp->bva, va, len);
xp->va = va;
xp->len = len;
xp->isread = isread;
pa = PCIWADDR(xp->bva);
}
else
xp->len = 0;
#endif /* tryPCI */
/*
* this setup must be atomic
*/
mode = (isread ? 0x44 : 0x48) | chan;
ilock(dp);
outb(dp->cbp, 0); /* set count & address to their first byte */
outb(dp->mode, mode); /* single mode dma (give CPU a chance at mem) */
outb(dp->addr[chan], pa>>dp->shift); /* set address */
outb(dp->addr[chan], pa>>(8+dp->shift));
outb(dp->page[chan], pa>>16);
#ifdef tryPCI
outb(0x400|dp->page[chan], pa>>24);
#endif /* tryPCI */
outb(dp->cbp, 0); /* set count & address to their first byte */
outb(dp->count[chan], (len>>dp->shift)-1); /* set count */
outb(dp->count[chan], ((len>>dp->shift)-1)>>8);
outb(dp->sbm, chan); /* enable the channel */
iunlock(dp);
//dmastatus(dp, chan, 'S');
return len;
}
int
dmadone(int chan)
{
DMA *dp;
dp = &dma[(chan>>2)&1];
chan = chan & 3;
return inb(dp->cmd) & (1<<chan);
}
/*
* this must be called after a dma has been completed.
*
* if a page has been allocated for the dma,
* copy the data into the actual destination
* and free the page.
*/
void
dmaend(int chan)
{
DMA *dp;
DMAxfer *xp;