h44993
s 00015/00016/00781
d D 1.3 94/06/22 14:10:54 hjs 4 3
c cstyle -p clean
e
s 00047/00030/00750
d D 1.2 94/04/04 14:04:27 hjs 3 1
c DDICT-clean
e
s 00000/00000/00000
d R 1.2 94/03/16 14:41:21 Codemgr 2 1
c SunPro Code Manager data about conflicts, renames, etc...
c Name history : 1 0 src/driver_dev/pio/pio.c
e
s 00780/00000/00000
d D 1.1 94/03/16 14:41:20 hjs 1 0
c date and time created 94/03/16 14:41:20 by hjs
e
u
U
f e 0
t
T
I 1
/*
 * Sample programmed I/O (pio) driver for Solaris
 */
D 4
 
E 4
I 4

E 4
/*
 * This file is a product of Sun Microsystems, Inc. and is provided for
 * unrestricted use provided that this legend is included on all tape
 * media and as a part of the software program in whole or part.  Users
 * may copy or modify this file without charge, but are not authorized to
 * license or distribute it to anyone else except as part of a product
 * or program developed by the user.
D 4
 * 
E 4
I 4
 *
E 4
 * THIS FILE IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
 * WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
D 4
 * 
E 4
I 4
 *
E 4
 * This file is provided with no support and without any obligation on the
 * part of Sun Microsystems, Inc. to assist in its use, correction,
 * modification or enhancement.
D 4
 * 
E 4
I 4
 *
E 4
 * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
 * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY THIS FILE
 * OR ANY PART THEREOF.
D 4
 * 
E 4
I 4
 *
E 4
 * In no event will Sun Microsystems, Inc. be liable for any lost revenue
 * or profits or other special, indirect and consequential damages, even
 * if Sun has been advised of the possibility of such damages.
D 4
 * 
E 4
I 4
 *
E 4
 * Sun Microsystems, Inc.
 * 2550 Garcia Avenue
 * Mountain View, California  94043
 */
#pragma ident	"%Z%%M%	%I%	%E% Sun Microsystems Inc."

/*
 * This driver is for a fictional SBus device that accepts one character
 * at a time and generates an interrupt when it is ready for another.
 *
 * This driver is not intended to be efficient or bulletproof; it is
 * intended to show how to write a simple DDI/DKI-compliant driver.
 *
 * For the i386 architecture:
 *
 * This device implements an 8-bit CSR register and an 8-bit data register;
D 4
 * they are addressed at the address in the "ioaddr" property for the 
E 4
I 4
 * they are addressed at the address in the "ioaddr" property for the
E 4
 * device, and are assumed to be contiguous.
 *
 * We demonstrate:
 *
 *	-- many of the new ddi_xx() routines
 *	-- MUTEXes (mutual exclusion locks to allow multi-threading)
 *	-- new style interrupt handling
 *	-- communication between top & bottom half of the 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>
#include <sys/open.h>
#include <sys/stat.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>

#include "pio_io.h"
#include "pio_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	pio_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg,
		    void **result);
static	int	pio_identify(dev_info_t *dip);

#ifdef i386
static	int	pio_probe(dev_info_t *dip);
#endif

static	int	pio_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
static	int	pio_open(dev_t *dev, int openflags, int otyp, cred_t *credp);
static	int	pio_close(dev_t dev, int openflags, int otyp, cred_t *credp);
static	int	pio_write(dev_t dev, struct uio *uiop, cred_t *credp);
static	int	pio_ioctl(dev_t dev, int cmd, int arg, int flag, cred_t *credp,
		    int *rvalp);
static	u_int	pio_intr();
static	int	pio_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);

/*
 * 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	pio_cb_ops = {
	pio_open,
	pio_close,
	nodev,			/* not a block driver	*/
	nodev,			/* no print routine	*/
	nodev,			/* no dump routine	*/
	nodev,			/* no read routine; write-only device */
	pio_write,
	pio_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	*/
	D_NEW | D_MP,		/* safe for multi-thread/multi-processor */
};

static struct dev_ops pio_ops = {
	DEVO_REV,		/* DEVO_REV indicated by manual	*/
	0,			/* device reference count	*/
	pio_getinfo,
	pio_identify,

#ifdef i386
	pio_probe,
#else
	nulldev,		/* device probe for non-self-id */
#endif

	pio_attach,
	pio_detach,
	nodev,			/* device reset routine		*/
	&pio_cb_ops,
	(struct bus_ops *)0,	/* bus operations		*/
};

