Writing Device Drivers

Data Transfer Methods

Data can be transferred using either programmed I/O or DMA. These data transfer methods can be used by either synchronous or asynchronous entry points, depending on the capabilities of the device.

Programmed I/O Transfers

Programmed I/O devices rely on the CPU to perform the data transfer. Programmed I/O data transfers are identical to other device register read and write operations. Various data access routines are used to read or store values to device memory.

uiomove(9F) can be used to transfer data to some programmed I/O devices. uiomove(9F) transfers data between the user space (defined by the uio(9S) structure) and the kernel. uiomove(9F) can handle page faults, so the memory to which data is transferred need not be locked down. It also updates the uio_resid field in the uio(9S) structure. Example 10–3 shows one way to write a ramdisk read(9E) routine. It uses synchronous I/O and relies on the presence of the following fields in the ramdisk state structure:

caddr_t        ram;            /* base address of ramdisk */
int            ramsize;        /* size of the ramdisk */

Example 10–3 Ramdisk read(9E) Routine Using uiomove(9F)

static int
rd_read(dev_t dev, struct uio *uiop, cred_t *credp)
{
     rd_devstate_t     *rsp;

     rsp = ddi_get_soft_state(rd_statep, getminor(dev));
     if (rsp == NULL)
           return (ENXIO);
     if (uiop->uio_offset >= rsp->ramsize)
           return (EINVAL);
     /*
      * uiomove takes the offset into the kernel buffer,
      * the data transfer count (minimum of the requested and
      * the remaining data), the UIO_READ flag, and a pointer
      * to the uio structure.
      */
     return (uiomove(rsp->ram + uiop->uio_offset,
             min(uiop->uio_resid, rsp->ramsize - uiop->uio_offset),
             UIO_READ, uiop));
}    

Another example of programmed I/O might be a driver writing data one byte at a time directly to the device's memory. Each byte is retrieved from the uio(9S) structure using uwritec(9F), then sent to the device. read(9E) can use ureadc(9F) to transfer a byte from the device to the area described by the uio(9S) structure.


Example 10–4 Programmed I/O write(9E) Routine Using uwritec(9F)

static int
xxwrite(dev_t dev, struct uio *uiop, cred_t *credp)
{
        int    value;
        struct xxstate     *xsp;

        xsp = ddi_get_soft_state(statep, getminor(dev));
        if (xsp == NULL)
                return (ENXIO);
        if the device implements a power manageable component, do this:
        pm_busy_component(xsp->dip, 0);
        if (xsp->pm_suspended)
                ddi_dev_is_needed(xsp->dip, normal power);

        while (uiop->uio_resid > 0) {
                /*
                 * do the programmed I/O access
                 */
                value = uwritec(uiop);
                if (value == -1)
                       return (EFAULT);
                ddi_put8(xsp->data_access_handle, &xsp->regp->data,
                    (uint8_t)value);
                ddi_put8(xsp->data_access_handle, &xsp->regp->csr,
                    START_TRANSFER);
                /*
                 * this device requires a ten microsecond delay
                 * between writes
                 */
                drv_usecwait(10);
        }
        pm_idle_component(xsp->dip, 0);
        return (0);
}    

DMA Transfers (Synchronous)

Most character drivers use physio(9F) to do most of the setup work for DMA transfers in read(9E) and write(9E), as is shown in Example 10–5.

int physio(int (*strat)(struct buf *), struct buf *bp,
     dev_t dev, int rw, void (*mincnt)(struct buf *),
     struct uio *uio);

physio(9F) requires the driver to provide the address of a strategy(9E) routine. physio(9F) ensures that memory space is locked down (cannot be paged out) for the duration of the data transfer. This is necessary for DMA transfers because they cannot handle page faults. physio(9F) also provides an automated way of breaking a larger transfer into a series of smaller, more manageable ones. See minphys() Entry Point for more information.


Example 10–5 read(9E) and write(9E) Routines Using physio(9F)

static int
xxread(dev_t dev, struct uio *uiop, cred_t *credp)
{
     struct xxstate *xsp;
     int ret;

     xsp = ddi_get_soft_state(statep, getminor(dev));
     if (xsp == NULL)
            return (ENXIO);
     ret = physio(xxstrategy, NULL, dev, B_READ, xxminphys, uiop);
     return (ret);
}    

static int
xxwrite(dev_t dev, struct uio *uiop, cred_t *credp)
{         
     struct xxstate *xsp;
     int ret;

     xsp = ddi_get_soft_state(statep, getminor(dev));
     if (xsp == NULL)
            return (ENXIO);
     ret = physio(xxstrategy, NULL, dev, B_WRITE, xxminphys, uiop);
     return (ret);
}    

In the call to physio(9F), xxstrategy() is a pointer to the driver strategy routine. Passing NULL as the buf(9S) structure pointer tells physio(9F) to allocate a buf(9S) structure. If the driver must provide physio(9F) with a buf(9S) structure, getrbuf(9F) should be used to allocate one. physio(9F) returns zero if the transfer completes successfully, or an error number on failure. After calling strategy(9E), physio(9F) calls biowait(9F) to block until the transfer is completed or fails. The return value of physio(9F) is determined by the error field in the buf(9S) structure set by bioerror(9F).

DMA Transfers (Asynchronous)

Character drivers supporting aread(9E) and awrite(9E) use aphysio(9F) instead of physio(9F).

