h22846
s 00007/00007/00783
d D 1.4 94/06/22 14:43:39 hjs 5 4
c cstyle -p clean
e
s 00009/00006/00781
d D 1.3 94/03/10 10:27:18 hjs 4 3
c DDICT-clean
e
s 00015/00007/00772
d D 1.2 94/03/04 10:38:07 hjs 3 1
c make ddict clean (remove sys/dmaga.h; make ioctl cmd w/ou _IOR)
e
s 00000/00000/00000
d R 1.2 94/02/14 14:56:33 Codemgr 2 1
c SunPro Code Manager data about conflicts, renames, etc...
c Name history : 1 0 src/driver_dev/dma/sparc/dma.c
e
s 00779/00000/00000
d D 1.1 94/02/14 14:56:32 hjs 1 0
c date and time created 94/02/14 14:56:32 by hjs
e
u
U
f e 0
t
T
I 3
D 5
#ifndef _DDICT
E 3
I 1
static char sccsid [] = "@(#)dma.c	1.9 copyright 93/07/21 Sun Microsystems, Inc.";
I 3
#endif
E 5
I 5

#pragma	ident	"%Z%%M% %I%	%E% SMI"

E 5
E 3
/*
 * Copyright (c) 1992, Sun Microsystems, Inc.  All Rights Reserved.
 * Sun considers its source code as an unpublished, proprietary
 * trade secret, and it is available only under strict license
 * provisions.  This copyright notice is placed here only to protect
 * Sun in the event the source is deemed a published work.
 * Disassembly, decompilation, or other means of reducing the
 * object code to human readable form is prohibited by the license
 * agreement under which this code is provided to the user or
 * company in possession of this copy.
D 5
 * 
E 5
I 5
 *
E 5
 * RESTRICTED RIGHTS LEGEND: Use, duplication, or disclosure by the
 * Government is subject to restrictions as set forth in
 * subparagraph (c)(1)(ii) of the Rights in Technical Data and
 * Computer Software clause at DFARS 52.227-7013 and in similar
 * clauses in the FAR and NASA FAR Supplement.
D 5
 * 
E 5
I 5
 *
E 5
 * These examples are provided with no warranties of any kind,
 * including without limitation accuracy and usefulness, and Sun
 * expressly disclaims all implied warranties of merchantability,
 * fitness for a particular purpose and non-infringement. In no
 * event shall Sun be liable for any damages, including without
 * limitation, direct, special, indirect, or consequential damages
 * arising out of, or relating to, use of these examples by customer
 * or any third party. Sun is under no obligation to provide support
 * to customer for this software.
 */


/*
 * Sample dma driver for Solaris 2.0/Sun OS 5.0
 */

/*
 * This driver is for a fictional SBus device that is capable of doing DMA.
 *
 * This driver is not intended to be efficient or bulletproof; it is
 * intended to show how to write a simple DDI/DKI-compliant driver.
 *
 */

#include <sys/types.h>
#include <sys/param.h>
#include <sys/errno.h>
#include <sys/file.h>
#include <sys/conf.h>
#include <sys/uio.h>
#include <sys/map.h>
#include <sys/debug.h>
#include <sys/modctl.h>
#include <sys/kmem.h>
#include <sys/cmn_err.h>
D 3
#include <sys/dmaga.h>
E 3
#include <sys/open.h>
#include <sys/stat.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>

#include "dma_io.h"
#include "dma_reg.h"

static	void *state_head;	/* opaque handle top of state structs */

/*
 * These are the entry points into our driver that are called when the
 * driver is loaded, during a system call, or in response to an interrupt.
 */
static	int	dma_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
		    void **result);
