Home | History | Annotate | Download | only in io
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  */
     25 
     26 
     27 #include <sys/types.h>
     28 #include <sys/conf.h>
     29 #include <sys/cmn_err.h>
     30 #include <sys/kmem.h>
     31 #include <sys/open.h>
     32 #include <sys/stat.h>
     33 #include <sys/modctl.h>
     34 #include <sys/errno.h>
     35 #include <sys/ddi.h>
     36 #include <sys/sunddi.h>
     37 
     38 /* #define SBUSMEM_DEBUG */
     39 
     40 #ifdef SBUSMEM_DEBUG
     41 #include <sys/ddi_impldefs.h>
     42 
     43 int sbusmem_debug_flag;
     44 #define	sbusmem_debug	if (sbusmem_debug_flag) printf
     45 #endif /* SBUSMEM_DEBUG */
     46 
     47 static void *sbusmem_state_head;
     48 
     49 struct sbusmem_unit {
     50 	uint_t size;
     51 	uint_t pagesize;
     52 	dev_info_t *dip;
     53 };
     54 
     55 static int sbmem_open(dev_t *, int, int, cred_t *);
     56 static int sbmem_close(dev_t, int, int, struct cred *);
     57 static int sbmem_read(dev_t, struct uio *, cred_t *);
     58 static int sbmem_write(dev_t, struct uio *, cred_t *);
     59 static int sbmem_devmap(dev_t, devmap_cookie_t, offset_t, size_t,
     60 		size_t *, uint_t);
     61 
     62 static struct cb_ops sbmem_cb_ops = {
     63 	sbmem_open,		/* open */
     64 	sbmem_close,		/* close */
     65 	nodev,			/* strategy */
     66 	nodev,			/* print */
     67 	nodev,			/* dump */
     68 	sbmem_read,		/* read */
     69 	sbmem_write,		/* write */
     70 	nodev,			/* ioctl */
     71 	sbmem_devmap,		/* devmap */
     72 	nodev,			/* mmap */
     73 	ddi_devmap_segmap,	/* segmap */
     74 	nochpoll,		/* poll */
     75 	ddi_prop_op,		/* cb_prop_op */
     76 	0,			/* streamtab  */
     77 	D_NEW|D_MP|D_DEVMAP|D_HOTPLUG,	/* Driver compatibility flag */
     78 	CB_REV,			/* rev */
     79 	nodev,			/* int (*cb_aread)() */
     80 	nodev			/* int (*cb_awrite)() */
     81 };
     82 
     83 static int sbmem_attach(dev_info_t *, ddi_attach_cmd_t);
     84 static int sbmem_detach(dev_info_t *, ddi_detach_cmd_t);
     85 static int sbmem_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
     86 
     87 static struct dev_ops sbmem_ops = {
     88 	DEVO_REV,		/* devo_rev, */
     89 	0,			/* refcnt  */
     90 	sbmem_info,		/* get_dev_info */
     91 	nulldev,		/* identify */
     92 	nulldev,		/* probe */
     93 	sbmem_attach,		/* attach */
     94 	sbmem_detach,		/* detach */
     95 	nodev,			/* reset */
     96 	&sbmem_cb_ops,		/* driver operations */
     97 	(struct bus_ops *)0,	/* bus operations */
     98 	nulldev,		/* power */
     99 	ddi_quiesce_not_needed,		/* quiesce */
    100 };
    101 
    102 static struct modldrv modldrv = {
    103 	&mod_driverops,	/* Type of module.  This one is a driver */
    104 	"SBus memory driver", /* Name of module. */
    105 	&sbmem_ops,	/* driver ops */
    106 };
    107 
    108 static struct modlinkage modlinkage = {
    109 	MODREV_1, (void *)&modldrv, NULL
    110 };
    111 
    112 static int sbmem_rw(dev_t, struct uio *, enum uio_rw, cred_t *);
    113 
    114 #if !defined(lint)
    115 static char sbusmem_initmsg[] = "sbusmem _init: sbusmem.c\t1.28\t08/19/2008\n";
    116 #endif
    117 
    118 int
    119 _init(void)
    120 {
    121 	int error;
    122 
    123 	if ((error = ddi_soft_state_init(&sbusmem_state_head,
    124 	    sizeof (struct sbusmem_unit), 1)) != 0) {
    125 		return (error);
    126 	}
    127 	if ((error = mod_install(&modlinkage)) != 0) {
    128 		ddi_soft_state_fini(&sbusmem_state_head);
    129 	}
    130 	return (error);
    131 }
    132 
    133 int
    134 _fini(void)
    135 {
    136 	int error;
    137 
    138 	if ((error = mod_remove(&modlinkage)) == 0) {
    139 		ddi_soft_state_fini(&sbusmem_state_head);
    140 	}
    141 	return (error);
    142 }
    143 
    144 int
    145 _info(struct modinfo *modinfop)
    146 {
    147 	return (mod_info(&modlinkage, modinfop));
    148 }
    149 
    150 static int
    151 sbmem_attach(dev_info_t *devi, ddi_attach_cmd_t cmd)
    152 {
    153 	struct sbusmem_unit *un;
    154 	int error = DDI_FAILURE;
    155 	int instance, ilen;
    156 	uint_t size;
    157 	char *ident;
    158 
    159 	switch (cmd) {
    160 	case DDI_ATTACH:
    161 		instance = ddi_get_instance(devi);
    162 
    163 		size = ddi_getprop(DDI_DEV_T_NONE, devi,
    164 		    DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP, "size", -1);
    165 		if (size == (uint_t)-1) {
    166 #ifdef SBUSMEM_DEBUG
    167 			sbusmem_debug(
    168 			    "sbmem_attach%d: No size property\n", instance);
    169 #endif /* SBUSMEM_DEBUG */
    170 			break;
    171 		}
    172 
    173 #ifdef SBUSMEM_DEBUG
    174 		{
    175 			struct regspec *rp = ddi_rnumber_to_regspec(devi, 0);
    176 
    177 			if (rp == NULL) {
    178 				sbusmem_debug(
    179 			    "sbmem_attach%d: No reg property\n", instance);
    180 			} else {
    181 				sbusmem_debug(
    182 			    "sbmem_attach%d: slot 0x%x size 0x%x\n", instance,
    183 				    rp->regspec_bustype, rp->regspec_size);
    184 			}
    185 		}
    186 #endif /* SBUSMEM_DEBUG */
    187 
    188 		if (ddi_getlongprop(DDI_DEV_T_ANY, devi,
    189 		    DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP, "ident",
    190 		    (caddr_t)&ident, &ilen) != DDI_PROP_SUCCESS) {
    191 #ifdef SBUSMEM_DEBUG
    192 			sbusmem_debug(
    193 			    "sbmem_attach%d: No ident property\n", instance);
    194 #endif /* SBUSMEM_DEBUG */
    195 			break;
    196 		}
    197 
    198 		if (ddi_soft_state_zalloc(sbusmem_state_head,
    199 		    instance) != DDI_SUCCESS)
    200 			break;
    201 
    202 		if ((un = ddi_get_soft_state(sbusmem_state_head,
    203 		    instance)) == NULL) {
    204 			ddi_soft_state_free(sbusmem_state_head, instance);
    205 			break;
    206 		}
    207 
    208 		if (ddi_create_minor_node(devi, ident, S_IFCHR, instance,
    209 		    DDI_PSEUDO, NULL) == DDI_FAILURE) {
    210 			kmem_free(ident, ilen);
    211 			ddi_remove_minor_node(devi, NULL);
    212 			ddi_soft_state_free(sbusmem_state_head, instance);
    213 			break;
    214 		}
    215 		kmem_free(ident, ilen);
    216 		un->dip = devi;
    217 		un->size = size;
    218 		un->pagesize = ddi_ptob(devi, 1);
    219 
    220 #ifdef SBUSMEM_DEBUG
    221 		sbusmem_debug("sbmem_attach%d: dip 0x%p size 0x%x\n",
    222 		    instance, devi, size);
    223 #endif /* SBUSMEM_DEBUG */
    224 
    225 		ddi_report_dev(devi);
    226 		error = DDI_SUCCESS;
    227 		break;
    228 	case DDI_RESUME:
    229 		error = DDI_SUCCESS;
    230 		break;
    231 	default:
    232 		break;
    233 	}
    234 	return (error);
    235 }
    236 
    237 static int
    238 sbmem_detach(dev_info_t *devi, ddi_detach_cmd_t cmd)
    239 {
    240 	int instance;
    241 
    242 	switch (cmd) {
    243 	case DDI_DETACH:
    244 		instance = ddi_get_instance(devi);
    245 		ddi_remove_minor_node(devi, NULL);
    246 		ddi_soft_state_free(sbusmem_state_head, instance);
    247 		return (DDI_SUCCESS);
    248 
    249 	case DDI_SUSPEND:
    250 		return (DDI_SUCCESS);
    251 	}
    252 	return (DDI_FAILURE);
    253 }
    254 
    255 /*ARGSUSED1*/
    256 static int
    257 sbmem_open(dev_t *devp, int flag, int typ, cred_t *cred)
    258 {
    259 	int instance;
    260 
    261 	if (typ != OTYP_CHR)
    262 		return (EINVAL);
    263 
    264 	instance = getminor(*devp);
    265 	if (ddi_get_soft_state(sbusmem_state_head, instance) == NULL) {
    266 		return (ENXIO);
    267 	}
    268 	return (0);
    269 }
    270 
    271 /*ARGSUSED*/
    272 static int
    273 sbmem_close(dev_t dev, int flag, int otyp, struct cred *cred)
    274 {
    275 	if (otyp != OTYP_CHR)
    276 		return (EINVAL);
    277 
    278 	return (0);
    279 }
    280 
    281 static int
    282 sbmem_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
    283 {
    284 	int instance, error = DDI_FAILURE;
    285 	struct sbusmem_unit *un;
    286 
    287 #if defined(lint) || defined(__lint)
    288 	dip = dip;
    289 #endif /* lint || __lint */
    290 
    291 	switch (infocmd) {
    292 	case DDI_INFO_DEVT2DEVINFO:
    293 		instance = getminor((dev_t)arg);
    294 		if ((un = ddi_get_soft_state(sbusmem_state_head,
    295 		    instance)) != NULL) {
    296 			*result = (void *)un->dip;
    297 			error = DDI_SUCCESS;
    298 #ifdef SBUSMEM_DEBUG
    299 		sbusmem_debug(
    300 		    "sbmem_info%d: returning dip 0x%p\n", instance, un->dip);
    301 #endif /* SBUSMEM_DEBUG */
    302 
    303 		}
    304 		break;
    305 
    306 	case DDI_INFO_DEVT2INSTANCE:
    307 		instance = getminor((dev_t)arg);
    308 		*result = (void *)(uintptr_t)instance;
    309 		error = DDI_SUCCESS;
    310 		break;
    311 
    312 	default:
    313 		break;
    314 	}
    315 	return (error);
    316 }
    317 
    318 static int
    319 sbmem_read(dev_t dev, struct uio *uio, cred_t *cred)
    320 {
    321 	return (sbmem_rw(dev, uio, UIO_READ, cred));
    322 }
    323 
    324 static int
    325 sbmem_write(dev_t dev, struct uio *uio, cred_t *cred)
    326 {
    327 	return (sbmem_rw(dev, uio, UIO_WRITE, cred));
    328 }
    329 
    330 static int
    331 sbmem_rw(dev_t dev, struct uio *uio, enum uio_rw rw, cred_t *cred)
    332 {
    333 	uint_t c;
    334 	struct iovec *iov;
    335 	struct sbusmem_unit *un;
    336 	uint_t pagesize, msize;
    337 	int instance, error = 0;
    338 	dev_info_t *dip;
    339 	caddr_t reg;
    340 
    341 #if defined(lint) || defined(__lint)
    342 	cred = cred;
    343 #endif /* lint || __lint */
    344 
    345 	instance = getminor(dev);
    346 	if ((un = ddi_get_soft_state(sbusmem_state_head, instance)) == NULL) {
    347 		return (ENXIO);
    348 	}
    349 	dip = un->dip;
    350 	pagesize = un->pagesize;
    351 
    352 	while (uio->uio_resid > 0 && error == 0) {
    353 		iov = uio->uio_iov;
    354 		if (iov->iov_len == 0) {
    355 			uio->uio_iov++;
    356 			uio->uio_iovcnt--;
    357 			if (uio->uio_iovcnt < 0)
    358 				cmn_err(CE_PANIC, "sbmem_rw");
    359 			continue;
    360 		}
    361 
    362 		if (uio->uio_offset > un->size) {
    363 			return (EFAULT);
    364 		}
    365 
    366 		if (uio->uio_offset == un->size) {
    367 			return (0);		/* EOF */
    368 		}
    369 		msize = pagesize - (uio->uio_offset & (pagesize - 1));
    370 		if (ddi_map_regs(dip, 0, &reg, uio->uio_offset,
    371 		    (off_t)msize) != DDI_SUCCESS) {
    372 			return (EFAULT);
    373 		}
    374 		c = min(msize, (uint_t)iov->iov_len);
    375 		if (ddi_peekpokeio(dip, uio, rw, reg, (int)c,
    376 		    sizeof (int)) != DDI_SUCCESS)
    377 			error = EFAULT;
    378 
    379 		ddi_unmap_regs(dip, 0, &reg, uio->uio_offset, (off_t)msize);
    380 	}
    381 	return (error);
    382 }
    383 
    384 static int
    385 sbmem_devmap(dev_t dev, devmap_cookie_t dhp, offset_t off, size_t len,
    386 	size_t *maplen, uint_t model)
    387 {
    388 	struct sbusmem_unit *un;
    389 	int instance, error;
    390 
    391 #if defined(lint) || defined(__lint)
    392 	model = model;
    393 #endif /* lint || __lint */
    394 
    395 	instance = getminor(dev);
    396 	if ((un = ddi_get_soft_state(sbusmem_state_head, instance)) == NULL) {
    397 		return (ENXIO);
    398 	}
    399 	if (off + len > un->size) {
    400 		return (ENXIO);
    401 	}
    402 	if ((error = devmap_devmem_setup(dhp, un->dip, NULL, 0,
    403 	    off, len, PROT_ALL, DEVMAP_DEFAULTS, NULL)) < 0) {
    404 		return (error);
    405 	}
    406 	*maplen = ptob(btopr(len));
    407 	return (0);
    408 }
    409