extern	struct	mod_ops mod_driverops;
static	struct modldrv modldrv = {
	&mod_driverops,
	"SUNW sample PIO (programmed I/O) driver 92/10/06",
	&pio_ops,
};

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

/*
 * For the sparc architecture:
 *
 * When our driver is loaded, pio_identify() is called with a dev_info_t
 * full of information from the FCode on our board.
 *
D 4
 *For the i386 architecture:
E 4
I 4
 * For the i386 architecture:
E 4
 * When our driver is loaded, pio_identify() is called with a dev_info_t
 * full of information from the driver.conf(4) file
 *
 * 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: "pio",
 * driver source: pio.c, driver module: /kernel/drv/pio).  You can get
 * around this by editing /etc/driver_aliases or by using the "-i" option
 * for add_drv(1).  (e.g. name: "SUNW,pio", driver module: /kernel/drv/pio.)
 */
static int
pio_identify(dev_info_t *dip)
{
	char *dev_name;
	dev_name = ddi_get_name(dip);

	if (strcmp(dev_name, PIO_NAME) == 0) {
		return (DDI_IDENTIFIED);
	} else {
		return (DDI_NOT_IDENTIFIED);
	}
}
#ifdef i386
I 3
/*ARGSUSED*/
E 3
D 4
static int 
E 4
I 4
static int
E 4
pio_probe(dev_info_t *dip)
{
D 4
	/* 
 	 * We have no way to check if the pio device actually exists,
E 4
I 4
	/*
	 * We have no way to check if the pio device actually exists,
E 4
	 * so return DONTCARE, which will cause the system to proceed
	 * as if the device *does* exist.
	 */
	return (DDI_PROBE_DONTCARE);
}
#endif

/*
 * On sparc:
 *
 * pio_attach gets called if pio_identify succeeded .
 *
 * On i386:
 *
 * pio_attach gets called if pio_identify and pio_probe succeed.
 *
 * 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 or from our driver.conf(4) file.
 */
static int
pio_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
	int			instance;
	register Pio		*pio_p;
D 3

#ifdef i386
	int			result;
#else
E 3
I 3
#ifdef	sparc
E 3
	u_int			result;
#endif

	int			error;

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

	instance = ddi_get_instance(dip);

	if (ddi_soft_state_zalloc(state_head, instance) != 0)
		return (DDI_FAILURE);

	pio_p = (Pio *)ddi_get_soft_state(state_head, instance);
	ddi_set_driver_private(dip, (caddr_t)pio_p);
	pio_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
	 * 	pio_p->iblock_cookie
	 *
	 * We make no attempt to handle hi-level interrupts.
	 */
	if (ddi_intr_hilevel(dip, 0) ||
	    ddi_add_intr(dip, 0, &pio_p->iblock_cookie,
D 4
	                 (ddi_idevice_cookie_t *) NULL, pio_intr,
	                 (caddr_t) instance) != DDI_SUCCESS) {
E 4
I 4
	    (ddi_idevice_cookie_t *)NULL, pio_intr,
	    (caddr_t)instance) != DDI_SUCCESS) {
E 4
		ddi_soft_state_free(state_head, instance);
		return (DDI_FAILURE);
	}

	/*
	 * initialize MUTEX; we will use this MUTEX later on to lock
	 * our instance structure
	 */
	mutex_init(&pio_p->mutex, "pio mutex",
		MUTEX_DRIVER, (void *)pio_p->iblock_cookie);

	/*
	 * we have one CSR register and one data register
	 *
	 * check to make sure nreg==2 and the registers are the correct sizes
	 */
#ifdef i386
	error = ddi_getprop(DDI_DEV_T_ANY, dip, 0, "ioaddr", -1);
