Writing Device Drivers

Canceling DMA Callbacks

DMA callbacks cannot be cancelled. 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 7-3.) 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:

	int			cancel_callbacks;					/* detach(9E) sets this to */
 														/* prevent callbacks from */
 														/* rescheduling themselves */
int			callback_count;					/* number of outstanding
 														/* callbacks */
kmutex_t			callback_mutex;				/* protects callback_count and */
 														/* cancel_callbacks. */
kcondvar_t 	callback_cv;						/* condition is that
 														/* callback_count is zero*/
 														/* detach(9E) waits on it */


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