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, ®, 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, ®, 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