D 3
	if (error = -1) {
E 3
I 3
	if (error == -1) {
E 3
		mutex_destroy(&pio_p->mutex);
		ddi_remove_intr(pio_p->dip, 0, pio_p->iblock_cookie);
		ddi_soft_state_free(state_head, instance);
		return (DDI_FAILURE);
	}

D 3
	pio_p->csr = error;
	pio_p->data = error;
E 3
I 3
	pio_p->csr = (Pio_csr *)error;
	pio_p->data = (u_char *)error;
E 3
#endif /* i386 */
#ifdef sparc
	error = ddi_dev_nregs(dip, (int *)&result);
	if (error == DDI_FAILURE || result != 2) {
		mutex_destroy(&pio_p->mutex);
		ddi_remove_intr(pio_p->dip, 0, pio_p->iblock_cookie);
		ddi_soft_state_free(state_head, instance);
		return (DDI_FAILURE);
	}

D 3
	ddi_dev_regsize(dip, 0, (off_t *)&result);
E 3
I 3
	if (ddi_dev_regsize(dip, 0, (off_t *)&result) != DDI_SUCCESS) {
		return (DDI_FAILURE);
	}

E 3
	if (result != sizeof (Pio_csr)) {
		mutex_destroy(&pio_p->mutex);
		ddi_remove_intr(pio_p->dip, 0, pio_p->iblock_cookie);
		ddi_soft_state_free(state_head, instance);
		return (DDI_FAILURE);
	}

D 3
	ddi_dev_regsize(dip, 1, (off_t *)&result);
E 3
I 3
	if (ddi_dev_regsize(dip, 1, (off_t *)&result) != DDI_SUCCESS) {
		return (DDI_FAILURE);
	}
D 4
	
E 4
I 4

E 4
E 3
	if (result != sizeof (u_char)) {
		mutex_destroy(&pio_p->mutex);
		ddi_remove_intr(pio_p->dip, 0, pio_p->iblock_cookie);
		ddi_soft_state_free(state_head, instance);
		return (DDI_FAILURE);
	}

	/*
	 * the registers look OK, so let's map them in so we can use them
	 */
	if (ddi_map_regs(dip, 0, (caddr_t *)&(pio_p->csr), 0,
	    sizeof (Pio_csr)) != DDI_SUCCESS) {
		mutex_destroy(&pio_p->mutex);
		ddi_remove_intr(pio_p->dip, 0, pio_p->iblock_cookie);
		ddi_soft_state_free(state_head, instance);
		return (DDI_FAILURE);
	}

	if (ddi_map_regs(dip, 1, (caddr_t *)&(pio_p->data), 0,
	    sizeof (u_char)) != DDI_SUCCESS) {
		mutex_destroy(&pio_p->mutex);
		ddi_remove_intr(pio_p->dip, 0, pio_p->iblock_cookie);
		ddi_soft_state_free(state_head, instance);
		ddi_unmap_regs(dip, 0, (caddr_t *)&(pio_p->csr), 0,
		    sizeof (Pio_csr));
		return (DDI_FAILURE);
	}
#endif /* sparc */

	/*
	 * 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
		mutex_destroy(&pio_p->mutex);
		ddi_remove_intr(pio_p->dip, 0, pio_p->iblock_cookie);
		ddi_soft_state_free(state_head, instance);
#ifdef sparc
		ddi_unmap_regs(dip, 0, (caddr_t *)&(pio_p->csr), 0,
		    sizeof (Pio_csr));
		ddi_unmap_regs(dip, 1, (caddr_t *)&(pio_p->data), 0,
		    sizeof (u_char));
#endif
		return (DDI_FAILURE);
	}

	/*
	 * reset device (including disabling interrupts)
	 */
#ifdef i386
D 3
	outb(pio_p->csr, PIO_RESET);
E 3
I 3
	outb((int)pio_p->csr, PIO_RESET);
E 3
#else
	*(pio_p->csr) = PIO_RESET;
#endif

	ddi_report_dev(dip);

	return (DDI_SUCCESS);
}

/*
 * This is a pretty generic getinfo routine as describe in the manual.
 */
/*ARGSUSED*/
static int
pio_getinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
{
	register int 	error;
	register Pio	*pio_p;

	switch (infocmd) {
	case DDI_INFO_DEVT2DEVINFO:
		pio_p = (Pio *)ddi_get_soft_state(state_head,
			    getminor((dev_t)arg));
		if (pio_p == NULL) {
			*result = NULL;
			error = DDI_FAILURE;
		} else {
			mutex_enter(&pio_p->mutex);
			*result = pio_p->dip;
			mutex_exit(&pio_p->mutex);
			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);
}

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

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

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

	return (error);
}

D 3
static int
E 3
I 3
int
E 3
_info(struct modinfo *modinfop)
{
	return (mod_info(&modlinkage, modinfop));
}

D 3
static int
E 3
I 3
int
E 3
_fini(void)
{
	int status;

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

	ddi_soft_state_fini(&state_head);

	return (status);
}

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

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

	instance = ddi_get_instance(dip);
	pio_p = (Pio *)ddi_get_soft_state(state_head, instance);

	/*
	 * turn off the device (including interrupts)
	 */