static	int	dma_identify(dev_info_t *dip);
static	int	dma_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
static	int	dma_open(dev_t *dev, int openflags, int otyp, cred_t *credp);
static	int	dma_close(dev_t dev, int openflags, int otyp, cred_t *credp);
static	int	dma_read(dev_t dev, struct uio *uiop, cred_t *credp);
static	int	dma_write(dev_t dev, struct uio *uiop, cred_t *credp);
static	int	dma_ioctl(dev_t dev, int cmd, int arg, int flag, cred_t *credp,
		    int *rvalp);
static	u_int	dma_intr();
static	int	dma_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);

/*
 * other driver routines
 */
static	int	dma_strategy(register struct buf *bp);
static	void	dma_minphys(struct buf *bp);

/*
 * When our driver is loaded or unloaded, the system calls our _init or
 * _fini routine with a modlinkage structure.  The modlinkage structure
 * contains:
 *
 *	modlinkage->
 *		modldrv->
 *			dev_ops->
 * 				cb_ops
 *
 * cb_ops contains the normal driver entry points and is roughly equivalent
 * to the cdevsw & bdevsw structures in previous releases.
 *
 * dev_ops contains, in addition to the pointer to cb_ops, the routines
 * that support loading and unloading our driver.
 */
static struct cb_ops	dma_cb_ops = {
	dma_open,
	dma_close,
	nodev,		/* not a block driver, strategy not an entry point */
	nodev,		/* no print routine */
	nodev,		/* no dump routine */
	dma_read,
	dma_write,
	dma_ioctl,
	nodev,		/* no devmap routine */
	nodev,		/* no mmap routine */
	nodev,		/* no segmap routine */
	nochpoll,	/* no chpoll routine */
	ddi_prop_op,
	0,		/* not a STREAMS driver, no cb_str routine */
	D_NEW | D_MP,	/* safe for multi-thread/multi-processor */
};

static struct dev_ops dma_ops = {
	DEVO_REV,		/* DEVO_REV indicated by manual	*/
	0,			/* device reference count	*/
	dma_getinfo,
	dma_identify,
	nulldev,		/* device probe for non-self-id */
	dma_attach,
	dma_detach,
	nodev,			/* device reset routine		*/
	&dma_cb_ops,
	(struct bus_ops *)0,	/* bus operations		*/
};

extern	struct	mod_ops mod_driverops;
static	struct modldrv modldrv = {
	&mod_driverops,
	"SUNW sample DMA driver 93/07/21",
	&dma_ops,
};

static	struct modlinkage modlinkage = {
	MODREV_1,		/* MODREV_1 indicated by manual */
	(void *)&modldrv,
	NULL,			/* termination of list of linkage structures */
};

static ddi_dma_lim_t	dma_lim = {
	DMA_DEFAULT_LOWER,
	DMA_DEFAULT_UPPER,
	DMA_DEFAULT_COUNTER,
	DMA_DEFAULT_BURSTSIZE,
	DMA_DEFAULT_MIN_XFER,
	DMA_DEFAULT_SPEED,
};

/*
 * _init, _info, and _fini support loading and unloading the driver.
 */
int
_init(void)
{
	register int	error;

	if ((error = ddi_soft_state_init(&state_head, sizeof (Dma), 1)) != 0)
		return (error);

	if ((error = mod_install(&modlinkage)) != 0)
		ddi_soft_state_fini(&state_head);

	return (error);
}

int
_info(struct modinfo *modinfop)
{
	return (mod_info(&modlinkage, modinfop));
}

int
_fini(void)
{
	int status;

	if ((status = mod_remove(&modlinkage)) != 0)
		return (status);

	ddi_soft_state_fini(&state_head);

	return (status);
}

/*
 * This is a pretty generic getinfo routine as described in the manual.
 */
