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 int 115 _init(void) 116 { 117 int error; 118 119 if ((error = ddi_soft_state_init(&sbusmem_state_head, 120 sizeof (struct sbusmem_unit), 1)) != 0) { 121 return (error); 122 } 123 if ((error = mod_install(&modlinkage)) != 0) { 124 ddi_soft_state_fini(&sbusmem_state_head); 125 } 126 return (error); 127 } 128 129 int 130 _fini(void) 131 { 132 int error; 133 134 if ((error = mod_remove(&modlinkage)) == 0) { 135 ddi_soft_state_fini(&sbusmem_state_head); 136 } 137 return (error); 138 } 139 140 int 141 _info(struct modinfo *modinfop) 142 { 143 return (mod_info(&modlinkage, modinfop)); 144 } 145 146 static int 147 sbmem_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) 148 { 149 struct sbusmem_unit *un; 150 int error = DDI_FAILURE; 151 int instance, ilen; 152 uint_t size; 153 char *ident; 154 155 switch (cmd) { 156 case DDI_ATTACH: 157 instance = ddi_get_instance(devi); 158 159 size = ddi_getprop(DDI_DEV_T_NONE, devi, 160 DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP, "size", -1); 161 if (size == (uint_t)-1) { 162 #ifdef SBUSMEM_DEBUG 163 sbusmem_debug( 164 "sbmem_attach%d: No size property\n", instance); 165 #endif /* SBUSMEM_DEBUG */ 166 break; 167 } 168 169 #ifdef SBUSMEM_DEBUG 170 { 171 struct regspec *rp = ddi_rnumber_to_regspec(devi, 0); 172 173 if (rp == NULL) { 174 sbusmem_debug( 175 "sbmem_attach%d: No reg property\n", instance); 176 } else { 177 sbusmem_debug( 178 "sbmem_attach%d: slot 0x%x size 0x%x\n", instance, 179 rp->regspec_bustype, rp->regspec_size); 180 } 181 } 182 #endif /* SBUSMEM_DEBUG */ 183 184 if (ddi_getlongprop(DDI_DEV_T_ANY, devi, 185 DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP, "ident", 186 (caddr_t)&ident, &ilen) != DDI_PROP_SUCCESS) { 187 #ifdef SBUSMEM_DEBUG 188 sbusmem_debug( 189 "sbmem_attach%d: No ident property\n", instance); 190 #endif /* SBUSMEM_DEBUG */ 191 break; 192 } 193 194 if (ddi_soft_state_zalloc(sbusmem_state_head, 195 instance) != DDI_SUCCESS) 196 break; 197 198 if ((un = ddi_get_soft_state(sbusmem_state_head, 199 instance)) == NULL) { 200 ddi_soft_state_free(sbusmem_state_head, instance); 201 break; 202 } 203 204 if (ddi_create_minor_node(devi, ident, S_IFCHR, instance, 205 DDI_PSEUDO, 0) == DDI_FAILURE) { 206 kmem_free(ident, ilen); 207 ddi_remove_minor_node(devi, NULL); 208 ddi_soft_state_free(sbusmem_state_head, instance); 209 break; 210 } 211 kmem_free(ident, ilen); 212 un->dip = devi; 213 un->size = size; 214 un->pagesize = ddi_ptob(devi, 1); 215 216 #ifdef SBUSMEM_DEBUG 217 sbusmem_debug("sbmem_attach%d: dip 0x%p size 0x%x\n", 218 instance, devi, size); 219 #endif /* SBUSMEM_DEBUG */ 220 221 ddi_report_dev(devi); 222 error = DDI_SUCCESS; 223 break; 224 case DDI_RESUME: 225 error = DDI_SUCCESS; 226 break; 227 default: 228 break; 229 } 230 return (error); 231 } 232 233 static int 234 sbmem_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) 235 { 236 int instance; 237 238 switch (cmd) { 239 case DDI_DETACH: 240 instance = ddi_get_instance(devi); 241 ddi_remove_minor_node(devi, NULL); 242 ddi_soft_state_free(sbusmem_state_head, instance); 243 return (DDI_SUCCESS); 244 245 case DDI_SUSPEND: 246 return (DDI_SUCCESS); 247 } 248 return (DDI_FAILURE); 249 } 250 251 /*ARGSUSED1*/ 252 static int 253 sbmem_open(dev_t *devp, int flag, int typ, cred_t *cred) 254 { 255 int instance; 256 257 if (typ != OTYP_CHR) 258 return (EINVAL); 259 260 instance = getminor(*devp); 261 if (ddi_get_soft_state(sbusmem_state_head, instance) == NULL) { 262 return (ENXIO); 263 } 264 return (0); 265 } 266 267 /*ARGSUSED*/ 268 static int 269 sbmem_close(dev_t dev, int flag, int otyp, struct cred *cred) 270 { 271 if (otyp != OTYP_CHR) 272 return (EINVAL); 273 274 return (0); 275 } 276 277 static int 278 sbmem_info(dev_info_t *dip __unused, ddi_info_cmd_t infocmd, void *arg, 279 void **result) 280 { 281 int instance, error = DDI_FAILURE; 282 struct sbusmem_unit *un; 283 284 switch (infocmd) { 285 case DDI_INFO_DEVT2DEVINFO: 286 instance = getminor((dev_t)arg); 287 if ((un = ddi_get_soft_state(sbusmem_state_head, 288 instance)) != NULL) { 289 *result = (void *)un->dip; 290 error = DDI_SUCCESS; 291 #ifdef SBUSMEM_DEBUG 292 sbusmem_debug( 293 "sbmem_info%d: returning dip 0x%p\n", instance, un->dip); 294 #endif /* SBUSMEM_DEBUG */ 295 296 } 297 break; 298 299 case DDI_INFO_DEVT2INSTANCE: 300 instance = getminor((dev_t)arg); 301 *result = (void *)(uintptr_t)instance; 302 error = DDI_SUCCESS; 303 break; 304 305 default: 306 break; 307 } 308 return (error); 309 } 310 311 static int 312 sbmem_read(dev_t dev, struct uio *uio, cred_t *cred) 313 { 314 return (sbmem_rw(dev, uio, UIO_READ, cred)); 315 } 316 317 static int 318 sbmem_write(dev_t dev, struct uio *uio, cred_t *cred) 319 { 320 return (sbmem_rw(dev, uio, UIO_WRITE, cred)); 321 } 322 323 static int 324 sbmem_rw(dev_t dev, struct uio *uio, enum uio_rw rw, cred_t *cred __unused) 325 { 326 uint_t c; 327 struct iovec *iov; 328 struct sbusmem_unit *un; 329 uint_t pagesize, msize; 330 int instance, error = 0; 331 dev_info_t *dip; 332 caddr_t reg; 333 334 instance = getminor(dev); 335 if ((un = ddi_get_soft_state(sbusmem_state_head, instance)) == NULL) { 336 return (ENXIO); 337 } 338 dip = un->dip; 339 pagesize = un->pagesize; 340 341 while (uio->uio_resid > 0 && error == 0) { 342 iov = uio->uio_iov; 343 if (iov->iov_len == 0) { 344 uio->uio_iov++; 345 uio->uio_iovcnt--; 346 if (uio->uio_iovcnt < 0) 347 cmn_err(CE_PANIC, "sbmem_rw"); 348 continue; 349 } 350 351 if (uio->uio_offset > un->size) { 352 return (EFAULT); 353 } 354 355 if (uio->uio_offset == un->size) { 356 return (0); /* EOF */ 357 } 358 msize = pagesize - (uio->uio_offset & (pagesize - 1)); 359 if (ddi_map_regs(dip, 0, ®, uio->uio_offset, 360 (off_t)msize) != DDI_SUCCESS) { 361 return (EFAULT); 362 } 363 c = min(msize, (uint_t)iov->iov_len); 364 if (ddi_peekpokeio(dip, uio, rw, reg, (int)c, 365 sizeof (int)) != DDI_SUCCESS) 366 error = EFAULT; 367 368 ddi_unmap_regs(dip, 0, ®, uio->uio_offset, (off_t)msize); 369 } 370 return (error); 371 } 372 373 static int 374 sbmem_devmap(dev_t dev, devmap_cookie_t dhp, offset_t off, size_t len, 375 size_t *maplen, uint_t model __unused) 376 { 377 struct sbusmem_unit *un; 378 int instance, error; 379 380 instance = getminor(dev); 381 if ((un = ddi_get_soft_state(sbusmem_state_head, instance)) == NULL) { 382 return (ENXIO); 383 } 384 if (off + len > un->size) { 385 return (ENXIO); 386 } 387 if ((error = devmap_devmem_setup(dhp, un->dip, NULL, 0, 388 off, len, PROT_ALL, DEVMAP_DEFAULTS, NULL)) < 0) { 389 return (error); 390 } 391 *maplen = ptob(btopr(len)); 392 return (0); 393 } 394