OpenSolaris_b135/uts/sun/io/sbusmem.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */


#include <sys/types.h>
#include <sys/conf.h>
#include <sys/cmn_err.h>
#include <sys/kmem.h>
#include <sys/open.h>
#include <sys/stat.h>
#include <sys/modctl.h>
#include <sys/errno.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>

/* #define SBUSMEM_DEBUG */

#ifdef SBUSMEM_DEBUG
#include <sys/ddi_impldefs.h>

int sbusmem_debug_flag;
#define	sbusmem_debug	if (sbusmem_debug_flag) printf
#endif /* SBUSMEM_DEBUG */

static void *sbusmem_state_head;

struct sbusmem_unit {
	uint_t size;
	uint_t pagesize;
	dev_info_t *dip;
};

static int sbmem_open(dev_t *, int, int, cred_t *);
static int sbmem_close(dev_t, int, int, struct cred *);
static int sbmem_read(dev_t, struct uio *, cred_t *);
static int sbmem_write(dev_t, struct uio *, cred_t *);
static int sbmem_devmap(dev_t, devmap_cookie_t, offset_t, size_t,
		size_t *, uint_t);

static struct cb_ops sbmem_cb_ops = {
	sbmem_open,		/* open */
	sbmem_close,		/* close */
	nodev,			/* strategy */
	nodev,			/* print */
	nodev,			/* dump */
	sbmem_read,		/* read */
	sbmem_write,		/* write */
	nodev,			/* ioctl */
	sbmem_devmap,		/* devmap */
	nodev,			/* mmap */
	ddi_devmap_segmap,	/* segmap */
	nochpoll,		/* poll */
	ddi_prop_op,		/* cb_prop_op */
	0,			/* streamtab  */
	D_NEW|D_MP|D_DEVMAP|D_HOTPLUG,	/* Driver compatibility flag */
	CB_REV,			/* rev */
	nodev,			/* int (*cb_aread)() */
	nodev			/* int (*cb_awrite)() */
};

static int sbmem_attach(dev_info_t *, ddi_attach_cmd_t);
static int sbmem_detach(dev_info_t *, ddi_detach_cmd_t);
static int sbmem_info(dev_info_t *, ddi_info_cmd_t, void *, void **);

static struct dev_ops sbmem_ops = {
	DEVO_REV,		/* devo_rev, */
	0,			/* refcnt  */
	sbmem_info,		/* get_dev_info */
	nulldev,		/* identify */
	nulldev,		/* probe */
	sbmem_attach,		/* attach */
	sbmem_detach,		/* detach */
	nodev,			/* reset */
	&sbmem_cb_ops,		/* driver operations */
	(struct bus_ops *)0,	/* bus operations */
	nulldev,		/* power */
	ddi_quiesce_not_needed,		/* quiesce */
};

static struct modldrv modldrv = {
	&mod_driverops,	/* Type of module.  This one is a driver */
	"SBus memory driver", /* Name of module. */
	&sbmem_ops,	/* driver ops */
};

static struct modlinkage modlinkage = {
	MODREV_1, (void *)&modldrv, NULL
};

static int sbmem_rw(dev_t, struct uio *, enum uio_rw, cred_t *);

#if !defined(lint)
static char sbusmem_initmsg[] = "sbusmem _init: sbusmem.c\t1.28\t08/19/2008\n";
#endif

int
_init(void)
{
	int error;

	if ((error = ddi_soft_state_init(&sbusmem_state_head,
	    sizeof (struct sbusmem_unit), 1)) != 0) {
		return (error);
	}
	if ((error = mod_install(&modlinkage)) != 0) {
		ddi_soft_state_fini(&sbusmem_state_head);
	}
	return (error);
}

int
_fini(void)
{
	int error;

	if ((error = mod_remove(&modlinkage)) == 0) {
		ddi_soft_state_fini(&sbusmem_state_head);
	}
	return (error);
}

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

static int
sbmem_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
{
	struct sbusmem_unit *un;
	int error = DDI_FAILURE;
	int instance, ilen;
	uint_t size;
	char *ident;

	switch (cmd) {
	case DDI_ATTACH:
		instance = ddi_get_instance(devi);

		size = ddi_getprop(DDI_DEV_T_NONE, devi,
		    DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP, "size", -1);
		if (size == (uint_t)-1) {
#ifdef SBUSMEM_DEBUG
			sbusmem_debug(
			    "sbmem_attach%d: No size property\n", instance);
#endif /* SBUSMEM_DEBUG */
			break;
		}

#ifdef SBUSMEM_DEBUG
		{
			struct regspec *rp = ddi_rnumber_to_regspec(devi, 0);

			if (rp == NULL) {
				sbusmem_debug(
			    "sbmem_attach%d: No reg property\n", instance);
			} else {
				sbusmem_debug(
			    "sbmem_attach%d: slot 0x%x size 0x%x\n", instance,
				    rp->regspec_bustype, rp->regspec_size);
			}
		}
#endif /* SBUSMEM_DEBUG */

		if (ddi_getlongprop(DDI_DEV_T_ANY, devi,
		    DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP, "ident",
		    (caddr_t)&ident, &ilen) != DDI_PROP_SUCCESS) {
#ifdef SBUSMEM_DEBUG
			sbusmem_debug(
			    "sbmem_attach%d: No ident property\n", instance);
#endif /* SBUSMEM_DEBUG */
			break;
		}

		if (ddi_soft_state_zalloc(sbusmem_state_head,
		    instance) != DDI_SUCCESS)
			break;

		if ((un = ddi_get_soft_state(sbusmem_state_head,
		    instance)) == NULL) {
			ddi_soft_state_free(sbusmem_state_head, instance);
			break;
		}

		if (ddi_create_minor_node(devi, ident, S_IFCHR, instance,
		    DDI_PSEUDO, NULL) == DDI_FAILURE) {
			kmem_free(ident, ilen);
			ddi_remove_minor_node(devi, NULL);
			ddi_soft_state_free(sbusmem_state_head, instance);
			break;
		}
		kmem_free(ident, ilen);
		un->dip = devi;
		un->size = size;
		un->pagesize = ddi_ptob(devi, 1);

#ifdef SBUSMEM_DEBUG
		sbusmem_debug("sbmem_attach%d: dip 0x%p size 0x%x\n",
		    instance, devi, size);
#endif /* SBUSMEM_DEBUG */

		ddi_report_dev(devi);
		error = DDI_SUCCESS;
		break;
	case DDI_RESUME:
		error = DDI_SUCCESS;
		break;
	default:
		break;
	}
	return (error);
}