/*ARGSUSED*/
static int
dma_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
{
	register int 	error;
	register Dma	*dma_p;

	switch (infocmd) {
	case DDI_INFO_DEVT2DEVINFO:
		dma_p = (Dma *)ddi_get_soft_state(state_head,
			    getminor((dev_t)arg));
		if (dma_p == NULL) {
			*result = NULL;
			error = DDI_FAILURE;
		} else {
			/*
			 * don't need to use a MUTEX even though we are
			 * accessing our instance structure; dma_p->dip
			 * never changes.
			 */
			*result = dma_p->dip;
			error = DDI_SUCCESS;
		}
		break;
	case DDI_INFO_DEVT2INSTANCE:
		*result = (void *)getminor((dev_t)arg);
		error = DDI_SUCCESS;
		break;
	default:
		*result = NULL;
		error = DDI_FAILURE;
	}

	return (error);
}

/*
 * When our driver is loaded, dma_identify() is called with a dev_info_t
 * full of information from the FCode on our board, or from our hardware
 * configuration file, or both.
 *
 * Note to 4.x developers:  In previous releases, the identify routine
 * would be called for every unclaimed device that identified itself
 * at boot time; in 5.0, identify() will only be called for devices whose
 * FCode "name" property matches the name of the driver (e.g. name: "dma",
 * driver source: dma.c, driver module: /usr/kernel/drv/dma).  You can get
 * around this by using the "-i" option for add_drv(1).
 * (E.g. name: "SUNW,dma", driver module: /usr/kernel/drv/dma.)
 */
static int
dma_identify(dev_info_t *dip)
{
	char *dev_name;
	dev_name = ddi_get_name(dip);

	if (strcmp(dev_name, DMA_NAME) == 0) {
		return (DDI_IDENTIFIED);
	} else {
		return (DDI_NOT_IDENTIFIED);
	}
}

/*
 * dma_attach gets called if dma_identify returns DDI_IDENTIFIED
 *
 * This is a very paranoid attatch routine.  We take all the knowledge we
 * have about our board and check it against what has been filled in for
 * us from our FCode.
 *
 * As written, this attach routine will fail if the register information
 * returned by ddi_dev_nregs, etc. does not match what the driver expects.
 * It would be better to write a driver that checks for at least DMA_NREG
 * registers, the first few of which are the expected size.  This way one
 * could build second-generation hardware that would still work with an
 * old driver.
 */
static int
dma_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
	int			instance;
	register Dma		*dma_p;
D 3
	u_int			result;
E 3
I 3
	int			result;
E 3
	int			error;

	if (cmd != DDI_ATTACH)
		goto bailout;

	instance = ddi_get_instance(dip);

	if (ddi_soft_state_zalloc(state_head, instance) != 0)
		goto bailout;

	dma_p = (Dma *)ddi_get_soft_state(state_head, instance);
	dma_p->attach_flags |= SOFT_STATE_ALLOCATED;
	dma_p->dip = dip;

	/*
	 * interrupt number is 0 (we only use one interrupt level)
	 * ddi_idevice_cookie_t is not used
	 * a magic cookie for later use in MUTEXes will be returned in
	 * 	dma_p->iblock_cookie
	 * if we were truly paranoid we would use ddi_dev_nintrs() to check
	 *	things out
	 */
	/*
	 * we should call ddi_intr_highlevel() to see if we are at too high
	 * a level; if so, we can fail or install a quickie interrupt routine
	 * that reschedules a soft interrupt.  the next version of this driver
	 * will demonstrate how to do that.
	 */
	if (ddi_add_intr(dip, 0, &dma_p->iblock_cookie,
D 5
	    (ddi_idevice_cookie_t *) NULL, dma_intr,
	    (caddr_t) dma_p) != DDI_SUCCESS) {
E 5
I 5
	    (ddi_idevice_cookie_t *)NULL, dma_intr,
	    (caddr_t)dma_p) != DDI_SUCCESS) {
E 5
		goto bailout;
	} else {
		dma_p->attach_flags |= INTERRUPT_ADDED;
	}

	/*
	 * initialize MUTEX; we will use this MUTEX later on to lock
	 * our instance structure
	 */
	mutex_init(&dma_p->mutex, "dma mutex",
		MUTEX_DRIVER, (void *)dma_p->iblock_cookie);
	dma_p->attach_flags |= MUTEX_ADDED;

	/*
	 * we have a CSR, and address, and a count register
	 *
	 * check to make sure nreg==3 and the registers are the correct sizes
	 */
