Next | Prev | Up | Top | Contents | Index

Using DMA Maps

On IRIS 4D/100, 4D/200, 4D/300, 4D/400, Crimson, CHALLENGE/Onyx, and POWER CHALLENGE/POWER Onyx series systems, a number of registers perform mapping from physical pages to other physical pages. Because the addresses that are mapped are really no longer "physical" addresses, they are now referred to as "bus virtual" addresses. Your driver should allocate these system mapping registers when it opens the device, remap these registers for every transfer, and then free them when it closes the device.

Internally, all the mapping routines deal with a DMA map structure. The values stored in members of the structure are subject to change from release to release. Therefore, when your driver manipulates the DMA maps, it must use the following routines only. Your driver must not try to access the structure members directly.

dma_mapalloc

Allocate a DMA Map

dma_map

Map a Virtual Address Space

dma_mapaddr

Return the Bus Virtual Address

Note: When using DMA maps, be sure that the source or destination address begins on a 32-bit word boundary.

Caution: Once you free a DMA map, it is gone and you can no longer use it. Free it only after the DMA operation has been successfully aborted.


Example Using DMA Maps

Suppose the mythical VME device is an A24 device for use with an
IRIS-4D/100 series workstation. The driver begins the transfer by giving the device the starting address, byte count, and transfer direction. Some driver excerpts could look like the following:

#define MAXTRANSFER  4      /* maximum transfer size in pages */
                            /* pointer to device registers */
volatile struct vdk_device*vdk_device;
dmamap_t    vdk_map;         /* pntr to DMA map */
struct buf *vdk_curbp;      /* current buffer */
caddr_t     vdk_curaddr;     /* current address to transfer */
int         vdk_curcount;
static      int vdk_vmeadap   /* computed during edtinit */

vdkopen(dev, flag, otyp, crp)
dev_t   *dev;
int     flag, otyp;
credit  *crp;
{
...
    vdk_map =
        dma_mapalloc(DMA_A24VME, vdk_vmeadap,
            MAXTRANSFER, 0);
...
}

vdkclose(dev, flag, otyp, crp)
dev_t   dev;
int     flag, otyp;
{
...
        dma_mapfree(vdk_map);
...
}

vdkstrategy(bp)
struct buf *bp
{
...

/* Save structure pointer for the interrupt  routine, vdkintr */
    vdk_curbp = bp;

    /* Set up the mapping registers */
    bp->b_resid = bp->b_bcount;
    vdk_curaddr = bp->b_dmaaddr;
    vdk_curcount = dma_map
        (vdk_map, vdk_curaddr, bp->b_resid);

/* Tell the device starting bus virtual address and count */
    vdk_device->startaddr =
            dma_mapaddr(vdk_map, vdk_curaddr);
    vdk_device->count = count;
    if (bp->b_flags & B_READ) == 0)
        vdk_device->direction = VDK_WRITE;
    else
        vdk_device->direction = VDK_READ;
        vdk_device->command = VDK_GO;
        /* Set up for next transfer */
        vdk_curaddr += count;
        ...
}

vdkintr(unit)
int unit;
{
    int count;
    register struct buf *bp = vdk_curbp;
    ...

    if(error) {
        bp->b_flags |= B_ERROR;
        iodone(bp);
        return( );
    }

    /*On successful transfer of last chunk, continue if necessary.*/
    bp->resid -= vdk_curcount;
    if (bp->b_resid > 0) {
        count = dma_map(vdk_map, vdk_curaddr, bp->b_resid);
        vdk_device->startaddr = dma_mapaddr(vdk_map,vdk_curaddr);
        vdk_device->count = count;
        if (bp->b_flags & B_READ) == 0)
            vdk_device->direction = VDK_WRITE;
        else
            vdk_device->direction = VDK_READ;
        vdk_device->command = VDK_GO;
        vdk_curaddr += count;
    } else {
        biodone(bp);
    }
    ...
}

Note: As with other examples, error checking is omitted, but should not be omitted in real code.


Next | Prev | Up | Top | Contents | Index