static int
sbmem_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
{
	int instance;

	switch (cmd) {
	case DDI_DETACH:
		instance = ddi_get_instance(devi);
		ddi_remove_minor_node(devi, NULL);
		ddi_soft_state_free(sbusmem_state_head, instance);
		return (DDI_SUCCESS);

	case DDI_SUSPEND:
		return (DDI_SUCCESS);
	}
	return (DDI_FAILURE);
}

/*ARGSUSED1*/
static int
sbmem_open(dev_t *devp, int flag, int typ, cred_t *cred)
{
	int instance;

	if (typ != OTYP_CHR)
		return (EINVAL);

	instance = getminor(*devp);
	if (ddi_get_soft_state(sbusmem_state_head, instance) == NULL) {
		return (ENXIO);
	}
	return (0);
}

/*ARGSUSED*/
static int
sbmem_close(dev_t dev, int flag, int otyp, struct cred *cred)
{
	if (otyp != OTYP_CHR)
		return (EINVAL);

	return (0);
}

static int
sbmem_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
{
	int instance, error = DDI_FAILURE;
	struct sbusmem_unit *un;

#if defined(lint) || defined(__lint)
	dip = dip;
#endif /* lint || __lint */

	switch (infocmd) {
	case DDI_INFO_DEVT2DEVINFO:
		instance = getminor((dev_t)arg);
		if ((un = ddi_get_soft_state(sbusmem_state_head,
		    instance)) != NULL) {
			*result = (void *)un->dip;
			error = DDI_SUCCESS;
#ifdef SBUSMEM_DEBUG
		sbusmem_debug(
		    "sbmem_info%d: returning dip 0x%p\n", instance, un->dip);
#endif /* SBUSMEM_DEBUG */

		}
		break;

	case DDI_INFO_DEVT2INSTANCE:
		instance = getminor((dev_t)arg);
		*result = (void *)(uintptr_t)instance;
		error = DDI_SUCCESS;
		break;

	default:
		break;
	}
	return (error);
}

static int
sbmem_read(dev_t dev, struct uio *uio, cred_t *cred)
{
	return (sbmem_rw(dev, uio, UIO_READ, cred));
}

static int
sbmem_write(dev_t dev, struct uio *uio, cred_t *cred)
{
	return (sbmem_rw(dev, uio, UIO_WRITE, cred));
}

static int
sbmem_rw(dev_t dev, struct uio *uio, enum uio_rw rw, cred_t *cred)
{
	uint_t c;
	struct iovec *iov;
	struct sbusmem_unit *un;
	uint_t pagesize, msize;
	int instance, error = 0;
	dev_info_t *dip;
	caddr_t reg;

#if defined(lint) || defined(__lint)
	cred = cred;
#endif /* lint || __lint */

	instance = getminor(dev);
	if ((un = ddi_get_soft_state(sbusmem_state_head, instance)) == NULL) {
		return (ENXIO);
	}
	dip = un->dip;
	pagesize = un->pagesize;

	while (uio->uio_resid > 0 && error == 0) {
		iov = uio->uio_iov;
		if (iov->iov_len == 0) {
			uio->uio_iov++;
			uio->uio_iovcnt--;
			if (uio->uio_iovcnt < 0)
				cmn_err(CE_PANIC, "sbmem_rw");
			continue;
		}

		if (uio->uio_offset > un->size) {
			return (EFAULT);
		}

		if (uio->uio_offset == un->size) {
			return (0);		/* EOF */
		}
		msize = pagesize - (uio->uio_offset & (pagesize - 1));
		if (ddi_map_regs(dip, 0, &reg, uio->uio_offset,
		    (off_t)msize) != DDI_SUCCESS) {
			return (EFAULT);
		}
		c = min(msize, (uint_t)iov->iov_len);
		if (ddi_peekpokeio(dip, uio, rw, reg, (int)c,
		    sizeof (int)) != DDI_SUCCESS)
			error = EFAULT;

		ddi_unmap_regs(dip, 0, &reg, uio->uio_offset, (off_t)msize);
	}
	return (error);
}

static int
sbmem_devmap(dev_t dev, devmap_cookie_t dhp, offset_t off, size_t len,
	size_t *maplen, uint_t model)
{
	struct sbusmem_unit *un;
	int instance, error;

#if defined(lint) || defined(__lint)
	model = model;
#endif /* lint || __lint */

	instance = getminor(dev);
	if ((un = ddi_get_soft_state(sbusmem_state_head, instance)) == NULL) {
		return (ENXIO);
	}
	if (off + len > un->size) {
		return (ENXIO);
	}
	if ((error = devmap_devmem_setup(dhp, un->dip, NULL, 0,
	    off, len, PROT_ALL, DEVMAP_DEFAULTS, NULL)) < 0) {
		return (error);
	}
	*maplen = ptob(btopr(len));
	return (0);
}