D 3
	error = ddi_dev_nreg(dip, &result);
E 3
I 3
	error = ddi_dev_nregs(dip, &result);
E 3
	if (error == DDI_FAILURE || result != DMA_NREG) {
		goto bailout;
	}

	if (ddi_dev_regsize(dip, 0, (off_t *)&result) == DDI_SUCCESS) {
		if (result != sizeof (Dma_csr)) {
			goto bailout;
		};
	} else {
		goto bailout;
	}

	if (ddi_dev_regsize(dip, 1, (off_t *)&result) == DDI_SUCCESS) {
		if (result != sizeof (Dma_addr)) {
			goto bailout;
		};
	} else {
		goto bailout;
	}

	if (ddi_dev_regsize(dip, 2, (off_t *)&result) == DDI_SUCCESS) {
		if (result != sizeof (Dma_count)) {
			goto bailout;
		};
	} else {
		goto bailout;
	}

	/*
	 * the registers look OK, so let's map them in so we can use them
	 */
	if (ddi_map_regs(dip, 0, (caddr_t *)&(dma_p->csr), 0,
	    sizeof (Dma_csr)) != DDI_SUCCESS) {
		goto bailout;
	} else {
		dma_p->attach_flags |= CSR_MAPPED;
	}

	if (ddi_map_regs(dip, 1, (caddr_t *)&(dma_p->addr), 0,
	    sizeof (Dma_addr)) != DDI_SUCCESS) {
		goto bailout;
	} else {
		dma_p->attach_flags |= ADDR_MAPPED;
	}

	if (ddi_map_regs(dip, 2, (caddr_t *)&(dma_p->count), 0,
	    sizeof (Dma_count)) != DDI_SUCCESS) {
		goto bailout;
	} else {
		dma_p->attach_flags |= COUNT_MAPPED;
	}

	/*
	 * ddi_create_minor_node creates an entry in an internal kernel
	 * table; the actual entry in the file system is created by
	 * drvconfig(1) when you run add_drv(1);
	 */
	if (ddi_create_minor_node(dip, ddi_get_name(dip), S_IFCHR, instance,
D 3
	    NULL, NULL) == DDI_FAILURE) {
E 3
I 3
	    NULL, 0) == DDI_FAILURE) {
E 3
		ddi_remove_minor_node(dip, NULL);
		goto bailout;
	} else {
		dma_p->attach_flags |= MINOR_NODE_CREATED;
	}

	/*
	 * reset device (including disabling interrupts)
	 */
	*(dma_p->csr) = DMA_RESET;

	/*
	 * this is an example of how to use ddi_getprop().  We'll pretend
	 * our device may know more about it's burst capability than we
	 * do.  In a real application, we would probably want to store
	 * a limit structure for each instance, rather than changing our
	 * global limit structure.
	 */
D 3
	dma_lim.dlim_burstsizes = ddi_getprop(DDI_DEV_T_ANY, dip, NULL,
E 3
I 3
	dma_lim.dlim_burstsizes = ddi_getprop(DDI_DEV_T_ANY, dip, 0,
E 3
	    "burst-sizes", dma_lim.dlim_burstsizes);

	ddi_report_dev(dip);

	return (DDI_SUCCESS);

/*
 * should probably change this to just call dma_detach
 */
bailout:
	if (dma_p->attach_flags & SOFT_STATE_ALLOCATED)
		ddi_soft_state_free(state_head, instance);
	if (dma_p->attach_flags & INTERRUPT_ADDED)
		ddi_remove_intr(dma_p->dip, 0, dma_p->iblock_cookie);
	if (dma_p->attach_flags & MUTEX_ADDED)
		mutex_destroy(&dma_p->mutex);
	if (dma_p->attach_flags & CSR_MAPPED)
		ddi_unmap_regs(dip, 0, (caddr_t *)&dma_p->csr, 0,
		    sizeof (Dma_csr));
	if (dma_p->attach_flags & ADDR_MAPPED)
		ddi_unmap_regs(dip, 0, (caddr_t *)&dma_p->addr, 0,
		    sizeof (Dma_addr));
	if (dma_p->attach_flags & COUNT_MAPPED)
		ddi_unmap_regs(dip, 0, (caddr_t *)&dma_p->count, 0,
		    sizeof (Dma_count));
	if (dma_p->attach_flags & MINOR_NODE_CREATED)
		ddi_remove_minor_node(dip, NULL);

	return (DDI_FAILURE);
}

