Writing Device Drivers

Canceling DMA Callbacks

DMA callbacks cannot be canceled. This requires some additional code in the drivers detach(9E) routine, as it must not return DDI_SUCCESS if there are any outstanding callbacks. (See Example 8-6.) When DMA callbacks occur, the detach(9E) routine must wait for the callback to run and must prevent it from rescheduling itself. This can be done using additional fields in the state structure, as shown below.


Example 8-6 Canceling DMA Callbacks

static int
xxdetach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
     ...
     mutex_enter(&xsp->callback_mutex);
     xsp->cancel_callbacks = 1;
     while (xsp->callback_count > 0) {
            cv_wait(&xsp->callback_cv, &xsp->callback_mutex);
     }
     mutex_exit(&xsp->callback_mutex);
     ...
 }

static int
xxstrategy(struct buf *bp)
{
     ...
     mutex_enter(&xsp->callback_mutex);
       xsp->bp = bp;
     error = ddi_dma_buf_bind_handle(xsp->handle, xsp->bp, flags,
                 xxdmacallback, (caddr_t)xsp, &cookie, &ccount);
     if (error == DDI_DMA_NORESOURCES)
           xsp->callback_count++;
     mutex_exit(&xsp->callback_mutex);
     ...
}

static int
xxdmacallback(caddr_t callbackarg)
{
     struct xxstate *xsp = (struct xxstate *)callbackarg;
     ...
     mutex_enter(&xsp->callback_mutex);
     if (xsp->cancel_callbacks) {
            /* do not reschedule, in process of detaching */
            xsp->callback_count--;
            if (xsp->callback_count == 0)
               cv_signal(&xsp->callback_cv);
            mutex_exit(&xsp->callback_mutex);
            return (DDI_DMA_CALLBACK_DONE);        /* don't reschedule it */
     }
     /*
        * Presumably at this point the device is still active
        * and will not be detached until the DMA has completed.
        * A return of 0 means try again later
        */
     error = ddi_dma_buf_bind_handle(xsp->handle, xsp->bp, flags,
                 DDI_DMA_DONTWAIT, NULL, &cookie, &ccount);
     if (error == DDI_DMA_MAPPED) {
            ...
            /* program the DMA engine */
             ...
            xsp->callback_count--;
            mutex_exit(&xsp->callback_mutex);
            return (DDI_DMA_CALLBACK_DONE);
     }
     if (error != DDI_DMA_NORESOURCES) {
            xsp->callback_count--;
            mutex_exit(&xsp->callback_mutex);
            return (DDI_DMA_CALLBACK_DONE);
     }
     mutex_exit(&xsp->callback_mutex);
     return (DDI_DMA_CALLBACK_RUNOUT);
}