#ifdef i386
D 3
	outb(pio_p->csr, PIO_RESET);
E 3
I 3
	outb((int)pio_p->csr, PIO_RESET);
E 3
#else
	*(pio_p->csr) = PIO_RESET;
#endif

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

#ifdef sparc
	ddi_unmap_regs(dip, 0, (caddr_t *)&pio_p->csr, 0, sizeof (Pio_csr));
	ddi_unmap_regs(dip, 1, (caddr_t *)&pio_p->data, 0, sizeof (u_char));
#endif
	ddi_remove_intr(pio_p->dip, 0, pio_p->iblock_cookie);
	mutex_destroy(&pio_p->mutex);
	ddi_soft_state_free(state_head, instance);

	return (DDI_SUCCESS);
}

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

	pio_p = (Pio *)ddi_get_soft_state(state_head, getminor(*dev));

	/*
	 * Verify instance structure
	 */
	if (pio_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(&pio_p->mutex);			/* start MUTEX */

	/*
	 * Only allow a single open
	 */
	if (pio_p->open != PIO_OPEN) {
		retval = EBUSY;
	} else {
		/*
		 * mark device as open
		 */
		pio_p->open |= PIO_OPEN;

		/*
		 * start device except for interrupts
		 */
#ifdef i386
D 3
		outb(pio_p->csr, PIO_ENABLE);
E 3
I 3
		outb((int)pio_p->csr, PIO_ENABLE);
E 3
#else /* sparc */
		*(pio_p->csr) = PIO_ENABLE;
#endif
	}

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

	return (retval);
}

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

	pio_p = (Pio *)ddi_get_soft_state(state_head, getminor(dev));

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

	/*
	 * let the next person open the device
	 */
	pio_p->open &= ~PIO_OPEN;

	/*
	 * shut down the device
	 */
#ifdef i386
D 3
	outb(pio_p->csr, PIO_RESET);
E 3
I 3
	outb((int)pio_p->csr, PIO_RESET);
E 3
#else /* sparc */
	*(pio_p->csr) = PIO_RESET;
#endif

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

	return (0);
}

/*ARGSUSED*/
static	int
pio_write(dev_t dev, struct uio *uiop, cred_t *credp)
{
	register volatile  Pio_csr	*csr;
#ifdef i386
D 3
	u_short				data;
E 3
I 3
	u_char				*data;
E 3
#else
	register volatile u_char	*data;
#endif
	register int			retval;
	int				error = OK;
	register Pio			*pio_p;

	pio_p = (Pio *)ddi_get_soft_state(state_head, getminor(dev));
	csr = pio_p->csr;
	data = pio_p->data;

	/*
	 * lock control status register and data register
	 */
	mutex_enter(&pio_p->mutex);			/* start MUTEX */

	/*
	 * enable interrupts
	 */
#ifdef i386
D 3
	outb(csr, (inb(csr) | PIO_INTR_ENABLE));
E 3
I 3
	outb((int)csr, (inb((int)csr) | PIO_INTR_ENABLE));
E 3
#else /* sparc */
	*csr |= PIO_INTR_ENABLE;
#endif

	while (uiop->uio_resid > 0) {
		/*
		 * this device issues an IDLE interrupt when it is ready
		 * to accept a character; the interrupt can be cleared
		 * by setting PIO_INTR_CLEAR.  The interrupt is reasserted
		 * after the next character is written or the next time
		 * PIO_INTR_ENABLE is toggled on.
		 *
		 * we wait for an interrupt between each character; in
		 * real life very few devices would behave this way, but
		 * this is a simple way to illustrate interrupt handling
		 */

		/*
		 * wait for interrupt (see pio_intr)
		 *
		 * Note that we currently hold the MUTEX.  cv_wait will
		 * temporarily drop the MUTEX so that pio_intr can
		 * acquire it; pio_intr drops the MUTEX after calling
		 * cv_signal, and cv_wait re-acquires it for us before
		 * it returns.
		 *
		 * Note to 4.x developers: this use of cv_wait/cv_signal
		 * within a MUTEX is similar to the use of sleep/wakeup
		 * within an spl.  There are, however, important differences.
		 * MUTEXes lock data structures; they do not block processor
		 * interrupts.  Also, the interrupt routine can block waiting
		 * for a MUTEX.
		 */
		cv_wait(&pio_p->cv, &pio_p->mutex);

		/*
		 * get a character from the user's write request
		 */
		retval = uwritec(uiop);
		if (retval == -1) {
			/*
			 * error: bail out
			 */
			error = retval;
			break;
		} else {
			/*
			 * send the character to the device
			 */
#ifdef i386
D 3
			outb(data, (u_char)retval);
E 3
I 3
			outb((int)data, (u_char)retval);
E 3
#else /* sparc */
			*data = (u_char)retval;
#endif
		}
	}

	/*
	 * disable interrupts
	 */
#ifdef i386
D 3
	outb(csr, (inb(csr) & ~PIO_INTR_ENABLE));
E 3
I 3
	outb((int)csr, (inb((int)csr) & ~PIO_INTR_ENABLE));
E 3
#else
	*csr &= ~PIO_INTR_ENABLE;
#endif

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

	return (error);
}