int aphysio(int (*strat)(struct buf *), int (*cancel)(struct buf *),
     dev_t dev, int rw, void (*mincnt)(struct buf *),
     struct aio_req *aio_reqp);

Note –

The address of anocancel(9F) is the only value that can currently be passed as the second argument to aphysio(9F).


aphysio(9F) requires the driver to pass the address of a strategy(9E) routine. aphysio(9F) ensures that memory space is locked down (cannot be paged out) for the duration of the data transfer. This is necessary for DMA transfers because they cannot handle page faults. aphysio(9F) also provides an automated way of breaking a larger transfer into a series of smaller, more manageable ones. See minphys() Entry Point for more information. Example 10–5 and Example 10–6 demonstrate that the aread(9E) and awrite(9E) entry points differ only slightly from the read(9E) and write(9E) entry points; the difference lies mainly in their use of aphysio(9F) instead of physio(9F).


Example 10–6 aread(9E) and awrite(9E) Routines Using aphysio(9F)

static int
xxaread(dev_t dev, struct aio_req *aiop, cred_t *cred_p)
{
     struct xxstate *xsp;

     xsp = ddi_get_soft_state(statep, getminor(dev));
     if (xsp == NULL)
             return (ENXIO);
     return (aphysio(xxstrategy, anocancel, dev, B_READ,
         xxminphys, aiop));
}

static int
xxawrite(dev_t dev, struct aio_req *aiop, cred_t *cred_p)
{
     struct xxstate *xsp;

     xsp = ddi_get_soft_state(statep, getminor(dev));
     if (xsp == NULL)
            return (ENXIO);
     return (aphysio(xxstrategy, anocancel, dev, B_WRITE,
         xxminphys,aiop));  
}

In the call to aphysio(9F), xxstrategy() is a pointer to the driver strategy routine. aiop is a pointer to the aio_req(9S) structure and is also passed to aread(9E) and awrite(9E). aio_req(9S) describes where the data is to be stored in user space. aphysio(9F) returns zero if the I/O request is scheduled successfully or an error number on failure. After calling strategy(9E), aphysio(9F) returns without waiting for the I/O to complete or fail.

minphys() Entry Point

xxminphys() is a pointer to a function to be called by physio(9F) or aphysio(9F) to ensure that the size of the requested transfer does not exceed a driver-imposed limit. If the user requests a larger transfer, strategy(9E) will be called repeatedly, requesting no more than the imposed limit at a time. This is important because DMA resources are limited. Drivers for slow devices, such as printers, should be careful not to tie up resources for a long time.

Usually, a driver passes the address of the kernel function minphys(9F), but the driver can define its own xxminphys() routine instead. The job of xxminphys() is to keep the b_bcount field of the buf(9S) structure below a driver limit. There might be additional system limits that the driver should not circumvent, so the driver xxminphys() routine should call the system minphys(9F) routine after setting the b_bcount field and before returning.


Example 10–7 minphys(9F) Routine

#define XXMINVAL (512 << 10)    /* 512 KB */
static void
xxminphys(struct buf *bp)
{
       if (bp->b_bcount > XXMINVAL)
            bp->b_bcount = XXMINVAL
      minphys(bp);
}

strategy() Entry Point

The strategy(9E) routine originated in block drivers and is so called because it can implement a strategy for efficient queuing of I/O requests to a block device. A driver for a character-oriented device can also use a strategy(9E) routine. In the character I/O model presented here, strategy(9E) does not maintain a queue of requests, but rather services one request at a time.

In Example 10–8, the strategy(9E) routine for a character-oriented DMA device allocates DMA resources for synchronous data transfer and starts the command by programming the device register (see Chapter 8, Direct Memory Access (DMA) for a detailed description).


Note –

strategy(9E) does not receive a device number (dev_t) as a parameter; instead, this is retrieved from the b_edev field of the buf(9S) structure passed to strategy(9E).



Example 10–8 strategy(9E) Routine

static int
xxstrategy(struct buf *bp)
{
     minor_t                    instance;
     struct xxstate             *xsp;
     ddi_dma_cookie_t           cookie;

     instance = getminor(bp->b_edev);
     xsp = ddi_get_soft_state(statep, instance);
     ...
     if the device has power manageable components 
     mark the device busy with pm_busy_components(9F),
     and then ensure that the device
     is powered up by calling ddi_dev_is_needed(9F).

     set up DMA resources with ddi_dma_alloc_handle(9F) and
     ddi_dma_buf_bind_handle(9F).
     xsp->bp = bp; /* remember bp */
     program DMA engine and start command
     return (0);
}


Note –

Although strategy(9E) is declared to return an int, it must always return zero.


On completion of the DMA transfer, the device generates an interrupt, causing the interrupt routine to be called. In Example 10–9, xxintr() receives a pointer to the state structure for the device that might have generated the interrupt.


Example 10–9 Interrupt Routine

static u_int
xxintr(caddr_t arg)
{
     struct xxstate *xsp = (struct xxstate *)arg;
     if (device did not interrupt) {
            return (DDI_INTR_UNCLAIMED);
     }
     if (error) {
            error handling
     }
     release any resources used in the transfer, such as DMA resources
     ddi_dma_unbind_handle(9F) and ddi_dma_free_handle(9F)
     /* notify threads that the transfer is complete */
     biodone(xsp->bp);
     return (DDI_INTR_CLAIMED);
}

The driver indicates an error by calling bioerror(9F). The driver must call biodone(9F) when the transfer is complete or after indicating an error with bioerror(9F).