Writing Device Drivers

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)

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));
}    

uwritec(9F) and uread(9F)

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);
}