/*
 * When our driver is unloaded, dma_detach cleans up and frees the resources
 * we allocated in dma_attach.
 */
static int
dma_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
	register Dma	*dma_p;	/* will point to this */
	int		instance;

	if (cmd != DDI_DETACH)
		return (DDI_FAILURE);

	instance = ddi_get_instance(dip);
	dma_p = (Dma *)ddi_get_soft_state(state_head, instance);

	/*
	 * turn off the device (including interrupts)
	 */
	*(dma_p->csr) = DMA_RESET;

	/*
	 * Remove the minor node created in attach
	 */
	ddi_remove_minor_node(dip, NULL);


	/*
	 * deallocate resources from attach
	 */
	ddi_unmap_regs(dip, 0, (caddr_t *)&dma_p->csr, 0, sizeof (Dma_csr));
	ddi_unmap_regs(dip, 0, (caddr_t *)&dma_p->addr, 0, sizeof (Dma_addr));
	ddi_unmap_regs(dip, 0, (caddr_t *)&dma_p->count, 0, sizeof (Dma_count));
	ddi_remove_intr(dma_p->dip, 0, dma_p->iblock_cookie);
	mutex_destroy(&dma_p->mutex);
	ddi_soft_state_free(state_head, instance);

	return (DDI_SUCCESS);
}

/*
 * dma_open is called in response to the open(2) system call
 */
/*ARGSUSED*/
static	int
dma_open(dev_t *dev, int openflags, int otyp, cred_t *credp)
{
	int		retval = 0;
	register Dma	*dma_p;

	dma_p = (Dma *)ddi_get_soft_state(state_head, getminor(*dev));

	/*
	 * Verify instance structure
	 */
	if (dma_p == NULL)
		return (ENXIO);

	/*
	 * Verify we are being opened as a character device
	 */
	if (otyp != OTYP_CHR)
		return (EINVAL);

	/*
	 * lock control status register and exclusive open flag
	 */
	mutex_enter(&dma_p->mutex);			/* start MUTEX */

	/*
	 * Only allow a single open
	 */
	if (dma_p->flags & DMA_DEV_OPEN) {
		retval = EBUSY;
	} else {
		/*
		 * mark device as open
		 */
		dma_p->flags |= DMA_DEV_OPEN;

		/*
		 * start device except for interrupts
		 */
		*(dma_p->csr) |= DMA_DEV_ENABLE;
	}

	mutex_exit(&dma_p->mutex);			/* end MUTEX */

	return (retval);
}

/*
 * dma_close is called after the last process that has the device open
 * calls close(2)
 *
 * since we enforce exclusive open, dma_close will always be called when
 * the user process calls close(2)
 */
/*ARGSUSED*/
static	int
dma_close(dev_t dev, int openflags, int otyp, cred_t *credp)
{
	register Dma	*dma_p;

	dma_p = (Dma *)ddi_get_soft_state(state_head, getminor(dev));

	/*
	 * lock control status register and exclusive open flag
	 */
	mutex_enter(&dma_p->mutex);			/* start MUTEX */

	/*
	 * let the next person open the device
	 */
	dma_p->flags &= ~DMA_DEV_OPEN;

	/*
	 * shut down the device
	 */
	*(dma_p->csr) = DMA_RESET;

	mutex_exit(&dma_p->mutex);			/* end MUTEX */

	return (0);
}

