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 /* 28 * The tphci driver can be used to exercise the mpxio framework together 29 * with tvhci/tclient. 30 */ 31 32 #include <sys/conf.h> 33 #include <sys/file.h> 34 #include <sys/open.h> 35 #include <sys/stat.h> 36 #include <sys/modctl.h> 37 #include <sys/ddi.h> 38 #include <sys/sunddi.h> 39 #include <sys/sunndi.h> 40 #include <sys/sunmdi.h> 41 #include <sys/disp.h> 42 43 /* cb_ops entry points */ 44 static int tphci_open(dev_t *, int, int, cred_t *); 45 static int tphci_close(dev_t, int, int, cred_t *); 46 static int tphci_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 47 static int tphci_attach(dev_info_t *, ddi_attach_cmd_t); 48 static int tphci_detach(dev_info_t *, ddi_detach_cmd_t); 49 static int tphci_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); 50 51 /* bus_ops entry points */ 52 static int tphci_ctl(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *, 53 void *); 54 static int tphci_initchild(dev_info_t *, dev_info_t *); 55 static int tphci_uninitchild(dev_info_t *, dev_info_t *); 56 static int tphci_bus_config(dev_info_t *, uint_t, ddi_bus_config_op_t, void *, 57 dev_info_t **); 58 static int tphci_bus_unconfig(dev_info_t *, uint_t, ddi_bus_config_op_t, 59 void *); 60 static int tphci_intr_op(dev_info_t *dip, dev_info_t *rdip, 61 ddi_intr_op_t op, ddi_intr_handle_impl_t *hdlp, void *result); 62 63 64 static void *tphci_state; 65 struct tphci_state { 66 dev_info_t *dip; 67 }; 68 69 static struct cb_ops tphci_cb_ops = { 70 tphci_open, /* open */ 71 tphci_close, /* close */ 72 nodev, /* strategy */ 73 nodev, /* print */ 74 nodev, /* dump */ 75 nodev, /* read */ 76 nodev, /* write */ 77 tphci_ioctl, /* ioctl */ 78 nodev, /* devmap */ 79 nodev, /* mmap */ 80 nodev, /* segmap */ 81 nochpoll, /* chpoll */ 82 ddi_prop_op, /* cb_prop_op */ 83 0, /* streamtab */ 84 D_NEW | D_MP, /* cb_flag */ 85 CB_REV, /* rev */ 86 nodev, /* aread */ 87 nodev /* awrite */ 88 }; 89 90 static struct bus_ops tphci_bus_ops = { 91 BUSO_REV, /* busops_rev */ 92 nullbusmap, /* bus_map */ 93 NULL, /* bus_get_intrspec */ 94 NULL, /* bus_add_interspec */ 95 NULL, /* bus_remove_interspec */ 96 i_ddi_map_fault, /* bus_map_fault */ 97 ddi_no_dma_map, /* bus_dma_map */ 98 ddi_no_dma_allochdl, /* bus_dma_allochdl */ 99 NULL, /* bus_dma_freehdl */ 100 NULL, /* bus_dma_bindhdl */ 101 NULL, /* bus_dma_unbindhdl */ 102 NULL, /* bus_dma_flush */ 103 NULL, /* bus_dma_win */ 104 NULL, /* bus_dma_ctl */ 105 tphci_ctl, /* bus_ctl */ 106 ddi_bus_prop_op, /* bus_prop_op */ 107 NULL, /* bus_get_eventcookie */ 108 NULL, /* bus_add_eventcall */ 109 NULL, /* bus_remove_event */ 110 NULL, /* bus_post_event */ 111 NULL, /* bus_intr_ctl */ 112 tphci_bus_config, /* bus_config */ 113 tphci_bus_unconfig, /* bus_unconfig */ 114 NULL, /* bus_fm_init */ 115 NULL, /* bus_fm_fini */ 116 NULL, /* bus_fm_access_enter */ 117 NULL, /* bus_fm_access_exit */ 118 NULL, /* bus_power */ 119 tphci_intr_op /* bus_intr_op */ 120 }; 121 122 static struct dev_ops tphci_ops = { 123 DEVO_REV, 124 0, 125 tphci_getinfo, 126 nulldev, /* identify */ 127 nulldev, /* probe */ 128 tphci_attach, /* attach and detach are mandatory */ 129 tphci_detach, 130 nodev, /* reset */ 131 &tphci_cb_ops, /* cb_ops */ 132 &tphci_bus_ops, /* bus_ops */ 133 NULL, /* power */ 134 ddi_quiesce_not_needed, /* quiesce */ 135 }; 136 137 extern struct mod_ops mod_driverops; 138 139 static struct modldrv modldrv = { 140 &mod_driverops, 141 "test phci driver", 142 &tphci_ops 143 }; 144 145 static struct modlinkage modlinkage = { 146 MODREV_1, 147 &modldrv, 148 NULL 149 }; 150 151 int 152 _init(void) 153 { 154 int rval; 155 156 if ((rval = ddi_soft_state_init(&tphci_state, 157 sizeof (struct tphci_state), 2)) != 0) { 158 return (rval); 159 } 160 161 if ((rval = mod_install(&modlinkage)) != 0) { 162 ddi_soft_state_fini(&tphci_state); 163 } 164 return (rval); 165 } 166 167 168 int 169 _fini(void) 170 { 171 int rval; 172 173 /* 174 * don't start cleaning up until we know that the module remove 175 * has worked -- if this works, then we know that each instance 176 * has successfully been detached 177 */ 178 if ((rval = mod_remove(&modlinkage)) != 0) { 179 return (rval); 180 } 181 182 ddi_soft_state_fini(&tphci_state); 183 184 return (rval); 185 } 186 187 int 188 _info(struct modinfo *modinfop) 189 { 190 return (mod_info(&modlinkage, modinfop)); 191 } 192 193 /* ARGSUSED */ 194 static int 195 tphci_open(dev_t *devp, int flag, int otype, cred_t *credp) 196 { 197 struct tphci_state *phci; 198 199 if (otype != OTYP_CHR) { 200 return (EINVAL); 201 } 202 203 phci = ddi_get_soft_state(tphci_state, getminor(*devp)); 204 if (phci == NULL) { 205 return (ENXIO); 206 } 207 208 return (0); 209 } 210 211 212 /* ARGSUSED */ 213 static int 214 tphci_close(dev_t dev, int flag, int otype, cred_t *credp) 215 { 216 struct tphci_state *phci; 217 if (otype != OTYP_CHR) { 218 return (EINVAL); 219 } 220 221 phci = ddi_get_soft_state(tphci_state, getminor(dev)); 222 if (phci == NULL) { 223 return (ENXIO); 224 } 225 226 return (0); 227 } 228 229 /* ARGSUSED */ 230 static int 231 tphci_ioctl(dev_t dev, int cmd, intptr_t data, int mode, 232 cred_t *credp, int *rval) 233 { 234 return (0); 235 } 236 237 /* 238 * attach the module 239 */ 240 static int 241 tphci_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 242 { 243 char *vclass; 244 int instance, phci_regis = 0; 245 struct tphci_state *phci = NULL; 246 247 instance = ddi_get_instance(dip); 248 249 switch (cmd) { 250 case DDI_ATTACH: 251 break; 252 253 case DDI_RESUME: 254 case DDI_PM_RESUME: 255 return (0); /* nothing to do */ 256 257 default: 258 return (DDI_FAILURE); 259 } 260 261 /* 262 * Allocate phci data structure. 263 */ 264 if (ddi_soft_state_zalloc(tphci_state, instance) != DDI_SUCCESS) { 265 return (DDI_FAILURE); 266 } 267 268 phci = ddi_get_soft_state(tphci_state, instance); 269 ASSERT(phci != NULL); 270 phci->dip = dip; 271 272 /* bus_addr has the form #,<vhci_class> */ 273 vclass = strchr(ddi_get_name_addr(dip), ','); 274 if (vclass == NULL || vclass[1] == '\0') { 275 cmn_err(CE_NOTE, "tphci invalid bus_addr %s", 276 ddi_get_name_addr(dip)); 277 goto attach_fail; 278 } 279 280 /* 281 * Attach this instance with the mpxio framework 282 */ 283 if (mdi_phci_register(vclass + 1, dip, 0) != MDI_SUCCESS) { 284 cmn_err(CE_WARN, "%s mdi_phci_register failed", 285 ddi_node_name(dip)); 286 goto attach_fail; 287 } 288 phci_regis++; 289 290 if (ddi_create_minor_node(dip, "devctl", S_IFCHR, 291 instance, DDI_NT_SCSI_NEXUS, 0) != DDI_SUCCESS) { 292 cmn_err(CE_NOTE, "%s ddi_create_minor_node failed", 293 ddi_node_name(dip)); 294 goto attach_fail; 295 } 296 297 (void) ddi_prop_update_int(DDI_DEV_T_NONE, dip, DDI_NO_AUTODETACH, 1); 298 ddi_report_dev(dip); 299 return (DDI_SUCCESS); 300 301 attach_fail: 302 if (phci_regis) 303 (void) mdi_phci_unregister(dip, 0); 304 305 ddi_soft_state_free(tphci_state, instance); 306 return (DDI_FAILURE); 307 } 308 309 310 /*ARGSUSED*/ 311 static int 312 tphci_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 313 { 314 int instance = ddi_get_instance(dip); 315 316 switch (cmd) { 317 case DDI_DETACH: 318 break; 319 320 case DDI_SUSPEND: 321 case DDI_PM_SUSPEND: 322 return (0); /* nothing to do */ 323 324 default: 325 return (DDI_FAILURE); 326 } 327 328 if (mdi_phci_unregister(dip, 0) != MDI_SUCCESS) 329 return (DDI_FAILURE); 330 331 ddi_remove_minor_node(dip, NULL); 332 ddi_soft_state_free(tphci_state, instance); 333 334 return (DDI_SUCCESS); 335 } 336 337 /* 338 * tphci_getinfo() 339 * Given the device number, return the devinfo pointer or the 340 * instance number. 341 * Note: always succeed DDI_INFO_DEVT2INSTANCE, even before attach. 342 */ 343 344 /*ARGSUSED*/ 345 static int 346 tphci_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **result) 347 { 348 struct tphci_state *phci; 349 int instance = getminor((dev_t)arg); 350 351 switch (cmd) { 352 case DDI_INFO_DEVT2DEVINFO: 353 phci = ddi_get_soft_state(tphci_state, instance); 354 if (phci != NULL) 355 *result = phci->dip; 356 else { 357 *result = NULL; 358 return (DDI_FAILURE); 359 } 360 break; 361 362 case DDI_INFO_DEVT2INSTANCE: 363 *result = (void *)(uintptr_t)instance; 364 break; 365 366 default: 367 return (DDI_FAILURE); 368 } 369 370 return (DDI_SUCCESS); 371 } 372 373 /* 374 * Interrupt stuff. NO OP for pseudo drivers. 375 */ 376 /*ARGSUSED*/ 377 static int 378 tphci_intr_op(dev_info_t *dip, dev_info_t *rdip, ddi_intr_op_t op, 379 ddi_intr_handle_impl_t *hdlp, void *result) 380 { 381 return (DDI_FAILURE); 382 } 383 384 static int 385 tphci_ctl(dev_info_t *dip, dev_info_t *rdip, 386 ddi_ctl_enum_t ctlop, void *arg, void *result) 387 { 388 switch (ctlop) { 389 case DDI_CTLOPS_REPORTDEV: 390 if (rdip == (dev_info_t *)0) 391 return (DDI_FAILURE); 392 cmn_err(CE_CONT, "?tphci-device: %s%d\n", 393 ddi_get_name(rdip), ddi_get_instance(rdip)); 394 return (DDI_SUCCESS); 395 396 case DDI_CTLOPS_INITCHILD: 397 { 398 dev_info_t *child = (dev_info_t *)arg; 399 return (tphci_initchild(dip, child)); 400 } 401 402 case DDI_CTLOPS_UNINITCHILD: 403 { 404 dev_info_t *child = (dev_info_t *)arg; 405 return (tphci_uninitchild(dip, child)); 406 } 407 408 case DDI_CTLOPS_DMAPMAPC: 409 case DDI_CTLOPS_REPORTINT: 410 case DDI_CTLOPS_REGSIZE: 411 case DDI_CTLOPS_NREGS: 412 case DDI_CTLOPS_SIDDEV: 413 case DDI_CTLOPS_SLAVEONLY: 414 case DDI_CTLOPS_AFFINITY: 415 case DDI_CTLOPS_POKE: 416 case DDI_CTLOPS_PEEK: 417 /* 418 * These ops correspond to functions that "shouldn't" be called 419 * by a pseudo driver. So we whine when we're called. 420 */ 421 cmn_err(CE_CONT, "%s%d: invalid op (%d) from %s%d\n", 422 ddi_get_name(dip), ddi_get_instance(dip), 423 ctlop, ddi_get_name(rdip), ddi_get_instance(rdip)); 424 return (DDI_FAILURE); 425 426 case DDI_CTLOPS_ATTACH: 427 case DDI_CTLOPS_BTOP: 428 case DDI_CTLOPS_BTOPR: 429 case DDI_CTLOPS_DETACH: 430 case DDI_CTLOPS_DVMAPAGESIZE: 431 case DDI_CTLOPS_IOMIN: 432 case DDI_CTLOPS_POWER: 433 case DDI_CTLOPS_PTOB: 434 default: 435 /* 436 * The ops that we pass up (default). We pass up memory 437 * allocation oriented ops that we receive - these may be 438 * associated with pseudo HBA drivers below us with target 439 * drivers below them that use ddi memory allocation 440 * interfaces like scsi_alloc_consistent_buf. 441 */ 442 return (ddi_ctlops(dip, rdip, ctlop, arg, result)); 443 } 444 } 445 446 static int 447 tphci_initchild(dev_info_t *dip, dev_info_t *child) 448 { 449 _NOTE(ARGUNUSED(dip)) 450 ddi_set_name_addr(child, "0"); 451 return (DDI_SUCCESS); 452 } 453 454 /*ARGSUSED*/ 455 static int 456 tphci_uninitchild(dev_info_t *dip, dev_info_t *child) 457 { 458 ddi_set_name_addr(child, NULL); 459 return (DDI_SUCCESS); 460 } 461 462 static int 463 tp_decode_name(char *devnm, char **cname, char **paddr, char **guid) 464 { 465 char *tmp; 466 467 i_ddi_parse_name(devnm, cname, paddr, NULL); 468 if ((strcmp(*cname, "tclient") != 0) && 469 (strcmp(*cname, "tphci") != 0) || *paddr == NULL) 470 return (-1); 471 472 tmp = strchr(*paddr, ','); 473 if (tmp == NULL || tmp[1] == '\0') 474 return (-1); 475 476 *guid = tmp + 1; 477 return (0); 478 } 479 480 static int 481 tphci_bus_config(dev_info_t *parent, uint_t flags, 482 ddi_bus_config_op_t op, void *arg, dev_info_t **childp) 483 { 484 _NOTE(ARGUNUSED(flags)) 485 char *cname, *paddr, *guid, *devnm; 486 mdi_pathinfo_t *pip; 487 int len, circ, rval; 488 489 switch (op) { 490 case BUS_CONFIG_ONE: 491 break; 492 case BUS_CONFIG_DRIVER: /* no direct children to configure */ 493 case BUS_CONFIG_ALL: 494 return (NDI_SUCCESS); 495 default: 496 return (NDI_FAILURE); 497 } 498 499 /* only implement BUS_CONFIG_ONE */ 500 devnm = i_ddi_strdup((char *)arg, KM_SLEEP); 501 len = strlen(devnm) + 1; 502 503 /* caddr is hardcoded in the form *,<guid> */ 504 if (tp_decode_name(devnm, &cname, &paddr, &guid) != 0) { 505 cmn_err(CE_NOTE, "tphci_bus_config -- invalid device %s", 506 (char *)arg); 507 kmem_free(devnm, len); 508 return (NDI_FAILURE); 509 } 510 511 mdi_devi_enter(parent, &circ); 512 rval = mdi_pi_alloc(parent, cname, guid, paddr, 0, &pip); 513 kmem_free(devnm, len); 514 if (rval != MDI_SUCCESS) { 515 cmn_err(CE_NOTE, "tphci_bus_config -- mdi_pi_alloc failed"); 516 mdi_devi_exit(parent, circ); 517 return (NDI_FAILURE); 518 } 519 520 /* 521 * Hold the path and exit the pHCI while calling mdi_pi_online 522 * to avoid deadlock with power management of pHCI. 523 */ 524 mdi_hold_path(pip); 525 mdi_devi_exit_phci(parent, circ); 526 rval = mdi_pi_online(pip, 0); 527 mdi_devi_enter_phci(parent, &circ); 528 mdi_rele_path(pip); 529 530 if (rval != MDI_SUCCESS) { 531 cmn_err(CE_NOTE, "tphci_bus_config -- mdi_pi_online failed"); 532 (void) mdi_pi_free(pip, 0); 533 mdi_devi_exit(parent, circ); 534 return (NDI_FAILURE); 535 } 536 537 if (childp) { 538 *childp = mdi_pi_get_client(pip); 539 ndi_hold_devi(*childp); 540 } 541 mdi_devi_exit(parent, circ); 542 543 return (NDI_SUCCESS); 544 } 545 546 static int 547 tphci_bus_unconfig(dev_info_t *parent, uint_t flags, 548 ddi_bus_config_op_t op, void *arg) 549 { 550 int rval = MDI_SUCCESS; 551 int circ; 552 mdi_pathinfo_t *pip, *next; 553 char *devnm, *cname, *caddr; 554 555 switch (op) { 556 case BUS_UNCONFIG_ONE: 557 devnm = (char *)arg; 558 i_ddi_parse_name(devnm, &cname, &caddr, NULL); 559 if (strcmp(cname, "tclient") != 0) 560 return (NDI_SUCCESS); /* no such device */ 561 562 mdi_devi_enter(parent, &circ); 563 pip = mdi_pi_find(parent, NULL, caddr); 564 if (pip) { 565 mdi_hold_path(pip); 566 mdi_devi_exit_phci(parent, circ); 567 rval = mdi_pi_offline(pip, NDI_DEVI_REMOVE); 568 mdi_devi_enter_phci(parent, &circ); 569 mdi_rele_path(pip); 570 571 if (rval == MDI_SUCCESS) 572 (void) mdi_pi_free(pip, 0); 573 } 574 mdi_devi_exit(parent, circ); 575 return (rval == MDI_SUCCESS ? NDI_SUCCESS : NDI_FAILURE); 576 577 case BUS_UNCONFIG_ALL: 578 if (flags & NDI_AUTODETACH) 579 return (NDI_FAILURE); 580 581 mdi_devi_enter(parent, &circ); 582 next = mdi_get_next_client_path(parent, NULL); 583 while ((pip = next) != NULL) { 584 next = mdi_get_next_client_path(parent, pip); 585 586 mdi_hold_path(pip); 587 mdi_devi_exit_phci(parent, circ); 588 rval = mdi_pi_offline(pip, NDI_DEVI_REMOVE); 589 mdi_devi_enter_phci(parent, &circ); 590 mdi_rele_path(pip); 591 592 if (rval != MDI_SUCCESS) 593 break; 594 (void) mdi_pi_free(pip, 0); 595 } 596 mdi_devi_exit(parent, circ); 597 return (rval == MDI_SUCCESS ? NDI_SUCCESS : NDI_FAILURE); 598 599 case BUS_UNCONFIG_DRIVER: /* nothing to do */ 600 return (NDI_SUCCESS); 601 602 default: 603 return (NDI_FAILURE); 604 } 605 /*NOTREACHED*/ 606 } 607