/*
 * 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
pio_ioctl(dev_t dev, int cmd, int arg, int flag, cred_t *credp, int *rvalp)
{
	register Pio	*pio_p;
	u_short		retval = 0;
#ifdef i386
	u_char 		tmp;
#endif

	pio_p = (Pio *)ddi_get_soft_state(state_head, getminor(dev));

	switch (cmd) {
	    case PIO_GET_CSR:
		mutex_enter(&pio_p->mutex);
#ifdef i386
D 3
		tmp = inb(pio_p->csr);
		copyout((caddr_t)&tmp, (caddr_t)arg, sizeof (Pio_csr));
else /*sparc */
		copyout((caddr_t)(pio_p->csr), (caddr_t)arg, sizeof (Pio_csr));
E 3
I 3
		tmp = inb((int)pio_p->csr);
		if (copyout((caddr_t)&tmp, (caddr_t)arg,
		    sizeof (Pio_csr)) != 0) {
			retval = EFAULT;
		}
D 4
#else /*sparc */
E 4
I 4
#else /* sparc */
E 4
		if (copyout((caddr_t)(pio_p->csr), (caddr_t)arg,
		    sizeof (Pio_csr)) != 0) {
			retval = EFAULT;
		}
E 3
#endif
		mutex_exit(&pio_p->mutex);
		break;
	    case PIO_SET_CSR:
		mutex_enter(&pio_p->mutex);
#ifdef i386
D 3
		copyin((caddr_t)arg, (caddr_t)&tmp, sizeof (Pio_csr));
		outb(pio_p->csr, tmp);
E 3
I 3
		if (copyin((caddr_t)arg, (caddr_t)&tmp,
		    sizeof (Pio_csr)) != 0) {
			retval = EFAULT;
		} else {
			outb((int)pio_p->csr, tmp);
		}
E 3
#else /* sparc */
D 3
		copyin((caddr_t)arg, (caddr_t)(pio_p->csr), sizeof (Pio_csr));
E 3
I 3
		if (copyin((caddr_t)arg, (caddr_t)(pio_p->csr),
		    sizeof (Pio_csr)) != 0) {
			retval = EFAULT;
		}
E 3
#endif
		mutex_exit(&pio_p->mutex);
		break;
	    default:
		retval = ENOTTY;
		break;
	}

	return (retval);
}

/*
 * Whenever the system sees an interrupt at the level we specified with
 * add_intr in pio_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
pio_intr(caddr_t instance)
{
	register Pio			*pio_p;
	register volatile Pio_csr	*csr;
	u_int   			int_serviced = DDI_INTR_UNCLAIMED;

	pio_p = (Pio *)ddi_get_soft_state(state_head, (u_int)instance);

	csr = pio_p->csr;

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

#ifdef i386
D 3
	if (inb(csr) & PIO_IDLE_INTR) {
E 3
I 3
	if (inb((int)csr) & PIO_IDLE_INTR) {
E 3
#else /* sparc */
	if (*csr & PIO_IDLE_INTR) {
#endif
		/*
		 * we are interrupting
		 */
		int_serviced = DDI_INTR_CLAIMED;

		/*
		 * clear the interrupt
		 */
#ifdef i386
D 3
		outb(csr, (inb(csr) | PIO_INTR_CLEAR));
E 3
I 3
		outb((int)csr, (inb((int)csr) | PIO_INTR_CLEAR));
E 3
#else /* sparc */
		*csr |= PIO_INTR_CLEAR;
#endif

		/*
		 * "wake up" the top half
		*/
		cv_signal(&pio_p->cv);
	}

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

	return (int_serviced);
}
D 4

E 4
E 1