/*ARGSUSED*/
static	int
dma_read(dev_t dev, struct uio *uiop, cred_t *credp)
{
	return (physio(dma_strategy, (struct buf *)NULL, dev, B_READ,
		dma_minphys, uiop));
}

/*ARGSUSED*/
static	int
dma_write(dev_t dev, struct uio *uiop, cred_t *credp)
{
	return (physio(dma_strategy, (struct buf *)NULL, dev, B_WRITE,
		dma_minphys, uiop));
}

/*ARGSUSED*/
static	int
dma_strategy(struct buf *bp)
{
	Dma			*dma_p;
I 4
#ifndef	_DDICT
E 4
	volatile Dma_csr	dummy;
I 4
#endif
E 4
	ddi_dma_cookie_t	dma_cookie;
	int			flags;

	dma_p = (Dma *)ddi_get_soft_state(state_head, getminor(bp->b_edev));

	/*
	 * lock control/status, address, & count registers
	 */
	mutex_enter(&dma_p->mutex);			/* start MUTEX */

	/*
	 * wait for other I/O, if any, to complete; then set the busy flag
	 */
	while (dma_p->flags & DMA_DEV_BUSY) {
		cv_wait(&dma_p->cv, &dma_p->mutex);
	}
	dma_p->flags |= DMA_DEV_BUSY;

	/*
	 * drop MUTEX now because ddi_dma_buf_setup may go to sleep and
	 * we don't want to sleep while holding the mutex
	 */
	mutex_exit(&dma_p->mutex);			/* end MUTEX */

	/*
	 * alocate DMA resources
	 */
	flags = DDI_DMA_READ;
	if (bp->b_flags & B_WRITE) flags = DDI_DMA_WRITE;
	if (ddi_dma_buf_setup(dma_p->dip, bp, flags, DDI_DMA_SLEEP, NULL,
		&dma_lim, dma_p->handle) != DDI_DMA_MAPPED) {
		bp->b_error |= EIO;
		bp->b_flags |= B_ERROR;
		return (0);
	}

	/*
	 * get a virtual address to hand to our device
	 */
D 3
	if (ddi_dma_htoc(dma_p->handle, NULL, &dma_cookie) != DDI_SUCCESS) {
E 3
I 3
	if (ddi_dma_htoc(dma_p->handle, (off_t)0, &dma_cookie) != DDI_SUCCESS) {
E 3
		bp->b_error |= EIO;
		bp->b_flags |= B_ERROR;
		return (0);
	}

	/*
	 * set up device and go!
	 *
	 * Note: we don't need to acquire the MUTEX here for two reasons:
	 * 1) we are the only top half routine that touches this stuff, and
	 * the mutex & conditional variable stuff surrounding DMA_DEV_BUSY
	 * art the start of this routine single threads us, therefore 2) only
	 * the interrupt routine can tough this stuff and interrupts are not
	 * yet enabled.
	 */
	dma_p->bp = bp;
	*dma_p->addr = dma_cookie.dmac_address;
	*dma_p->count = dma_cookie.dmac_size;
	*dma_p->csr |= DMA_INTR_ENABLE | DMA_DMA_ENABLE;
	/*
	 * some hardware has a "store" buffer, separate from the cache, that
	 * may delay writes; reading the csr ensures that previous writes
	 * have completed.
	 */
I 4
#ifndef _DDICT
E 4
	dummy = *dma_p->csr;
I 3
D 4
#ifdef _DDICT
	dummy = dummy;
E 4
#endif
E 3

	/*
	 * Note: we don't have to wait for the transfer to finish; physio
	 * will wait for us.  It might be worth putting in a timeout in
	 * case our device goes to out lunch.
	 */

	return (0);
}

static void
dma_minphys(struct buf *bp)
{
	/*
	 * We'll be good citizens and limit ourselves to the system
	 * minphys.  Drivers for some devices may have to use a
	 * larger minphys.
	 */
	minphys(bp);
	bp->b_bcount = (min(bp->b_bcount, DMA_MINPHYS));
}

/*
 * This is a sample ioctl routine to show the use of copyin/copyout.
 *
 * Note to 4.x developers: in previous releases, up to 128 bytes of data
 * were copied for you into the area pointed to by "arg".  This is no
 * longer true.  You must always use copyin/copyout; you can no longer
 * simply dereference "arg".
 */
/*ARGSUSED*/
static	int
dma_ioctl(dev_t dev, int cmd, int arg, int flag, cred_t *credp, int *rvalp)
{
	register Dma	*dma_p;
	u_short		retval = 0;

	dma_p = (Dma *)ddi_get_soft_state(state_head, getminor(dev));

	switch (cmd) {
	    case DMA_GET_CSR:
		mutex_enter(&dma_p->mutex);
		if (copyout((caddr_t)(dma_p->csr), (caddr_t)arg,
		    sizeof (Dma_csr)) == -1) {
			retval = EIO;
		}
		mutex_exit(&dma_p->mutex);
		break;
	    default:
		retval = ENOTTY;
		break;
	}

	return (retval);
}

/*
 * Whenever the SBus sees an interrupt at the level we specified with
 * add_intr in dma_attach, we get a chance to determine if it is our
 * interrupt or somebody else's.
 *
 * This routine will get called for each instance of each driver that uses
 * that level interrupt until somebody returns DDI_INTR_CLAIMED.
 *
 * Note to 4.x developers: in 4.x your xxpoll() routine had to check each
 * of your instances (units) to determine if any were interrupting.  This
 * is no longer necessary as the system now calls your xxintr() routine
 * for each attached instance of your device.
 */
static u_int
dma_intr(register Dma *dma_p)
{
/*
	register Dma			*dma_p;
*/
	register volatile Dma_csr	*csr;
I 4
#ifndef	_DDICT
E 4
	volatile Dma_csr		dummy;
I 4
#endif
E 4
	u_int   			int_serviced = DDI_INTR_UNCLAIMED;

/*
	dma_p = (Dma *) private;
*/
	csr = dma_p->csr;

	/*
	 * lock the control status register
	 */
	mutex_enter(&dma_p->mutex);			/* start MUTEX */

	if (*csr & DMA_DONE_INTR) {
		/*
		 * we are interrupting
		 */
		int_serviced = DDI_INTR_CLAIMED;

		/*
		 * clear the interrupt
		 */
		*csr &= ~DMA_INTR_ENABLE;
		*csr |= DMA_INTR_CLEAR;
I 4
#ifndef	_DDICT
E 4
		dummy = *csr;		/* see the comment in dma_strategy */
I 4
#endif
E 4

		/*
		 * check for errors
		 */
		if (*csr & DMA_ERROR) {
			*csr &= ~DMA_ERROR;
I 4
#ifndef	_DDICT
E 4
			dummy = *csr;	/* see the comment in dma_strategy */
I 4
#endif
E 4
			dma_p->bp->b_flags = EIO;
			dma_p->bp->b_flags |= B_ERROR;
		}

		/*
		 * release DMA resources
		 */
D 3
		ddi_dma_free(dma_p->handle);
E 3
I 3
		(void) ddi_dma_free(dma_p->handle);
E 3

		/*
		 * "wake up" the top half
		*/
		dma_p->flags &= ~DMA_DEV_BUSY;
		cv_signal(&dma_p->cv);
	}
I 3
D 4

#ifdef _DDICT
	dummy = dummy;
#endif
E 4
E 3

	mutex_exit(&dma_p->mutex);			/* end MUTEX */

	biodone(dma_p->bp);

	return (int_serviced);
}
E 1
