Writing Device Drivers

strategy(9E)

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).