Next | Prev | Up | Top | Contents | Index

VME Devices with Scatter/Gather Capability

Chapter 2, "Writing a Device Driver," tells you to use the physiock() kernel routine to fault in and lock the physical pages corresponding to the user's buffer. physiock() also remaps these physical pages to a kernel virtual address that remains constant even when the user's virtual addresses are no longer mapped.

Internally, physiock() allocates a structure of type buf if you pass a NULL pointer (physiock() uses this structure to embody the transfer information). physiock() then calls your drvstrategy() routine and passes it a pointer to the buf type structure that it has allocated and primed. Your drvstrategy() routine must then loop through each page, starting at the kernel virtual address, and load each device scatter/gather register in turn with the corresponding physical address. Use the kvtophys() routine to convert a kernel virtual address to a physical address.

For example, suppose the mythical device is now an A32 VME device that supports scatter/gather. The scatter/gather registers for the device are simply a table of integers that store the physical pages corresponding to the current transfer. To start the transfer, the driver gives the device the beginning byte offset, byte count, and transfer direction. The code is:

#include <sys/sysmacros.h>
/* pointer to device registers */
volatile struct vdk_device *vdk_device;
vdkstrategy(bp)
struct buf *bp;
{
   int npages;
   register volatile int *sgregisters;
   register int i, v_addr;

   /* Get address of the scatter/gather registers */
    *sgregisters = vdk_device->sgregisters;

   /* Get the kernel virtual address of the data */
    v_addr = bp->b_un.b_addr;

   /* Compute number of pages received.
    * The dma_len field provides the number of pages to
    * map. Note that this may be larger than the actual
    * number of bytes involved in the transfer. This is
    * because the transfer may cross page boundaries,
    * requiring an extra page to be mapped.*/
    npages = numpages (v_addr, bp->b_bcount);

   /* Translate the virtual address of each page to a
    * physical page number and load it into the next
    * scatter/gather register.  The btoct macro
    * converts the byte value to a page value after
    * rounding down the byte value to a full page.
    */
    for (i = 0; i < npages; i++) {
      *sgregisters++ = btoct(kvtophys(v_addr));

   /*
   /* Get the next virtual address to translate.
    * (NBPC is a symbolic constant for the page
    * size in bytes.)
    */

   v_addr += NBPC;
   }

   /*
    * Provide the beginning byte offset and count to the
    * device.
    */

   vdk_device->offset = (unsigned int)bp->b_dmaaddr & (NBPC-1);
   vdk_device->count = bp->b_bcount;
   if ((bp->b_flags & B_READ) == 0)
      vdk_device->direction = VDK_WRITE;
   else
      vdk_device->direction = VDK_READ;
}

Next | Prev | Up | Top | Contents | Index